@opengeni/core 1.2.1-canary.37222965366001 → 1.3.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/index.js CHANGED
@@ -8,9 +8,11 @@ import {
8
8
  getManagedAuthRequestActorLeaseStamp,
9
9
  getManagedSession,
10
10
  markManagedAuthRequestActorTransitionApplied,
11
+ recordManagedAuthLoggedFailure,
11
12
  releaseManagedAuthRequestActorLease,
12
- validateManagedAuthRequestActorLease
13
- } from "./chunk-MM6N7CTO.js";
13
+ validateManagedAuthRequestActorLease,
14
+ withManagedAuthSessionLookup
15
+ } from "./chunk-672TAJCH.js";
14
16
  import "./chunk-QO5GVFFO.js";
15
17
  import {
16
18
  EDITABLE_ARTIFACT_HINT_MAX_BYTES,
@@ -835,7 +837,7 @@ function expandApiIntegrationAccountRoutes(input) {
835
837
  }
836
838
 
837
839
  // src/domain/session-connection-accounts.ts
838
- import { requireSession as requireSession3, requireWorkspace as requireWorkspace5 } from "@opengeni/db";
840
+ import { requireSession as requireSession3, requireWorkspace as requireWorkspace6 } from "@opengeni/db";
839
841
 
840
842
  // src/domain/capabilities.ts
841
843
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -4886,7 +4888,7 @@ import {
4886
4888
  listDistinctRigVersionIdsInGroup,
4887
4889
  listInstalledPortableSkills,
4888
4890
  listEnabledMcpCapabilityServers as listEnabledMcpCapabilityServers2,
4889
- requireApprovalWithFloor,
4891
+ resolveMcpApprovalRecommendation,
4890
4892
  getSandbox as getSandbox4,
4891
4893
  getSession as getSession3,
4892
4894
  getInitializedSessionCreateReplay,
@@ -4903,7 +4905,7 @@ import {
4903
4905
  getSessionTurnXaiProviderAccountAuthoritySnapshot,
4904
4906
  getSessionTurnClaudeProviderAccountAuthoritySnapshot,
4905
4907
  getWorkspaceModelPolicy as getWorkspaceModelPolicy2,
4906
- requireWorkspace as requireWorkspace4,
4908
+ requireWorkspace as requireWorkspace5,
4907
4909
  initializeSessionStartAtomically,
4908
4910
  listSessionTurns,
4909
4911
  listSessionMcpServersForChildInheritance,
@@ -8444,7 +8446,9 @@ import {
8444
8446
  areOpenGeniSlackBotScopesAccepted
8445
8447
  } from "@opengeni/contracts";
8446
8448
  import {
8447
- getConnectionMetadata as getConnectionMetadata3
8449
+ getConnectionMetadata as getConnectionMetadata3,
8450
+ availableSlackBotConnectionMetadata,
8451
+ requireWorkspace as requireWorkspace4
8448
8452
  } from "@opengeni/db";
8449
8453
  import { HTTPException as HTTPException16 } from "hono/http-exception";
8450
8454
  function openGeniSlackBotMetadata(metadata) {
@@ -8463,7 +8467,9 @@ function hasReservedOpenGeniSlackBotSessionMetadata(metadata) {
8463
8467
  );
8464
8468
  }
8465
8469
  async function requireOpenGeniSlackBotConnection(db, workspaceId, connectionId) {
8466
- const connection = await getConnectionMetadata3(db, workspaceId, connectionId, null);
8470
+ const local = await getConnectionMetadata3(db, workspaceId, connectionId, null);
8471
+ const workspace = await requireWorkspace4(db, workspaceId);
8472
+ const connection = local ?? (await availableSlackBotConnectionMetadata(db, { accountId: workspace.accountId, workspaceId })).find((candidate) => candidate.id === connectionId) ?? null;
8467
8473
  if (!connection || !isOpenGeniSlackBotConnection(connection)) {
8468
8474
  throw new HTTPException16(422, {
8469
8475
  message: "slackBotConnectionId must reference an Opengeni Slack bot connection"
@@ -8536,9 +8542,10 @@ async function validateScheduledTaskSlackChannel(input) {
8536
8542
  });
8537
8543
  }
8538
8544
  function withScheduledSlackBotPostingTools(tools, agentConfig, allowedTools) {
8539
- if (!agentConfig.slackBotConnectionId || !agentConfig.slackBotChannelId) return [...tools];
8545
+ const posting = new Set(SCHEDULED_SLACK_BOT_POSTING_TOOLS);
8546
+ const next = tools.filter((tool) => !posting.has(tool));
8547
+ if (!agentConfig.slackBotConnectionId || !agentConfig.slackBotChannelId) return next;
8540
8548
  const allowed = new Set(allowedTools);
8541
- const next = [...tools];
8542
8549
  for (const tool of SCHEDULED_SLACK_BOT_POSTING_TOOLS) {
8543
8550
  if (allowed.has(tool) && !next.includes(tool)) next.push(tool);
8544
8551
  }
@@ -10212,7 +10219,7 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
10212
10219
  const workspace = await measureSessionStartPhase(
10213
10220
  unresolvedDeps.observability,
10214
10221
  "workspace_read",
10215
- () => requireWorkspace4(db, workspaceId)
10222
+ () => requireWorkspace5(db, workspaceId)
10216
10223
  );
10217
10224
  const workspaceSessionToolDefaults = resolveWorkspaceSessionToolDefaults2(workspace.settings);
10218
10225
  const parentAuthority = parentSession ? await getSessionAuthorityProjection4(db, workspaceId, parentSession.id) : null;
@@ -10480,7 +10487,7 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
10480
10487
  message: `MCP approval policy must name an enabled inherited capability: ${id}${/^api_(openapi|graphql)_/.test(id) ? "; API Integration tool approval is set at install with autoApprovedTools" : ""}`
10481
10488
  });
10482
10489
  }
10483
- mcpApprovalPolicies[id] = requireApprovalWithFloor(policy, inherited.approvalFloor, true) ?? false;
10490
+ mcpApprovalPolicies[id] = resolveMcpApprovalRecommendation(policy, inherited.approvalFloor) ?? false;
10484
10491
  }
10485
10492
  }
10486
10493
  const resources = normalizeResources(
@@ -11454,7 +11461,7 @@ async function acceptSessionUserMessageInFileScope(deps, grant, workspaceId, ses
11454
11461
  runtimeMcpServers: runtimeSettings.mcpServers,
11455
11462
  defaultMcpServerIds: workspaceSessionToolPolicyDefaultServerIdsFor(
11456
11463
  capabilityRuntimeSettings.mcpServers,
11457
- (await requireWorkspace4(db, workspaceId)).settings
11464
+ (await requireWorkspace5(db, workspaceId)).settings
11458
11465
  )
11459
11466
  });
11460
11467
  const { personalConnectionDelegations, mcpAccountBindings } = await freezeConnectionAccounts({
@@ -11765,7 +11772,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
11765
11772
  requirePermission(grant, "sessions:control");
11766
11773
  const agentAttemptCaller = grantHasAgentAttemptAuthority(grant);
11767
11774
  const existingSession = await requireSession2(deps.db, grant.workspaceId, sessionId);
11768
- const workspace = await requireWorkspace4(deps.db, grant.workspaceId);
11775
+ const workspace = await requireWorkspace5(deps.db, grant.workspaceId);
11769
11776
  const workspaceSessionToolDefaults = resolveWorkspaceSessionToolDefaults2(workspace.settings);
11770
11777
  const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(
11771
11778
  deps.db,
@@ -12032,7 +12039,7 @@ async function updateSessionAgent(deps, grant, sessionId, request) {
12032
12039
  });
12033
12040
  requirePermission(grant, "sessions:control");
12034
12041
  const agentAttemptCaller = grantHasAgentAttemptAuthority(grant);
12035
- const workspace = await requireWorkspace4(deps.db, grant.workspaceId);
12042
+ const workspace = await requireWorkspace5(deps.db, grant.workspaceId);
12036
12043
  const goal = await getSessionGoal(deps.db, grant.workspaceId, sessionId);
12037
12044
  const goalOpen = goal !== null && goal.status !== "completed";
12038
12045
  const deploymentPolicy = agentConfigDeploymentPolicy2(deps.settings);
@@ -12319,7 +12326,7 @@ async function freezeSessionRealtimeConnectionAccounts(input) {
12319
12326
  runtimeMcpServers: runtimeSettings.mcpServers,
12320
12327
  defaultMcpServerIds: workspaceSessionToolPolicyDefaultServerIdsFor(
12321
12328
  capabilitySettings.mcpServers,
12322
- (await requireWorkspace5(db, workspaceId)).settings
12329
+ (await requireWorkspace6(db, workspaceId)).settings
12323
12330
  )
12324
12331
  });
12325
12332
  return await freezeConnectionAccounts({
@@ -13001,7 +13008,7 @@ import {
13001
13008
  grantWorkspaceAccess,
13002
13009
  listWorkspaceMembers,
13003
13010
  lockExternalWorkspaceMembershipLifecycle as lockExternalWorkspaceMembershipLifecycle3,
13004
- requireWorkspace as requireWorkspace6,
13011
+ requireWorkspace as requireWorkspace7,
13005
13012
  setRlsContext,
13006
13013
  withWorkspaceSubjectRls as withWorkspaceSubjectRls3,
13007
13014
  addExternalWorkspaceMemberOperation,
@@ -13061,7 +13068,7 @@ async function addExternalWorkspaceMemberForRequest(c, deps, workspaceId, input)
13061
13068
  if (live.accountId !== grant.accountId || live.subjectId !== grant.subjectId || payload.permissions.some((permission) => !hasPermission(live.permissions, permission))) {
13062
13069
  throw new HTTPException18(403, { message: "membership authority changed" });
13063
13070
  }
13064
- const workspace = await requireWorkspace6(tx, workspaceId);
13071
+ const workspace = await requireWorkspace7(tx, workspaceId);
13065
13072
  if (workspace.kind !== "shared" || workspace.accountId !== authority.accountId)
13066
13073
  throw new HTTPException18(403, {
13067
13074
  message: "external onboarding requires a shared organization workspace"
@@ -15617,7 +15624,7 @@ import {
15617
15624
  getWorkspaceDefaultRigId as getWorkspaceDefaultRigId2,
15618
15625
  withSessionRlsActorContext as withSessionRlsActorContext5,
15619
15626
  nestedPostgresSqlState as nestedPostgresSqlState6,
15620
- requireWorkspace as requireWorkspace7,
15627
+ requireWorkspace as requireWorkspace8,
15621
15628
  scopedKnowledgeScopeKey,
15622
15629
  updateScheduledTask,
15623
15630
  ScheduledTaskHeadChangedError,
@@ -15793,7 +15800,7 @@ async function createValidatedScheduledTask(input) {
15793
15800
  message: "Personal source tasks create a private session for each run"
15794
15801
  });
15795
15802
  if (learning) {
15796
- const workspace = await requireWorkspace7(input.db, input.grant.workspaceId);
15803
+ const workspace = await requireWorkspace8(input.db, input.grant.workspaceId);
15797
15804
  const destination = knowledgeAction?.destination.kind === "personal" || workspace.kind === "personal" || target?.tenancy?.visibility === "private" || target?.memoryScope === "user" ? "personal" : "workspace";
15798
15805
  if (learning.scope !== destination)
15799
15806
  throw new HTTPException26(422, {
@@ -17149,7 +17156,7 @@ async function validateScheduledTaskAgentConfig(input) {
17149
17156
  );
17150
17157
  const retainTools = unchangedCreationField("tools");
17151
17158
  const requestedTools = retainTools ? input.payload.agentConfig.tools : validateToolRefs(input.payload.agentConfig.tools ?? [], runtimeSettings);
17152
- const workspace = await requireWorkspace7(input.db, input.workspaceId);
17159
+ const workspace = await requireWorkspace8(input.db, input.workspaceId);
17153
17160
  if (parent?.tenancy?.visibility === "private" && workspace.kind !== "personal" && input.payload.runMode !== "existing_session" && !input.payload.targetSessionId) {
17154
17161
  throw new HTTPException26(422, {
17155
17162
  message: "Schedule this private chat as the existing-session target, or create the task in your Personal workspace."
@@ -17330,7 +17337,7 @@ import {
17330
17337
  listScheduledTaskHumanWaitAttention,
17331
17338
  listScheduledTaskCreatorPolicies,
17332
17339
  listScheduledTaskRunAuthNeededEvents,
17333
- requireWorkspace as requireWorkspace8,
17340
+ requireWorkspace as requireWorkspace9,
17334
17341
  ScheduledTaskHeadChangedError as ScheduledTaskHeadChangedError2
17335
17342
  } from "@opengeni/db";
17336
17343
  import { HTTPException as HTTPException27 } from "hono/http-exception";
@@ -17522,7 +17529,7 @@ async function computeScheduledTaskAccessPlan(input) {
17522
17529
  const [callerRegistry, ownerRegistry, workspaceSettings] = await Promise.all([
17523
17530
  runtimeRegistry(db, task.workspaceId, settings, input.grant.subjectId, cache),
17524
17531
  runtimeRegistry(db, task.workspaceId, settings, ownerSubjectId, cache),
17525
- input.workspaceSettings !== void 0 ? Promise.resolve(input.workspaceSettings) : requireWorkspace8(db, task.workspaceId).then((workspace) => workspace.settings)
17532
+ input.workspaceSettings !== void 0 ? Promise.resolve(input.workspaceSettings) : requireWorkspace9(db, task.workspaceId).then((workspace) => workspace.settings)
17526
17533
  ]);
17527
17534
  const followsTask = scheduledTaskToolsFollowTask(task);
17528
17535
  const target = task.runMode === "existing_session" && task.targetSessionId ? await getSession6(db, task.workspaceId, task.targetSessionId) : null;
@@ -17628,7 +17635,7 @@ async function withScheduledTaskPolicyDrift(input) {
17628
17635
  workspaceId,
17629
17636
  actionable.map((task) => task.id)
17630
17637
  ),
17631
- requireWorkspace8(input.db, workspaceId).then((workspace) => workspace.settings)
17638
+ requireWorkspace9(input.db, workspaceId).then((workspace) => workspace.settings)
17632
17639
  ]);
17633
17640
  } catch (error) {
17634
17641
  input.onError?.(error);
@@ -17790,7 +17797,7 @@ async function scheduledTasksWithUnavailableAccounts(input) {
17790
17797
  if (actionable.length === 0) return [];
17791
17798
  let workspaceSettings;
17792
17799
  try {
17793
- workspaceSettings = (await requireWorkspace8(input.db, input.grant.workspaceId)).settings;
17800
+ workspaceSettings = (await requireWorkspace9(input.db, input.grant.workspaceId)).settings;
17794
17801
  } catch (error) {
17795
17802
  input.onError?.(error);
17796
17803
  return [];
@@ -17949,7 +17956,7 @@ import {
17949
17956
  listScheduledTasks,
17950
17957
  readWorkspaceInsightsModelBundle,
17951
17958
  readWorkspaceInsightsUsageBundle,
17952
- requireWorkspace as requireWorkspace9
17959
+ requireWorkspace as requireWorkspace10
17953
17960
  } from "@opengeni/db";
17954
17961
 
17955
17962
  // src/domain/insights-timing.ts
@@ -18189,7 +18196,7 @@ async function getWorkspaceInsights(db, settings, input, observePhase) {
18189
18196
  await measureInsightsPhase(
18190
18197
  observePhase,
18191
18198
  "require_workspace",
18192
- () => requireWorkspace9(db, input.workspaceId)
18199
+ () => requireWorkspace10(db, input.workspaceId)
18193
18200
  );
18194
18201
  const [visibleRootScope, visibleSessionScope] = await Promise.all([
18195
18202
  rootSessionId ? getSessionAccessProjection(db, input.workspaceId, rootSessionId) : null,
@@ -21755,6 +21762,7 @@ import {
21755
21762
  directIntegrationTransport,
21756
21763
  IntegrationInvocationError
21757
21764
  } from "@opengeni/capabilities";
21765
+ import { apiIntegrationToolEffect } from "@opengeni/db";
21758
21766
  function buildApiIntegrationMcpServers(input) {
21759
21767
  const transport = createPinnedIntegrationTransport({
21760
21768
  network: input.settings,
@@ -21784,6 +21792,14 @@ function buildApiIntegrationMcpServers(input) {
21784
21792
  return {
21785
21793
  id: integration.serverId,
21786
21794
  server,
21795
+ effectAuthority: (toolName) => ({
21796
+ kind: "api_integration",
21797
+ instanceId: integration.instanceId,
21798
+ baseUrl: integration.baseUrl,
21799
+ connectionRef: integration.connectionRef,
21800
+ connectionAuthorityGeneration: integration.connectionAuthorityGeneration,
21801
+ effect: apiIntegrationToolEffect(integration.revision, toolName)
21802
+ }),
21787
21803
  approvalAuthority: {
21788
21804
  kind: "api_integration",
21789
21805
  capabilityId: integration.capabilityId,
@@ -22113,6 +22129,7 @@ async function retainKnowledgeMessage(db, context, messageId) {
22113
22129
 
22114
22130
  // src/domain/connector-tool-permissions.ts
22115
22131
  import { slackRestMcpToolsForScopes } from "@opengeni/contracts/slack-rest-mcp";
22132
+ import { connectionAccountIdentityLabel as connectionAccountIdentityLabel2 } from "@opengeni/contracts/connection-account-label";
22116
22133
  import { createHash as createHash11 } from "crypto";
22117
22134
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
22118
22135
  import { StreamableHTTPClientTransport as StreamableHTTPClientTransport2 } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -22128,8 +22145,12 @@ import {
22128
22145
  listConnectionsMetadata as listConnectionsMetadata4,
22129
22146
  listEnabledMcpCapabilityServers as listEnabledMcpCapabilityServers3,
22130
22147
  listConnectorToolPermissionPolicies,
22148
+ listChangedConnectorToolPermissions,
22131
22149
  updateConnectorToolPermissionPolicies,
22132
- resolveConnectorActionPolicy as resolveConnectorActionPolicy2
22150
+ projectConnectorToolPermission,
22151
+ connectorToolPolicyRevision,
22152
+ ConnectorToolPermissionConflictError,
22153
+ listInstalledApiIntegrations as listInstalledApiIntegrations2
22133
22154
  } from "@opengeni/db";
22134
22155
  import { HTTPException as HTTPException32 } from "hono/http-exception";
22135
22156
  import {
@@ -22159,9 +22180,50 @@ function connectorToolPermissionReference(ref, connectionId) {
22159
22180
  }
22160
22181
  async function resolveTarget2(input) {
22161
22182
  if (input.capabilityId.startsWith("api:")) {
22162
- throw new HTTPException32(404, {
22163
- message: "Connected MCP connector not found; API Integration tool approval is set with installApiIntegration autoApprovedTools"
22164
- });
22183
+ const integrations = (await listInstalledApiIntegrations2(
22184
+ input.db,
22185
+ input.workspaceId,
22186
+ input.personalOwnerVerified ? input.grant.subjectId : void 0
22187
+ )).filter((integration2) => integration2.capabilityId === input.capabilityId);
22188
+ const accountId = (integration2) => integration2.connectionRef?.connectionId ?? `session-mcp:${integration2.serverId}:${createHash11("sha256").update(integration2.baseUrl, "utf8").digest("hex")}`;
22189
+ const integration = integrations.find(
22190
+ (candidate) => (!input.instanceKey || candidate.instanceKey === input.instanceKey) && (!input.connectionId || accountId(candidate) === input.connectionId)
22191
+ );
22192
+ if (!integration) throw new HTTPException32(404, { message: "Integration account unavailable" });
22193
+ const server2 = settingsWithApiIntegrationServers({ ...input.settings, mcpServers: [] }, [
22194
+ integration
22195
+ ]).mcpServers[0];
22196
+ const connection2 = integration.connectionRef?.connectionId ? await getConnectionMetadata5(
22197
+ input.db,
22198
+ input.workspaceId,
22199
+ integration.connectionRef.connectionId,
22200
+ input.grant.subjectId
22201
+ ) : null;
22202
+ const accounts = integrations.map((candidate) => ({
22203
+ connectionId: accountId(candidate),
22204
+ label: candidate.displayName,
22205
+ scope: candidate.connectionRef?.subjectScope === "subject" ? "personal" : candidate.connectionRef ? "workspace" : "none",
22206
+ instanceKey: candidate.instanceKey
22207
+ }));
22208
+ return {
22209
+ server: server2,
22210
+ connection: connection2,
22211
+ connectionId: accountId(integration),
22212
+ accountLabel: connection2 ? connectionAccountIdentityLabel2(connection2.metadata, integration.displayName) : integration.displayName,
22213
+ accounts,
22214
+ instanceKey: integration.instanceKey,
22215
+ listedTools: integration.revision.tools.filter((tool) => integration.allowedTools.includes(tool.id)).map(
22216
+ (tool) => ({
22217
+ name: tool.id,
22218
+ title: tool.name,
22219
+ description: tool.description,
22220
+ annotations: {
22221
+ readOnlyHint: tool.safety === "read",
22222
+ destructiveHint: tool.safety === "destructive"
22223
+ }
22224
+ })
22225
+ )
22226
+ };
22165
22227
  }
22166
22228
  const catalog = await buildCapabilityCatalog(input);
22167
22229
  const item = catalog.items.find((candidate) => candidate.id === input.capabilityId);
@@ -22174,25 +22236,31 @@ async function resolveTarget2(input) {
22174
22236
  if (!server || server.url !== item.endpointUrl)
22175
22237
  throw new HTTPException32(409, { message: "Connector configuration is unavailable" });
22176
22238
  const ref = server.connectionRef;
22177
- if (ref?.authoritySource === "host")
22178
- throw new HTTPException32(409, {
22179
- message: "Tool permissions for this connection are managed by its host"
22239
+ if (ref?.subjectScope === "subject" && ref.accountSelection !== "all_eligible" && !input.personalOwnerVerified)
22240
+ throw new HTTPException32(403, {
22241
+ message: "Only the authenticated connection owner may manage personal tool permissions"
22180
22242
  });
22181
- let connection = ref?.connectionId ? await getConnectionMetadata5(
22243
+ const visible = ref ? await listConnectionsMetadata4(
22182
22244
  input.db,
22183
22245
  input.workspaceId,
22184
- ref.connectionId,
22185
- input.grant.subjectId
22186
- ) : null;
22187
- if (ref && !ref.connectionId && (ref.subjectScope === "subject" || ref.accountSelection === "all_eligible")) {
22188
- const visible = await listConnectionsMetadata4(
22189
- input.db,
22190
- input.workspaceId,
22191
- input.grant.subjectId
22192
- );
22193
- connection = unpinnedConnectorToolPermissionAccount(ref, visible, input.grant.subjectId);
22194
- }
22195
- if (ref && (!connection || connection.providerDomain !== ref.providerDomain || ref.kind && connection.kind !== ref.kind || (ref.subjectScope === "subject" ? connection.subjectId !== input.grant.subjectId : connection.subjectId !== null))) {
22246
+ input.personalOwnerVerified ? input.grant.subjectId : null
22247
+ ) : [];
22248
+ const bindings = mcpAccountBindingsFromVisibleConnections({
22249
+ accountId: input.grant.accountId,
22250
+ workspaceId: input.workspaceId,
22251
+ subjectId: input.personalOwnerVerified ? input.grant.subjectId : null,
22252
+ servers: [server],
22253
+ // Runtime admission can enumerate every eligible account. A permission
22254
+ // selector must also preserve the installation's explicit owner scope.
22255
+ connections: ref?.accountSelection === "all_eligible" ? visible : visible.filter(
22256
+ (candidate) => ref?.subjectScope === "subject" ? candidate.subjectId === input.grant.subjectId : candidate.subjectId === null
22257
+ )
22258
+ });
22259
+ const binding = bindings.find(
22260
+ (candidate) => !input.connectionId || candidate.connectionId === input.connectionId
22261
+ );
22262
+ const connection = visible.find((candidate) => candidate.id === binding?.connectionId) ?? null;
22263
+ if (ref && !connection) {
22196
22264
  throw new HTTPException32(409, { message: "Reconnect this connector to manage its tools" });
22197
22265
  }
22198
22266
  if (connection?.subjectId && !input.personalOwnerVerified)
@@ -22200,13 +22268,30 @@ async function resolveTarget2(input) {
22200
22268
  message: "Only the authenticated connection owner may manage personal tool permissions"
22201
22269
  });
22202
22270
  const connectionId = connection?.id ?? `session-mcp:${server.id}:${createHash11("sha256").update(server.url, "utf8").digest("hex")}`;
22203
- return { server, connection, connectionId };
22271
+ if (input.connectionId && input.connectionId !== connectionId)
22272
+ throw new HTTPException32(409, {
22273
+ message: "Connector account unavailable. Reload its permissions."
22274
+ });
22275
+ return {
22276
+ server: binding ? { ...server, connectionRef: binding.connectionRef } : server,
22277
+ connection,
22278
+ connectionId,
22279
+ accountLabel: binding?.accountLabel ?? server.name ?? server.id,
22280
+ accounts: bindings.map((candidate) => ({
22281
+ connectionId: candidate.connectionId,
22282
+ label: candidate.accountLabel,
22283
+ scope: candidate.subjectScope === "subject" ? "personal" : "workspace"
22284
+ })),
22285
+ instanceKey: void 0,
22286
+ listedTools: null
22287
+ };
22204
22288
  }
22205
22289
  function reviewedConnectorToolCatalog(server, grantedScopes) {
22206
22290
  const tools = isOfficialGmailMcpConfig(server.url, server.connectionRef) ? GMAIL_REST_MCP_TOOLS.filter((tool) => gmailToolSupportsScopes(tool.name, grantedScopes)) : isOfficialSlackMcpConfig(server.url, server.connectionRef) ? slackRestMcpToolsForScopes(grantedScopes) : null;
22207
22291
  return tools?.filter((tool) => !server.allowedTools || server.allowedTools.includes(tool.name)) ?? null;
22208
22292
  }
22209
22293
  async function listTools(input, target) {
22294
+ if (target.listedTools !== null) return target.listedTools;
22210
22295
  const reviewed = reviewedConnectorToolCatalog(
22211
22296
  target.server,
22212
22297
  target.connection?.grantedScopes ?? []
@@ -22288,19 +22373,14 @@ async function getConnectorToolPermissions(input) {
22288
22373
  workspaceId: input.workspaceId,
22289
22374
  connectionId: target.connectionId
22290
22375
  });
22291
- const permission = (name) => {
22292
- const resolved = resolveConnectorActionPolicy2(policies, {
22376
+ const changedTools = new Set(
22377
+ await listChangedConnectorToolPermissions(input.db, {
22378
+ accountId: input.grant.accountId,
22379
+ workspaceId: input.workspaceId,
22293
22380
  connectionId: target.connectionId,
22294
- serverId: target.server.id,
22295
- toolName: name,
22296
- actionName: "*"
22297
- });
22298
- if (!resolved.managed) return { permission: "allow", inherited: true };
22299
- return {
22300
- permission: resolved.entry?.policy ?? "block",
22301
- inherited: resolved.entry?.toolName !== name
22302
- };
22303
- };
22381
+ serverId: target.server.id
22382
+ })
22383
+ );
22304
22384
  const defaultPolicy = policies.find(
22305
22385
  (row) => row.serverId === target.server.id && row.toolName === "*" && row.actionName === "*"
22306
22386
  );
@@ -22322,15 +22402,25 @@ async function getConnectorToolPermissions(input) {
22322
22402
  tools: tools.map(
22323
22403
  (tool) => ({
22324
22404
  name: tool.name,
22405
+ ...changedTools.has(tool.name) ? { resetReason: "operation_changed" } : {},
22325
22406
  ...tool.title ?? tool.annotations?.title ? { title: tool.title ?? tool.annotations?.title } : {},
22326
22407
  ...tool.description ? { description: tool.description } : {},
22327
22408
  group: connectorToolGroup(tool),
22328
- ...permission(tool.name),
22329
- approvalRequired: target.server.requireApproval === true || Array.isArray(target.server.requireApproval) && target.server.requireApproval.includes(tool.name)
22409
+ ...projectConnectorToolPermission(policies, {
22410
+ connectionId: target.connectionId,
22411
+ serverId: target.server.id,
22412
+ toolName: tool.name,
22413
+ defaultDecision: target.server.requireApproval === true || Array.isArray(target.server.requireApproval) && target.server.requireApproval.includes(tool.name) ? "ask" : "allow"
22414
+ })
22330
22415
  })
22331
22416
  ),
22332
22417
  discoveryError,
22333
- canManage: canManageConnectorPermissions(input.grant)
22418
+ canManage: canManageConnectorPermissions(input.grant),
22419
+ appliesTo: "next_attempt",
22420
+ revision: connectorToolPolicyRevision(policies, target.connectionId, target.server.id),
22421
+ accountLabel: target.accountLabel,
22422
+ accounts: target.accounts,
22423
+ ...target.instanceKey ? { instanceKey: target.instanceKey } : {}
22334
22424
  };
22335
22425
  }
22336
22426
  function canManageConnectorPermissions(grant) {
@@ -22343,20 +22433,32 @@ async function updateConnectorToolPermissions(input) {
22343
22433
  throw new HTTPException32(400, {
22344
22434
  message: "Tool names must be exact and cannot use the connector-default wildcard."
22345
22435
  });
22346
- const target = await resolveTarget2(input);
22436
+ const target = await resolveTarget2({
22437
+ ...input,
22438
+ connectionId: input.payload.connectionId,
22439
+ ...input.payload.instanceKey ? { instanceKey: input.payload.instanceKey } : {}
22440
+ });
22347
22441
  if (target.connectionId !== input.payload.connectionId)
22348
22442
  throw new HTTPException32(409, {
22349
22443
  message: "The connector account changed. Reload its permissions."
22350
22444
  });
22351
- await updateConnectorToolPermissionPolicies(input.db, {
22352
- accountId: input.grant.accountId,
22353
- workspaceId: input.workspaceId,
22354
- subjectId: input.grant.subjectId,
22355
- connectionId: target.connectionId,
22356
- serverId: target.server.id,
22357
- toolNames: input.payload.target === "default" ? ["*"] : input.payload.toolNames,
22358
- policy: input.payload.permission
22359
- });
22445
+ try {
22446
+ await updateConnectorToolPermissionPolicies(input.db, {
22447
+ accountId: input.grant.accountId,
22448
+ workspaceId: input.workspaceId,
22449
+ subjectId: input.grant.subjectId,
22450
+ connectionId: target.connectionId,
22451
+ serverId: target.server.id,
22452
+ toolNames: input.payload.target === "default" ? ["*"] : input.payload.target === "action" ? [input.payload.toolName] : input.payload.toolNames,
22453
+ ...input.payload.target === "action" ? { actionName: input.payload.actionName } : {},
22454
+ ...input.payload.expectedRevision ? { expectedRevision: input.payload.expectedRevision } : {},
22455
+ policy: input.payload.permission
22456
+ });
22457
+ } catch (error) {
22458
+ if (error instanceof ConnectorToolPermissionConflictError)
22459
+ throw new HTTPException32(409, { message: error.message });
22460
+ throw error;
22461
+ }
22360
22462
  }
22361
22463
  export {
22362
22464
  ArchivedSessionImportError2 as ArchivedSessionImportError,
@@ -22818,6 +22920,7 @@ export {
22818
22920
  readSessionAttachmentFiles,
22819
22921
  readSessionLineage,
22820
22922
  readSkill,
22923
+ recordManagedAuthLoggedFailure,
22821
22924
  recordRigAuditEvent,
22822
22925
  recordSessionCreated,
22823
22926
  recordUserMessageAccepted,
@@ -23013,6 +23116,7 @@ export {
23013
23116
  withAgentConfigHttpErrors,
23014
23117
  withDefaultEnabledCapabilityMcpTools,
23015
23118
  withFrozenPersonalConnectionDelegations,
23119
+ withManagedAuthSessionLookup,
23016
23120
  withResolvedSessionAuthorization,
23017
23121
  withSameRequestSessionMcpServerTools,
23018
23122
  withScheduledSlackBotPostingTools,