diff --git a/packages/host/apiproxy/src/api-proxy.ts b/packages/host/apiproxy/src/api-proxy.ts index 517688de34..501390e488 100644 --- a/packages/host/apiproxy/src/api-proxy.ts +++ b/packages/host/apiproxy/src/api-proxy.ts @@ -1066,12 +1066,20 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro } async function agentFor(sessionId: SessionId): Promise<{ agent: Agent } | { error: RpcError }> { - const attached = ctx.sessions.get(sessionId) const live = ctx.agents.get(sessionId) - if (attached !== undefined && hasSubagentOwner(attached, live)) { + if (live !== undefined) { + // Fence the live agent's own session rather than trusting a + // "registered ⇒ attached-store" invariant: a registered subagent whose + // session is ever absent from the attached store must still not be + // handed out through generic Host routing (ensureSession's `.catch` + // already fences `live.session`; this is the same check on the fast path). + if (hasSubagentOwner(live.session, live)) return { error: subagentOwnershipError(sessionId) } + return { agent: live } + } + const attached = ctx.sessions.get(sessionId) + if (attached !== undefined && hasSubagentOwner(attached, undefined)) { return { error: subagentOwnershipError(sessionId) } } - if (live !== undefined) return { agent: live } let resume = resumes.get(sessionId) if (resume === undefined) { resume = (async () => {