| 1 | # parallel_tools.py DOX |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | - Own the shared runtime for parallel tool-call jobs. |
| 6 | - Normalize wrapped tool-call payloads, start background jobs, await or cancel jobs, and render prompt extras for active parallel work. |
| 7 | - Keep this file-level DOX profile synchronized with `parallel_tools.py` because this directory is intentionally flat. |
| 8 | |
| 9 | ## Ownership |
| 10 | |
| 11 | - `parallel_tools.py` owns the runtime implementation. |
| 12 | - `parallel_tools.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation. |
| 13 | - Public concepts: |
| 14 | - `NormalizedToolCall` |
| 15 | - `ParallelJob` |
| 16 | - `start_parallel_jobs(...)` |
| 17 | - `await_parallel_jobs(...)` |
| 18 | - `cancel_parallel_jobs(...)` |
| 19 | - `build_parallel_jobs_extras(...)` |
| 20 | - `format_parallel_results(...)` |
| 21 | |
| 22 | ## Runtime Contracts |
| 23 | |
| 24 | - Helper modules own reusable framework APIs and must preserve public callers unless all callers, tests, and docs are updated together. |
| 25 | - Wrapped tool-call items must use the same shape as normal tool calls: a tool name plus arguments. |
| 26 | - Normalization accepts full agent-reply-shaped objects when `tool_name` and `tool_args` are present; non-contract planning fields such as `thoughts` or `headline` are ignored. |
| 27 | - `tool_calls` should be an array, but normalization also accepts a valid JSON string encoding of that array to recover provider/model stringification. |
| 28 | - Normalization rejects `document_query` and `response` inside `parallel`: document parsing and Q&A must run sequentially, while `response` must remain top-level so it can end the message loop. |
| 29 | - `call_subordinate` jobs first enforce the actual calling agent's delegation policy, then call the same creation and execution functions as direct delegation in `tools/call_subordinate.py`; this helper does not construct or prompt a second kind of subordinate. |
| 30 | - Fresh parallel sibling calls create distinct `parent.number + 1` child agents. Their job snapshots expose stable `context_id` values that direct or parallel `reset=false` calls can continue after success or failure. |
| 31 | - Jobs retain their actual parent agent so parallel calls made by A1 create A2 rather than falling back to a context's A0. |
| 32 | - Subordinate child chats are tagged with job metadata, remain outside the scheduler task list, and may use normal child-chat tools including `parallel`. |
| 33 | - Nested parallel jobs started by a parallel subordinate are registered as child `DeferredTask` instances so stopping the ancestor also stops its descendants. |
| 34 | - Direct tool jobs run in isolated background contexts and are blocked from recursively invoking `parallel`. |
| 35 | - Direct tool jobs inherit the parent's active per-chat model override and current user message. |
| 36 | - Direct tool background context cleanup removes both the in-memory context and any transient chat folder left on disk. |
| 37 | - Parent-visible child log items are created for each wrapped call so the WebUI can inspect concurrent children separately while the wrapper result remains model-history-only. |
| 38 | - Child tool logs mirror normal tool-call visible args; job ids remain available through wrapper results and prompt extras rather than visible process-step args. |
| 39 | - Wrapped tool child logs use each tool's native `get_log_object()` output when available, preserving special log rendering (for example: `code_execution_tool` uses `code_exe`, `wait` uses `progress`, MCP tools use `mcp`, and regular tools use `tool`). |
| 40 | - Direct parallel worker execution reuses the parent-visible child log item so tool `before_execution()` cannot create a second generic worker log or lose the native badge type. |
| 41 | - Direct tools may explicitly queue model-visible history for their parent. Terminal collection records the outer `parallel` result first, promotes queued messages in job order, and only then removes disposable worker state; background jobs retain queued history until they are collected. |
| 42 | - Job IDs are stable handles for later await, collect, or cancel operations. |
| 43 | - Prompt extras must stay bounded and expose only job IDs, tool names, status, and compact result/error summaries. |
| 44 | |
| 45 | ## Key Concepts |
| 46 | |
| 47 | - The parent context stores in-flight jobs under a private data key; collected terminal jobs are removed from that registry. |
| 48 | - `wait=True` starts jobs and awaits them before returning until all requested jobs finish or the wait timeout is reached; the timeout stops waiting but does not cancel running jobs. |
| 49 | - `collect` returns already-finished job results without waiting; `await` waits for requested job IDs. |
| 50 | - Canceled jobs should be marked terminal and should stop their background `DeferredTask` when cancellation is possible. |
| 51 | - `queue_parallel_parent_history(...)` accepts messages only from registered direct tool workers. `collect_parallel_jobs(...)` optionally promotes those messages while collecting terminal jobs; arbitrary worker history is never copied. |
| 52 | |
| 53 | ## Work Guidance |
| 54 | |
| 55 | - Keep normalization compatible with provider tool-call envelopes and direct JSON objects. |
| 56 | - Avoid importing heavy runtime modules at import time unless startup behavior is verified. |
| 57 | - Coordinate argument, output, or status changes with `tools/parallel.py`, prompt instructions, and tests. |
| 58 | |
| 59 | ## Verification |
| 60 | |
| 61 | - Run targeted tests for normalization, recursion guard, prompt extras, and tool result formatting. |
| 62 | - Run a live Agent Zero chat when changing parallel execution, child chat metadata, or subordinate task behavior. |
| 63 | |
| 64 | ## Child DOX Index |
| 65 | |
| 66 | No child DOX files. |