Skip to content

Make the A2A task store per-server-instance and bounded#2

Open
daryllundy wants to merge 1 commit into
dubh3124:mainfrom
daryllundy:fix/a2a-task-store-isolation
Open

Make the A2A task store per-server-instance and bounded#2
daryllundy wants to merge 1 commit into
dubh3124:mainfrom
daryllundy:fix/a2a-task-store-isolation

Conversation

@daryllundy

@daryllundy daryllundy commented Jun 19, 2026

Copy link
Copy Markdown

Fixes #1.

Problem

The A2A server kept its task registry in a module-scope Map
(src/a2a/server.ts), so all createDirectorA2AServer instances in a process
shared it — ListTasks on one server returned another server's tasks, and the
map was never pruned (unbounded growth on a long-running server).

Change

  • Move the Map into each createDirectorA2AServer instance; thread it
    explicitly through handleJsonRpc and createAndRunTask, matching the
    existing pattern for dependencies.
  • Add a MAX_TASKS = 1000 cap with oldest-first eviction (Map insertion order)
    so the store can't grow without bound.
  • Add a regression test: two servers each get their own store; a task sent to
    server A is not visible from server B.

No changes to JSON-RPC method names, A2ATask/artifact shapes, or any SDK file.
A2A stays a thin adapter over the SDK.

Note on scope (happy to adjust)

The isolation fix is the core of this PR. The eviction cap is an
optional add-on — it changes one observable behavior (GetTask on a very old,
evicted id returns "not found" on a busy server). If you'd prefer the minimal
isolation-only change, or a different cap / configurable limit, say the word and
I'll trim it.

Testing

  • npm run typecheck — passes
  • npm test — passes (16 tests incl. the new isolation test)
  • npm run build — passes
  • npm audit --audit-level=moderate — 0 vulnerabilities
  • npm run pack:dry — unaffected (change is src/-only)

Disclosure

I used Claude Code (Anthropic's agentic coding tool) to identify and implement
this fix. I reviewed the diff and ran the full check suite locally. The commit
carries a Co-Authored-By trailer for that assistance.

Move the task Map from module scope into each createDirectorA2AServer call
so server instances no longer share one global registry (ListTasks on one
instance previously returned another instance's tasks). Add a MAX_TASKS=1000
cap with oldest-first eviction so a long-running server's store cannot grow
unbounded. Thread the store explicitly through handleJsonRpc and
createAndRunTask, matching how dependencies are already passed. Add an
isolation test verifying two servers do not share tasks.

Found and fixed with assistance from Claude Code (Anthropic).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

createDirectorA2AServer() uses a module-scoped task Map, causing cross-instance leakage and unbounded growth

1 participant