Problem
The core SDK has 15+ unprotected global singletons sharing mutable state across all agent instances. In multi-agent or multi-threaded scenarios, this causes race conditions, data corruption, and cross-agent state leakage.
Principle violated: "Multi-agent + async safe by default"
Critical Locations
| Singleton |
File |
Thread-safe? |
_global_client / _global_client_params |
praisonaiagents/llm/openai_client.py:2198-2224 |
No — race on param check + client creation |
error_logs, sync_display_callbacks, approval_callback |
praisonaiagents/main.py:27-34 |
No — concurrent dict/list mutations |
_default_emitter |
praisonaiagents/trace/protocol.py:389-404 |
No — global set without lock |
_telemetry_instance |
praisonaiagents/telemetry/telemetry.py:642-706 |
No — force_shutdown_telemetry() kills ALL agents |
_global_store + reset_global_store() |
praisonaiagents/context/store.py:463-480 |
Partial — reset destroys ALL agents' context |
APPROVAL_REQUIRED_TOOLS (eager) |
praisonaiagents/approval/__init__.py:65-66 |
No — forces eager init, shared mutable set |
_global_registry (eager) |
praisonaiagents/tools/circuit_breaker.py:438 |
No — eagerly instantiated at import |
_global_health_monitor (eager) |
praisonaiagents/tools/health_monitor.py:459 |
No — eagerly instantiated at import |
_store_instance |
praisonaiagents/tools/schedule_tools.py:26 |
No |
_default_registry |
praisonaiagents/hooks/registry.py:321 |
No |
Impact
- One agent calling
force_shutdown_telemetry() kills telemetry for ALL agents
- One agent calling
reset_global_store() destroys context for ALL agents
- OpenAI client params can race between threads — potentially using wrong API key/base_url
- Approval policy set by one agent affects all subsequent agent initializations
Suggested Fix
Introduce a per-agent AgentScope or session-scoped registry pattern. Global singletons should become agent-instance-scoped or at minimum protected with threading.Lock(). Eagerly-instantiated singletons (circuit_breaker.py:438, health_monitor.py:459, approval/__init__.py:65-66) should be converted to lazy getters.
Problem
The core SDK has 15+ unprotected global singletons sharing mutable state across all agent instances. In multi-agent or multi-threaded scenarios, this causes race conditions, data corruption, and cross-agent state leakage.
Principle violated: "Multi-agent + async safe by default"
Critical Locations
_global_client/_global_client_paramspraisonaiagents/llm/openai_client.py:2198-2224error_logs,sync_display_callbacks,approval_callbackpraisonaiagents/main.py:27-34_default_emitterpraisonaiagents/trace/protocol.py:389-404_telemetry_instancepraisonaiagents/telemetry/telemetry.py:642-706force_shutdown_telemetry()kills ALL agents_global_store+reset_global_store()praisonaiagents/context/store.py:463-480APPROVAL_REQUIRED_TOOLS(eager)praisonaiagents/approval/__init__.py:65-66_global_registry(eager)praisonaiagents/tools/circuit_breaker.py:438_global_health_monitor(eager)praisonaiagents/tools/health_monitor.py:459_store_instancepraisonaiagents/tools/schedule_tools.py:26_default_registrypraisonaiagents/hooks/registry.py:321Impact
force_shutdown_telemetry()kills telemetry for ALL agentsreset_global_store()destroys context for ALL agentsSuggested Fix
Introduce a per-agent
AgentScopeor session-scoped registry pattern. Global singletons should become agent-instance-scoped or at minimum protected withthreading.Lock(). Eagerly-instantiated singletons (circuit_breaker.py:438,health_monitor.py:459,approval/__init__.py:65-66) should be converted to lazy getters.