@opengeni/db 0.36.1 → 3.1.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-GYO7ED2Q.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-VTPRIXKG.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-HSOWSYOX.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-5KTIEDX7.js → chunk-PR2WMLOQ.js} +7714 -4443
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-Z7VYMNPZ.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.d.ts +10 -0
- package/dist/editable-artifact-durable-export.js +18 -3
- package/dist/editable-artifact-durable-export.js.map +1 -1
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1633 -87
- package/dist/index.js +22004 -8109
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/knowledge-source-sync-schema.d.ts +730 -0
- package/dist/knowledge-source-sync.d.ts +60 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +23 -9
- package/dist/schema.d.ts +24925 -13697
- package/dist/schema.js +103 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +42 -5
- package/dist/session-realtime-ledger.d.ts +3 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +153 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +1 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0240_model_context_user_messages.sql +204 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0243_google_drive_object_acl_authority.sql +554 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-artifact-durable-export.ts +27 -0
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +12001 -1699
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/knowledge-source-sync-schema.ts +83 -0
- package/src/knowledge-source-sync.ts +530 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +931 -24
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1315 -52
- package/src/schema.ts +3336 -438
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +450 -98
- package/src/session-realtime-context.ts +19 -3
- package/src/session-realtime-ledger.ts +75 -12
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +17 -1
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-5KTIEDX7.js.map +0 -1
- package/dist/chunk-FPVAD4CO.js +0 -117
- package/dist/chunk-FPVAD4CO.js.map +0 -1
- package/dist/chunk-HSOWSYOX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-VKLB2ZGI.js +0 -2389
- package/dist/chunk-VKLB2ZGI.js.map +0 -1
- package/dist/chunk-Z7VYMNPZ.js.map +0 -1
- /package/dist/{chunk-GYO7ED2Q.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-VTPRIXKG.js.map → chunk-NJVZPWXY.js.map} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { OrganizationMember } from "@opengeni/contracts";
|
|
2
|
+
import { sql } from "drizzle-orm";
|
|
3
|
+
import { rawRows, type Database } from "./database";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* THE canonical live-workspace-authority resolver for one exact subject, run on
|
|
7
|
+
* a handle that is ALREADY inside the caller's transaction with that subject's
|
|
8
|
+
* RLS context applied.
|
|
9
|
+
*
|
|
10
|
+
* `getWorkspaceGrant` is a bare `workspace_memberships` join and is therefore
|
|
11
|
+
* NOT a complete authority answer: a managed human's personal workspace
|
|
12
|
+
* deliberately has no row in that table (migration 0219 raises on one), so its
|
|
13
|
+
* owner's access is the `organization_memberships.personal_workspace_id`
|
|
14
|
+
* pointer instead. Any seam that re-derives "does this subject still hold
|
|
15
|
+
* workspace authority here" from that bare join denies the one human who always
|
|
16
|
+
* belongs. This module exists so that rule has exactly one implementation.
|
|
17
|
+
*
|
|
18
|
+
* Seams that must serialize the answer with their own writes — session listing,
|
|
19
|
+
* session pins, the composer draft — call this variant so the read and the write
|
|
20
|
+
* share one transaction snapshot and one advisory fence. Seams that need to ask
|
|
21
|
+
* about a subject that is NOT the caller call
|
|
22
|
+
* {@link namedSubjectHasLiveWorkspaceAuthority} (`@opengeni/db`), which is a thin
|
|
23
|
+
* scope wrapper over exactly this body.
|
|
24
|
+
*
|
|
25
|
+
* True when the subject currently holds workspace authority the same way the
|
|
26
|
+
* route-time access builder derives it: a `workspace_memberships` row whose
|
|
27
|
+
* owning organization membership (when one exists) is active, or an active
|
|
28
|
+
* organization membership whose personal-workspace pointer is exactly this
|
|
29
|
+
* workspace. Never infers authority any other way; a deleted membership row and
|
|
30
|
+
* a suspended/revoked organization membership are both false. Only the
|
|
31
|
+
* membership's own `personal_workspace_id` counts as stated authority — nothing
|
|
32
|
+
* is read from `created_by`, a default workspace, a resource name, or current
|
|
33
|
+
* access.
|
|
34
|
+
*
|
|
35
|
+
* # This function is NOT an authorization. Read this before relying on it.
|
|
36
|
+
*
|
|
37
|
+
* It answers "does subject X hold authority over workspace W". It does NOT
|
|
38
|
+
* establish that the caller is X. Nothing here authenticates anybody.
|
|
39
|
+
*
|
|
40
|
+
* The scope assertion below is a **consistency check, not an authorization**.
|
|
41
|
+
* It prevents one thing: a caller naming a third-party subject while its
|
|
42
|
+
* transaction is scoped to someone else. It cannot detect a caller that scoped
|
|
43
|
+
* the transaction to an attacker-chosen subject in the first place, because
|
|
44
|
+
* `withWorkspaceSubjectRls(db, workspaceId, subjectId, ...)` takes that subject
|
|
45
|
+
* from its argument too. At today's three call sites both sides come from the
|
|
46
|
+
* same variable, so the check is a tautology there and fires only if a future
|
|
47
|
+
* refactor makes them diverge. Treat it as a tripwire against that refactor —
|
|
48
|
+
* never as evidence that the subject was authenticated.
|
|
49
|
+
*
|
|
50
|
+
* **The actual authorization for the personal-workspace exception lives in the
|
|
51
|
+
* API layer**: `AccessGrantAuthorization.canonicalManagedHumanSession`
|
|
52
|
+
* (`@opengeni/core`), stamped only where a Better Auth cookie was verified. A
|
|
53
|
+
* caller that has not proven that must not enable the exception. If you are
|
|
54
|
+
* adding a caller, that stamp is the part you have to get right; this function
|
|
55
|
+
* will happily answer `true` for any subject you scope a transaction to.
|
|
56
|
+
*
|
|
57
|
+
* Non-`user:` subjects (API keys, delegated service principals, configured and
|
|
58
|
+
* local principals) can never own an organization membership, so they
|
|
59
|
+
* short-circuit to the plain membership answer instead of tripping
|
|
60
|
+
* `list_self_organization_memberships`' `42501`.
|
|
61
|
+
*/
|
|
62
|
+
export async function subjectHasLiveWorkspaceAuthorityInScope(
|
|
63
|
+
scopedDb: Database,
|
|
64
|
+
input: { accountId: string; workspaceId: string; subjectId: string },
|
|
65
|
+
): Promise<boolean> {
|
|
66
|
+
// CONSISTENCY CHECK, NOT AN AUTHORIZATION. This function deliberately does not
|
|
67
|
+
// set `opengeni.subject_id`, so it cannot be handed a scope of its own — that
|
|
68
|
+
// makes the oracle shape unrepresentable here. But the scope it inherits was
|
|
69
|
+
// set by `withWorkspaceSubjectRls` from ITS caller's argument, so a match
|
|
70
|
+
// proves only that one caller used one subject consistently; it proves
|
|
71
|
+
// nothing about who that subject is. Authorization is the API layer's
|
|
72
|
+
// canonical-managed-cookie stamp. See the doc comment above.
|
|
73
|
+
const scopedSubjectId = await subjectIdInRlsScope(scopedDb);
|
|
74
|
+
if (scopedSubjectId !== input.subjectId) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
"subjectHasLiveWorkspaceAuthorityInScope: subject does not match the applied RLS scope",
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
const [membershipRow] = await rawRows<{ present: number }>(
|
|
80
|
+
scopedDb,
|
|
81
|
+
sql`select 1 as present from workspace_memberships
|
|
82
|
+
where subject_id = ${input.subjectId}
|
|
83
|
+
and workspace_id = ${input.workspaceId}
|
|
84
|
+
limit 1`,
|
|
85
|
+
);
|
|
86
|
+
if (!input.subjectId.startsWith("user:")) {
|
|
87
|
+
return Boolean(membershipRow);
|
|
88
|
+
}
|
|
89
|
+
const [organizationMembershipResult] = await rawRows<{ result: unknown }>(
|
|
90
|
+
scopedDb,
|
|
91
|
+
sql`select list_self_organization_memberships(${input.subjectId}) as result`,
|
|
92
|
+
);
|
|
93
|
+
const organizationMemberships = OrganizationMember.array().parse(
|
|
94
|
+
organizationMembershipResult?.result ?? [],
|
|
95
|
+
);
|
|
96
|
+
const selfOrganizationMembership = organizationMemberships.find(
|
|
97
|
+
(organizationMembership) => organizationMembership.organizationId === input.accountId,
|
|
98
|
+
);
|
|
99
|
+
if (membershipRow) {
|
|
100
|
+
// Mirror the access builder's inactive-organization filter: a persisted
|
|
101
|
+
// membership row is dead while its organization membership is
|
|
102
|
+
// suspended/revoked. A subject with no organization membership at all
|
|
103
|
+
// (legacy/standalone) keeps its persisted row's authority.
|
|
104
|
+
return !selfOrganizationMembership || selfOrganizationMembership.status === "active";
|
|
105
|
+
}
|
|
106
|
+
return (
|
|
107
|
+
selfOrganizationMembership?.status === "active" &&
|
|
108
|
+
selfOrganizationMembership.personalWorkspaceId === input.workspaceId
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The applied subject GUC, or `""` when none is set. `""` is the canonical
|
|
114
|
+
* "unset" for this GUC: `current_subject_id()` is
|
|
115
|
+
* `nullif(current_setting('opengeni.subject_id', true), '')` (0239 precedent), so
|
|
116
|
+
* a caller restoring `""` genuinely clears it rather than pinning an empty
|
|
117
|
+
* subject. Never throws, so it is safe for capturing a prior value that may
|
|
118
|
+
* legitimately be absent.
|
|
119
|
+
*/
|
|
120
|
+
export async function rlsSubjectIdOrEmpty(scopedDb: Database): Promise<string> {
|
|
121
|
+
const [row] = await rawRows<{ subjectId: string | null }>(
|
|
122
|
+
scopedDb,
|
|
123
|
+
sql`select current_setting('opengeni.subject_id', true) as "subjectId"`,
|
|
124
|
+
);
|
|
125
|
+
return row?.subjectId ?? "";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The subject of the caller's ALREADY-applied RLS scope, required to be present.
|
|
130
|
+
* Used by the in-scope resolver to check that a caller is asking about its own
|
|
131
|
+
* scope rather than naming a third party — a consistency check, not proof that
|
|
132
|
+
* anything authenticated that subject.
|
|
133
|
+
*/
|
|
134
|
+
async function subjectIdInRlsScope(scopedDb: Database): Promise<string> {
|
|
135
|
+
const subjectId = await rlsSubjectIdOrEmpty(scopedDb);
|
|
136
|
+
if (!subjectId) {
|
|
137
|
+
throw new Error("RLS subject context is not applied on the active backend");
|
|
138
|
+
}
|
|
139
|
+
return subjectId;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The tenant account of the caller's ALREADY-applied RLS scope. `withRlsContext`
|
|
144
|
+
* sets and read-back-verifies this GUC on the pinned backend before running the
|
|
145
|
+
* scoped body, so it is exact rather than a re-derivation — which lets a seam
|
|
146
|
+
* that only received a workspace id consult
|
|
147
|
+
* {@link subjectHasLiveWorkspaceAuthorityInScope} without a second round trip to
|
|
148
|
+
* resolve the account.
|
|
149
|
+
*/
|
|
150
|
+
export async function accountIdInRlsScope(scopedDb: Database): Promise<string> {
|
|
151
|
+
const [row] = await rawRows<{ accountId: string | null }>(
|
|
152
|
+
scopedDb,
|
|
153
|
+
sql`select current_setting('opengeni.account_id', true) as "accountId"`,
|
|
154
|
+
);
|
|
155
|
+
const accountId = row?.accountId ?? "";
|
|
156
|
+
if (!accountId) {
|
|
157
|
+
throw new Error("RLS account context is not applied on the active backend");
|
|
158
|
+
}
|
|
159
|
+
return accountId;
|
|
160
|
+
}
|
|
@@ -172,6 +172,55 @@ export const workspaceInstructionPolicyActivationEvents = pgTable(
|
|
|
172
172
|
}),
|
|
173
173
|
);
|
|
174
174
|
|
|
175
|
+
export const workspaceInstructionPolicyDeactivationEvents = pgTable(
|
|
176
|
+
"workspace_instruction_policy_deactivation_events",
|
|
177
|
+
{
|
|
178
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
179
|
+
operationId: uuid("operation_id").notNull(),
|
|
180
|
+
requestFingerprint: text("request_fingerprint").notNull(),
|
|
181
|
+
accountId: uuid("account_id").notNull(),
|
|
182
|
+
workspaceId: uuid("workspace_id").notNull(),
|
|
183
|
+
kind: text("kind").notNull(),
|
|
184
|
+
scope: text("scope").notNull(),
|
|
185
|
+
roleKey: text("role_key"),
|
|
186
|
+
type: text("type").notNull(),
|
|
187
|
+
activationVersion: bigint("activation_version", { mode: "number" }).notNull(),
|
|
188
|
+
oldRevisionId: uuid("old_revision_id").notNull(),
|
|
189
|
+
oldRevision: bigint("old_revision", { mode: "number" }).notNull(),
|
|
190
|
+
oldContentHash: text("old_content_hash").notNull(),
|
|
191
|
+
actorSubjectId: text("actor_subject_id").notNull(),
|
|
192
|
+
reason: text("reason").notNull(),
|
|
193
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
194
|
+
},
|
|
195
|
+
(table) => ({
|
|
196
|
+
workspaceOperation: uniqueIndex(
|
|
197
|
+
"workspace_instruction_policy_deactivate_workspace_operation_uq",
|
|
198
|
+
).on(table.workspaceId, table.operationId),
|
|
199
|
+
workspaceActivationVersion: uniqueIndex(
|
|
200
|
+
"workspace_instruction_policy_deactivations_target_version_uq",
|
|
201
|
+
).on(
|
|
202
|
+
table.workspaceId,
|
|
203
|
+
table.kind,
|
|
204
|
+
table.scope,
|
|
205
|
+
sql`coalesce(${table.roleKey}, '')`,
|
|
206
|
+
table.activationVersion,
|
|
207
|
+
),
|
|
208
|
+
workspaceTimeline: index("workspace_instruction_policy_deactivations_workspace_time_idx").on(
|
|
209
|
+
table.workspaceId,
|
|
210
|
+
table.createdAt,
|
|
211
|
+
table.id,
|
|
212
|
+
),
|
|
213
|
+
target: check(
|
|
214
|
+
"workspace_instruction_policy_deactivations_target_chk",
|
|
215
|
+
sql`(
|
|
216
|
+
(${table.kind} = 'charter' and ${table.scope} = 'global' and ${table.roleKey} is null)
|
|
217
|
+
or (${table.kind} = 'policy' and ${table.scope} = 'global' and ${table.roleKey} is null)
|
|
218
|
+
or (${table.kind} = 'policy' and ${table.scope} = 'role' and ${table.roleKey} is not null)
|
|
219
|
+
)`,
|
|
220
|
+
),
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
223
|
+
|
|
175
224
|
export const workspaceInstructionPolicyOnboardingProposals = pgTable(
|
|
176
225
|
"workspace_instruction_policy_onboarding_proposals",
|
|
177
226
|
{
|
|
@@ -211,6 +260,9 @@ export const workspaceInstructionPolicyOnboardingProposals = pgTable(
|
|
|
211
260
|
sql`coalesce(${table.roleKey}, '')`,
|
|
212
261
|
table.sourceId,
|
|
213
262
|
table.sourceVersion,
|
|
263
|
+
// One proposal per source per baseline: a rebaselined successor is
|
|
264
|
+
// admissible, a duplicate against the same baseline is not.
|
|
265
|
+
table.baselineActivationVersion,
|
|
214
266
|
),
|
|
215
267
|
workspaceTimeline: index(
|
|
216
268
|
"workspace_instruction_policy_onboarding_proposals_workspace_time_idx",
|
|
@@ -239,7 +291,7 @@ export const workspaceInstructionPolicyOnboardingProposals = pgTable(
|
|
|
239
291
|
${table.baselineRevisionId} is null
|
|
240
292
|
and ${table.baselineRevision} is null
|
|
241
293
|
and ${table.baselineContentHash} is null
|
|
242
|
-
and ${table.baselineActivationVersion}
|
|
294
|
+
and ${table.baselineActivationVersion} >= 0
|
|
243
295
|
and ${table.baselineActivatedAt} is null
|
|
244
296
|
) or (
|
|
245
297
|
${table.baselineRevisionId} is not null
|