Skip to content

fix(actor): scope forkContext by session to prevent cross-session collisions#1870

Open
onlyfeng wants to merge 1 commit into
XiaomiMiMo:mainfrom
onlyfeng:fix/forkctx-session-scope
Open

fix(actor): scope forkContext by session to prevent cross-session collisions#1870
onlyfeng wants to merge 1 commit into
XiaomiMiMo:mainfrom
onlyfeng:fix/forkctx-session-scope

Conversation

@onlyfeng

Copy link
Copy Markdown
Contributor

Summary

  • The forkContexts Map in Actor.Service is keyed by actorID alone, but actorID is only unique within a (sessionID, agentType) scope (see ActorRegistry.allocateActorID). Two concurrent sessions spawning the same agent type (e.g. explore) get the same actorID (e.g. explore-1), causing one session's ForkContext snapshot to overwrite or be deleted by the other's.
  • Scope every forkContexts.set/get/delete by a composite (sessionID, actorID) key instead of actorID alone. getForkContext now takes sessionID as its first parameter, matching the existing cancel(sessionID, actorID, mode) signature.
  • Added a regression test that spawns same-agentType subagents in two different sessions, confirms they collide on the same actorID, and asserts each session reads back only its own ForkContext.

Test plan

  • bun typecheck passes
  • bun test test/actor/spawn.test.ts — 24 pass, 0 fail (includes new cross-session collision test)
  • All 15 affected test files updated for new getForkContext(sessionID, actorID) signature

…lisions

The Actor service's forkContexts map is keyed by actorID alone, but
actorID is not globally unique. allocateActorID (actor/registry.ts)
allocates its incrementing suffix scoped to (sessionID, agentType), so
the first "explore" subagent spawned in any session gets actorID
"explore-1" regardless of which session spawned it.

Two concurrent sessions each spawning their first subagent of the same
agentType therefore get the same actorID, and since forkContexts is a
single process-wide Map keyed only by actorID, one session's frozen
ForkContext snapshot (system prompt + inherited messages + tools) can
overwrite or be deleted by the other's.

Fix: scope every forkContexts.set/get/delete by a composite
(sessionID, actorID) key instead of actorID alone. getForkContext now
takes sessionID as its first parameter, matching the existing
cancel(sessionID, actorID, mode) signature. Updated the one production
call site (session/prompt.ts) and all test call sites/mocks
accordingly, and added a regression test that spawns same-agentType
subagents in two different sessions, confirms they collide on the same
actorID, and asserts each session reads back only its own forkContext.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant