@opengeni/core 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +1 -1
  2. package/dist/application/connect-operation.d.ts +3 -0
  3. package/dist/application/sandbox-recovery.d.ts +49 -0
  4. package/dist/{chunk-H7X52RI2.js → chunk-TFHSE7XR.js} +13 -2
  5. package/dist/chunk-TFHSE7XR.js.map +1 -0
  6. package/dist/dependencies.d.ts +1 -1
  7. package/dist/domain/capabilities.d.ts +2 -2
  8. package/dist/domain/connection-account-selection-error.d.ts +6 -0
  9. package/dist/domain/connector-tool-permissions.d.ts +27 -0
  10. package/dist/domain/editable-artifacts/agent-application.d.ts +8 -0
  11. package/dist/domain/environments.d.ts +3 -7
  12. package/dist/domain/insights-timing.d.ts +16 -0
  13. package/dist/domain/insights.d.ts +4 -1
  14. package/dist/domain/knowledge-files.d.ts +1 -1
  15. package/dist/domain/knowledge-preparation.d.ts +9 -0
  16. package/dist/domain/knowledge-preparation.test.d.ts +1 -0
  17. package/dist/domain/mcp-account-bindings.d.ts +19 -0
  18. package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
  19. package/dist/domain/organization-integration-catalog.d.ts +15 -0
  20. package/dist/domain/personal-connection-delegations.d.ts +19 -10
  21. package/dist/domain/pr-review.d.ts +56 -4
  22. package/dist/domain/scheduled-tasks.d.ts +8 -5
  23. package/dist/domain/session-file-access.d.ts +4 -0
  24. package/dist/domain/sessions.d.ts +8 -6
  25. package/dist/domain/skills.d.ts +29 -1
  26. package/dist/editable-artifacts.js +1 -1
  27. package/dist/index.d.ts +7 -5
  28. package/dist/index.js +4868 -4803
  29. package/dist/index.js.map +1 -1
  30. package/dist/sandbox/routing.d.ts +2 -1
  31. package/dist/sandbox/runtime-settings.d.ts +1 -15
  32. package/package.json +11 -19
  33. package/src/application/composer-submit.ts +2 -1
  34. package/src/application/connect-operation.ts +32 -3
  35. package/src/application/sandbox-recovery.ts +55 -0
  36. package/src/application/user-resource-grants.ts +1 -16
  37. package/src/dependencies.ts +5 -1
  38. package/src/domain/capabilities.ts +169 -122
  39. package/src/domain/connection-account-selection-error.ts +9 -0
  40. package/src/domain/connector-tool-permissions.ts +305 -0
  41. package/src/domain/editable-artifacts/agent-application.ts +31 -2
  42. package/src/domain/environments.ts +4 -9
  43. package/src/domain/file-owner.ts +16 -1
  44. package/src/domain/insights-timing.ts +95 -0
  45. package/src/domain/insights.ts +59 -35
  46. package/src/domain/knowledge-files.ts +10 -3
  47. package/src/domain/knowledge-messages.ts +1 -0
  48. package/src/domain/knowledge-preparation.test.ts +251 -0
  49. package/src/domain/knowledge-preparation.ts +126 -0
  50. package/src/domain/mcp-account-bindings.ts +152 -0
  51. package/src/domain/native-mcp-connection-admission.ts +14 -0
  52. package/src/domain/organization-integration-catalog.ts +83 -0
  53. package/src/domain/personal-connection-delegations.ts +226 -177
  54. package/src/domain/pr-review.ts +22 -6
  55. package/src/domain/resources.ts +9 -2
  56. package/src/domain/scheduled-tasks.ts +200 -155
  57. package/src/domain/session-file-access.ts +47 -0
  58. package/src/domain/session-tool-policy.ts +12 -4
  59. package/src/domain/sessions.ts +390 -261
  60. package/src/domain/skills.ts +9 -0
  61. package/src/index.ts +10 -6
  62. package/src/rigs/index.ts +17 -17
  63. package/src/sandbox/routing.ts +18 -7
  64. package/src/sandbox/runtime-settings.ts +7 -91
  65. package/dist/application/host-mcp-owner.d.ts +0 -6
  66. package/dist/application/host-mcp-resolvers.d.ts +0 -13
  67. package/dist/chunk-5WZYFL45.js +0 -266
  68. package/dist/chunk-5WZYFL45.js.map +0 -1
  69. package/dist/chunk-H7X52RI2.js.map +0 -1
  70. package/dist/domain/connection-authority.d.ts +0 -97
  71. package/dist/domain/connection-authority.js +0 -169
  72. package/dist/domain/connection-authority.js.map +0 -1
  73. package/dist/domain/host-mcp-authority-source-admission.d.ts +0 -10
  74. package/dist/domain/host-mcp-task-admission.d.ts +0 -18
  75. package/dist/domain/packs.d.ts +0 -59
  76. package/dist/domain/product-integration-pack.d.ts +0 -44
  77. package/dist/domain/product-integration-skill.gen.d.ts +0 -5
  78. package/dist/remote-mcp-credentials.d.ts +0 -23
  79. package/dist/remote-mcp-credentials.js +0 -11
  80. package/dist/remote-mcp-credentials.js.map +0 -1
  81. package/src/application/host-mcp-owner.ts +0 -41
  82. package/src/application/host-mcp-resolvers.ts +0 -84
  83. package/src/domain/connection-authority.ts +0 -329
  84. package/src/domain/host-mcp-authority-source-admission.ts +0 -25
  85. package/src/domain/host-mcp-task-admission.ts +0 -121
  86. package/src/domain/packs.ts +0 -699
  87. package/src/domain/product-integration-pack.ts +0 -42
  88. package/src/domain/product-integration-skill.gen.ts +0 -52
  89. package/src/remote-mcp-credentials.ts +0 -361
