ADR-015: Enforce a bounded tool-execution contract¶
- Status: Accepted
- Date: 2026-07-03
- Updated: 2026-07-25
Context¶
In react_loop(), model output selects a registered tool and supplies its
arguments. The loop must limit malformed or excessive requests before calling
application code.
Decision¶
Before execution, the loop:
- rejects duplicate registered tool names;
- limits tool calls per round;
- requires a JSON object and validates the supported top-level schema subset;
- fails closed on unsupported constraints when
jsonschemais absent; - applies full validation when the optional
jsonschemaextra is present; - asks an
ApprovalGatewhen configured; - applies a bounded timeout;
- runs calls from the same round concurrently; and
- truncates observations before returning them to the model.
Trace events redact tool arguments by default.
Consequences¶
- Common malformed calls are rejected before application code runs.
- Optional full schema validation does not become a base dependency.
- Concurrency reduces latency but means same-round tools must not rely on call order.
- Registration-time duplicate checks prevent ambiguous dispatch.
- Tool code remains responsible for authorization, resource validation, idempotency, and side-effect control.
Rejected alternatives¶
Executing calls without validation gives model output direct access to callable arguments. Running every tool in a subprocess would not provide a complete sandbox and would break normal in-process integrations.