refactor(agent): simplify subagent concurrency with rejection over semaphore

Replace the asyncio.Semaphore queueing approach with a simple count
check in SpawnTool.execute(). When the concurrency limit is reached,
the tool returns an error string so the agent can perceive the reason
and adjust its behavior instead of silently queueing.

- Remove max_concurrent_subagents parameter threading through
  AgentLoop, commands.py, and nanobot.py
- SubagentManager reads the limit directly from AgentDefaults
- SpawnTool checks get_running_count() before calling spawn()
- Simplify tests to verify rejection behavior
This commit is contained in:
chengyongru
2026-05-05 22:22:04 +08:00
committed by Xubin Ren
parent 9d6afd86b5
commit c30e4d86f3
6 changed files with 118 additions and 1 deletions
+22
View File
@@ -1009,6 +1009,28 @@ MCP tools are automatically discovered and registered on startup. The LLM can us
**Docker security**: The official Docker image runs as a non-root user (`nanobot`, UID 1000) with bubblewrap pre-installed. When using `docker-compose.yml`, the container drops all Linux capabilities except `SYS_ADMIN` (required for bwrap's namespace isolation).
## Subagent Concurrency
By default, nanobot only allows one spawned subagent at a time. When the limit is
reached, the `spawn` tool returns an error so the agent can decide to wait or
rearrange its work. This protects local LLM servers from loading multiple KV caches
at once. If your provider can handle more parallel work, raise the limit:
```json
{
"agents": {
"defaults": {
"maxConcurrentSubagents": 2
}
}
}
```
| Option | Default | Description |
|--------|---------|-------------|
| `agents.defaults.maxConcurrentSubagents` | `1` | Maximum number of spawned subagents that may run at the same time. Attempts to spawn beyond this limit return an error. |
## Auto Compact
When a user is idle for longer than a configured threshold, nanobot **proactively** compresses the older part of the session context into a summary while keeping a recent legal suffix of live messages. This reduces token cost and first-token latency when the user returns — instead of re-processing a long stale context with an expired KV cache, the model receives a compact summary, the most recent live context, and fresh input.