Skip to content

Commit cd202ae

Browse files
committed
fix(agents): always force synthetic operator client for in-process subagent dispatch
When in-process subagent Gateway dispatch omitted forceSyntheticClient for non-admin methods (e.g. "agent"), the call could inherit the caller's channel-plugin client, which may not carry operator.write (#98614). Always force the synthetic operator client for in-process dispatch so scope resolution does not depend on the calling channel plugin's client scopes. Closes #98614
1 parent 6b90610 commit cd202ae

2 files changed

Lines changed: 7 additions & 3 deletions

File tree

src/agents/subagent-spawn.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ describe("spawnSubagentDirect seam flow", () => {
346346
sessionKey: result.childSessionKey,
347347
}),
348348
expect.objectContaining({
349+
forceSyntheticClient: true,
349350
timeoutMs: expect.any(Number),
350351
}),
351352
);

src/agents/subagent-spawn.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,12 @@ async function callSubagentGateway(
241241
// scope-upgrade handshake that headless gateway-client connections cannot
242242
// complete interactively, causing close(1008) "pairing required" (#59428).
243243
//
244+
// In-process dispatch always uses a synthetic operator client so that scope
245+
// resolution does not depend on the calling channel plugin's client scopes.
244246
// Only admin-requiring calls are pinned to ADMIN_SCOPE; other methods (e.g.
245-
// "agent" -> write) keep their least-privilege scope. The params-aware
246-
// resolver keeps spawn-metadata sessions.patch calls on the admin tier.
247+
// "agent" -> write) keep the synthetic client's least-privilege default.
248+
// The params-aware resolver keeps spawn-metadata sessions.patch calls on the
249+
// admin tier.
247250
const leastPrivilegeScopes = resolveLeastPrivilegeOperatorScopesForMethod(
248251
params.method,
249252
params.params,
@@ -267,7 +270,7 @@ async function callSubagentGateway(
267270
request.params as Record<string, unknown>,
268271
{
269272
expectFinal: request.expectFinal,
270-
...(scopes != null ? { forceSyntheticClient: true } : {}),
273+
forceSyntheticClient: true,
271274
...(typeof request.timeoutMs === "number" ? { timeoutMs: request.timeoutMs } : {}),
272275
...(scopes != null ? { syntheticScopes: scopes } : {}),
273276
},

0 commit comments

Comments
 (0)