Add a per-tool setting to restrict a tool from being called more than once per response
## Summary
Add a per-tool agent setting — "Restrict model from calling this tool more than once in a response." When enabled for a tool, if a single model response contains more than one call to that tool, execute none of the response's tool calls and return a configurable error message so the agent retries on the next step. Tools without the setting are unaffected and may still be called multiple times.
## Why
A step-wise, externally-driven agent needs certain tools to run at most once per step, so each call can build on the previous call's result. Canvas's client-side orchestration loop requires this for `place_components`: it places one component per step and references the returned UUID when placing the next, so two `place_components` calls in one response cannot work — while other tools (e.g. `edit_components`) may legitimately be called several times in one response. Because it applies to specific tools only, it is a per-tool setting.
- Roadmap: https://git.drupalcode.org/project/canvas/-/work_items/3591777
- Consumer (POC): https://git.drupalcode.org/project/canvas/-/merge_requests/1214
## Current behavior
`AiAgentEntityWrapper::determineSolvability()` parks every tool call in `$response->getTools()` (`src/PluginBase/AiAgentEntityWrapper.php:680`, loop at 683–690); all execute. ai_agents already carries a per-tool `tool_settings` map (`require_usage`, `use_artifacts` — lines 1147, 1183) that this extends. No per-tool call-count guard exists.
## Proposed
- Add `restrict_multiple_calls` (bool, default `FALSE` = current behavior) and `multiple_call_error_message` (string, with a sensible default) to per-tool `tool_settings` — config schema + agent form + runtime-overridable via `functionsOverride`, matching the existing keys.
- In `determineSolvability()`, before parking tools (line 683): count calls per plugin id in the response. If any tool whose `tool_settings` has `restrict_multiple_calls` appears more than once, park nothing, append the configured error message to `chatHistory` (like line 700), and return so the agent retries.
## Tests
- Flagged tool called twice → nothing parked, error message in `chatHistory`, execution defers.
- Flagged tool ×2 + a second (unflagged) tool ×1 → all rejected.
- Flagged tool ×1 + an unflagged tool ×2 → all execute.
- No flagged tool present → all execute (locks backward compatibility).
---
_Issue generated with AI assistance._
issue