@opengeni/db 5.0.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automations.d.ts +0 -2
- package/dist/canonical-human-identities.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-components.d.ts +5 -13
- package/dist/capability-integrations.d.ts +23 -8
- package/dist/capability-reconciliation.d.ts +5 -0
- package/dist/capability-reconciliation.js +21 -0
- package/dist/capability-reconciliation.js.map +1 -0
- package/dist/{chunk-6T2GGYWE.js → chunk-4UJCXBR2.js} +4 -5800
- package/dist/chunk-4UJCXBR2.js.map +1 -0
- package/dist/{chunk-MWMXWHJD.js → chunk-7JCDSMX2.js} +2 -2
- package/dist/chunk-AJA2ZIUW.js +219 -0
- package/dist/chunk-AJA2ZIUW.js.map +1 -0
- package/dist/chunk-CDAT6EKW.js +247 -0
- package/dist/chunk-CDAT6EKW.js.map +1 -0
- package/dist/{chunk-DPONPYJD.js → chunk-CJI6G254.js} +70 -18
- package/dist/chunk-CJI6G254.js.map +1 -0
- package/dist/chunk-G32NVMRJ.js +276 -0
- package/dist/chunk-G32NVMRJ.js.map +1 -0
- package/dist/chunk-GABXCG3N.js +5811 -0
- package/dist/chunk-GABXCG3N.js.map +1 -0
- package/dist/{chunk-ERKVSPDX.js → chunk-LVY33EWL.js} +3 -3
- package/dist/{chunk-45CP2YKY.js → chunk-MAMBI2HR.js} +9 -3
- package/dist/{chunk-45CP2YKY.js.map → chunk-MAMBI2HR.js.map} +1 -1
- package/dist/{chunk-F73UKJY6.js → chunk-MN5PZMEK.js} +91 -108
- package/dist/chunk-MN5PZMEK.js.map +1 -0
- package/dist/chunk-PJI4MUZ7.js +131 -0
- package/dist/chunk-PJI4MUZ7.js.map +1 -0
- package/dist/{chunk-T6BWIGWJ.js → chunk-RIYHP2WU.js} +2 -2
- package/dist/{chunk-7HMK3B45.js → chunk-RL2MUXM3.js} +16 -7
- package/dist/chunk-RL2MUXM3.js.map +1 -0
- package/dist/{chunk-HQDVI2DB.js → chunk-SYKHLPTK.js} +4 -3
- package/dist/{chunk-HQDVI2DB.js.map → chunk-SYKHLPTK.js.map} +1 -1
- package/dist/{chunk-T6YLHJLS.js → chunk-XIXVVBW2.js} +198 -51
- package/dist/chunk-XIXVVBW2.js.map +1 -0
- package/dist/chunk-ZPJ6V2VG.js +156 -0
- package/dist/chunk-ZPJ6V2VG.js.map +1 -0
- package/dist/codex-capacity-recovery.d.ts +12 -0
- package/dist/codex-failure-eligibility.d.ts +9 -0
- package/dist/company-profile-schema.d.ts +4 -2
- package/dist/connect-attempts.d.ts +15 -0
- package/dist/connect-attempts.js +25 -0
- package/dist/connect-attempts.js.map +1 -0
- package/dist/connection-authority.d.ts +7 -63
- package/dist/connection-refresh-lock.d.ts +4 -0
- package/dist/connection-token-resolver.d.ts +8 -43
- package/dist/database-timing.d.ts +10 -0
- package/dist/database.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +9 -0
- package/dist/editable-artifacts.js +7 -5
- package/dist/index.d.ts +447 -78
- package/dist/index.js +12705 -12872
- package/dist/index.js.map +1 -1
- package/dist/insights-usage-bundle.d.ts +3 -0
- package/dist/integration-bindings.d.ts +1 -1
- package/dist/integration-facets.d.ts +18 -1
- package/dist/knowledge-entries-schema.d.ts +2 -0
- package/dist/knowledge-entries.d.ts +1 -0
- package/dist/knowledge-indexing.d.ts +1 -0
- package/dist/live-session-attempt.d.ts +35 -137
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-account-bindings.d.ts +4 -0
- package/dist/mcp-oauth.d.ts +11 -0
- package/dist/mcp-operations.js +2 -2
- package/dist/migrate.js +1 -1
- package/dist/organization-integration-policy.d.ts +51 -0
- package/dist/organization-integration-policy.js +18 -0
- package/dist/organization-integration-policy.js.map +1 -0
- package/dist/organization-usage.d.ts +20 -0
- package/dist/plugin-packages.d.ts +9 -12
- package/dist/pr-review.d.ts +0 -11
- package/dist/preference-registry-schema.d.ts +17 -0
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +124 -6
- package/dist/retained-provider-commands.js +29 -97
- package/dist/retained-provider-commands.js.map +1 -1
- package/dist/retired-session-create-metadata.d.ts +4 -0
- package/dist/runtime-posture.d.ts +7 -5
- package/dist/schema.d.ts +876 -656
- package/dist/schema.js +5 -7
- package/dist/session-admission-block.d.ts +32 -0
- package/dist/session-attempt-writers.d.ts +4 -0
- package/dist/session-background-commands.js +5 -4
- package/dist/session-command-output.js +8 -126
- package/dist/session-command-output.js.map +1 -1
- package/dist/session-control.d.ts +5 -1
- package/dist/session-event-slices.d.ts +10 -0
- package/dist/session-event-slices.js +8 -222
- package/dist/session-event-slices.js.map +1 -1
- package/dist/session-file-attachments.d.ts +28 -0
- package/dist/session-goal-reports.d.ts +43 -0
- package/dist/session-mcp-approval.d.ts +4 -0
- package/dist/session-mcp-credential-rotation.js +5 -4
- package/dist/session-mcp-credential-rotation.js.map +1 -1
- package/dist/session-message-search.d.ts +22 -0
- package/dist/session-queue-commands.d.ts +2 -6
- package/dist/session-retry.d.ts +36 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/skill-source-release-impact.d.ts +18 -0
- package/dist/skill-source-release.d.ts +9 -0
- package/dist/skills.d.ts +7 -1
- package/dist/user-resource-authority.d.ts +0 -8
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +5 -4
- package/dist/workspace-tool-defaults.js.map +1 -1
- package/drizzle/0468_knowledge_relationship_projection.sql +30 -0
- package/drizzle/0469_knowledge_source_discovery.sql +85 -0
- package/drizzle/0470_organization_integration_policy.sql +151 -0
- package/drizzle/0471_usage_events_account_recent_index.sql +4 -0
- package/drizzle/0472_usage_events_workspace_recent_index.sql +4 -0
- package/drizzle/0473_organization_usage_analytical_capability.sql +237 -0
- package/drizzle/0474_goal_report_requirements.sql +156 -0
- package/drizzle/0475_session_event_history_statistics.sql +15 -0
- package/drizzle/0476_session_event_history_policy_planning.sql +29 -0
- package/drizzle/0477_managed_sign_in_methods.sql +303 -0
- package/drizzle/0478_sender_owned_connections.sql +2772 -0
- package/drizzle/0479_connection_create_idempotency.sql +16 -0
- package/drizzle/0480_private_session_organization_key_administration.sql +154 -0
- package/drizzle/0481_personal_connection_visibility_cleanup.sql +45 -0
- package/drizzle/0482_remove_packs.sql +417 -0
- package/drizzle/0483_preclaim_admission_block.sql +85 -0
- package/drizzle/0484_insights_usage_projection.sql +211 -0
- package/drizzle/0485_inherited_mcp_approval_policies.sql +6 -0
- package/drizzle/0486_reasoning_configuration_message_forks.sql +35 -0
- package/drizzle/0487_skill_catalog_message_forks.sql +26 -0
- package/drizzle/0488_permanent_skill_removal.sql +207 -0
- package/drizzle/0489_modal_command_byte_offsets.sql +17 -0
- package/drizzle/0490_periodic_capture_attempt_cadence.sql +6 -0
- package/drizzle/0491_warm_capture_holder_reclamation.sql +57 -0
- package/drizzle/0492_codex_accepted_source_authority.sql +249 -0
- package/drizzle/0493_stopping_command_error_containment.sql +58 -0
- package/drizzle/0494_mcp_account_bindings.sql +334 -0
- package/drizzle/0495_consented_sandbox_recovery.sql +220 -0
- package/drizzle/0496_supervised_command_settlement.sql +386 -0
- package/drizzle/0497_modal_inventory_owner_read_capability.sql +112 -0
- package/drizzle/0498_enrollment_membership_fence.sql +87 -0
- package/drizzle/0499_session_attachment_access.sql +224 -0
- package/drizzle/0500_session_execution_epoch.sql +37 -0
- package/drizzle/0501_session_sharing_execution.sql +591 -0
- package/drizzle/0502_active_message_boundary_forks.sql +30 -0
- package/package.json +17 -5
- package/src/automations.ts +17 -73
- package/src/canonical-human-identities.ts +17 -5
- package/src/capability-components.ts +7 -24
- package/src/capability-integrations.ts +301 -21
- package/src/capability-reconciliation.ts +19 -0
- package/src/codex-capacity-recovery.ts +47 -0
- package/src/codex-failure-eligibility.ts +93 -0
- package/src/connect-attempts.ts +49 -18
- package/src/connection-authority.ts +12 -158
- package/src/connection-refresh-lock.ts +22 -0
- package/src/connection-token-resolver.ts +58 -530
- package/src/database-timing.ts +43 -0
- package/src/database.ts +61 -26
- package/src/editable-artifacts.ts +1 -1
- package/src/index.ts +3578 -1273
- package/src/insights-model-bundle.ts +19 -7
- package/src/insights-usage-bundle.ts +16 -19
- package/src/integration-bindings.ts +2 -7
- package/src/integration-facets.ts +73 -6
- package/src/knowledge-entries.ts +8 -1
- package/src/live-session-attempt.ts +0 -22
- package/src/mcp-account-bindings.ts +12 -0
- package/src/mcp-oauth.ts +26 -0
- package/src/organization-integration-policy.ts +235 -0
- package/src/organization-usage.ts +83 -0
- package/src/plugin-packages.ts +226 -42
- package/src/pr-review.ts +7 -69
- package/src/preference-registry-schema.ts +1 -0
- package/src/provision-roles.ts +50 -37
- package/src/retained-provider-commands.ts +286 -9
- package/src/retired-session-create-metadata.ts +16 -0
- package/src/runtime-posture.ts +217 -13
- package/src/schema.ts +104 -120
- package/src/session-admission-block.ts +29 -0
- package/src/session-attempt-writers.ts +48 -0
- package/src/session-control.ts +21 -5
- package/src/session-event-slices.ts +76 -26
- package/src/session-file-attachments.ts +66 -0
- package/src/session-goal-reports.ts +255 -0
- package/src/session-mcp-approval.ts +30 -0
- package/src/session-message-search.ts +348 -0
- package/src/session-queue-commands.ts +60 -36
- package/src/session-retry.ts +319 -0
- package/src/skill-config-migration.ts +11 -2
- package/src/skill-source-release-impact.ts +33 -0
- package/src/skill-source-release.ts +67 -26
- package/src/skills.ts +44 -6
- package/src/user-resource-authority.ts +0 -56
- package/dist/chunk-6T2GGYWE.js.map +0 -1
- package/dist/chunk-7HMK3B45.js.map +0 -1
- package/dist/chunk-DPONPYJD.js.map +0 -1
- package/dist/chunk-F73UKJY6.js.map +0 -1
- package/dist/chunk-T6YLHJLS.js.map +0 -1
- package/dist/host-mcp-binding-match.d.ts +0 -5
- package/dist/host-mcp-bindings.d.ts +0 -79
- package/dist/host-mcp-resolvers.d.ts +0 -47
- package/dist/host-mcp-task-authority.d.ts +0 -43
- package/dist/host-selection-identity.d.ts +0 -6
- package/dist/pack-components.d.ts +0 -83
- package/dist/pack-installations.d.ts +0 -86
- package/src/host-mcp-binding-match.ts +0 -34
- package/src/host-mcp-bindings.ts +0 -777
- package/src/host-mcp-resolvers.ts +0 -266
- package/src/host-mcp-task-authority.ts +0 -384
- package/src/host-selection-identity.ts +0 -29
- package/src/pack-components.ts +0 -980
- package/src/pack-installations.ts +0 -950
- /package/dist/{chunk-MWMXWHJD.js.map → chunk-7JCDSMX2.js.map} +0 -0
- /package/dist/{chunk-ERKVSPDX.js.map → chunk-LVY33EWL.js.map} +0 -0
- /package/dist/{chunk-T6BWIGWJ.js.map → chunk-RIYHP2WU.js.map} +0 -0
package/src/provision-roles.ts
CHANGED
|
@@ -540,6 +540,7 @@ async function grantAppRoleIfSchemaExists(
|
|
|
540
540
|
"get_organization_administration_overview(uuid,text)",
|
|
541
541
|
"get_workspace_kind(uuid,uuid)",
|
|
542
542
|
"resolve_workspace_codex_subscription_source(uuid,uuid)",
|
|
543
|
+
"capture_legacy_codex_turn_sources(uuid,uuid)",
|
|
543
544
|
"list_organization_workspace_ids(uuid)",
|
|
544
545
|
"list_organization_codex_workspace_ids(uuid)",
|
|
545
546
|
"organization_workspace_command(jsonb)",
|
|
@@ -588,6 +589,10 @@ async function grantAppRoleIfSchemaExists(
|
|
|
588
589
|
.map(literal)
|
|
589
590
|
.join(", ")}]`;
|
|
590
591
|
const managedAuthSessionSetRoutines = `ARRAY[${[
|
|
592
|
+
"assert_managed_sign_in_recovery(text,text,uuid,jsonb)",
|
|
593
|
+
"replay_managed_sign_in_method(text,text,jsonb)",
|
|
594
|
+
"claim_managed_sign_in_notification(uuid,text,text,integer)",
|
|
595
|
+
"settle_managed_sign_in_notification(uuid,uuid,text)",
|
|
591
596
|
"get_canonical_human_exact_login_binding(text,text)",
|
|
592
597
|
"managed_auth_session_set_authority_state(text)",
|
|
593
598
|
"managed_auth_session_set_snapshot(text,text,boolean,boolean,boolean)",
|
|
@@ -626,6 +631,9 @@ BEGIN
|
|
|
626
631
|
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.mcp_operation_command(jsonb,text,jsonb) TO %I', ${literal(schema)}, ${literal(role)});
|
|
627
632
|
END IF;
|
|
628
633
|
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});
|
|
634
|
+
IF to_regprocedure('opengeni_private.codex_credential_serves_turn(uuid,uuid,uuid,uuid)') IS NOT NULL THEN
|
|
635
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION opengeni_private.codex_credential_serves_turn(uuid,uuid,uuid,uuid) TO %I', ${literal(role)});
|
|
636
|
+
END IF;
|
|
629
637
|
EXECUTE format('REVOKE CREATE ON SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});
|
|
630
638
|
EXECUTE format('REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});
|
|
631
639
|
FOREACH runtime_table IN ARRAY ${runtimeFullDmlTables} LOOP
|
|
@@ -1325,6 +1333,9 @@ BEGIN
|
|
|
1325
1333
|
${literal(role)}
|
|
1326
1334
|
);
|
|
1327
1335
|
END IF;
|
|
1336
|
+
IF to_regprocedure(format('%I.mutate_managed_sign_in_method(text,text,jsonb)', ${literal(schema)})) IS NOT NULL THEN
|
|
1337
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.mutate_managed_sign_in_method(text,text,jsonb) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1338
|
+
END IF;
|
|
1328
1339
|
FOREACH routine_signature IN ARRAY ${managedAuthSessionSetRoutines} LOOP
|
|
1329
1340
|
IF to_regprocedure(format('%I.%s', ${literal(schema)}, routine_signature)) IS NOT NULL THEN
|
|
1330
1341
|
EXECUTE format(
|
|
@@ -1716,6 +1727,10 @@ BEGIN
|
|
|
1716
1727
|
${literal(role)}
|
|
1717
1728
|
);
|
|
1718
1729
|
END IF;
|
|
1730
|
+
IF to_regprocedure(format('%I.list_owned_connection_accounts(uuid,uuid)', ${literal(schema)})) IS NOT NULL THEN
|
|
1731
|
+
EXECUTE format('REVOKE ALL ON FUNCTION %I.list_owned_connection_accounts(uuid,uuid) FROM PUBLIC', ${literal(schema)});
|
|
1732
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.list_owned_connection_accounts(uuid,uuid) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1733
|
+
END IF;
|
|
1719
1734
|
IF to_regprocedure(format('%I.list_self_user_resource_authorities(uuid,uuid,text,uuid,integer)', ${literal(schema)}))
|
|
1720
1735
|
IS NOT NULL THEN
|
|
1721
1736
|
EXECUTE format('REVOKE ALL ON FUNCTION %I.list_self_user_resource_authorities(uuid) FROM PUBLIC', ${literal(schema)});
|
|
@@ -1733,29 +1748,9 @@ BEGIN
|
|
|
1733
1748
|
EXECUTE format('REVOKE ALL ON FUNCTION %I.authorize_session_attempt_personal_resource_reads(uuid, uuid, uuid) FROM PUBLIC', ${literal(schema)});
|
|
1734
1749
|
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.authorize_session_attempt_personal_resource_reads(uuid, uuid, uuid) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1735
1750
|
END IF;
|
|
1736
|
-
IF to_regprocedure(format('%I.
|
|
1737
|
-
|
|
1738
|
-
EXECUTE format('
|
|
1739
|
-
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.issue_self_local_connection_use_grant(uuid, uuid, uuid, text, boolean) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1740
|
-
END IF;
|
|
1741
|
-
IF to_regprocedure(format('%I.resolve_connection_use_authority(uuid,uuid,uuid,jsonb)', ${literal(schema)}))
|
|
1742
|
-
IS NOT NULL THEN
|
|
1743
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.list_self_connection_authorities(uuid) FROM PUBLIC', ${literal(schema)});
|
|
1744
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.list_self_connection_authorities(uuid) FROM %I', ${literal(schema)}, ${literal(role)});
|
|
1745
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.issue_self_connection_use_grant(uuid, uuid, uuid, text, text, uuid, boolean) FROM PUBLIC', ${literal(schema)});
|
|
1746
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.issue_self_connection_use_grant(uuid, uuid, uuid, text, text, uuid, boolean) FROM %I', ${literal(schema)}, ${literal(role)});
|
|
1747
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.revoke_self_connection_use_grant(uuid, uuid) FROM PUBLIC', ${literal(schema)});
|
|
1748
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.revoke_self_connection_use_grant(uuid, uuid) FROM %I', ${literal(schema)}, ${literal(role)});
|
|
1749
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.resolve_connection_use_authority(uuid, uuid, uuid, jsonb) FROM PUBLIC', ${literal(schema)});
|
|
1750
|
-
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.resolve_connection_use_authority(uuid, uuid, uuid, jsonb) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1751
|
-
IF to_regprocedure(format('%I.resolve_personal_connection_authority_selection(uuid,uuid,text,uuid,jsonb)', ${literal(schema)})) IS NOT NULL THEN
|
|
1752
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.resolve_personal_connection_authority_selection(uuid, uuid, text, uuid, jsonb) FROM PUBLIC', ${literal(schema)});
|
|
1753
|
-
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.resolve_personal_connection_authority_selection(uuid, uuid, text, uuid, jsonb) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1754
|
-
END IF;
|
|
1755
|
-
IF to_regprocedure(format('%I.resolve_accepted_connection_use(uuid,uuid,uuid,uuid,uuid,integer,uuid,text,text,uuid,text,text,text,text)', ${literal(schema)})) IS NOT NULL THEN
|
|
1756
|
-
EXECUTE format('REVOKE ALL ON FUNCTION %I.resolve_accepted_connection_use(uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text) FROM PUBLIC', ${literal(schema)});
|
|
1757
|
-
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.resolve_accepted_connection_use(uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1758
|
-
END IF;
|
|
1751
|
+
IF to_regprocedure(format('%I.resolve_accepted_connection_use(uuid,uuid,uuid,uuid,uuid,integer,uuid,text,text,uuid,text,text,text,text)', ${literal(schema)})) IS NOT NULL THEN
|
|
1752
|
+
EXECUTE format('REVOKE ALL ON FUNCTION %I.resolve_accepted_connection_use(uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text) FROM PUBLIC', ${literal(schema)});
|
|
1753
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.resolve_accepted_connection_use(uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text) TO %I', ${literal(schema)}, ${literal(role)});
|
|
1759
1754
|
END IF;
|
|
1760
1755
|
IF to_regprocedure(
|
|
1761
1756
|
format('%I.get_self_personal_github_repository_selection(uuid,uuid,text,uuid)', ${literal(schema)})
|
|
@@ -1885,20 +1880,7 @@ BEGIN
|
|
|
1885
1880
|
${literal(schema)},
|
|
1886
1881
|
${literal(role)}
|
|
1887
1882
|
);
|
|
1888
|
-
IF to_regprocedure(
|
|
1889
|
-
format(
|
|
1890
|
-
'%I.refresh_scheduled_task_personal_resources_clone_connections(uuid,uuid,uuid,bigint,bigint)',
|
|
1891
|
-
${literal(schema)}
|
|
1892
|
-
)
|
|
1893
|
-
) IS NOT NULL THEN
|
|
1894
|
-
EXECUTE format(
|
|
1895
|
-
'REVOKE ALL ON FUNCTION %I.refresh_scheduled_task_personal_resources_clone_connections(uuid, uuid, uuid, bigint, bigint) FROM PUBLIC',
|
|
1896
|
-
${literal(schema)}
|
|
1897
|
-
);
|
|
1898
|
-
EXECUTE format(
|
|
1899
|
-
'GRANT EXECUTE ON FUNCTION %I.refresh_scheduled_task_personal_resources_clone_connections(uuid, uuid, uuid, bigint, bigint) TO %I',
|
|
1900
|
-
${literal(schema)}, ${literal(role)}
|
|
1901
|
-
);
|
|
1883
|
+
IF to_regprocedure(format('%I.create_scheduled_agent_run_with_admission(uuid,uuid,uuid,uuid,bigint,text,text,text,timestamp with time zone,timestamp with time zone,jsonb)', ${literal(schema)})) IS NOT NULL THEN
|
|
1902
1884
|
EXECUTE format(
|
|
1903
1885
|
'REVOKE ALL ON FUNCTION %I.create_scheduled_agent_run_with_admission(uuid, uuid, uuid, uuid, bigint, text, text, text, timestamp with time zone, timestamp with time zone, jsonb) FROM PUBLIC',
|
|
1904
1886
|
${literal(schema)}
|
|
@@ -2089,6 +2071,34 @@ BEGIN
|
|
|
2089
2071
|
EXECUTE format('GRANT USAGE ON SCHEMA opengeni_private TO %I', ${literal(role)});
|
|
2090
2072
|
EXECUTE format('REVOKE CREATE ON SCHEMA opengeni_private FROM %I', ${literal(role)});
|
|
2091
2073
|
EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA opengeni_private TO %I', ${literal(role)});
|
|
2074
|
+
IF to_regclass('opengeni_private.modal_inventory_read_capabilities') IS NOT NULL THEN
|
|
2075
|
+
-- Inventory is the only capability mint. Reprovisioning must repair
|
|
2076
|
+
-- accidental table and column grants, never expose its private ledger.
|
|
2077
|
+
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.modal_inventory_read_capabilities FROM %I', ${literal(role)});
|
|
2078
|
+
EXECUTE format('REVOKE ALL (backend_pid, transaction_id, data_schema) ON TABLE opengeni_private.modal_inventory_read_capabilities FROM %I', ${literal(role)});
|
|
2079
|
+
REVOKE ALL ON TABLE opengeni_private.modal_inventory_read_capabilities FROM PUBLIC;
|
|
2080
|
+
REVOKE ALL (backend_pid, transaction_id, data_schema) ON TABLE opengeni_private.modal_inventory_read_capabilities FROM PUBLIC;
|
|
2081
|
+
REVOKE ALL ON FUNCTION opengeni_private.list_live_modal_sandbox_leases() FROM PUBLIC;
|
|
2082
|
+
END IF;
|
|
2083
|
+
IF to_regclass('opengeni_private.sandbox_recovery_rollout') IS NOT NULL THEN
|
|
2084
|
+
-- Migration may precede this role's creation. Converge only read access;
|
|
2085
|
+
-- runtime identities and PUBLIC never receive recovery activation writes.
|
|
2086
|
+
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.sandbox_recovery_rollout FROM %I', ${literal(role)});
|
|
2087
|
+
EXECUTE format('REVOKE ALL (singleton, consent_enabled, release_evidence) ON TABLE opengeni_private.sandbox_recovery_rollout FROM %I', ${literal(role)});
|
|
2088
|
+
REVOKE ALL ON TABLE opengeni_private.sandbox_recovery_rollout FROM PUBLIC;
|
|
2089
|
+
REVOKE ALL (singleton, consent_enabled, release_evidence) ON TABLE opengeni_private.sandbox_recovery_rollout FROM PUBLIC;
|
|
2090
|
+
EXECUTE format('GRANT SELECT ON TABLE opengeni_private.sandbox_recovery_rollout TO %I', ${literal(role)});
|
|
2091
|
+
END IF;
|
|
2092
|
+
IF to_regclass('opengeni_private.organization_usage_read_capabilities') IS NOT NULL THEN
|
|
2093
|
+
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.organization_usage_read_capabilities FROM %I', ${literal(role)});
|
|
2094
|
+
REVOKE ALL ON TABLE opengeni_private.organization_usage_read_capabilities FROM PUBLIC;
|
|
2095
|
+
REVOKE ALL ON FUNCTION opengeni_private.organization_usage_summary(uuid,timestamptz,timestamptz,text,uuid,boolean) FROM PUBLIC;
|
|
2096
|
+
END IF;
|
|
2097
|
+
IF to_regclass('opengeni_private.session_file_attachments') IS NOT NULL THEN
|
|
2098
|
+
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.session_file_attachments, opengeni_private.session_file_read_capabilities FROM %I', ${literal(role)});
|
|
2099
|
+
REVOKE ALL ON TABLE opengeni_private.session_file_attachments, opengeni_private.session_file_read_capabilities FROM PUBLIC;
|
|
2100
|
+
REVOKE ALL ON FUNCTION opengeni_private.session_file_read_allowed(uuid,uuid,uuid), opengeni_private.accept_session_file_attachments(uuid,uuid,uuid,uuid,text,uuid[]), opengeni_private.read_session_file_attachments(uuid,uuid,uuid,integer,uuid[],jsonb) FROM PUBLIC;
|
|
2101
|
+
END IF;
|
|
2092
2102
|
IF to_regclass('opengeni_private.sandbox_file_publications') IS NOT NULL THEN
|
|
2093
2103
|
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.sandbox_file_publications FROM %I', ${literal(role)});
|
|
2094
2104
|
REVOKE ALL ON TABLE opengeni_private.sandbox_file_publications FROM PUBLIC;
|
|
@@ -2096,6 +2106,9 @@ BEGIN
|
|
|
2096
2106
|
REVOKE ALL ON FUNCTION opengeni_private.list_sandbox_file_publications(uuid,uuid,jsonb) FROM PUBLIC;
|
|
2097
2107
|
END IF;
|
|
2098
2108
|
FOREACH routine_signature IN ARRAY ARRAY[
|
|
2109
|
+
'read_sender_connection(uuid,uuid,uuid,text)',
|
|
2110
|
+
'validate_mcp_account_bindings(jsonb,jsonb)',
|
|
2111
|
+
'fence_mcp_account_bindings()',
|
|
2099
2112
|
'guard_mcp_operation_immutable()',
|
|
2100
2113
|
'mcp_operation_command_scoped(jsonb,text,jsonb)',
|
|
2101
2114
|
'guard_workspace_owned_skill_head_delete()',
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { and, eq } from "drizzle-orm";
|
|
1
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
SandboxProviderCommand,
|
|
5
|
+
ModalRouterProviderCommand,
|
|
6
|
+
CommandSupervisionReceipt,
|
|
7
|
+
type SessionEvent,
|
|
8
|
+
} from "@opengeni/contracts";
|
|
9
|
+
import { withRlsContext, withSessionActivityRlsContext, type Database } from "./database";
|
|
10
|
+
import { lockSessionEventWriteRows } from "./session-control";
|
|
11
|
+
import { appendSessionCommandOutput } from "./session-command-output";
|
|
5
12
|
import * as schema from "./schema";
|
|
6
13
|
|
|
7
|
-
type ProcessScope = {
|
|
14
|
+
export type ProcessScope = {
|
|
8
15
|
accountId: string;
|
|
9
16
|
workspaceId: string;
|
|
10
17
|
sessionId: string;
|
|
@@ -12,12 +19,28 @@ type ProcessScope = {
|
|
|
12
19
|
};
|
|
13
20
|
|
|
14
21
|
/** Bound to an already-authorized exact process scope by runtime wiring. */
|
|
15
|
-
export function retainedProviderCommandPersistence(
|
|
22
|
+
export function retainedProviderCommandPersistence(
|
|
23
|
+
db: Database,
|
|
24
|
+
scope: ProcessScope,
|
|
25
|
+
publish?: (events: SessionEvent[]) => Promise<void>,
|
|
26
|
+
) {
|
|
16
27
|
return {
|
|
28
|
+
recordSupervisionReceipt: (receipt: SupervisionReceipt) =>
|
|
29
|
+
recordRetainedSupervisionReceipt(db, scope, receipt),
|
|
30
|
+
loadSupervisionReceipt: () => loadRetainedSupervisionReceipt(db, scope),
|
|
31
|
+
requestCancellation: (reason: "provider_deadline" | "explicit_stop") =>
|
|
32
|
+
requestRetainedProcessCancellation(db, scope, reason),
|
|
33
|
+
cancellationRequested: () => retainedProcessCancellationRequested(db, scope),
|
|
17
34
|
load: () => getRetainedProviderCommand(db, scope),
|
|
18
35
|
acknowledge: (command: SandboxProviderCommand) =>
|
|
19
36
|
acknowledgeRetainedProviderOutput(db, scope, command),
|
|
20
|
-
reserveInput: () => reserveRetainedProviderInput(db, scope),
|
|
37
|
+
reserveInput: (byteLength?: number) => reserveRetainedProviderInput(db, scope, byteLength),
|
|
38
|
+
captureRouterPage: async (page: RetainedRouterOutputPage) => {
|
|
39
|
+
const result = await captureRetainedRouterOutput(db, scope, page);
|
|
40
|
+
// A committed capture is authoritative even if live fanout fails.
|
|
41
|
+
if (result.events.length && publish) await publish(result.events).catch(() => undefined);
|
|
42
|
+
return { command: result.command, captured: result.captured };
|
|
43
|
+
},
|
|
21
44
|
};
|
|
22
45
|
}
|
|
23
46
|
|
|
@@ -36,7 +59,8 @@ function sameExecution(a: SandboxProviderCommand, b: SandboxProviderCommand): bo
|
|
|
36
59
|
a.sandboxId === b.sandboxId &&
|
|
37
60
|
a.taskId === b.taskId &&
|
|
38
61
|
a.execId === b.execId &&
|
|
39
|
-
Boolean(a.pty) === Boolean(b.pty)
|
|
62
|
+
Boolean(a.pty) === Boolean(b.pty) &&
|
|
63
|
+
isDeepStrictEqual(supervisionDescriptor(a), supervisionDescriptor(b))
|
|
40
64
|
);
|
|
41
65
|
}
|
|
42
66
|
|
|
@@ -110,6 +134,8 @@ export async function acknowledgeRetainedProviderOutput(
|
|
|
110
134
|
candidate: SandboxProviderCommand,
|
|
111
135
|
): Promise<SandboxProviderCommand> {
|
|
112
136
|
const next = SandboxProviderCommand.parse(candidate);
|
|
137
|
+
if (next.kind !== "modal-control-v1")
|
|
138
|
+
throw new Error("Byte-offset command output requires atomic capture");
|
|
113
139
|
return withRlsContext(db, scope, async (tx) => {
|
|
114
140
|
const [row] = await tx
|
|
115
141
|
.select()
|
|
@@ -119,6 +145,8 @@ export async function acknowledgeRetainedProviderOutput(
|
|
|
119
145
|
.limit(1);
|
|
120
146
|
if (!row?.providerCommand) throw new Error("Retained provider command is unavailable");
|
|
121
147
|
const previous = SandboxProviderCommand.parse(row.providerCommand);
|
|
148
|
+
if (previous.kind !== "modal-control-v1")
|
|
149
|
+
throw new Error("Legacy acknowledgment cannot advance byte-offset output");
|
|
122
150
|
if (!sameExecution(previous, next))
|
|
123
151
|
throw new Error("Output acknowledgment changed provider execution identity");
|
|
124
152
|
const merged = structuredClone(previous);
|
|
@@ -141,11 +169,119 @@ export async function acknowledgeRetainedProviderOutput(
|
|
|
141
169
|
});
|
|
142
170
|
}
|
|
143
171
|
|
|
172
|
+
export type RetainedRouterOutputPage = {
|
|
173
|
+
expected: ModalRouterProviderCommand;
|
|
174
|
+
command: ModalRouterProviderCommand;
|
|
175
|
+
stdout: string;
|
|
176
|
+
stderr: string;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export function validateRouterOutputAdvance(page: RetainedRouterOutputPage): void {
|
|
180
|
+
const before = ModalRouterProviderCommand.parse(page.expected);
|
|
181
|
+
const after = ModalRouterProviderCommand.parse(page.command);
|
|
182
|
+
if (!sameExecution(before, after)) throw new Error("Output capture changed execution identity");
|
|
183
|
+
for (const stream of ["stdout", "stderr"] as const) {
|
|
184
|
+
const oldCursor = before.streams[stream],
|
|
185
|
+
cursor = after.streams[stream];
|
|
186
|
+
if (
|
|
187
|
+
cursor.byteOffset < oldCursor.byteOffset ||
|
|
188
|
+
(oldCursor.eof && (!cursor.eof || cursor.byteOffset !== oldCursor.byteOffset)) ||
|
|
189
|
+
(oldCursor.exitCode !== null && !isDeepStrictEqual(oldCursor, cursor))
|
|
190
|
+
)
|
|
191
|
+
throw new Error("Output capture cannot regress or rewrite terminal evidence");
|
|
192
|
+
if (
|
|
193
|
+
cursor.byteOffset === oldCursor.byteOffset &&
|
|
194
|
+
!(!oldCursor.eof && cursor.eof) &&
|
|
195
|
+
(page[stream].length > 0 || cursor.utf8Remainder !== oldCursor.utf8Remainder)
|
|
196
|
+
)
|
|
197
|
+
throw new Error("Output without byte advancement requires the first EOF");
|
|
198
|
+
if (cursor.eof && cursor.utf8Remainder !== "")
|
|
199
|
+
throw new Error("EOF must flush pending UTF-8 bytes");
|
|
200
|
+
if (Buffer.byteLength(page[stream]) > 4 * 1024 * 1024)
|
|
201
|
+
throw new Error("Command output page exceeds its capture bound");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Capture and offsets commit atomically. The session lock is acquired before
|
|
206
|
+
* the process lock, matching the event-write lock order. A concurrent reader
|
|
207
|
+
* with a stale cursor discards its page and reloads; it never appends overlapping
|
|
208
|
+
* output or silently advances the other reader's cursor. */
|
|
209
|
+
export async function captureRetainedRouterOutput(
|
|
210
|
+
db: Database,
|
|
211
|
+
scope: ProcessScope,
|
|
212
|
+
page: RetainedRouterOutputPage,
|
|
213
|
+
): Promise<{ command: ModalRouterProviderCommand; events: SessionEvent[]; captured: boolean }> {
|
|
214
|
+
validateRouterOutputAdvance(page);
|
|
215
|
+
return await withSessionActivityRlsContext(db, scope, async (tx) => {
|
|
216
|
+
const locks = await lockSessionEventWriteRows(tx, {
|
|
217
|
+
workspaceId: scope.workspaceId,
|
|
218
|
+
sessionIds: [scope.sessionId],
|
|
219
|
+
controlLock: "share",
|
|
220
|
+
});
|
|
221
|
+
if (locks.sessions[0]?.accountId !== scope.accountId)
|
|
222
|
+
throw new Error("Command output session not found");
|
|
223
|
+
const [row] = await tx
|
|
224
|
+
.select()
|
|
225
|
+
.from(schema.sandboxRetainedProcesses)
|
|
226
|
+
.where(processWhere(scope))
|
|
227
|
+
.for("update")
|
|
228
|
+
.limit(1);
|
|
229
|
+
if (!row?.providerCommand || row.providerBackend !== "modal")
|
|
230
|
+
throw new Error("Retained provider command is unavailable");
|
|
231
|
+
const current = ModalRouterProviderCommand.parse(row.providerCommand);
|
|
232
|
+
if (!sameExecution(current, page.expected))
|
|
233
|
+
throw new Error("Output capture does not own this execution");
|
|
234
|
+
if (!isDeepStrictEqual(current, page.expected))
|
|
235
|
+
return { command: current, events: [], captured: false };
|
|
236
|
+
const events: SessionEvent[] = [];
|
|
237
|
+
for (const stream of ["stdout", "stderr"] as const) {
|
|
238
|
+
const before = current.streams[stream],
|
|
239
|
+
after = page.command.streams[stream];
|
|
240
|
+
if (page[stream])
|
|
241
|
+
events.push(
|
|
242
|
+
...(await appendSessionCommandOutput(tx, {
|
|
243
|
+
...scope,
|
|
244
|
+
commandId: scope.processId,
|
|
245
|
+
stream,
|
|
246
|
+
streamFidelity: current.pty ? "merged" : "separate",
|
|
247
|
+
chunkId: `modal-router:${current.execId}:${stream}:${before.byteOffset}:${after.byteOffset}:${after.eof ? 1 : 0}`,
|
|
248
|
+
chunk: page[stream],
|
|
249
|
+
})),
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (supervisionDescriptor(page.command)) {
|
|
253
|
+
await tx.execute(
|
|
254
|
+
sql`select set_config('opengeni.supervised_output_capture_process_id', ${scope.processId}, true)`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
await tx
|
|
258
|
+
.update(schema.sandboxRetainedProcesses)
|
|
259
|
+
.set({
|
|
260
|
+
providerCommand: page.command,
|
|
261
|
+
...(supervisionDescriptor(page.command) &&
|
|
262
|
+
page.command.streams.stdout.eof &&
|
|
263
|
+
page.command.streams.stderr.eof &&
|
|
264
|
+
page.command.streams.stdout.exitCode === 0 &&
|
|
265
|
+
page.command.streams.stderr.exitCode === 0
|
|
266
|
+
? { supervisionOutputCaptured: true }
|
|
267
|
+
: {}),
|
|
268
|
+
})
|
|
269
|
+
.where(processWhere(scope));
|
|
270
|
+
if (supervisionDescriptor(page.command)) {
|
|
271
|
+
await tx.execute(
|
|
272
|
+
sql`select set_config('opengeni.supervised_output_capture_process_id', '', true)`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return { command: page.command, events, captured: true };
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
144
279
|
/** Reserve a strictly increasing provider stdin index before dispatch. An
|
|
145
280
|
* ambiguous send consumes its index and is never silently replayed. */
|
|
146
281
|
export async function reserveRetainedProviderInput(
|
|
147
282
|
db: Database,
|
|
148
283
|
scope: ProcessScope,
|
|
284
|
+
byteLength?: number,
|
|
149
285
|
): Promise<number> {
|
|
150
286
|
return withRlsContext(db, scope, async (tx) => {
|
|
151
287
|
const [row] = await tx
|
|
@@ -156,12 +292,153 @@ export async function reserveRetainedProviderInput(
|
|
|
156
292
|
.limit(1);
|
|
157
293
|
if (!row?.providerCommand || row.state !== "active")
|
|
158
294
|
throw new Error("Active retained provider command is unavailable");
|
|
159
|
-
|
|
295
|
+
if (row.cancellationRequestedAt || row.supervisionReceipt)
|
|
296
|
+
throw new Error("Retained command input is closed");
|
|
297
|
+
const router = row.providerCommand.kind === "modal-router-v1";
|
|
298
|
+
if (router && (!Number.isSafeInteger(byteLength) || byteLength! <= 0))
|
|
299
|
+
throw new Error("Byte-offset stdin requires a positive byte reservation");
|
|
300
|
+
const index = row.providerCommandInputIndex + (router ? byteLength! : 1);
|
|
160
301
|
if (!Number.isSafeInteger(index)) throw new Error("Provider stdin sequence exhausted");
|
|
161
302
|
await tx
|
|
162
303
|
.update(schema.sandboxRetainedProcesses)
|
|
163
304
|
.set({ providerCommandInputIndex: index })
|
|
164
305
|
.where(processWhere(scope));
|
|
165
|
-
return index;
|
|
306
|
+
return router ? row.providerCommandInputIndex : index;
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export type SupervisionReceipt = CommandSupervisionReceipt;
|
|
311
|
+
|
|
312
|
+
function supervisionDescriptor(command: SandboxProviderCommand) {
|
|
313
|
+
return command.kind === "modal-router-v1" ? command.supervision : undefined;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Fail before provider start, not after discovering an unprotected DB at retention. */
|
|
317
|
+
export async function supervisedCommandProtocolReady(db: Database): Promise<boolean> {
|
|
318
|
+
const [row] = await db.execute<{ ready: boolean }>(sql`
|
|
319
|
+
select count(*) = 5 as ready from pg_catalog.pg_trigger
|
|
320
|
+
where (tgrelid, tgname) in (
|
|
321
|
+
('sandbox_retained_processes'::regclass, 'supervised_command_guard'),
|
|
322
|
+
('sandbox_retained_processes'::regclass, 'supervised_provider_loss_commit_guard'),
|
|
323
|
+
('sandbox_workspace_mutation_admissions'::regclass, 'supervised_command_admission_guard'),
|
|
324
|
+
('sandbox_lease_holders'::regclass, 'supervised_command_holder_guard'),
|
|
325
|
+
('sandbox_leases'::regclass, 'supervised_command_capture_guard')
|
|
326
|
+
) and tgenabled in ('O', 'A') and not tgisinternal
|
|
327
|
+
`);
|
|
328
|
+
return row?.ready === true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Only an authenticated control-channel receipt; never parsed stdout/stderr.
|
|
332
|
+
* ACK the supervisor only after this transaction returns successfully. */
|
|
333
|
+
export async function recordRetainedSupervisionReceipt(
|
|
334
|
+
db: Database,
|
|
335
|
+
scope: ProcessScope,
|
|
336
|
+
receipt: SupervisionReceipt,
|
|
337
|
+
): Promise<void> {
|
|
338
|
+
const proof = CommandSupervisionReceipt.parse(receipt);
|
|
339
|
+
await withRlsContext(db, scope, async (tx) => {
|
|
340
|
+
const [row] = await tx
|
|
341
|
+
.select()
|
|
342
|
+
.from(schema.sandboxRetainedProcesses)
|
|
343
|
+
.where(processWhere(scope))
|
|
344
|
+
.for("update")
|
|
345
|
+
.limit(1);
|
|
346
|
+
if (!row?.providerCommand || !supervisionDescriptor(row.providerCommand))
|
|
347
|
+
throw new Error("Supervised retained command is unavailable");
|
|
348
|
+
// Trigger validates invocation binding, immutable replay and unsettled input.
|
|
349
|
+
await tx
|
|
350
|
+
.update(schema.sandboxRetainedProcesses)
|
|
351
|
+
.set({ supervisionReceipt: proof })
|
|
352
|
+
.where(processWhere(scope));
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export async function loadRetainedSupervisionReceipt(
|
|
357
|
+
db: Database,
|
|
358
|
+
scope: ProcessScope,
|
|
359
|
+
): Promise<SupervisionReceipt | null> {
|
|
360
|
+
return withRlsContext(db, scope, async (tx) => {
|
|
361
|
+
const [row] = await tx
|
|
362
|
+
.select({ receipt: schema.sandboxRetainedProcesses.supervisionReceipt })
|
|
363
|
+
.from(schema.sandboxRetainedProcesses)
|
|
364
|
+
.where(processWhere(scope))
|
|
365
|
+
.limit(1);
|
|
366
|
+
if (!row) throw new Error("Retained command is unavailable");
|
|
367
|
+
return row.receipt ? CommandSupervisionReceipt.parse(row.receipt) : null;
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export async function requestRetainedProcessCancellation(
|
|
372
|
+
db: Database,
|
|
373
|
+
scope: ProcessScope,
|
|
374
|
+
reason: "provider_deadline" | "explicit_stop",
|
|
375
|
+
): Promise<void> {
|
|
376
|
+
if (reason !== "provider_deadline" && reason !== "explicit_stop")
|
|
377
|
+
throw new Error("Invalid cancellation reason");
|
|
378
|
+
await withRlsContext(db, scope, async (tx) => {
|
|
379
|
+
const [row] = await tx
|
|
380
|
+
.select()
|
|
381
|
+
.from(schema.sandboxRetainedProcesses)
|
|
382
|
+
.where(processWhere(scope))
|
|
383
|
+
.for("update")
|
|
384
|
+
.limit(1);
|
|
385
|
+
if (!row) throw new Error("Retained command is unavailable");
|
|
386
|
+
if (row.cancellationRequestedAt || row.state !== "active") return;
|
|
387
|
+
await tx
|
|
388
|
+
.update(schema.sandboxRetainedProcesses)
|
|
389
|
+
.set({ cancellationRequestedAt: new Date(), cancellationReason: reason })
|
|
390
|
+
.where(processWhere(scope));
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export async function retainedProcessCancellationRequested(
|
|
395
|
+
db: Database,
|
|
396
|
+
scope: ProcessScope,
|
|
397
|
+
): Promise<boolean> {
|
|
398
|
+
return withRlsContext(db, scope, async (tx) => {
|
|
399
|
+
const [row] = await tx
|
|
400
|
+
.select({ at: schema.sandboxRetainedProcesses.cancellationRequestedAt })
|
|
401
|
+
.from(schema.sandboxRetainedProcesses)
|
|
402
|
+
.where(processWhere(scope))
|
|
403
|
+
.limit(1);
|
|
404
|
+
if (!row) throw new Error("Retained command is unavailable");
|
|
405
|
+
return row.at !== null;
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** Called by the reaper for an exact retained command. Ordinary completed turns
|
|
410
|
+
* are intentionally irrelevant. Lock order remains process -> lease. */
|
|
411
|
+
export async function requestRetainedProcessDeadlineCancellation(
|
|
412
|
+
db: Database,
|
|
413
|
+
scope: ProcessScope,
|
|
414
|
+
): Promise<boolean> {
|
|
415
|
+
return withRlsContext(db, scope, async (tx) => {
|
|
416
|
+
const [row] = await tx
|
|
417
|
+
.select()
|
|
418
|
+
.from(schema.sandboxRetainedProcesses)
|
|
419
|
+
.where(processWhere(scope))
|
|
420
|
+
.for("update")
|
|
421
|
+
.limit(1);
|
|
422
|
+
if (
|
|
423
|
+
!row ||
|
|
424
|
+
row.state !== "active" ||
|
|
425
|
+
!row.providerCommand ||
|
|
426
|
+
!supervisionDescriptor(row.providerCommand)
|
|
427
|
+
)
|
|
428
|
+
return false;
|
|
429
|
+
const [lease] = await tx.execute<{ id: string }>(sql`
|
|
430
|
+
select id from sandbox_leases where id = ${row.leaseId}
|
|
431
|
+
and account_id = ${row.accountId} and workspace_id = ${row.workspaceId}
|
|
432
|
+
and sandbox_group_id = ${row.sandboxGroupId} and lease_epoch = ${row.leaseEpoch}
|
|
433
|
+
and backend = ${row.providerBackend} and instance_id = ${row.providerInstanceId}
|
|
434
|
+
and rotation_requested_at is not null and rotation_reason = 'provider_deadline' for update
|
|
435
|
+
`);
|
|
436
|
+
if (!lease) return false;
|
|
437
|
+
if (!row.cancellationRequestedAt)
|
|
438
|
+
await tx
|
|
439
|
+
.update(schema.sandboxRetainedProcesses)
|
|
440
|
+
.set({ cancellationRequestedAt: new Date(), cancellationReason: "provider_deadline" })
|
|
441
|
+
.where(processWhere(scope));
|
|
442
|
+
return true;
|
|
166
443
|
});
|
|
167
444
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const key = "_opengeni_session_create_host_delegations_v1";
|
|
2
|
+
|
|
3
|
+
/** New creates cannot write a retired authority-selection identity. */
|
|
4
|
+
export function withoutRetiredSessionCreateMetadata(
|
|
5
|
+
metadata: Record<string, unknown>,
|
|
6
|
+
): Record<string, unknown> {
|
|
7
|
+
const next = { ...metadata };
|
|
8
|
+
delete next[key];
|
|
9
|
+
return next;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Historical nonempty or malformed selections must not replay as native creates. */
|
|
13
|
+
export function hasRetiredSessionCreateSelection(metadata: Record<string, unknown>): boolean {
|
|
14
|
+
const value = metadata[key];
|
|
15
|
+
return value != null && !(Array.isArray(value) && value.length === 0);
|
|
16
|
+
}
|