@opengeni/db 1.0.1 → 3.2.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-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
- package/dist/chunk-2EH4WRHL.js.map +1 -0
- package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
- package/dist/chunk-6HMM2R5T.js +390 -0
- package/dist/chunk-6HMM2R5T.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
- package/dist/chunk-AZHW7R5V.js.map +1 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
- package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
- package/dist/chunk-KRLPEMLH.js.map +1 -0
- package/dist/chunk-NAB6FKFN.js +516 -0
- package/dist/chunk-NAB6FKFN.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-UZNXG44K.js +4508 -0
- package/dist/chunk-UZNXG44K.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 +85 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +23 -4
- package/dist/editable-artifact-durable-export.js +2 -2
- 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 +1606 -81
- package/dist/index.js +21898 -8147
- 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/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 +29 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -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 +43 -4
- package/dist/session-realtime-ledger.d.ts +2 -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 +268 -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 +28 -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/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -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/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
- package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
- package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
- package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
- package/drizzle/0346_document_migration_audit_surface.sql +454 -0
- package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -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 +167 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +87 -2
- 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 +11676 -1550
- 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/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 +995 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +905 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1317 -72
- package/src/schema.ts +3316 -428
- 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 +441 -85
- package/src/session-realtime-context.ts +4 -6
- package/src/session-realtime-ledger.ts +28 -14
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +410 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +867 -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 +138 -11
- 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-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
rawRows,
|
|
3
3
|
withRlsContext,
|
|
4
4
|
withWorkspaceRls
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AZHW7R5V.js";
|
|
6
6
|
import {
|
|
7
7
|
connections,
|
|
8
8
|
creditLedgerEntries,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
workspaceVideoGenerationPolicies,
|
|
15
15
|
workspaceVideoGenerationQuotas,
|
|
16
16
|
workspaces
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-KRLPEMLH.js";
|
|
18
18
|
|
|
19
19
|
// src/video-generation.ts
|
|
20
20
|
import {
|
|
@@ -992,4 +992,4 @@ export {
|
|
|
992
992
|
mediaGenerationResultForStoredOperation,
|
|
993
993
|
getVideoGenerationOperationSummary
|
|
994
994
|
};
|
|
995
|
-
//# sourceMappingURL=chunk-
|
|
995
|
+
//# sourceMappingURL=chunk-2PRS4J5Q.js.map
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import {
|
|
2
|
+
nestedPostgresSqlState,
|
|
3
|
+
rawRows,
|
|
4
|
+
rlsContextForWorkspace,
|
|
5
|
+
withWorkspaceRls,
|
|
6
|
+
withWorkspaceSubjectRls,
|
|
7
|
+
withWorkspaceSubjectSessionActivityRls
|
|
8
|
+
} from "./chunk-AZHW7R5V.js";
|
|
9
|
+
|
|
10
|
+
// src/session-tenancy.ts
|
|
11
|
+
import { createHash } from "crypto";
|
|
12
|
+
import { sql } from "drizzle-orm";
|
|
13
|
+
import {
|
|
14
|
+
SessionTenancyBlocker as SessionTenancyBlockerSchema
|
|
15
|
+
} from "@opengeni/contracts";
|
|
16
|
+
var SESSION_TENANCY_ACTIVATION_VERSION = 1;
|
|
17
|
+
var SessionTenancyConflictError = class extends Error {
|
|
18
|
+
constructor(reason, blocker = null, options) {
|
|
19
|
+
super(
|
|
20
|
+
reason === "not_quiescent" ? "Session must be fully quiescent before its tenancy can change" : reason === "authority_epoch" ? "Session authority changed before the operation committed" : "Session tenancy operation key was reused with different input",
|
|
21
|
+
options
|
|
22
|
+
);
|
|
23
|
+
this.reason = reason;
|
|
24
|
+
this.blocker = blocker;
|
|
25
|
+
}
|
|
26
|
+
name = "SessionTenancyConflictError";
|
|
27
|
+
};
|
|
28
|
+
var SessionTenancyNotActivatedError = class extends Error {
|
|
29
|
+
name = "SessionTenancyNotActivatedError";
|
|
30
|
+
constructor(options) {
|
|
31
|
+
super("Session tenancy product surface is not activated for this organization", options);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var SessionTenancyAccessError = class extends Error {
|
|
35
|
+
name = "SessionTenancyAccessError";
|
|
36
|
+
constructor(options) {
|
|
37
|
+
super("Session tenancy target is unavailable", options);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var SessionTenancyInvalidRequestError = class extends Error {
|
|
41
|
+
name = "SessionTenancyInvalidRequestError";
|
|
42
|
+
constructor(options) {
|
|
43
|
+
super("Session tenancy request is invalid", options);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function isRecord(value) {
|
|
47
|
+
return Boolean(value && typeof value === "object");
|
|
48
|
+
}
|
|
49
|
+
function nestedSessionTenancyBlocker(error) {
|
|
50
|
+
const queue = [error];
|
|
51
|
+
const seen = /* @__PURE__ */ new Set();
|
|
52
|
+
while (queue.length > 0 && seen.size < 64) {
|
|
53
|
+
const current = queue.shift();
|
|
54
|
+
if (!isRecord(current) || seen.has(current)) continue;
|
|
55
|
+
seen.add(current);
|
|
56
|
+
for (const key of ["detail", "detailMessage"]) {
|
|
57
|
+
const parsed = SessionTenancyBlockerSchema.safeParse(current[key]);
|
|
58
|
+
if (parsed.success) return parsed.data;
|
|
59
|
+
}
|
|
60
|
+
for (const key of ["cause", "original", "driverError", "error", "errors"]) {
|
|
61
|
+
const nested = current[key];
|
|
62
|
+
if (Array.isArray(nested)) queue.push(...nested);
|
|
63
|
+
else if (nested !== void 0) queue.push(nested);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
async function sessionTenancyProductActivated(db, workspaceId) {
|
|
69
|
+
const { accountId } = await rlsContextForWorkspace(db, workspaceId);
|
|
70
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
71
|
+
const rows = await rawRows(
|
|
72
|
+
scopedDb,
|
|
73
|
+
sql`select session_tenancy_product_activated(
|
|
74
|
+
${accountId}::uuid,
|
|
75
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
76
|
+
) as activated`
|
|
77
|
+
);
|
|
78
|
+
return rows[0]?.activated === true;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function getPrivateSessionCreatePolicy(db, input) {
|
|
82
|
+
const { accountId } = await rlsContextForWorkspace(db, input.workspaceId);
|
|
83
|
+
return await withWorkspaceSubjectRls(
|
|
84
|
+
db,
|
|
85
|
+
input.workspaceId,
|
|
86
|
+
input.actorSubjectId,
|
|
87
|
+
async (scopedDb) => {
|
|
88
|
+
const rows = await rawRows(
|
|
89
|
+
scopedDb,
|
|
90
|
+
sql`select
|
|
91
|
+
personal_workspace as "personalWorkspace",
|
|
92
|
+
platform_available as "platformAvailable",
|
|
93
|
+
organization_enabled as "organizationEnabled"
|
|
94
|
+
from get_private_session_create_policy(
|
|
95
|
+
${accountId}::uuid,
|
|
96
|
+
${input.workspaceId}::uuid,
|
|
97
|
+
${input.actorSubjectId}
|
|
98
|
+
)`
|
|
99
|
+
);
|
|
100
|
+
const policy = rows[0];
|
|
101
|
+
if (!policy) throw new SessionTenancyAccessError();
|
|
102
|
+
return policy;
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
async function openPrivateSessionCreateCapability(db, input) {
|
|
107
|
+
let rows;
|
|
108
|
+
try {
|
|
109
|
+
rows = await rawRows(
|
|
110
|
+
db,
|
|
111
|
+
sql`select
|
|
112
|
+
capability_id as "capabilityId",
|
|
113
|
+
owner_membership_id as "ownerMembershipId"
|
|
114
|
+
from open_private_session_create_capability(
|
|
115
|
+
${input.accountId}::uuid,
|
|
116
|
+
${input.workspaceId}::uuid,
|
|
117
|
+
${input.sessionId}::uuid,
|
|
118
|
+
${input.actorSubjectId}
|
|
119
|
+
)`
|
|
120
|
+
);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (nestedPostgresSqlState(error) === "55000") {
|
|
123
|
+
throw new SessionTenancyNotActivatedError({ cause: error });
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
const capabilityId = rows[0]?.capabilityId;
|
|
128
|
+
const ownerMembershipId = rows[0]?.ownerMembershipId;
|
|
129
|
+
if (!capabilityId || !ownerMembershipId) {
|
|
130
|
+
throw new Error("Private session create capability was not returned");
|
|
131
|
+
}
|
|
132
|
+
return { capabilityId, ownerMembershipId };
|
|
133
|
+
}
|
|
134
|
+
async function openPrivateChildSessionCreateCapability(db, input) {
|
|
135
|
+
const rows = await rawRows(
|
|
136
|
+
db,
|
|
137
|
+
sql`select
|
|
138
|
+
capability_id as "capabilityId",
|
|
139
|
+
owner_membership_id as "ownerMembershipId",
|
|
140
|
+
owner_subject_id as "ownerSubjectId"
|
|
141
|
+
from open_private_child_session_create_capability(
|
|
142
|
+
${input.accountId}::uuid,
|
|
143
|
+
${input.workspaceId}::uuid,
|
|
144
|
+
${input.sessionId}::uuid,
|
|
145
|
+
${input.parentSessionId}::uuid,
|
|
146
|
+
${input.actorTurnId}::uuid,
|
|
147
|
+
${input.actorAttemptId}::uuid,
|
|
148
|
+
${input.actorExecutionGeneration}::integer
|
|
149
|
+
)`
|
|
150
|
+
);
|
|
151
|
+
const capabilityId = rows[0]?.capabilityId;
|
|
152
|
+
const ownerMembershipId = rows[0]?.ownerMembershipId;
|
|
153
|
+
const ownerSubjectId = rows[0]?.ownerSubjectId;
|
|
154
|
+
if (!capabilityId || !ownerMembershipId || !ownerSubjectId) {
|
|
155
|
+
throw new Error("Private child session create capability was not returned");
|
|
156
|
+
}
|
|
157
|
+
return { capabilityId, ownerMembershipId, ownerSubjectId };
|
|
158
|
+
}
|
|
159
|
+
async function closePrivateSessionCreateCapability(db, capabilityId) {
|
|
160
|
+
await db.execute(sql`select close_private_session_create_capability(${capabilityId}::uuid)`);
|
|
161
|
+
}
|
|
162
|
+
async function assertSessionTenancyProductActivated(db, workspaceId) {
|
|
163
|
+
if (!await sessionTenancyProductActivated(db, workspaceId)) {
|
|
164
|
+
throw new SessionTenancyNotActivatedError();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function mapSessionTenancyPersistenceError(error, options) {
|
|
168
|
+
const state = nestedPostgresSqlState(error);
|
|
169
|
+
if (state === "55P03") {
|
|
170
|
+
throw new SessionTenancyConflictError("not_quiescent", nestedSessionTenancyBlocker(error), {
|
|
171
|
+
cause: error
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (state === "40001" && options.authorityEpochConflict) {
|
|
175
|
+
throw new SessionTenancyConflictError("authority_epoch", null, { cause: error });
|
|
176
|
+
}
|
|
177
|
+
if (state === "23505") {
|
|
178
|
+
throw new SessionTenancyConflictError("operation_reuse", null, { cause: error });
|
|
179
|
+
}
|
|
180
|
+
if (state === "55000") {
|
|
181
|
+
throw new SessionTenancyNotActivatedError({ cause: error });
|
|
182
|
+
}
|
|
183
|
+
if (state === "42501" || state === "P0002") {
|
|
184
|
+
throw new SessionTenancyAccessError({ cause: error });
|
|
185
|
+
}
|
|
186
|
+
if (state === "22023") {
|
|
187
|
+
throw new SessionTenancyInvalidRequestError({ cause: error });
|
|
188
|
+
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
function canonicalSessionVisibilityTransitionHash(input) {
|
|
192
|
+
return createHash("sha256").update(
|
|
193
|
+
JSON.stringify({
|
|
194
|
+
version: 1,
|
|
195
|
+
sessionId: input.sessionId,
|
|
196
|
+
targetVisibility: input.targetVisibility,
|
|
197
|
+
expectedAuthorityEpoch: input.expectedAuthorityEpoch
|
|
198
|
+
})
|
|
199
|
+
).digest("hex");
|
|
200
|
+
}
|
|
201
|
+
function canonicalSessionForkHash(input) {
|
|
202
|
+
const legacyCompatiblePrivateRequest = input.destinationVisibility === "user_private" && !input.workspaceSharedAcknowledged;
|
|
203
|
+
return createHash("sha256").update(
|
|
204
|
+
JSON.stringify(
|
|
205
|
+
legacyCompatiblePrivateRequest ? {
|
|
206
|
+
version: 1,
|
|
207
|
+
sourceSessionId: input.sourceSessionId,
|
|
208
|
+
destinationWorkspaceId: input.destinationWorkspaceId,
|
|
209
|
+
destinationVisibility: input.destinationVisibility
|
|
210
|
+
} : {
|
|
211
|
+
version: 2,
|
|
212
|
+
sourceSessionId: input.sourceSessionId,
|
|
213
|
+
destinationWorkspaceId: input.destinationWorkspaceId,
|
|
214
|
+
destinationVisibility: input.destinationVisibility,
|
|
215
|
+
workspaceSharedAcknowledged: input.workspaceSharedAcknowledged
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
).digest("hex");
|
|
219
|
+
}
|
|
220
|
+
async function transitionSessionVisibility(db, input) {
|
|
221
|
+
if (!Number.isSafeInteger(input.expectedAuthorityEpoch) || input.expectedAuthorityEpoch < 1) {
|
|
222
|
+
throw new Error("expectedAuthorityEpoch must be a positive safe integer");
|
|
223
|
+
}
|
|
224
|
+
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
225
|
+
const requestHash = canonicalSessionVisibilityTransitionHash(input);
|
|
226
|
+
const { accountId } = await rlsContextForWorkspace(db, input.workspaceId);
|
|
227
|
+
await assertSessionTenancyProductActivated(db, input.workspaceId);
|
|
228
|
+
try {
|
|
229
|
+
return await withWorkspaceSubjectSessionActivityRls(
|
|
230
|
+
db,
|
|
231
|
+
input.workspaceId,
|
|
232
|
+
input.actorSubjectId,
|
|
233
|
+
async (scopedDb) => {
|
|
234
|
+
const rows = await rawRows(
|
|
235
|
+
scopedDb,
|
|
236
|
+
sql`select
|
|
237
|
+
operation_id as "operationId",
|
|
238
|
+
event_id as "eventId",
|
|
239
|
+
event_sequence as "eventSequence",
|
|
240
|
+
visibility,
|
|
241
|
+
authority_epoch as "authorityEpoch",
|
|
242
|
+
owner_organization_membership_id as "ownerOrganizationMembershipId",
|
|
243
|
+
changed,
|
|
244
|
+
replay,
|
|
245
|
+
interrupted_attempt_count as "interruptedAttemptCount",
|
|
246
|
+
cancelled_turn_count as "cancelledTurnCount",
|
|
247
|
+
cancelled_update_count as "cancelledUpdateCount",
|
|
248
|
+
paused_goal_count as "pausedGoalCount",
|
|
249
|
+
revoked_grant_count as "revokedGrantCount"
|
|
250
|
+
from transition_session_visibility(
|
|
251
|
+
${accountId}::uuid,
|
|
252
|
+
${input.workspaceId}::uuid,
|
|
253
|
+
${input.sessionId}::uuid,
|
|
254
|
+
${input.actorSubjectId},
|
|
255
|
+
${input.targetVisibility},
|
|
256
|
+
${input.expectedAuthorityEpoch},
|
|
257
|
+
${input.operationKey},
|
|
258
|
+
${requestHash},
|
|
259
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
260
|
+
)`
|
|
261
|
+
);
|
|
262
|
+
const result = rows[0];
|
|
263
|
+
if (!result) throw new Error("Session visibility transition returned no result");
|
|
264
|
+
return result;
|
|
265
|
+
},
|
|
266
|
+
void 0,
|
|
267
|
+
"none"
|
|
268
|
+
);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
mapSessionTenancyPersistenceError(error, { authorityEpochConflict: true });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async function forkSessionContent(db, input) {
|
|
274
|
+
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
275
|
+
if (input.destinationWorkspaceId !== input.sourceWorkspaceId) {
|
|
276
|
+
throw new Error("The first session fork contract is same-workspace only");
|
|
277
|
+
}
|
|
278
|
+
const { accountId } = await rlsContextForWorkspace(db, input.sourceWorkspaceId);
|
|
279
|
+
await assertSessionTenancyProductActivated(db, input.sourceWorkspaceId);
|
|
280
|
+
const requestHash = canonicalSessionForkHash(input);
|
|
281
|
+
try {
|
|
282
|
+
return await withWorkspaceSubjectRls(
|
|
283
|
+
db,
|
|
284
|
+
input.sourceWorkspaceId,
|
|
285
|
+
input.actorSubjectId,
|
|
286
|
+
async (scopedDb) => {
|
|
287
|
+
const rows = await rawRows(
|
|
288
|
+
scopedDb,
|
|
289
|
+
sql`select
|
|
290
|
+
operation_id as "operationId",
|
|
291
|
+
event_id as "eventId",
|
|
292
|
+
event_sequence as "eventSequence",
|
|
293
|
+
session_id as "sessionId",
|
|
294
|
+
workspace_id as "workspaceId",
|
|
295
|
+
visibility,
|
|
296
|
+
authority_epoch as "authorityEpoch",
|
|
297
|
+
copied_history_item_count as "copiedHistoryItemCount",
|
|
298
|
+
replay
|
|
299
|
+
from fork_session_content(
|
|
300
|
+
${accountId}::uuid,
|
|
301
|
+
${input.sourceWorkspaceId}::uuid,
|
|
302
|
+
${input.sourceSessionId}::uuid,
|
|
303
|
+
${input.actorSubjectId},
|
|
304
|
+
${input.destinationWorkspaceId}::uuid,
|
|
305
|
+
${input.destinationVisibility},
|
|
306
|
+
${input.workspaceSharedAcknowledged},
|
|
307
|
+
${input.operationKey},
|
|
308
|
+
${requestHash},
|
|
309
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
310
|
+
)`
|
|
311
|
+
);
|
|
312
|
+
const result = rows[0];
|
|
313
|
+
if (!result) throw new Error("Session fork returned no result");
|
|
314
|
+
return result;
|
|
315
|
+
},
|
|
316
|
+
void 0,
|
|
317
|
+
"none"
|
|
318
|
+
);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (nestedPostgresSqlState(error) === "55000") {
|
|
321
|
+
throw new SessionTenancyNotActivatedError({ cause: error });
|
|
322
|
+
}
|
|
323
|
+
mapSessionTenancyPersistenceError(error, { authorityEpochConflict: false });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async function replayAppliedSessionFork(db, input) {
|
|
327
|
+
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
328
|
+
if (input.destinationWorkspaceId !== input.sourceWorkspaceId) {
|
|
329
|
+
throw new Error("The first session fork contract is same-workspace only");
|
|
330
|
+
}
|
|
331
|
+
const { accountId } = await rlsContextForWorkspace(db, input.sourceWorkspaceId);
|
|
332
|
+
await assertSessionTenancyProductActivated(db, input.sourceWorkspaceId);
|
|
333
|
+
const requestHash = canonicalSessionForkHash(input);
|
|
334
|
+
try {
|
|
335
|
+
return await withWorkspaceSubjectRls(
|
|
336
|
+
db,
|
|
337
|
+
input.sourceWorkspaceId,
|
|
338
|
+
input.actorSubjectId,
|
|
339
|
+
async (scopedDb) => {
|
|
340
|
+
const rows = await rawRows(
|
|
341
|
+
scopedDb,
|
|
342
|
+
sql`select
|
|
343
|
+
operation_id as "operationId",
|
|
344
|
+
event_id as "eventId",
|
|
345
|
+
event_sequence as "eventSequence",
|
|
346
|
+
session_id as "sessionId",
|
|
347
|
+
workspace_id as "workspaceId",
|
|
348
|
+
visibility,
|
|
349
|
+
authority_epoch as "authorityEpoch",
|
|
350
|
+
copied_history_item_count as "copiedHistoryItemCount",
|
|
351
|
+
replay
|
|
352
|
+
from replay_applied_session_fork(
|
|
353
|
+
${accountId}::uuid,
|
|
354
|
+
${input.sourceWorkspaceId}::uuid,
|
|
355
|
+
${input.sourceSessionId}::uuid,
|
|
356
|
+
${input.actorSubjectId},
|
|
357
|
+
${input.destinationWorkspaceId}::uuid,
|
|
358
|
+
${input.destinationVisibility},
|
|
359
|
+
${input.workspaceSharedAcknowledged},
|
|
360
|
+
${input.operationKey},
|
|
361
|
+
${requestHash},
|
|
362
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
363
|
+
)`
|
|
364
|
+
);
|
|
365
|
+
return rows[0] ?? null;
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
mapSessionTenancyPersistenceError(error, { authorityEpochConflict: false });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export {
|
|
374
|
+
SessionTenancyConflictError,
|
|
375
|
+
SessionTenancyNotActivatedError,
|
|
376
|
+
SessionTenancyAccessError,
|
|
377
|
+
SessionTenancyInvalidRequestError,
|
|
378
|
+
nestedSessionTenancyBlocker,
|
|
379
|
+
sessionTenancyProductActivated,
|
|
380
|
+
getPrivateSessionCreatePolicy,
|
|
381
|
+
openPrivateSessionCreateCapability,
|
|
382
|
+
openPrivateChildSessionCreateCapability,
|
|
383
|
+
closePrivateSessionCreateCapability,
|
|
384
|
+
canonicalSessionVisibilityTransitionHash,
|
|
385
|
+
canonicalSessionForkHash,
|
|
386
|
+
transitionSessionVisibility,
|
|
387
|
+
forkSessionContent,
|
|
388
|
+
replayAppliedSessionFork
|
|
389
|
+
};
|
|
390
|
+
//# sourceMappingURL=chunk-6HMM2R5T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session-tenancy.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { sql } from \"drizzle-orm\";\nimport {\n SessionTenancyBlocker as SessionTenancyBlockerSchema,\n type SessionTenancyBlocker,\n} from \"@opengeni/contracts\";\nimport {\n rawRows,\n type Database,\n rlsContextForWorkspace,\n withWorkspaceRls,\n withWorkspaceSubjectRls,\n withWorkspaceSubjectSessionActivityRls,\n} from \"./database\";\nimport { nestedPostgresSqlState } from \"./persistence-errors\";\n\nconst SESSION_TENANCY_ACTIVATION_VERSION = 1;\n\nexport class SessionTenancyConflictError extends Error {\n readonly name = \"SessionTenancyConflictError\";\n constructor(\n readonly reason: \"not_quiescent\" | \"authority_epoch\" | \"operation_reuse\",\n readonly blocker: SessionTenancyBlocker | null = null,\n options?: ErrorOptions,\n ) {\n super(\n reason === \"not_quiescent\"\n ? \"Session must be fully quiescent before its tenancy can change\"\n : reason === \"authority_epoch\"\n ? \"Session authority changed before the operation committed\"\n : \"Session tenancy operation key was reused with different input\",\n options,\n );\n }\n}\n\nexport class SessionTenancyNotActivatedError extends Error {\n readonly name = \"SessionTenancyNotActivatedError\";\n constructor(options?: ErrorOptions) {\n super(\"Session tenancy product surface is not activated for this organization\", options);\n }\n}\n\nexport class SessionTenancyAccessError extends Error {\n readonly name = \"SessionTenancyAccessError\";\n constructor(options?: ErrorOptions) {\n super(\"Session tenancy target is unavailable\", options);\n }\n}\n\nexport class SessionTenancyInvalidRequestError extends Error {\n readonly name = \"SessionTenancyInvalidRequestError\";\n constructor(options?: ErrorOptions) {\n super(\"Session tenancy request is invalid\", options);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n\n/** Extract only the fixed quiescence DETAIL vocabulary, never arbitrary driver detail. */\nexport function nestedSessionTenancyBlocker(error: unknown): SessionTenancyBlocker | null {\n const queue: unknown[] = [error];\n const seen = new Set<unknown>();\n while (queue.length > 0 && seen.size < 64) {\n const current = queue.shift();\n if (!isRecord(current) || seen.has(current)) continue;\n seen.add(current);\n for (const key of [\"detail\", \"detailMessage\"] as const) {\n const parsed = SessionTenancyBlockerSchema.safeParse(current[key]);\n if (parsed.success) return parsed.data;\n }\n for (const key of [\"cause\", \"original\", \"driverError\", \"error\", \"errors\"] as const) {\n const nested = current[key];\n if (Array.isArray(nested)) queue.push(...nested);\n else if (nested !== undefined) queue.push(nested);\n }\n }\n return null;\n}\n\nexport type SessionTenancyVisibility = \"user_private\" | \"workspace_shared\";\n\nexport type PrivateSessionCreatePolicy = {\n personalWorkspace: boolean;\n platformAvailable: boolean;\n organizationEnabled: boolean;\n};\n\nexport type TransitionSessionVisibilityInput = {\n workspaceId: string;\n sessionId: string;\n actorSubjectId: string;\n targetVisibility: SessionTenancyVisibility;\n expectedAuthorityEpoch: number;\n operationKey: string;\n};\n\nexport type TransitionSessionVisibilityResult = {\n operationId: string;\n eventId: string | null;\n eventSequence: number | null;\n visibility: SessionTenancyVisibility;\n authorityEpoch: number;\n ownerOrganizationMembershipId: string | null;\n changed: boolean;\n replay: boolean;\n interruptedAttemptCount: number;\n cancelledTurnCount: number;\n cancelledUpdateCount: number;\n pausedGoalCount: number;\n revokedGrantCount: number;\n};\n\nexport type ForkSessionContentInput = {\n sourceWorkspaceId: string;\n sourceSessionId: string;\n actorSubjectId: string;\n destinationWorkspaceId: string;\n destinationVisibility: SessionTenancyVisibility;\n workspaceSharedAcknowledged: boolean;\n operationKey: string;\n};\n\nexport type ForkSessionContentResult = {\n operationId: string;\n eventId: string;\n eventSequence: number;\n sessionId: string;\n workspaceId: string;\n visibility: SessionTenancyVisibility;\n authorityEpoch: number;\n copiedHistoryItemCount: number;\n replay: boolean;\n};\n\nexport async function sessionTenancyProductActivated(\n db: Database,\n workspaceId: string,\n): Promise<boolean> {\n const { accountId } = await rlsContextForWorkspace(db, workspaceId);\n return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {\n const rows = await rawRows<{ activated: boolean }>(\n scopedDb,\n sql`select session_tenancy_product_activated(\n ${accountId}::uuid,\n ${SESSION_TENANCY_ACTIVATION_VERSION}\n ) as activated`,\n );\n return rows[0]?.activated === true;\n });\n}\n\nexport async function getPrivateSessionCreatePolicy(\n db: Database,\n input: { workspaceId: string; actorSubjectId: string },\n): Promise<PrivateSessionCreatePolicy> {\n const { accountId } = await rlsContextForWorkspace(db, input.workspaceId);\n return await withWorkspaceSubjectRls(\n db,\n input.workspaceId,\n input.actorSubjectId,\n async (scopedDb) => {\n const rows = await rawRows<{\n personalWorkspace: boolean;\n platformAvailable: boolean;\n organizationEnabled: boolean;\n }>(\n scopedDb,\n sql`select\n personal_workspace as \"personalWorkspace\",\n platform_available as \"platformAvailable\",\n organization_enabled as \"organizationEnabled\"\n from get_private_session_create_policy(\n ${accountId}::uuid,\n ${input.workspaceId}::uuid,\n ${input.actorSubjectId}\n )`,\n );\n const policy = rows[0];\n if (!policy) throw new SessionTenancyAccessError();\n return policy;\n },\n );\n}\n\n/** Open the exact transaction-local trigger capability used by atomic private create. */\nexport async function openPrivateSessionCreateCapability(\n db: Database,\n input: { accountId: string; workspaceId: string; sessionId: string; actorSubjectId: string },\n): Promise<{ capabilityId: string; ownerMembershipId: string }> {\n let rows: Array<{ capabilityId: string; ownerMembershipId: string }>;\n try {\n rows = await rawRows<{ capabilityId: string; ownerMembershipId: string }>(\n db,\n sql`select\n capability_id as \"capabilityId\",\n owner_membership_id as \"ownerMembershipId\"\n from open_private_session_create_capability(\n ${input.accountId}::uuid,\n ${input.workspaceId}::uuid,\n ${input.sessionId}::uuid,\n ${input.actorSubjectId}\n )`,\n );\n } catch (error) {\n // The definer raises 55000 only when the organization owner/admin setting\n // does not permit a new private session in this shared workspace; every\n // authority failure remains 42501 and propagates unchanged.\n if (nestedPostgresSqlState(error) === \"55000\") {\n throw new SessionTenancyNotActivatedError({ cause: error });\n }\n throw error;\n }\n const capabilityId = rows[0]?.capabilityId;\n const ownerMembershipId = rows[0]?.ownerMembershipId;\n if (!capabilityId || !ownerMembershipId) {\n throw new Error(\"Private session create capability was not returned\");\n }\n return { capabilityId, ownerMembershipId };\n}\n\n/** Open the exact transaction-local capability for one private child insert. */\nexport async function openPrivateChildSessionCreateCapability(\n db: Database,\n input: {\n accountId: string;\n workspaceId: string;\n sessionId: string;\n parentSessionId: string;\n actorTurnId: string;\n actorAttemptId: string;\n actorExecutionGeneration: number;\n },\n): Promise<{ capabilityId: string; ownerMembershipId: string; ownerSubjectId: string }> {\n const rows = await rawRows<{\n capabilityId: string;\n ownerMembershipId: string;\n ownerSubjectId: string;\n }>(\n db,\n sql`select\n capability_id as \"capabilityId\",\n owner_membership_id as \"ownerMembershipId\",\n owner_subject_id as \"ownerSubjectId\"\n from open_private_child_session_create_capability(\n ${input.accountId}::uuid,\n ${input.workspaceId}::uuid,\n ${input.sessionId}::uuid,\n ${input.parentSessionId}::uuid,\n ${input.actorTurnId}::uuid,\n ${input.actorAttemptId}::uuid,\n ${input.actorExecutionGeneration}::integer\n )`,\n );\n const capabilityId = rows[0]?.capabilityId;\n const ownerMembershipId = rows[0]?.ownerMembershipId;\n const ownerSubjectId = rows[0]?.ownerSubjectId;\n if (!capabilityId || !ownerMembershipId || !ownerSubjectId) {\n throw new Error(\"Private child session create capability was not returned\");\n }\n return { capabilityId, ownerMembershipId, ownerSubjectId };\n}\n\nexport async function closePrivateSessionCreateCapability(\n db: Database,\n capabilityId: string,\n): Promise<void> {\n await db.execute(sql`select close_private_session_create_capability(${capabilityId}::uuid)`);\n}\n\nasync function assertSessionTenancyProductActivated(\n db: Database,\n workspaceId: string,\n): Promise<void> {\n if (!(await sessionTenancyProductActivated(db, workspaceId))) {\n throw new SessionTenancyNotActivatedError();\n }\n}\n\nfunction mapSessionTenancyPersistenceError(\n error: unknown,\n options: { authorityEpochConflict: boolean },\n): never {\n const state = nestedPostgresSqlState(error);\n if (state === \"55P03\") {\n throw new SessionTenancyConflictError(\"not_quiescent\", nestedSessionTenancyBlocker(error), {\n cause: error,\n });\n }\n if (state === \"40001\" && options.authorityEpochConflict) {\n throw new SessionTenancyConflictError(\"authority_epoch\", null, { cause: error });\n }\n if (state === \"23505\") {\n throw new SessionTenancyConflictError(\"operation_reuse\", null, { cause: error });\n }\n if (state === \"55000\") {\n throw new SessionTenancyNotActivatedError({ cause: error });\n }\n if (state === \"42501\" || state === \"P0002\") {\n throw new SessionTenancyAccessError({ cause: error });\n }\n if (state === \"22023\") {\n throw new SessionTenancyInvalidRequestError({ cause: error });\n }\n throw error;\n}\n\nexport function canonicalSessionVisibilityTransitionHash(\n input: Pick<\n TransitionSessionVisibilityInput,\n \"sessionId\" | \"targetVisibility\" | \"expectedAuthorityEpoch\"\n >,\n): string {\n return createHash(\"sha256\")\n .update(\n JSON.stringify({\n version: 1,\n sessionId: input.sessionId,\n targetVisibility: input.targetVisibility,\n expectedAuthorityEpoch: input.expectedAuthorityEpoch,\n }),\n )\n .digest(\"hex\");\n}\n\nexport function canonicalSessionForkHash(\n input: Pick<\n ForkSessionContentInput,\n | \"sourceSessionId\"\n | \"destinationWorkspaceId\"\n | \"destinationVisibility\"\n | \"workspaceSharedAcknowledged\"\n >,\n): string {\n const legacyCompatiblePrivateRequest =\n input.destinationVisibility === \"user_private\" && !input.workspaceSharedAcknowledged;\n return createHash(\"sha256\")\n .update(\n JSON.stringify(\n legacyCompatiblePrivateRequest\n ? {\n version: 1,\n sourceSessionId: input.sourceSessionId,\n destinationWorkspaceId: input.destinationWorkspaceId,\n destinationVisibility: input.destinationVisibility,\n }\n : {\n version: 2,\n sourceSessionId: input.sourceSessionId,\n destinationWorkspaceId: input.destinationWorkspaceId,\n destinationVisibility: input.destinationVisibility,\n workspaceSharedAcknowledged: input.workspaceSharedAcknowledged,\n },\n ),\n )\n .digest(\"hex\");\n}\n\nexport async function transitionSessionVisibility(\n db: Database,\n input: TransitionSessionVisibilityInput,\n): Promise<TransitionSessionVisibilityResult> {\n if (!Number.isSafeInteger(input.expectedAuthorityEpoch) || input.expectedAuthorityEpoch < 1) {\n throw new Error(\"expectedAuthorityEpoch must be a positive safe integer\");\n }\n if (!input.operationKey.trim()) throw new Error(\"operationKey must not be empty\");\n const requestHash = canonicalSessionVisibilityTransitionHash(input);\n const { accountId } = await rlsContextForWorkspace(db, input.workspaceId);\n await assertSessionTenancyProductActivated(db, input.workspaceId);\n try {\n return await withWorkspaceSubjectSessionActivityRls(\n db,\n input.workspaceId,\n input.actorSubjectId,\n async (scopedDb) => {\n const rows = await rawRows<{\n operationId: string;\n eventId: string | null;\n eventSequence: number | null;\n visibility: SessionTenancyVisibility;\n authorityEpoch: number;\n ownerOrganizationMembershipId: string | null;\n changed: boolean;\n replay: boolean;\n interruptedAttemptCount: number;\n cancelledTurnCount: number;\n cancelledUpdateCount: number;\n pausedGoalCount: number;\n revokedGrantCount: number;\n }>(\n scopedDb,\n sql`select\n operation_id as \"operationId\",\n event_id as \"eventId\",\n event_sequence as \"eventSequence\",\n visibility,\n authority_epoch as \"authorityEpoch\",\n owner_organization_membership_id as \"ownerOrganizationMembershipId\",\n changed,\n replay,\n interrupted_attempt_count as \"interruptedAttemptCount\",\n cancelled_turn_count as \"cancelledTurnCount\",\n cancelled_update_count as \"cancelledUpdateCount\",\n paused_goal_count as \"pausedGoalCount\",\n revoked_grant_count as \"revokedGrantCount\"\n from transition_session_visibility(\n ${accountId}::uuid,\n ${input.workspaceId}::uuid,\n ${input.sessionId}::uuid,\n ${input.actorSubjectId},\n ${input.targetVisibility},\n ${input.expectedAuthorityEpoch},\n ${input.operationKey},\n ${requestHash},\n ${SESSION_TENANCY_ACTIVATION_VERSION}\n )`,\n );\n const result = rows[0];\n if (!result) throw new Error(\"Session visibility transition returned no result\");\n return result;\n },\n undefined,\n \"none\",\n );\n } catch (error) {\n mapSessionTenancyPersistenceError(error, { authorityEpochConflict: true });\n }\n}\n\nexport async function forkSessionContent(\n db: Database,\n input: ForkSessionContentInput,\n): Promise<ForkSessionContentResult> {\n if (!input.operationKey.trim()) throw new Error(\"operationKey must not be empty\");\n if (input.destinationWorkspaceId !== input.sourceWorkspaceId) {\n throw new Error(\"The first session fork contract is same-workspace only\");\n }\n const { accountId } = await rlsContextForWorkspace(db, input.sourceWorkspaceId);\n await assertSessionTenancyProductActivated(db, input.sourceWorkspaceId);\n const requestHash = canonicalSessionForkHash(input);\n try {\n return await withWorkspaceSubjectRls(\n db,\n input.sourceWorkspaceId,\n input.actorSubjectId,\n async (scopedDb) => {\n const rows = await rawRows<ForkSessionContentResult>(\n scopedDb,\n sql`select\n operation_id as \"operationId\",\n event_id as \"eventId\",\n event_sequence as \"eventSequence\",\n session_id as \"sessionId\",\n workspace_id as \"workspaceId\",\n visibility,\n authority_epoch as \"authorityEpoch\",\n copied_history_item_count as \"copiedHistoryItemCount\",\n replay\n from fork_session_content(\n ${accountId}::uuid,\n ${input.sourceWorkspaceId}::uuid,\n ${input.sourceSessionId}::uuid,\n ${input.actorSubjectId},\n ${input.destinationWorkspaceId}::uuid,\n ${input.destinationVisibility},\n ${input.workspaceSharedAcknowledged},\n ${input.operationKey},\n ${requestHash},\n ${SESSION_TENANCY_ACTIVATION_VERSION}\n )`,\n );\n const result = rows[0];\n if (!result) throw new Error(\"Session fork returned no result\");\n return result;\n },\n undefined,\n \"none\",\n );\n } catch (error) {\n // A private destination inside a shared workspace carries the same\n // organization owner/admin product decision the create path enforces\n // (migration 0323), and the fork routine raises the same 55000. Surface it\n // as the not-activated product state rather than an opaque database error.\n if (nestedPostgresSqlState(error) === \"55000\") {\n throw new SessionTenancyNotActivatedError({ cause: error });\n }\n mapSessionTenancyPersistenceError(error, { authorityEpochConflict: false });\n }\n}\n\n/**\n * Recover one exact committed fork before mutable source-session authorization.\n *\n * The database capability requires the authenticated actor's active workspace\n * authority and matches the complete actor/workspace/source/key/request-hash\n * tuple. A fresh key returns null; changed intent raises the ordinary tenancy\n * idempotency conflict. This is receipt recovery, not session discovery.\n */\nexport async function replayAppliedSessionFork(\n db: Database,\n input: ForkSessionContentInput,\n): Promise<ForkSessionContentResult | null> {\n if (!input.operationKey.trim()) throw new Error(\"operationKey must not be empty\");\n if (input.destinationWorkspaceId !== input.sourceWorkspaceId) {\n throw new Error(\"The first session fork contract is same-workspace only\");\n }\n const { accountId } = await rlsContextForWorkspace(db, input.sourceWorkspaceId);\n await assertSessionTenancyProductActivated(db, input.sourceWorkspaceId);\n const requestHash = canonicalSessionForkHash(input);\n try {\n return await withWorkspaceSubjectRls(\n db,\n input.sourceWorkspaceId,\n input.actorSubjectId,\n async (scopedDb) => {\n const rows = await rawRows<ForkSessionContentResult>(\n scopedDb,\n sql`select\n operation_id as \"operationId\",\n event_id as \"eventId\",\n event_sequence as \"eventSequence\",\n session_id as \"sessionId\",\n workspace_id as \"workspaceId\",\n visibility,\n authority_epoch as \"authorityEpoch\",\n copied_history_item_count as \"copiedHistoryItemCount\",\n replay\n from replay_applied_session_fork(\n ${accountId}::uuid,\n ${input.sourceWorkspaceId}::uuid,\n ${input.sourceSessionId}::uuid,\n ${input.actorSubjectId},\n ${input.destinationWorkspaceId}::uuid,\n ${input.destinationVisibility},\n ${input.workspaceSharedAcknowledged},\n ${input.operationKey},\n ${requestHash},\n ${SESSION_TENANCY_ACTIVATION_VERSION}\n )`,\n );\n return rows[0] ?? null;\n },\n );\n } catch (error) {\n mapSessionTenancyPersistenceError(error, { authorityEpochConflict: false });\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
sessions,
|
|
5
5
|
workspaceSessionActivityRevisions,
|
|
6
6
|
workspaces
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KRLPEMLH.js";
|
|
8
8
|
|
|
9
9
|
// src/persistence-errors.ts
|
|
10
10
|
var SQLSTATE_KEYS = ["sqlState", "sqlstate", "code"];
|
|
@@ -331,7 +331,8 @@ function createDb(databaseUrl, options = {}) {
|
|
|
331
331
|
const db = drizzle(client, { schema: schema_exports });
|
|
332
332
|
dbBindings.set(db, {
|
|
333
333
|
rlsStrategy: options.rlsStrategy ?? "force",
|
|
334
|
-
...options.userLookup ? { userLookup: options.userLookup } : {}
|
|
334
|
+
...options.userLookup ? { userLookup: options.userLookup } : {},
|
|
335
|
+
...options.userProfileLookup ? { userProfileLookup: options.userProfileLookup } : {}
|
|
335
336
|
});
|
|
336
337
|
return {
|
|
337
338
|
db,
|
|
@@ -343,7 +344,8 @@ function createDb(databaseUrl, options = {}) {
|
|
|
343
344
|
function registerDbBinding(db, binding) {
|
|
344
345
|
dbBindings.set(db, {
|
|
345
346
|
rlsStrategy: binding.rlsStrategy ?? "force",
|
|
346
|
-
...binding.userLookup ? { userLookup: binding.userLookup } : {}
|
|
347
|
+
...binding.userLookup ? { userLookup: binding.userLookup } : {},
|
|
348
|
+
...binding.userProfileLookup ? { userProfileLookup: binding.userProfileLookup } : {}
|
|
347
349
|
});
|
|
348
350
|
}
|
|
349
351
|
async function setRlsContext(db, context) {
|
|
@@ -407,12 +409,17 @@ async function restoreRlsContextSettings(db, settings) {
|
|
|
407
409
|
throw new Error("RLS context could not be restored after a nested scope");
|
|
408
410
|
}
|
|
409
411
|
}
|
|
410
|
-
async function withRlsContext(db, context, fn, transactionConfig) {
|
|
412
|
+
async function withRlsContext(db, context, fn, transactionConfig, sessionTenancyFence = "shared") {
|
|
411
413
|
const restoreParentScope = isTransactionHandle(db);
|
|
412
414
|
return await db.transaction(async (tx) => {
|
|
413
415
|
const scoped = tx;
|
|
414
416
|
const parentScope = restoreParentScope ? await readRlsContextSettings(scoped) : null;
|
|
415
417
|
await setRlsContext(scoped, context);
|
|
418
|
+
if (context.workspaceId && sessionTenancyFence === "shared") {
|
|
419
|
+
await scoped.execute(
|
|
420
|
+
sql`select pg_advisory_xact_lock_shared(hashtextextended(${`session-tenancy:${context.workspaceId}`}, 0))`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
416
423
|
await assertRlsContextApplied(scoped, context);
|
|
417
424
|
const value = await fn(scoped);
|
|
418
425
|
if (parentScope) await restoreRlsContextSettings(scoped, parentScope);
|
|
@@ -573,8 +580,11 @@ async function finalizeSessionActivityGate(db, workspaceId) {
|
|
|
573
580
|
await db.execute(
|
|
574
581
|
sql`set constraints sessions_activity_insert_commit_guard, sessions_activity_update_commit_guard immediate`
|
|
575
582
|
);
|
|
583
|
+
await db.execute(
|
|
584
|
+
sql`set constraints sessions_activity_insert_commit_guard, sessions_activity_update_commit_guard deferred`
|
|
585
|
+
);
|
|
576
586
|
}
|
|
577
|
-
async function withSessionActivityRlsContext(db, context, fn, transactionConfig) {
|
|
587
|
+
async function withSessionActivityRlsContext(db, context, fn, transactionConfig, fenceMode = "shared") {
|
|
578
588
|
await assertSessionActivityGateEntry(db);
|
|
579
589
|
return await withRlsContext(
|
|
580
590
|
db,
|
|
@@ -588,7 +598,8 @@ async function withSessionActivityRlsContext(db, context, fn, transactionConfig)
|
|
|
588
598
|
}
|
|
589
599
|
return value;
|
|
590
600
|
},
|
|
591
|
-
transactionConfig
|
|
601
|
+
transactionConfig,
|
|
602
|
+
fenceMode
|
|
592
603
|
);
|
|
593
604
|
}
|
|
594
605
|
async function withWorkspaceSessionActivityRls(db, workspaceId, fn, transactionConfig) {
|
|
@@ -599,6 +610,37 @@ async function withWorkspaceSessionActivityRls(db, workspaceId, fn, transactionC
|
|
|
599
610
|
transactionConfig
|
|
600
611
|
);
|
|
601
612
|
}
|
|
613
|
+
async function withRestoredSessionActivityRlsContext(db, context, fn) {
|
|
614
|
+
if (!isTransactionHandle(db)) {
|
|
615
|
+
throw new Error("Sequential workspace activity scopes require a caller-owned transaction");
|
|
616
|
+
}
|
|
617
|
+
const [prior] = await rawRows(
|
|
618
|
+
db,
|
|
619
|
+
sql`select
|
|
620
|
+
coalesce(current_setting('opengeni.session_activity_gate_state', true), '') as state,
|
|
621
|
+
coalesce(
|
|
622
|
+
current_setting('opengeni.session_activity_gate_workspace_id', true), ''
|
|
623
|
+
) as workspace_id`
|
|
624
|
+
);
|
|
625
|
+
try {
|
|
626
|
+
return await withRlsContext(db, context, async (scopedDb) => {
|
|
627
|
+
const gate = await beginSessionActivityGate(scopedDb, context.workspaceId);
|
|
628
|
+
if (!gate.owner) {
|
|
629
|
+
throw new Error("Sequential workspace activity scope unexpectedly reused an open gate");
|
|
630
|
+
}
|
|
631
|
+
const value = await fn(gate.db);
|
|
632
|
+
await assertRlsContextApplied(gate.db, context);
|
|
633
|
+
await finalizeSessionActivityGate(gate.db, context.workspaceId);
|
|
634
|
+
return value;
|
|
635
|
+
});
|
|
636
|
+
} finally {
|
|
637
|
+
await db.execute(sql`select
|
|
638
|
+
set_config('opengeni.session_activity_gate_state', ${prior?.state ?? ""}, true),
|
|
639
|
+
set_config(
|
|
640
|
+
'opengeni.session_activity_gate_workspace_id', ${prior?.workspace_id ?? ""}, true
|
|
641
|
+
)`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
602
644
|
async function withSessionActivitySavepoint(db, fn) {
|
|
603
645
|
return await db.transaction(async (tx) => await fn(tx));
|
|
604
646
|
}
|
|
@@ -617,7 +659,7 @@ async function retryRlsPersistence(db, context, options, fn) {
|
|
|
617
659
|
return await withRlsContext(db, context, fn);
|
|
618
660
|
});
|
|
619
661
|
}
|
|
620
|
-
async function withWorkspaceSubjectRls(db, workspaceId, subjectId, fn, transactionConfig) {
|
|
662
|
+
async function withWorkspaceSubjectRls(db, workspaceId, subjectId, fn, transactionConfig, sessionTenancyFence = "shared") {
|
|
621
663
|
if (!subjectId.trim()) {
|
|
622
664
|
throw new Error("withWorkspaceSubjectRls: a non-empty subjectId is required");
|
|
623
665
|
}
|
|
@@ -629,10 +671,11 @@ async function withWorkspaceSubjectRls(db, workspaceId, subjectId, fn, transacti
|
|
|
629
671
|
await setSubjectRlsContext(scopedDb, subjectId);
|
|
630
672
|
return await fn(scopedDb);
|
|
631
673
|
},
|
|
632
|
-
transactionConfig
|
|
674
|
+
transactionConfig,
|
|
675
|
+
sessionTenancyFence
|
|
633
676
|
);
|
|
634
677
|
}
|
|
635
|
-
async function withWorkspaceSubjectSessionActivityRls(db, workspaceId, subjectId, fn, transactionConfig) {
|
|
678
|
+
async function withWorkspaceSubjectSessionActivityRls(db, workspaceId, subjectId, fn, transactionConfig, fenceMode = "shared") {
|
|
636
679
|
if (!subjectId.trim()) {
|
|
637
680
|
throw new Error("withWorkspaceSubjectSessionActivityRls: a non-empty subjectId is required");
|
|
638
681
|
}
|
|
@@ -644,7 +687,8 @@ async function withWorkspaceSubjectSessionActivityRls(db, workspaceId, subjectId
|
|
|
644
687
|
await setSubjectRlsContext(scopedDb, subjectId);
|
|
645
688
|
return await fn(scopedDb);
|
|
646
689
|
},
|
|
647
|
-
transactionConfig
|
|
690
|
+
transactionConfig,
|
|
691
|
+
fenceMode
|
|
648
692
|
);
|
|
649
693
|
}
|
|
650
694
|
async function setSubjectRlsContext(db, subjectId) {
|
|
@@ -697,6 +741,7 @@ export {
|
|
|
697
741
|
withWorkspaceRls,
|
|
698
742
|
withSessionActivityRlsContext,
|
|
699
743
|
withWorkspaceSessionActivityRls,
|
|
744
|
+
withRestoredSessionActivityRlsContext,
|
|
700
745
|
withSessionActivitySavepoint,
|
|
701
746
|
retrySessionActivityRls,
|
|
702
747
|
retryWorkspacePersistence,
|
|
@@ -708,4 +753,4 @@ export {
|
|
|
708
753
|
withAccountRls,
|
|
709
754
|
dbBindingFor
|
|
710
755
|
};
|
|
711
|
-
//# sourceMappingURL=chunk-
|
|
756
|
+
//# sourceMappingURL=chunk-AZHW7R5V.js.map
|