@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.
- package/README.md +1 -1
- package/dist/application/connect-operation.d.ts +3 -0
- package/dist/application/sandbox-recovery.d.ts +49 -0
- package/dist/{chunk-H7X52RI2.js → chunk-TFHSE7XR.js} +13 -2
- package/dist/chunk-TFHSE7XR.js.map +1 -0
- package/dist/dependencies.d.ts +1 -1
- package/dist/domain/capabilities.d.ts +2 -2
- package/dist/domain/connection-account-selection-error.d.ts +6 -0
- package/dist/domain/connector-tool-permissions.d.ts +27 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +8 -0
- package/dist/domain/environments.d.ts +3 -7
- package/dist/domain/insights-timing.d.ts +16 -0
- package/dist/domain/insights.d.ts +4 -1
- package/dist/domain/knowledge-files.d.ts +1 -1
- package/dist/domain/knowledge-preparation.d.ts +9 -0
- package/dist/domain/knowledge-preparation.test.d.ts +1 -0
- package/dist/domain/mcp-account-bindings.d.ts +19 -0
- package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
- package/dist/domain/organization-integration-catalog.d.ts +15 -0
- package/dist/domain/personal-connection-delegations.d.ts +19 -10
- package/dist/domain/pr-review.d.ts +56 -4
- package/dist/domain/scheduled-tasks.d.ts +8 -5
- package/dist/domain/session-file-access.d.ts +4 -0
- package/dist/domain/sessions.d.ts +8 -6
- package/dist/domain/skills.d.ts +29 -1
- package/dist/editable-artifacts.js +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4868 -4803
- package/dist/index.js.map +1 -1
- package/dist/sandbox/routing.d.ts +2 -1
- package/dist/sandbox/runtime-settings.d.ts +1 -15
- package/package.json +11 -19
- package/src/application/composer-submit.ts +2 -1
- package/src/application/connect-operation.ts +32 -3
- package/src/application/sandbox-recovery.ts +55 -0
- package/src/application/user-resource-grants.ts +1 -16
- package/src/dependencies.ts +5 -1
- package/src/domain/capabilities.ts +169 -122
- package/src/domain/connection-account-selection-error.ts +9 -0
- package/src/domain/connector-tool-permissions.ts +305 -0
- package/src/domain/editable-artifacts/agent-application.ts +31 -2
- package/src/domain/environments.ts +4 -9
- package/src/domain/file-owner.ts +16 -1
- package/src/domain/insights-timing.ts +95 -0
- package/src/domain/insights.ts +59 -35
- package/src/domain/knowledge-files.ts +10 -3
- package/src/domain/knowledge-messages.ts +1 -0
- package/src/domain/knowledge-preparation.test.ts +251 -0
- package/src/domain/knowledge-preparation.ts +126 -0
- package/src/domain/mcp-account-bindings.ts +152 -0
- package/src/domain/native-mcp-connection-admission.ts +14 -0
- package/src/domain/organization-integration-catalog.ts +83 -0
- package/src/domain/personal-connection-delegations.ts +226 -177
- package/src/domain/pr-review.ts +22 -6
- package/src/domain/resources.ts +9 -2
- package/src/domain/scheduled-tasks.ts +200 -155
- package/src/domain/session-file-access.ts +47 -0
- package/src/domain/session-tool-policy.ts +12 -4
- package/src/domain/sessions.ts +390 -261
- package/src/domain/skills.ts +9 -0
- package/src/index.ts +10 -6
- package/src/rigs/index.ts +17 -17
- package/src/sandbox/routing.ts +18 -7
- package/src/sandbox/runtime-settings.ts +7 -91
- package/dist/application/host-mcp-owner.d.ts +0 -6
- package/dist/application/host-mcp-resolvers.d.ts +0 -13
- package/dist/chunk-5WZYFL45.js +0 -266
- package/dist/chunk-5WZYFL45.js.map +0 -1
- package/dist/chunk-H7X52RI2.js.map +0 -1
- package/dist/domain/connection-authority.d.ts +0 -97
- package/dist/domain/connection-authority.js +0 -169
- package/dist/domain/connection-authority.js.map +0 -1
- package/dist/domain/host-mcp-authority-source-admission.d.ts +0 -10
- package/dist/domain/host-mcp-task-admission.d.ts +0 -18
- package/dist/domain/packs.d.ts +0 -59
- package/dist/domain/product-integration-pack.d.ts +0 -44
- package/dist/domain/product-integration-skill.gen.d.ts +0 -5
- package/dist/remote-mcp-credentials.d.ts +0 -23
- package/dist/remote-mcp-credentials.js +0 -11
- package/dist/remote-mcp-credentials.js.map +0 -1
- package/src/application/host-mcp-owner.ts +0 -41
- package/src/application/host-mcp-resolvers.ts +0 -84
- package/src/domain/connection-authority.ts +0 -329
- package/src/domain/host-mcp-authority-source-admission.ts +0 -25
- package/src/domain/host-mcp-task-admission.ts +0 -121
- package/src/domain/packs.ts +0 -699
- package/src/domain/product-integration-pack.ts +0 -42
- package/src/domain/product-integration-skill.gen.ts +0 -52
- 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
|
-
|
|
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
|
-
|
|
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?:
|
|
325
|
-
rejectUnselectedActivatedConnections?: boolean;
|
|
379
|
+
authoritySelections?: McpConnectionAccountSelection[];
|
|
326
380
|
}): McpPersonalConnectionDelegation[] {
|
|
327
381
|
const delegations: McpPersonalConnectionDelegation[] = [];
|
|
328
|
-
const 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
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
383
|
-
`
|
|
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
|
|
446
|
+
return delegation ? [{ ...delegation }] : [];
|
|
418
447
|
});
|
|
419
448
|
const projected = [
|
|
420
449
|
...mcp,
|
|
421
450
|
...input.parentDelegations
|
|
422
451
|
.filter(
|
|
423
452
|
(item) =>
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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?:
|
|
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 (
|
|
518
|
-
|
|
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
|
-
|
|
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) =>
|
|
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?:
|
|
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
|
-
|
|
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?:
|
|
813
|
-
|
|
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
|
-
|
|
880
|
-
input.
|
|
881
|
-
|
|
882
|
-
|
|
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
|
-
|
|
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
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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 [
|