From 858f0381e5864aa115b855c29b52743336f6a796 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sat, 18 Apr 2026 10:33:24 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20move=20`$bitsAllSet`=20fi?= =?UTF-8?q?lter=20out=20of=20remote-agent=20aggregation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `enrichRemoteAgentPrincipals` used `$bitsAllSet` inside an aggregation `$match`, which Azure Cosmos DB for MongoDB does not implement. Project `permBits` through the pipeline and filter for `PermissionBits.SHARE` in application code. The extra documents fetched are bounded by ACL entries on a single agent resource, so the cost is negligible. Refs #12729. --- packages/api/src/apiKeys/permissions.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/api/src/apiKeys/permissions.ts b/packages/api/src/apiKeys/permissions.ts index b617b0a892..f6ce2afd91 100644 --- a/packages/api/src/apiKeys/permissions.ts +++ b/packages/api/src/apiKeys/permissions.ts @@ -47,13 +47,18 @@ export async function enrichRemoteAgentPrincipals( ? Types.ObjectId.createFromHexString(resourceId) : resourceId; + /** + * Filter `permBits` application-side (not via `$bitsAllSet`) for compatibility + * with MongoDB forks (e.g. Azure Cosmos DB for MongoDB) that do not implement + * the `$bitsAllSet` query operator. Entry count is bounded by ACL entries on a + * single agent, so the extra documents fetched are negligible. + */ const agentOwnerEntries = await deps.aggregateAclEntries([ { $match: { resourceType: ResourceType.AGENT, resourceId: resourceObjectId, principalType: PrincipalType.USER, - permBits: { $bitsAllSet: PermissionBits.SHARE }, }, }, { @@ -67,6 +72,7 @@ export async function enrichRemoteAgentPrincipals( { $project: { principalId: 1, + permBits: 1, userInfo: { $arrayElemAt: ['$userInfo', 0] }, }, }, @@ -76,6 +82,10 @@ export async function enrichRemoteAgentPrincipals( const entriesToBackfill: Types.ObjectId[] = []; for (const entry of agentOwnerEntries) { + const permBits = entry.permBits as number; + if ((permBits & PermissionBits.SHARE) !== PermissionBits.SHARE) { + continue; + } if (!entry.userInfo) { continue; }