make_model_settings sets parallel_tool_calls=False (one tool call per turn), so
an agent mapping a codebase pays a full model round-trip per file read / command.
These two function tools let it fan out in a SINGLE call: the reads/execs run
concurrently inside the sandbox (asyncio.gather, semaphore-bounded to 8) and the
combined results come back in one tool response — a real wall-clock win on the
recon-heavy phase of a scan.
- batch_view_files(paths): concurrent read of up to 50 files; per-file result
{path, content|error, truncated}, order preserved (results[i] ↔ paths[i]).
- batch_terminal_execute(commands): concurrent run of up to 50 INDEPENDENT shell
commands; per-command {command, exit_code, stdout, stderr}.
Both are session-injected at the core (_view_files_impl / _exec_impl) with a thin
@function_tool shell, so the concurrency + isolation logic is directly testable
without the SDK tool-call layer. Per-item error isolation: one failing read/exec
returns its own error entry, never aborts the batch. Concurrency is bounded so a
large batch can't swamp the sandbox; batch size capped at 50.
Registered in _BASE_TOOLS so root + child agents get them. Session accessed via
the same ctx.context["sandbox_session"] convention the existing tools use. Tools
self-describe; no prompt change — models reach for them unprompted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>