Summary
The core SDK has 10+ global mutable variables that are not fully protected for concurrent multi-agent use, violating the "multi-agent + async safe by default" principle.
Unprotected Globals Identified
| Location |
Variable |
Risk |
bus/bus.py |
_default_bus |
Shared event bus across agents |
trace/protocol.py |
_default_emitter |
Shared trace emitter |
telemetry/integration.py |
_queue_processor_running, _telemetry_executor, _telemetry_queue, _performance_mode_enabled |
Race conditions in telemetry |
paths.py |
_data_dir_cache |
Minor — cache contention |
main.py |
approval_callback, sync_display_callbacks, async_display_callbacks |
Callback registration races |
What's Already Done Well
_lazy.py uses proper _cache_lock with double-checked locking
trace/context_events.py uses contextvars for async-safe state
- Thread safety tests exist in
tests/unit/test_thread_safety.py
What's Missing
- Display callback registries (
sync_display_callbacks, async_display_callbacks) in main.py are plain lists — concurrent modification during multi-agent runs can cause RuntimeError
_default_bus and _default_emitter are set without locks — two agents starting simultaneously could race
- Telemetry globals use module-level state that could race during shutdown
- No
contextvars isolation for per-agent state in async scenarios
Suggested Fix
- Use
contextvars.ContextVar for per-agent/per-session state (bus, emitter, callbacks)
- Add thread locks around global registration operations
- Consider a
threading.Lock or asyncio.Lock wrapper for callback lists
- Expand
test_thread_safety.py to cover these specific globals
Summary
The core SDK has 10+ global mutable variables that are not fully protected for concurrent multi-agent use, violating the "multi-agent + async safe by default" principle.
Unprotected Globals Identified
bus/bus.py_default_bustrace/protocol.py_default_emittertelemetry/integration.py_queue_processor_running,_telemetry_executor,_telemetry_queue,_performance_mode_enabledpaths.py_data_dir_cachemain.pyapproval_callback,sync_display_callbacks,async_display_callbacksWhat's Already Done Well
_lazy.pyuses proper_cache_lockwith double-checked lockingtrace/context_events.pyusescontextvarsfor async-safe statetests/unit/test_thread_safety.pyWhat's Missing
sync_display_callbacks,async_display_callbacks) inmain.pyare plain lists — concurrent modification during multi-agent runs can causeRuntimeError_default_busand_default_emitterare set without locks — two agents starting simultaneously could racecontextvarsisolation for per-agent state in async scenariosSuggested Fix
contextvars.ContextVarfor per-agent/per-session state (bus, emitter, callbacks)threading.Lockorasyncio.Lockwrapper for callback liststest_thread_safety.pyto cover these specific globals