@@ -2,7 +2,8 @@ import type { McpServerConfig, Settings } from "@opengeni/config";
2
2
  import type {
3
3
  AccessGrant,
4
4
  ConnectionMetadata,
5
- McpConnectionAuthoritySelection,
5
+ McpConnectionAccountBinding,
6
+ McpConnectionAccountSelection,
6
7
  McpPersonalConnectionDelegation,
7
8
  McpServerConnectionRef,
8
9
  ResourceRef,
@@ -24,17 +25,24 @@ import {
24
25
  import {
25
26
  getPersonalGitHubRepositorySelectionState,
26
27
  getSessionTurnPersonalConnectionDelegations,
28
+ getSessionTurnMcpAccountBindings,
27
29
  getConnectionMetadata,
28
30
  getSocialConnection,
29
31
  listConnectionsMetadata,
30
32
  listSocialConnections,
31
- resolvePersonalConnectionAuthoritySelectionOrigin,
33
+ listOwnedConnectionAccounts,
32
34
  namedSubjectHasLiveWorkspaceAuthority,
33
35
  type Database,
34
36
  type ResolveConnectionCredentialInput,
35
37
  type ResolveConnectionCredentialResult,
36
38
  } from "@opengeni/db";
37
39
  import { personalGitHubRepositoryResources } from "./resources";
40
+ import { ConnectionAccountSelectionError } from "./connection-account-selection-error";
41
+ export { ConnectionAccountSelectionError } from "./connection-account-selection-error";
42
+ import {
43
+ mcpAccountBindingsFromVisibleConnections,
44
+ personalDelegationsForAccountBindings,
45
+ } from "./mcp-account-bindings";
38
46
 
39
47
  export type PersonalConnectionDelegationSource =
40
48
  | {
@@ -157,6 +165,53 @@ export function personalConnectionDelegationSourceForGrant(
157
165
  return { kind: "subject", subjectId: grant.subjectId, accountId: grant.accountId };
158
166
  }
159
167
 
168
+ /** Account-picker inventory: shared workspace accounts and this sender's own
169
+ * personal accounts. Visibility is never an execution grant or a credential
170
+ * response; every selected connection is validated again at admission/use. */
171
+ export async function listOwnConnectionAccountsForGrant(
172
+ db: Database,
173
+ grant: AccessGrant,
174
+ ): Promise<ConnectionMetadata[]> {
175
+ const source = personalConnectionDelegationSourceForGrant(grant);
176
+ const workspace = (await listConnectionsMetadata(db, grant.workspaceId, null)).filter(
177
+ (connection) =>
178
+ connection.subjectId === null &&
179
+ connection.workspaceId === grant.workspaceId &&
180
+ connection.accountId === grant.accountId &&
181
+ connection.status === "active",
182
+ );
183
+ if (source.kind !== "subject") return workspace;
184
+ const personal = await listOwnConnectionMetadata(db, {
185
+ ...source,
186
+ workspaceId: grant.workspaceId,
187
+ });
188
+ return [...workspace, ...personal];
189
+ }
190
+
191
+ async function listOwnConnectionMetadata(
192
+ db: Database,
193
+ input: { accountId: string; workspaceId: string; subjectId: string },
194
+ ): Promise<ConnectionMetadata[]> {
195
+ const accounts = await listOwnedConnectionAccounts(db, input);
196
+ const connections = await Promise.all(
197
+ accounts.map(async (account) => {
198
+ const connection = await getConnectionMetadata(
199
+ db,
200
+ account.originWorkspaceId,
201
+ account.connectionId,
202
+ input.subjectId,
203
+ );
204
+ return connection?.accountId === input.accountId &&
205
+ connection.subjectId === input.subjectId &&
206
+ connection.authorityId != null &&
207
+ connection.status === "active"
208
+ ? connection
209
+ : null;
210
+ }),
211
+ );
212
+ return connections.filter((connection) => connection !== null);
213
+ }
214
+
160
215
  export async function authorizedSocialConnectionsForGrant(input: {
161
216
  db: Database;
162
217
  grant: AccessGrant;
@@ -267,7 +322,7 @@ export async function authorizedAtlassianConnectionsForGrant(input: {
267
322
  }
268
323
  const connection = await getConnectionMetadata(
269
324
  input.db,
270
- input.grant.workspaceId,
325
+ delegation.originWorkspaceId ?? input.grant.workspaceId,
271
326
  delegation.connectionId,
272
327
  delegation.ownerSubjectId,
273
328
  );
@@ -301,31 +356,30 @@ function isNativeSubjectConnectionRef(
301
356
  return ref?.subjectScope === "subject" && ref.authoritySource !== "host";
302
357
  }
303
358
 
304
- function canonicalPersonalConnections(connections: ConnectionMetadata[]): ConnectionMetadata[] {
305
- return [...connections].sort((left, right) => {
306
- const active = Number(right.status === "active") - Number(left.status === "active");
307
- if (active !== 0) return active;
308
- const updated = Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
309
- if (updated !== 0) return updated;
310
- const created = Date.parse(right.createdAt) - Date.parse(left.createdAt);
311
- if (created !== 0) return created;
312
- return right.id.localeCompare(left.id);
313
- });
314
- }
315
-
316
359
  function sameProviderDomain(left: string, right: string): boolean {
317
360
  return left.toLowerCase() === right.toLowerCase();
318
361
  }
319
362
 
363
+ function canonicalConnectionResource(value: string): string {
364
+ try {
365
+ const url = new URL(value);
366
+ if (url.protocol !== "http:" && url.protocol !== "https:") return value.trim();
367
+ url.hash = "";
368
+ url.pathname = url.pathname.replace(/\/+$/, "") || "/";
369
+ return url.toString();
370
+ } catch {
371
+ return value.trim();
372
+ }
373
+ }
374
+
320
375
  export function personalConnectionDelegationsFromVisibleConnections(input: {
321
376
  servers: McpServerConfig[];
322
377
  subjectId: string;
323
378
  connections: ConnectionMetadata[];
324
- authoritySelections?: McpConnectionAuthoritySelection[];
325
- rejectUnselectedActivatedConnections?: boolean;
379
+ authoritySelections?: McpConnectionAccountSelection[];
326
380
  }): McpPersonalConnectionDelegation[] {
327
381
  const delegations: McpPersonalConnectionDelegation[] = [];
328
- const connections = canonicalPersonalConnections(input.connections);
382
+ const connections = input.connections;
329
383
  const selections = new Map(
330
384
  (input.authoritySelections ?? []).map((selection) => [selection.serverId, selection]),
331
385
  );
@@ -337,52 +391,38 @@ export function personalConnectionDelegationsFromVisibleConnections(input: {
337
391
  (candidate) =>
338
392
  candidate.subjectId === input.subjectId &&
339
393
  candidate.status === "active" &&
394
+ candidate.authorityId !== undefined &&
395
+ candidate.authorityId !== null &&
340
396
  sameProviderDomain(candidate.providerDomain, ref.providerDomain) &&
341
397
  (!ref.kind || candidate.kind === ref.kind) &&
398
+ (!ref.resource ||
399
+ (typeof candidate.metadata.resource === "string" &&
400
+ canonicalConnectionResource(candidate.metadata.resource) ===
401
+ canonicalConnectionResource(ref.resource))) &&
342
402
  (!ref.connectionId || candidate.id === ref.connectionId),
343
403
  );
344
- // An explicit accepted-work selection is authoritative. Canonical newest
345
- // ordering is only the bounded legacy/omission fallback; it must never
346
- // replace a caller's exact opaque connection UUID when several accounts
347
- // share one server/provider tuple.
404
+ if (!selection && eligible.length > 1) {
405
+ throw new ConnectionAccountSelectionError(
406
+ `Choose an account for ${server.id}: multiple connected accounts match.`,
407
+ );
408
+ }
348
409
  const connection = selection
349
410
  ? eligible.find((candidate) => candidate.id === selection.connectionId)
350
411
  : eligible[0];
351
412
  if (!connection) continue;
352
- if (connection.authorityId) {
353
- if (
354
- !selection ||
355
- selection.connectionId !== connection.id ||
356
- selection.userDelegation.authorityId !== connection.authorityId
357
- ) {
358
- if (input.rejectUnselectedActivatedConnections) {
359
- throw new Error(
360
- `scheduled connection authority selection is required for activated server ${server.id}`,
361
- );
362
- }
363
- // Activated organization-user connections are never admitted through
364
- // the legacy owner tuple. The caller must select one exact opaque grant.
365
- continue;
366
- }
367
- selections.delete(server.id);
368
- } else if (selection) {
369
- throw new Error(`connection authority is not available for legacy server ${server.id}`);
370
- }
413
+ selections.delete(server.id);
371
414
  delegations.push({
372
415
  serverId: server.id,
373
416
  connectionId: connection.id,
374
- ...(selection ? { originWorkspaceId: connection.workspaceId } : {}),
417
+ originWorkspaceId: connection.workspaceId,
375
418
  ownerSubjectId: input.subjectId,
376
419
  providerDomain: connection.providerDomain,
377
420
  kind: connection.kind,
378
- ...(selection ? { userDelegation: selection.userDelegation } : {}),
379
421
  });
380
422
  }
381
423
  if (selections.size > 0) {
382
- throw new Error(
383
- `connection authority selection did not match an active selected server: ${[
384
- ...selections.keys(),
385
- ].join(", ")}`,
424
+ throw new ConnectionAccountSelectionError(
425
+ `Selected account is unavailable for: ${[...selections.keys()].join(", ")}`,
386
426
  );
387
427
  }
388
428
  return delegations;
@@ -393,18 +433,7 @@ export function personalConnectionDelegationsFromParent(input: {
393
433
  parentDelegations: McpPersonalConnectionDelegation[];
394
434
  personalGitHubResources?: ResourceRef[];
395
435
  targetSessionId?: string;
396
- rejectActivatedConnections?: boolean;
397
436
  }): McpPersonalConnectionDelegation[] {
398
- // Every successor is new logical work. `once` remains bound to the original
399
- // accepted turn, `session` may be re-admitted only into that exact session,
400
- // and `always` may cross to another authorized session.
401
- const childEligible = (delegation: McpPersonalConnectionDelegation) => {
402
- const authority = delegation.userDelegation;
403
- if (!authority) return true;
404
- if (authority.mode === "once") return false;
405
- if (authority.mode === "session") return authority.sessionId === input.targetSessionId;
406
- return true;
407
- };
408
437
  const mcp = input.servers.flatMap((server) => {
409
438
  const ref = server.connectionRef;
410
439
  if (!isNativeSubjectConnectionRef(ref)) return [];
@@ -414,18 +443,17 @@ export function personalConnectionDelegationsFromParent(input: {
414
443
  sameProviderDomain(candidate.providerDomain, ref.providerDomain) &&
415
444
  (!ref.kind || !candidate.kind || candidate.kind === ref.kind),
416
445
  );
417
- return delegation && childEligible(delegation) ? [{ ...delegation }] : [];
446
+ return delegation ? [{ ...delegation }] : [];
418
447
  });
419
448
  const projected = [
420
449
  ...mcp,
421
450
  ...input.parentDelegations
422
451
  .filter(
423
452
  (item) =>
424
- childEligible(item) &&
425
- (item.connectionType === "social" ||
426
- item.connectionType === "atlassian" ||
427
- item.connectionType === "github_personal" ||
428
- item.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID),
453
+ item.connectionType === "social" ||
454
+ item.connectionType === "atlassian" ||
455
+ item.connectionType === "github_personal" ||
456
+ item.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID,
429
457
  )
430
458
  .map((item) => ({ ...item })),
431
459
  ];
@@ -464,14 +492,6 @@ export function personalConnectionDelegationsFromParent(input: {
464
492
  ) {
465
493
  throw new Error("agent-created personal GitHub repository authority is unavailable");
466
494
  }
467
- if (
468
- input.rejectActivatedConnections &&
469
- inherited.some((delegation) => delegation.userDelegation)
470
- ) {
471
- throw new Error(
472
- "scheduled connection authority is not available until task occurrence authority is activated",
473
- );
474
- }
475
495
  return inherited;
476
496
  }
477
497
 
@@ -484,14 +504,14 @@ export function personalConnectionDelegationsFromParent(input: {
484
504
  export function googleDrivePublicationDelegationFromVisibleConnections(input: {
485
505
  subjectId: string;
486
506
  connections: ConnectionMetadata[];
487
- authoritySelection?: McpConnectionAuthoritySelection;
488
- rejectUnselectedActivatedConnection?: boolean;
507
+ authoritySelection?: McpConnectionAccountSelection;
489
508
  }): McpPersonalConnectionDelegation | null {
490
509
  const selection = input.authoritySelection;
491
510
  const eligible = input.connections.filter((connection) => {
492
511
  if (
493
512
  connection.subjectId !== input.subjectId ||
494
513
  connection.status !== "active" ||
514
+ !connection.authorityId ||
495
515
  connection.kind !== "oauth2" ||
496
516
  !sameProviderDomain(connection.providerDomain, GOOGLE_DRIVE_PROVIDER_DOMAIN) ||
497
517
  !googleDriveScopesAllowCapability(connection.grantedScopes, "publish_file")
@@ -514,22 +534,8 @@ export function googleDrivePublicationDelegationFromVisibleConnections(input: {
514
534
  }
515
535
  if (!selection && selectedEligible.length !== 1) return null;
516
536
  const connection = selectedEligible[0]!;
517
- if (connection.authorityId) {
518
- if (
519
- !selection ||
520
- selection.serverId !== GOOGLE_DRIVE_PUBLICATION_SERVER_ID ||
521
- selection.connectionId !== connection.id ||
522
- selection.userDelegation.authorityId !== connection.authorityId
523
- ) {
524
- if (input.rejectUnselectedActivatedConnection) {
525
- throw new Error(
526
- "scheduled connection authority selection is required for activated Google Drive publication",
527
- );
528
- }
529
- return null;
530
- }
531
- } else if (selection) {
532
- throw new Error("connection authority is not available for legacy Google Drive publication");
537
+ if (selection && selection.serverId !== GOOGLE_DRIVE_PUBLICATION_SERVER_ID) {
538
+ throw new Error("Google Drive publication account selection is invalid");
533
539
  }
534
540
  // Freeze the exact output destination on the accepted delegation: a later
535
541
  // connection-settings change must never redirect an already-accepted turn's
@@ -542,11 +548,10 @@ export function googleDrivePublicationDelegationFromVisibleConnections(input: {
542
548
  return {
543
549
  serverId: GOOGLE_DRIVE_PUBLICATION_SERVER_ID,
544
550
  connectionId: connection.id,
545
- ...(selection ? { originWorkspaceId: connection.workspaceId } : {}),
551
+ originWorkspaceId: connection.workspaceId,
546
552
  ownerSubjectId: input.subjectId,
547
553
  providerDomain: connection.providerDomain,
548
554
  kind: connection.kind,
549
- ...(selection ? { userDelegation: selection.userDelegation } : {}),
550
555
  outputDestination: frozenMetadata.outputDestination,
551
556
  };
552
557
  }
@@ -554,7 +559,6 @@ export function googleDrivePublicationDelegationFromVisibleConnections(input: {
554
559
  export function personalAtlassianDelegationsFromVisibleConnections(input: {
555
560
  subjectId: string;
556
561
  connections: ConnectionMetadata[];
557
- rejectActivatedConnections?: boolean;
558
562
  }): McpPersonalConnectionDelegation[] {
559
563
  const eligible = input.connections.filter(
560
564
  (connection) =>
@@ -562,19 +566,12 @@ export function personalAtlassianDelegationsFromVisibleConnections(input: {
562
566
  connection.status === "active" &&
563
567
  sameProviderDomain(connection.providerDomain, "api.atlassian.com"),
564
568
  );
565
- if (input.rejectActivatedConnections && eligible.some((connection) => connection.authorityId)) {
566
- throw new Error(
567
- "scheduled connection authority selection is required for activated Atlassian access",
568
- );
569
- }
570
- // Atlassian's first-party multi-call adapter is a named successor. Until it
571
- // can consume an accepted snapshot, common-authority rows are omitted rather
572
- // than falling through the legacy owner-tuple resolver.
573
569
  return eligible
574
- .filter((connection) => !connection.authorityId)
570
+ .filter((connection) => connection.authorityId != null)
575
571
  .map((connection) => ({
576
572
  serverId: `atlassian:${connection.id}`,
577
573
  connectionId: connection.id,
574
+ originWorkspaceId: connection.workspaceId,
578
575
  ownerSubjectId: input.subjectId,
579
576
  providerDomain: connection.providerDomain,
580
577
  kind: connection.kind,
@@ -588,7 +585,7 @@ async function personalGitHubDelegationFromVisibleConnections(input: {
588
585
  subjectId: string;
589
586
  connections: ConnectionMetadata[];
590
587
  resources: ResourceRef[];
591
- authoritySelection?: McpConnectionAuthoritySelection;
588
+ authoritySelection?: McpConnectionAccountSelection;
592
589
  }): Promise<McpPersonalConnectionDelegation | null> {
593
590
  const resources = personalGitHubRepositoryResources(input.resources);
594
591
  if (resources.length === 0) {
@@ -602,27 +599,25 @@ async function personalGitHubDelegationFromVisibleConnections(input: {
602
599
  throw new Error("accepted work may use only one personal GitHub account");
603
600
  }
604
601
  const authoritySelection = input.authoritySelection;
605
- if (!authoritySelection) {
606
- throw new Error("personal GitHub repository resources require explicit connection authority");
607
- }
602
+ const credentialBindingId = [...bindingIds][0]!;
608
603
  const connection = input.connections.find(
609
604
  (candidate) =>
610
- candidate.id === authoritySelection.connectionId &&
605
+ (!authoritySelection || candidate.id === authoritySelection.connectionId) &&
611
606
  candidate.subjectId === input.subjectId &&
612
607
  candidate.status === "active" &&
613
- isPersonalGitHubConnection(candidate),
608
+ isPersonalGitHubConnection(candidate) &&
609
+ PersonalGitHubConnectionMetadata.safeParse(candidate.metadata).data?.credentialBindingId ===
610
+ credentialBindingId,
614
611
  );
615
612
  if (
616
613
  !connection ||
617
614
  !connection.authorityId ||
618
- connection.authorityId !== authoritySelection.userDelegation.authorityId ||
619
615
  connection.grantedScopes.length !== 1 ||
620
616
  connection.grantedScopes[0] !== "repo"
621
617
  ) {
622
618
  throw new Error("personal GitHub connection authority selection is unavailable");
623
619
  }
624
620
  const metadata = PersonalGitHubConnectionMetadata.parse(connection.metadata);
625
- const credentialBindingId = [...bindingIds][0]!;
626
621
  if (metadata.credentialBindingId !== credentialBindingId) {
627
622
  throw new Error("personal GitHub credential binding does not match the selected connection");
628
623
  }
@@ -669,7 +664,6 @@ async function personalGitHubDelegationFromVisibleConnections(input: {
669
664
  providerDomain: "github.com",
670
665
  kind: "oauth2",
671
666
  connectionType: "github_personal",
672
- userDelegation: authoritySelection.userDelegation,
673
667
  personalGitHubRepositorySelection: {
674
668
  credentialBindingId,
675
669
  connectionAuthorityGeneration: selection.connectionAuthorityGeneration,
@@ -689,13 +683,12 @@ export function personalConnectionDelegationsEqual(
689
683
  const other = byServer.get(delegation.serverId);
690
684
  return (
691
685
  other?.connectionId === delegation.connectionId &&
686
+ other.canonicalServerId === delegation.canonicalServerId &&
692
687
  other.originWorkspaceId === delegation.originWorkspaceId &&
693
688
  other.ownerSubjectId === delegation.ownerSubjectId &&
694
689
  sameProviderDomain(other.providerDomain, delegation.providerDomain) &&
695
690
  other.kind === delegation.kind &&
696
691
  other.connectionType === delegation.connectionType &&
697
- JSON.stringify(other.userDelegation ?? null) ===
698
- JSON.stringify(delegation.userDelegation ?? null) &&
699
692
  JSON.stringify(other.personalGitHubRepositorySelection ?? null) ===
700
693
  JSON.stringify(delegation.personalGitHubRepositorySelection ?? null)
701
694
  );
@@ -776,11 +769,7 @@ export function withFrozenPersonalConnectionDelegations(input: {
776
769
  : publicationDelegations.length === 1
777
770
  ? publicationDelegations[0]!
778
771
  : null;
779
- if (
780
- !delegation ||
781
- (!delegation.userDelegation &&
782
- !(await input.ownerHasWorkspaceMembership(delegation.ownerSubjectId)))
783
- ) {
772
+ if (!delegation || !(await input.ownerHasWorkspaceMembership(delegation.ownerSubjectId))) {
784
773
  return personalAuthorityUnavailable(request);
785
774
  }
786
775
  effectiveRequest = {
@@ -802,6 +791,101 @@ export function withFrozenPersonalConnectionDelegations(input: {
802
791
  };
803
792
  }
804
793
 
794
+ /** Freeze all native account routes without conflating workspace ownership
795
+ * with the exact causal human required for personal connections. */
796
+ export async function freezeConnectionAccounts(
797
+ input: Parameters<typeof freezePersonalConnectionDelegations>[0] & { accountId: string },
798
+ ): Promise<{
799
+ mcpAccountBindings: McpConnectionAccountBinding[] | null;
800
+ personalConnectionDelegations: McpPersonalConnectionDelegation[];
801
+ }> {
802
+ const selectedIds = new Set(input.tools.map((tool) => tool.id));
803
+ const servers = input.settings.mcpServers.filter(
804
+ (server) =>
805
+ selectedIds.has(server.id) &&
806
+ server.connectionRef &&
807
+ server.connectionRef.authoritySource !== "host",
808
+ );
809
+ const serverIds = new Set(servers.map((server) => server.id));
810
+ let mcpAccountBindings: McpConnectionAccountBinding[];
811
+ if (input.source.kind === "turn") {
812
+ if (input.authoritySelections?.length) {
813
+ throw new ConnectionAccountSelectionError(
814
+ "Agent-created work inherits the exact parent accounts",
815
+ );
816
+ }
817
+ const inherited = await getSessionTurnMcpAccountBindings(
818
+ input.db,
819
+ input.workspaceId,
820
+ input.source.sessionId,
821
+ input.source.turnId,
822
+ );
823
+ if (inherited === null) {
824
+ return {
825
+ mcpAccountBindings: null,
826
+ personalConnectionDelegations: await freezePersonalConnectionDelegations(input),
827
+ };
828
+ }
829
+ mcpAccountBindings = inherited.filter((binding) => serverIds.has(binding.canonicalServerId));
830
+ } else {
831
+ const personal =
832
+ input.source.kind === "subject" &&
833
+ (await ownerStillBelongsToWorkspace(input.db, {
834
+ accountId: input.accountId,
835
+ workspaceId: input.workspaceId,
836
+ subjectId: input.source.subjectId,
837
+ }))
838
+ ? await listOwnConnectionMetadata(input.db, {
839
+ accountId: input.accountId,
840
+ workspaceId: input.workspaceId,
841
+ subjectId: input.source.subjectId,
842
+ })
843
+ : [];
844
+ const workspace = await listConnectionsMetadata(input.db, input.workspaceId, null);
845
+ mcpAccountBindings = mcpAccountBindingsFromVisibleConnections({
846
+ accountId: input.accountId,
847
+ workspaceId: input.workspaceId,
848
+ subjectId: input.source.kind === "subject" ? input.source.subjectId : null,
849
+ servers,
850
+ selectionsFrozen: input.authoritySelectionsFrozen === true,
851
+ connections: [
852
+ ...workspace.filter((connection) => connection.subjectId === null),
853
+ ...personal,
854
+ ],
855
+ ...(input.authoritySelections
856
+ ? {
857
+ selections: input.authoritySelections.filter((selection) =>
858
+ serverIds.has(selection.serverId),
859
+ ),
860
+ }
861
+ : {}),
862
+ });
863
+ }
864
+ // Dedicated first-party publication/repository/social surfaces keep their
865
+ // existing selection and authority contracts; they are not generic MCP routes.
866
+ const special = await freezePersonalConnectionDelegations({
867
+ ...input,
868
+ settings: {
869
+ ...input.settings,
870
+ mcpServers: input.settings.mcpServers.filter((server) => !serverIds.has(server.id)),
871
+ },
872
+ ...(input.authoritySelections
873
+ ? {
874
+ authoritySelections: input.authoritySelections.filter(
875
+ (selection) => !serverIds.has(selection.serverId),
876
+ ),
877
+ }
878
+ : {}),
879
+ });
880
+ return {
881
+ mcpAccountBindings,
882
+ personalConnectionDelegations: [
883
+ ...personalDelegationsForAccountBindings(mcpAccountBindings),
884
+ ...special,
885
+ ],
886
+ };
887
+ }
888
+
805
889
  export async function freezePersonalConnectionDelegations(input: {
806
890
  db: Database;
807
891
  workspaceId: string;
@@ -809,8 +893,9 @@ export async function freezePersonalConnectionDelegations(input: {
809
893
  tools: ToolRef[];
810
894
  resources?: ResourceRef[];
811
895
  source: PersonalConnectionDelegationSource;
812
- authoritySelections?: McpConnectionAuthoritySelection[];
813
- rejectUnselectedActivatedConnections?: boolean;
896
+ authoritySelections?: McpConnectionAccountSelection[];
897
+ /** Internal durable schedule snapshot, not a default account selector. */
898
+ authoritySelectionsFrozen?: boolean;
814
899
  /** Exact first-party export tool + permission gate, not broad opengeni attachment. */
815
900
  googleDrivePublicationEnabled?: boolean;
816
901
  /** Exact first-party Atlassian tool + permission gate. */
@@ -853,9 +938,6 @@ export async function freezePersonalConnectionDelegations(input: {
853
938
  ),
854
939
  personalGitHubResources,
855
940
  ...(input.targetSessionId ? { targetSessionId: input.targetSessionId } : {}),
856
- ...(input.rejectUnselectedActivatedConnections !== undefined
857
- ? { rejectActivatedConnections: input.rejectUnselectedActivatedConnections }
858
- : {}),
859
941
  });
860
942
  return inherited.filter((item) => {
861
943
  if (item.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID) {
@@ -876,54 +958,22 @@ export async function freezePersonalConnectionDelegations(input: {
876
958
  workspaceId: input.workspaceId,
877
959
  subjectId: ownerSubjectId,
878
960
  });
879
- const targetLocalConnections = await listConnectionsMetadata(
880
- input.db,
881
- input.workspaceId,
882
- ownerSubjectId,
883
- );
884
- const portableSelections = await Promise.all(
885
- (input.authoritySelections ?? []).map(async (selection) => {
886
- const originWorkspaceId = await resolvePersonalConnectionAuthoritySelectionOrigin(input.db, {
887
- accountId: selection.userDelegation.organizationId,
888
- targetWorkspaceId: input.workspaceId,
889
- subjectId: ownerSubjectId,
890
- connectionId: selection.connectionId,
891
- delegation: selection.userDelegation,
892
- });
893
- if (!originWorkspaceId) {
894
- throw new Error(`connection authority selection is unavailable: ${selection.serverId}`);
895
- }
896
- const connection = await getConnectionMetadata(
897
- input.db,
898
- originWorkspaceId,
899
- selection.connectionId,
900
- ownerSubjectId,
961
+ if (!membership) {
962
+ if ((input.authoritySelections?.length ?? 0) > 0) {
963
+ throw new ConnectionAccountSelectionError(
964
+ "Selected connection account requires live owner workspace access",
901
965
  );
902
- if (
903
- !connection ||
904
- connection.workspaceId !== originWorkspaceId ||
905
- connection.subjectId !== ownerSubjectId ||
906
- connection.authorityId !== selection.userDelegation.authorityId
907
- ) {
908
- throw new Error(`connection authority selection is unavailable: ${selection.serverId}`);
909
- }
910
- return connection;
911
- }),
912
- );
913
- if (!membership && portableSelections.length === 0) {
966
+ }
914
967
  if (personalGitHubResources.length > 0) {
915
968
  throw new Error("personal GitHub repository authority requires live causal user authority");
916
969
  }
917
970
  return [];
918
971
  }
919
- const visibleConnections = [
920
- ...new Map(
921
- [...targetLocalConnections, ...portableSelections].map((connection) => [
922
- connection.id,
923
- connection,
924
- ]),
925
- ).values(),
926
- ];
972
+ const visibleConnections = await listOwnConnectionMetadata(input.db, {
973
+ accountId: input.source.accountId,
974
+ workspaceId: input.workspaceId,
975
+ subjectId: ownerSubjectId,
976
+ });
927
977
  const selectedMcpServerIds = new Set(servers.map((server) => server.id));
928
978
  const supportedSelectionIds = new Set(selectedMcpServerIds);
929
979
  if (input.googleDrivePublicationEnabled) {
@@ -936,7 +986,7 @@ export async function freezePersonalConnectionDelegations(input: {
936
986
  (selection) => !supportedSelectionIds.has(selection.serverId),
937
987
  );
938
988
  if (unsupportedSelections.length > 0) {
939
- throw new Error(
989
+ throw new ConnectionAccountSelectionError(
940
990
  `connection authority selection did not match a selected MCP server: ${unsupportedSelections
941
991
  .map((selection) => selection.serverId)
942
992
  .join(", ")}`,
@@ -949,9 +999,6 @@ export async function freezePersonalConnectionDelegations(input: {
949
999
  authoritySelections: (input.authoritySelections ?? []).filter((selection) =>
950
1000
  selectedMcpServerIds.has(selection.serverId),
951
1001
  ),
952
- ...(input.rejectUnselectedActivatedConnections !== undefined
953
- ? { rejectUnselectedActivatedConnections: input.rejectUnselectedActivatedConnections }
954
- : {}),
955
1002
  });
956
1003
  const personalGitHubAuthoritySelection = (input.authoritySelections ?? []).find(
957
1004
  (selection) => selection.serverId === PERSONAL_GITHUB_CONNECTION_SURFACE_ID,
@@ -960,7 +1007,11 @@ export async function freezePersonalConnectionDelegations(input: {
960
1007
  db: input.db,
961
1008
  accountId: input.source.accountId,
962
1009
  subjectId: ownerSubjectId,
963
- connections: visibleConnections,
1010
+ connections: input.authoritySelectionsFrozen
1011
+ ? visibleConnections.filter(
1012
+ (connection) => connection.id === personalGitHubAuthoritySelection?.connectionId,
1013
+ )
1014
+ : visibleConnections,
964
1015
  resources: input.resources ?? [],
965
1016
  ...(personalGitHubAuthoritySelection
966
1017
  ? { authoritySelection: personalGitHubAuthoritySelection }
@@ -986,9 +1037,6 @@ export async function freezePersonalConnectionDelegations(input: {
986
1037
  ? personalAtlassianDelegationsFromVisibleConnections({
987
1038
  subjectId: ownerSubjectId,
988
1039
  connections: visibleConnections,
989
- ...(input.rejectUnselectedActivatedConnections !== undefined
990
- ? { rejectActivatedConnections: input.rejectUnselectedActivatedConnections }
991
- : {}),
992
1040
  })
993
1041
  : [];
994
1042
  const googleDriveAuthoritySelection = input.googleDrivePublicationEnabled
@@ -999,13 +1047,14 @@ export async function freezePersonalConnectionDelegations(input: {
999
1047
  const googleDrivePublication = input.googleDrivePublicationEnabled
1000
1048
  ? googleDrivePublicationDelegationFromVisibleConnections({
1001
1049
  subjectId: ownerSubjectId,
1002
- connections: visibleConnections,
1050
+ connections: input.authoritySelectionsFrozen
1051
+ ? visibleConnections.filter(
1052
+ (connection) => connection.id === googleDriveAuthoritySelection?.connectionId,
1053
+ )
1054
+ : visibleConnections,
1003
1055
  ...(googleDriveAuthoritySelection
1004
1056
  ? { authoritySelection: googleDriveAuthoritySelection }
1005
1057
  : {}),
1006
- ...(input.rejectUnselectedActivatedConnections !== undefined
1007
- ? { rejectUnselectedActivatedConnection: input.rejectUnselectedActivatedConnections }
1008
- : {}),
1009
1058
  })
1010
1059
  : null;
1011
1060
  return [