Просмотр исходного кода

fix(host): check subagent ownership before cwd conflict in ensureSession

Explicit-id adoption of a cold session-backed subagent under a *different*
cwd answered `session-conflict` because the cwd check ran before the
persistence inspection classified the identity. The api/commands.ts
contract states explicit-id `session.create` adoption rejects
session-backed subagents with `agent-busy` — ownership is an identity
property, so it must win regardless of the requested workspace.

Reorder the stored-session branch to inspect and classify ownership
first, then enforce the cwd match, making the response match the
documented contract.
Tianyi Cui 2 месяцев назад
Родитель
Сommit
c68c3dbb43
1 измененных файлов с 11 добавлено и 3 удалено
  1. 11 3
      packages/host/apiproxy/src/api-proxy.ts

+ 11 - 3
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 () => {