Skip to content

subagent-registry: cleanupBrowserSessionsForLifecycleEnd wrapper invoked twice for same runId in embedded mode #68668

Description

@Feelw00

Summary

completeSubagentRun can be invoked in parallel from two completion paths for the same runId in embedded mode:

  • In-process listener on phase: "end"src/agents/subagent-registry.ts (via onAgentEvent).
  • Gateway waitForSubagentCompletion RPC — src/agents/subagent-registry-run-manager.ts.

registerSubagentRun pairs both unconditionally. Both callers hit cleanupBrowserSessionsForLifecycleEnd(...) at src/agents/subagent-registry-lifecycle.ts for the same childSessionKey, so the wrapper runs twice.

Scope

Actual CDP browserCloseTab IPC is already idempotent: extensions/browser/src/browser/session-tab-registry.ts's takeTrackedTabsForSessionKeys drains trackedTabsBySession.delete(sessionKey), and closeTrackedBrowserTabsForSessions short-circuits with tabs.length === 0 on the second call.

The remaining duplication is at the wrapper layer:

  • cleanupBrowserSessionsForLifecycleEnd wrapper call (runs twice)
  • normalizeSessionKeys + runBestEffortCleanup (runs twice)
  • plugin-sdk facade dispatch (runs twice; resolution itself is cached)
  • onWarn logging path in the non-empty window

Why it matters

  • Sibling race 48042c3875 fix(agents): avoid duplicate subagent ended hook loads already introduced endedHookEmittedAt in the same file for the same dual-dispatch pattern. Extending that pattern to browser cleanup keeps the dedup surface consistent.
  • Closes a defense-in-depth gap if the bundled browser extension ever removes or replaces the take-and-drain idempotency.
  • Trims wrapper overhead on every embedded subagent completion.

Reproduction

Promise.all([completeSubagentRun(p), completeSubagentRun(p)]) for the same runId + triggerCleanup: true calls cleanupBrowserSessionsForLifecycleEnd twice without a guard. See accompanying PR's regression test.

Proposed fix

Add a dedicated dispatch flag browserCleanupDispatchedAt?: number on SubagentRunRecord and a sync check-then-set guard in completeSubagentRun that matches the endedHookEmittedAt pattern. Reset the flag on rearm alongside endedHookEmittedAt in subagent-registry-run-manager.ts.

PR: coming.

[AI-assisted]

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions