@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
package/src/company-profile.ts
CHANGED
|
@@ -857,6 +857,27 @@ export async function getCompanyProfileRevision(
|
|
|
857
857
|
});
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
+
export async function getCompanyProfileActivationEvent(
|
|
861
|
+
db: Database,
|
|
862
|
+
input: { accountId: string; workspaceId: string; eventId: string },
|
|
863
|
+
): Promise<CompanyProfileActivationEvent> {
|
|
864
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
865
|
+
const [event] = await scopedDb
|
|
866
|
+
.select()
|
|
867
|
+
.from(schema.companyProfileActivationEvents)
|
|
868
|
+
.where(
|
|
869
|
+
and(
|
|
870
|
+
eq(schema.companyProfileActivationEvents.accountId, input.accountId),
|
|
871
|
+
eq(schema.companyProfileActivationEvents.id, input.eventId),
|
|
872
|
+
),
|
|
873
|
+
)
|
|
874
|
+
.limit(1);
|
|
875
|
+
if (!event)
|
|
876
|
+
throw new CompanyProfileNotFoundError("Company-profile activation event was not found");
|
|
877
|
+
return eventFromRow(event);
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
|
|
860
881
|
function unifiedJsonDiff(from: CompanyProfileRevision, to: CompanyProfileRevision): string {
|
|
861
882
|
const before = JSON.stringify(from.profile, null, 2).split("\n");
|
|
862
883
|
const after = JSON.stringify(to.profile, null, 2).split("\n");
|
package/src/computer-sessions.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type InteractionPlacement,
|
|
18
18
|
} from "@opengeni/contracts";
|
|
19
19
|
import { and, desc, eq, inArray, sql } from "drizzle-orm";
|
|
20
|
+
import { attachedDeviceGenerationMatches } from "./attached-browser-devices";
|
|
20
21
|
import { type Database, withRlsContext } from "./database";
|
|
21
22
|
import {
|
|
22
23
|
advanceWorkspaceInteractionRevision,
|
|
@@ -846,7 +847,6 @@ export async function prepareComputerSessionEnd(
|
|
|
846
847
|
"suspended",
|
|
847
848
|
"restoring",
|
|
848
849
|
"repair_required",
|
|
849
|
-
"lost",
|
|
850
850
|
"ending",
|
|
851
851
|
]),
|
|
852
852
|
),
|
|
@@ -1037,6 +1037,15 @@ export async function touchComputerSessionController(
|
|
|
1037
1037
|
)
|
|
1038
1038
|
.limit(1);
|
|
1039
1039
|
if (!observed) return false;
|
|
1040
|
+
if (observed.placementKind === "attached_device") {
|
|
1041
|
+
if (!observed.deviceId || !observed.placementInstanceId) return false;
|
|
1042
|
+
const current = await attachedDeviceGenerationMatches(tx, {
|
|
1043
|
+
workspaceId: input.workspaceId,
|
|
1044
|
+
deviceId: observed.deviceId,
|
|
1045
|
+
placementInstanceId: observed.placementInstanceId,
|
|
1046
|
+
});
|
|
1047
|
+
if (!current) return false;
|
|
1048
|
+
}
|
|
1040
1049
|
if (observed.placementKind === "sandbox_group") {
|
|
1041
1050
|
const lease = await tx.execute<{ id: string }>(sql`
|
|
1042
1051
|
select lease.id
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConnectionAuthorityGrant,
|
|
3
|
+
ConnectionAuthoritySummary,
|
|
4
|
+
ConnectionUseDenialReason,
|
|
5
|
+
ConnectionUseAttribution,
|
|
6
|
+
ConnectionUseAuthorizationResult,
|
|
7
|
+
ConnectionUseAuthoritySnapshot,
|
|
8
|
+
type IssueConnectionUseGrantRequest,
|
|
9
|
+
type ConnectionUseDenialReason as ConnectionUseDenialReasonValue,
|
|
10
|
+
} from "@opengeni/contracts/connection-authority";
|
|
11
|
+
import {
|
|
12
|
+
ConnectionKind,
|
|
13
|
+
type ConnectionKind as ConnectionKindValue,
|
|
14
|
+
type UserResourceDelegation,
|
|
15
|
+
} from "@opengeni/contracts";
|
|
16
|
+
import { sql } from "drizzle-orm";
|
|
17
|
+
import { rawRows, setSubjectRlsContext, withRlsContext, type Database } from "./database";
|
|
18
|
+
import {
|
|
19
|
+
issueSelfUserResourceGrant,
|
|
20
|
+
listSelfUserResourceAuthorities,
|
|
21
|
+
revokeSelfUserResourceGrant,
|
|
22
|
+
} from "./user-resource-authority";
|
|
23
|
+
|
|
24
|
+
async function withOwnerContext<T>(
|
|
25
|
+
db: Database,
|
|
26
|
+
input: { accountId: string; workspaceId: string; subjectId: string },
|
|
27
|
+
fn: (db: Database) => Promise<T>,
|
|
28
|
+
): Promise<T> {
|
|
29
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
30
|
+
await setSubjectRlsContext(scopedDb, input.subjectId);
|
|
31
|
+
return await fn(scopedDb);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function listSelfConnectionAuthorities(
|
|
36
|
+
db: Database,
|
|
37
|
+
input: { accountId: string; workspaceId: string; subjectId: string },
|
|
38
|
+
): Promise<ConnectionAuthoritySummary[]> {
|
|
39
|
+
const page = await listSelfUserResourceAuthorities(db, {
|
|
40
|
+
...input,
|
|
41
|
+
resourceKind: "connection",
|
|
42
|
+
limit: 100,
|
|
43
|
+
});
|
|
44
|
+
return page.authorities.map(({ resourceKind: _resourceKind, ...authority }) =>
|
|
45
|
+
ConnectionAuthoritySummary.parse(authority),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function issueSelfConnectionUseGrant(
|
|
50
|
+
db: Database,
|
|
51
|
+
input: {
|
|
52
|
+
accountId: string;
|
|
53
|
+
workspaceId: string;
|
|
54
|
+
subjectId: string;
|
|
55
|
+
authorityId: string;
|
|
56
|
+
request: IssueConnectionUseGrantRequest;
|
|
57
|
+
},
|
|
58
|
+
) {
|
|
59
|
+
return ConnectionAuthorityGrant.parse(
|
|
60
|
+
await issueSelfUserResourceGrant(db, {
|
|
61
|
+
accountId: input.accountId,
|
|
62
|
+
workspaceId: input.workspaceId,
|
|
63
|
+
subjectId: input.subjectId,
|
|
64
|
+
authorityId: input.authorityId,
|
|
65
|
+
resourceKind: "connection",
|
|
66
|
+
mode: input.request.mode,
|
|
67
|
+
context: input.request.context,
|
|
68
|
+
sessionId: input.request.sessionId ?? null,
|
|
69
|
+
expectedAuthorityEpoch: input.request.expectedAuthorityEpoch ?? null,
|
|
70
|
+
workspaceSharedAcknowledged: input.request.workspaceSharedAcknowledged,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function revokeSelfConnectionUseGrant(
|
|
76
|
+
db: Database,
|
|
77
|
+
input: { accountId: string; workspaceId: string; subjectId: string; grantId: string },
|
|
78
|
+
): Promise<{ grantId: string; generation: number; status: "revoked"; revokedAt: string }> {
|
|
79
|
+
return await revokeSelfUserResourceGrant(db, input);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolves the immutable physical origin for one explicit common-user
|
|
84
|
+
* connection selection. The SECURITY DEFINER database boundary proves the
|
|
85
|
+
* owning human, active organization membership, target-workspace access, and
|
|
86
|
+
* exact authority/grant generations without exposing credentials or allowing
|
|
87
|
+
* an ambient cross-workspace inventory.
|
|
88
|
+
*/
|
|
89
|
+
export async function resolvePersonalConnectionAuthoritySelectionOrigin(
|
|
90
|
+
db: Database,
|
|
91
|
+
input: {
|
|
92
|
+
accountId: string;
|
|
93
|
+
targetWorkspaceId: string;
|
|
94
|
+
subjectId: string;
|
|
95
|
+
connectionId: string;
|
|
96
|
+
delegation: UserResourceDelegation;
|
|
97
|
+
},
|
|
98
|
+
): Promise<string | null> {
|
|
99
|
+
return await withOwnerContext(
|
|
100
|
+
db,
|
|
101
|
+
{
|
|
102
|
+
accountId: input.accountId,
|
|
103
|
+
workspaceId: input.targetWorkspaceId,
|
|
104
|
+
subjectId: input.subjectId,
|
|
105
|
+
},
|
|
106
|
+
async (scopedDb) => {
|
|
107
|
+
const [row] = await rawRows<{ originWorkspaceId: string }>(
|
|
108
|
+
scopedDb,
|
|
109
|
+
sql`
|
|
110
|
+
select origin_workspace_id as "originWorkspaceId"
|
|
111
|
+
from resolve_personal_connection_authority_selection(
|
|
112
|
+
${input.accountId}::uuid, ${input.targetWorkspaceId}::uuid,
|
|
113
|
+
${input.subjectId}, ${input.connectionId}::uuid,
|
|
114
|
+
${JSON.stringify(input.delegation)}::text::jsonb
|
|
115
|
+
)
|
|
116
|
+
`,
|
|
117
|
+
);
|
|
118
|
+
return row?.originWorkspaceId ?? null;
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function resolveConnectionUseAuthority(
|
|
124
|
+
db: Database,
|
|
125
|
+
input: { snapshot: unknown },
|
|
126
|
+
): Promise<ConnectionUseAuthorizationResult> {
|
|
127
|
+
const snapshot = ConnectionUseAuthoritySnapshot.parse(input.snapshot);
|
|
128
|
+
return await withRlsContext(
|
|
129
|
+
db,
|
|
130
|
+
{ accountId: snapshot.organizationId, workspaceId: snapshot.targetWorkspaceId },
|
|
131
|
+
async (scopedDb) => {
|
|
132
|
+
if (snapshot.ownerSubjectId) {
|
|
133
|
+
await setSubjectRlsContext(scopedDb, snapshot.ownerSubjectId);
|
|
134
|
+
}
|
|
135
|
+
const [row] = await rawRows<{
|
|
136
|
+
authorizationStatus: "authorized" | "denied";
|
|
137
|
+
denialReason: string | null;
|
|
138
|
+
connectionId: string | null;
|
|
139
|
+
connectionGeneration: number | null;
|
|
140
|
+
authorityScope: "workspace" | "user" | null;
|
|
141
|
+
ownerSubjectId: string | null;
|
|
142
|
+
authorityId: string | null;
|
|
143
|
+
grantId: string | null;
|
|
144
|
+
}>(
|
|
145
|
+
scopedDb,
|
|
146
|
+
sql`
|
|
147
|
+
select authorization_status as "authorizationStatus",
|
|
148
|
+
denial_reason as "denialReason", connection_id as "connectionId",
|
|
149
|
+
connection_generation::int as "connectionGeneration",
|
|
150
|
+
authority_scope as "authorityScope", owner_subject_id as "ownerSubjectId",
|
|
151
|
+
authority_id as "authorityId", grant_id as "grantId"
|
|
152
|
+
from resolve_connection_use_authority(
|
|
153
|
+
${snapshot.organizationId}::uuid, ${snapshot.targetWorkspaceId}::uuid,
|
|
154
|
+
${snapshot.targetSessionId}::uuid, ${JSON.stringify(snapshot)}::text::jsonb
|
|
155
|
+
)
|
|
156
|
+
`,
|
|
157
|
+
);
|
|
158
|
+
if (!row) throw new Error("connection use authorization was not returned");
|
|
159
|
+
if (row.authorizationStatus === "denied") {
|
|
160
|
+
return ConnectionUseAuthorizationResult.parse({
|
|
161
|
+
status: "denied",
|
|
162
|
+
reason: row.denialReason,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return ConnectionUseAuthorizationResult.parse({
|
|
166
|
+
status: "authorized",
|
|
167
|
+
attribution: ConnectionUseAttribution.parse({
|
|
168
|
+
organizationId: snapshot.organizationId,
|
|
169
|
+
workspaceId: snapshot.targetWorkspaceId,
|
|
170
|
+
sessionId: snapshot.targetSessionId,
|
|
171
|
+
connectionId: row.connectionId,
|
|
172
|
+
connectionGeneration: row.connectionGeneration,
|
|
173
|
+
scope: row.authorityScope,
|
|
174
|
+
ownerSubjectId: row.ownerSubjectId,
|
|
175
|
+
authorityId: row.authorityId,
|
|
176
|
+
grantId: row.grantId,
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export type AcceptedConnectionUseContext = {
|
|
184
|
+
accountId: string;
|
|
185
|
+
workspaceId: string;
|
|
186
|
+
sessionId: string;
|
|
187
|
+
turnId: string;
|
|
188
|
+
attemptId: string;
|
|
189
|
+
executionGeneration: number;
|
|
190
|
+
physicalRequestId: string;
|
|
191
|
+
usePhase: "credential_resolution" | "provider_request";
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export type AcceptedConnectionUseResolution =
|
|
195
|
+
| {
|
|
196
|
+
status: "denied";
|
|
197
|
+
reason: ConnectionUseDenialReasonValue;
|
|
198
|
+
}
|
|
199
|
+
| {
|
|
200
|
+
status: "authorized";
|
|
201
|
+
originWorkspaceId: string;
|
|
202
|
+
connectionKind: ConnectionKindValue;
|
|
203
|
+
attribution: ConnectionUseAttribution;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export async function resolveAcceptedConnectionUse(
|
|
207
|
+
db: Database,
|
|
208
|
+
input: AcceptedConnectionUseContext & {
|
|
209
|
+
serverId: string;
|
|
210
|
+
connectionId?: string;
|
|
211
|
+
providerDomain: string;
|
|
212
|
+
connectionKind?: ConnectionKindValue;
|
|
213
|
+
subjectScope?: "workspace" | "subject";
|
|
214
|
+
ownerSubjectId?: string;
|
|
215
|
+
},
|
|
216
|
+
): Promise<AcceptedConnectionUseResolution> {
|
|
217
|
+
return await withRlsContext(
|
|
218
|
+
db,
|
|
219
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
220
|
+
async (scopedDb) => {
|
|
221
|
+
const [row] = await rawRows<{
|
|
222
|
+
authorizationStatus: "authorized" | "denied";
|
|
223
|
+
denialReason: string | null;
|
|
224
|
+
connectionId: string | null;
|
|
225
|
+
connectionGeneration: number | null;
|
|
226
|
+
originWorkspaceId: string | null;
|
|
227
|
+
connectionKind: string | null;
|
|
228
|
+
authorityScope: "workspace" | "user" | "legacy_user" | null;
|
|
229
|
+
ownerSubjectId: string | null;
|
|
230
|
+
authorityId: string | null;
|
|
231
|
+
grantId: string | null;
|
|
232
|
+
}>(
|
|
233
|
+
scopedDb,
|
|
234
|
+
sql`
|
|
235
|
+
select authorization_status as "authorizationStatus",
|
|
236
|
+
denial_reason as "denialReason",
|
|
237
|
+
resolved_connection_id as "connectionId",
|
|
238
|
+
connection_generation::int as "connectionGeneration",
|
|
239
|
+
origin_workspace_id as "originWorkspaceId",
|
|
240
|
+
resolved_connection_kind as "connectionKind",
|
|
241
|
+
authority_scope as "authorityScope",
|
|
242
|
+
owner_subject_id as "ownerSubjectId",
|
|
243
|
+
authority_id as "authorityId", grant_id as "grantId"
|
|
244
|
+
from resolve_accepted_connection_use(
|
|
245
|
+
${input.accountId}::uuid, ${input.workspaceId}::uuid,
|
|
246
|
+
${input.sessionId}::uuid, ${input.turnId}::uuid,
|
|
247
|
+
${input.attemptId}::uuid, ${input.executionGeneration},
|
|
248
|
+
${input.physicalRequestId}::uuid, ${input.usePhase}, ${input.serverId},
|
|
249
|
+
${input.connectionId ?? null}::uuid, ${input.providerDomain},
|
|
250
|
+
${input.connectionKind ?? null}, ${input.subjectScope ?? "workspace"},
|
|
251
|
+
${input.ownerSubjectId ?? null}
|
|
252
|
+
)
|
|
253
|
+
`,
|
|
254
|
+
);
|
|
255
|
+
if (!row) throw new Error("accepted connection use resolution was not returned");
|
|
256
|
+
if (row.authorizationStatus === "denied") {
|
|
257
|
+
return {
|
|
258
|
+
status: "denied",
|
|
259
|
+
reason: ConnectionUseDenialReason.parse(row.denialReason),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (
|
|
263
|
+
!row.connectionId ||
|
|
264
|
+
!row.connectionGeneration ||
|
|
265
|
+
!row.originWorkspaceId ||
|
|
266
|
+
!row.connectionKind ||
|
|
267
|
+
!row.authorityScope
|
|
268
|
+
) {
|
|
269
|
+
throw new Error("authorized connection use resolution is incomplete");
|
|
270
|
+
}
|
|
271
|
+
const scope = row.authorityScope;
|
|
272
|
+
return {
|
|
273
|
+
status: "authorized",
|
|
274
|
+
originWorkspaceId: row.originWorkspaceId,
|
|
275
|
+
connectionKind: ConnectionKind.parse(row.connectionKind),
|
|
276
|
+
attribution: ConnectionUseAttribution.parse({
|
|
277
|
+
organizationId: input.accountId,
|
|
278
|
+
workspaceId: input.workspaceId,
|
|
279
|
+
sessionId: input.sessionId,
|
|
280
|
+
connectionId: row.connectionId,
|
|
281
|
+
connectionGeneration: row.connectionGeneration,
|
|
282
|
+
scope,
|
|
283
|
+
ownerSubjectId: row.ownerSubjectId,
|
|
284
|
+
authorityId: row.authorityId,
|
|
285
|
+
grantId: row.grantId,
|
|
286
|
+
}),
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical comparison key for provider-reported OAuth scopes.
|
|
3
|
+
*
|
|
4
|
+
* Google may return the canonical userinfo scope URLs after the application
|
|
5
|
+
* requested the standard `email` / `profile` aliases. Microsoft scope names
|
|
6
|
+
* are case-insensitive. All other providers retain exact comparison.
|
|
7
|
+
*/
|
|
8
|
+
export function connectionScopeKey(providerDomain: string, value: string): string {
|
|
9
|
+
const scope = value.trim();
|
|
10
|
+
const domain = providerDomain.toLowerCase();
|
|
11
|
+
if (domain === "googleapis.com" || domain.endsWith(".googleapis.com")) {
|
|
12
|
+
if (scope === "email" || scope === "https://www.googleapis.com/auth/userinfo.email") {
|
|
13
|
+
return "google:email";
|
|
14
|
+
}
|
|
15
|
+
if (scope === "profile" || scope === "https://www.googleapis.com/auth/userinfo.profile") {
|
|
16
|
+
return "google:profile";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return domain === "graph.microsoft.com" ? scope.toLowerCase() : scope;
|
|
20
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { rawRows, withRlsContext, type Database } from "./database";
|
|
3
|
+
|
|
4
|
+
export type ConnectionAuthorityClassificationReport = {
|
|
5
|
+
schemaVersion: 1;
|
|
6
|
+
organizationId: string;
|
|
7
|
+
runKey: string | null;
|
|
8
|
+
rewroteConnectionRows: false;
|
|
9
|
+
receiptId?: string;
|
|
10
|
+
connections: {
|
|
11
|
+
total: number;
|
|
12
|
+
workspaceOwned: number;
|
|
13
|
+
userOwned: number;
|
|
14
|
+
deterministicRepairPending: number;
|
|
15
|
+
unresolved: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ConnectionAuthorityBackfillReport = {
|
|
20
|
+
schemaVersion: 1;
|
|
21
|
+
organizationId: string;
|
|
22
|
+
dryRun: boolean;
|
|
23
|
+
limit: number;
|
|
24
|
+
candidates: number;
|
|
25
|
+
upgraded: number;
|
|
26
|
+
moreLikely: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Full-population connection authority classification. Supplying a fresh run
|
|
31
|
+
* key writes one immutable migration-0300 receipt and exact unresolved rows.
|
|
32
|
+
*/
|
|
33
|
+
export async function classifyOrganizationConnectionAuthority(
|
|
34
|
+
db: Database,
|
|
35
|
+
input: { organizationId: string; runKey?: string | null },
|
|
36
|
+
): Promise<ConnectionAuthorityClassificationReport> {
|
|
37
|
+
return await withRlsContext(
|
|
38
|
+
db,
|
|
39
|
+
{ accountId: input.organizationId, workspaceId: null },
|
|
40
|
+
async (scopedDb) => {
|
|
41
|
+
const [row] = await rawRows<{ report: ConnectionAuthorityClassificationReport }>(
|
|
42
|
+
scopedDb,
|
|
43
|
+
sql`select classify_organization_connection_authority(
|
|
44
|
+
${input.organizationId}::uuid, ${input.runKey ?? null}::text
|
|
45
|
+
) as report`,
|
|
46
|
+
);
|
|
47
|
+
if (!row) throw new Error("Connection authority classification returned no report");
|
|
48
|
+
return row.report;
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* One bounded, resumable legacy_user -> user upgrade batch. Dry-run is the
|
|
55
|
+
* default; candidates are claimed with SKIP LOCKED when applying.
|
|
56
|
+
*/
|
|
57
|
+
export async function backfillOrganizationConnectionAuthority(
|
|
58
|
+
db: Database,
|
|
59
|
+
input: { organizationId: string; limit?: number; dryRun?: boolean },
|
|
60
|
+
): Promise<ConnectionAuthorityBackfillReport> {
|
|
61
|
+
const limit = input.limit ?? 500;
|
|
62
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 5000) {
|
|
63
|
+
throw new Error("Connection authority backfill limit must be an integer from 1 to 5000");
|
|
64
|
+
}
|
|
65
|
+
return await withRlsContext(
|
|
66
|
+
db,
|
|
67
|
+
{ accountId: input.organizationId, workspaceId: null },
|
|
68
|
+
async (scopedDb) => {
|
|
69
|
+
const [row] = await rawRows<{ report: ConnectionAuthorityBackfillReport }>(
|
|
70
|
+
scopedDb,
|
|
71
|
+
sql`select backfill_organization_connection_authority(
|
|
72
|
+
${input.organizationId}::uuid,
|
|
73
|
+
${limit}::integer,
|
|
74
|
+
${input.dryRun ?? true}::boolean
|
|
75
|
+
) as report`,
|
|
76
|
+
);
|
|
77
|
+
if (!row) throw new Error("Connection authority backfill returned no report");
|
|
78
|
+
return row.report;
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
}
|