Issue #3591882: Add an Agents & Tools settings form.

Adds the Agents & Tools settings form to canvas_dev_ai, populating drupalSettings.canvas.ai.tools for the dropdown in #3591856.

AI-Generated: Yes (Used Claude Code to help write the form, the two test classes and this description. All of it was reviewed, run and manually verified locally — see Testing instructions below.)

What changed

New in canvas_dev_ai:

  • src/Form/CanvasDevAiAgentSelectionForm.phpConfigFormBase with a required main_agent select and a tools checkboxes element, both built from the ai_agent config entities filtered to the three Canvas chat agents. Validation rejects selecting the main agent as a Tool.
  • canvas_dev_ai.links.task.yml — fourth local task, base_route: canvas_ai.setting, weight 30 (existing tabs are 0/10/20).
  • config/schema/canvas_dev_ai.schema.yml — schema for canvas_dev_ai.settings.
  • config/install/canvas_dev_ai.settings.yml — defaults.

Edited in canvas_dev_ai:

  • canvas_dev_ai.routing.yml — added canvas_dev_ai.agent_selection.
  • src/Hook/CanvasDevAiHooks.phpjsSettingsAlter() now emits canvas.ai.tools alongside the existing aiDevMode flag.

New tests in canvas_ai (where the dev module's coverage lives, per the issue and the existing CanvasAiDevControllerTest):

  • tests/src/Kernel/CanvasDevAiAgentSelectionTest.php — 3 tests / 17 assertions: Tools exposed to JS with label/description read from the ai_agent entities, no key emitted when no Tools are selected, and uninstall removing the config object and the JS key.
  • tests/src/Functional/Form/CanvasDevAiAgentSelectionFormTest.php — 3 tests / 27 assertions: the tab is present under the settings route and gone after uninstall; the form saves both values; the main-agent-as-Tool submission is rejected.

Config stores only agent IDs; labels and descriptions are read from the ai_agent entities at runtime, as the issue specifies.

Note: component_agentcanvas_component_agent

The issue summary lists component_agent, but the shipped entity is canvas_component_agent. Confirmed with @AkhilBabu that this is a typo in the summary, so the prefixed ID is used here.

Question: which permission?

I used _permission: 'use Drupal Canvas AI' because it matches the base route canvas_ai.setting. Note the sibling tabs are inconsistent — canvas_ai.theme_region_settings uses use Drupal Canvas AI while canvas_ai.component_description_settings uses administer components. Happy to switch if administer components is preferred for settings forms.

Note: the local task assertion lives in the functional test

The issue lists local task availability among the kernel test's assertions, but asserting it there means enumerating local task definitions, which runs core's views local-task deriver. In a kernel test views' routes are not built, so the deriver emits:

views/src/Plugin/Derivative/ViewsLocalTask.php:82
Trying to access array offset on null

PHPUnit exits non-zero on any warning, so the kernel job failed despite every assertion passing. #[WithoutErrorHandler] did not suppress it.

The assertion is therefore in the functional test, where routes are built and no warning occurs — and where it checks something stronger: that the tab is actually rendered under canvas_ai.setting, and is gone once canvas_dev_ai is uninstalled. This needs block in $modules and drupalPlaceBlock('local_tasks_block'), since the test profile places no blocks and local tasks would otherwise not render at all.

Happy to move it back if you'd prefer it in the kernel test with the warning handled some other way.

Testing instructions

  • Enable canvas_dev_ai (drush en canvas_dev_ai -y).
  • Go to /admin/config/ai/canvas-ai-settings. Confirm a fourth Agents & Tools tab appears after Page Region Descriptions.
  • On that tab, confirm the dropdown and checkboxes offer exactly three agents — Orchestrator, Component Agent, Page Builder Agent — and not the other ai_agent entities on the site.
  • Confirm the shipped defaults are pre-selected: Orchestrator as main agent, Component Agent and Page Builder Agent ticked.
  • Set the main agent to Component Agent while leaving it ticked as a Tool and save. Expect: The main agent cannot also be offered as a Tool.
  • Set the main agent to Page Builder Agent, tick only Component Agent, save. Expect the saved-configuration message.
  • drush cget canvas_dev_ai.settings — expect main_agent: canvas_page_builder_agent and tools: containing only canvas_component_agent, i.e. IDs with no labels.
  • Open the Canvas editor and check drupalSettings.canvas.ai.tools in the browser console. Expect one entry with id, label and description for Component Agent, and no main agent in the payload.
  • Untick all Tools, save, reload the editor. Expect no canvas.ai.tools key at all.
  • Uninstall canvas_dev_ai. Expect the tab, the config object and the canvas.ai.tools key all to disappear.

Verification already done

  • Both new test classes pass on MariaDB, and the kernel class also exits 0 on SQLite — the configuration the earlier pipeline failed on.
  • canvas_ai kernel suite: 140 tests pass with these changes, including CanvasAiDevControllerTest, which covers the jsSettingsAlter() method edited here. (10 of those tests need drupal/ai_agents_test, which is in Canvas's require-dev and so is not present when Canvas is installed as a site dependency — it had to be added locally to run the full suite.)
  • phpcs --standard=Drupal,DrupalPractice clean over all changed files.
  • PHPStan level 8 clean over the changed files apart from missingType.iterableValue, which phpstan.neon ignores globally, and the two findings from the \assert($admin_user instanceof …) line in setUp() — that line is copied from CanvasAiComponentDescriptionSettingsFormTest::setUp(), which reports the identical pair. Happy to drop the assert if you'd rather the new file be clean. (Canvas's own phpstan.neon could not be run locally: it references Canvas\PHPStan\Rules\* classes that are only autoloadable when Canvas is the root composer project.)
  • All the manual steps above were performed, with one exception: reading drupalSettings.canvas.ai.tools in the editor. ui/dist is not committed on 1.x (built assets are added at release time), so the editor does not boot from a dev checkout. That payload is asserted in the kernel test instead, and was verified by invoking hook_js_settings_alter() directly.

Closes #3591882

Edited by Tekla Aivazashvili

Merge request reports

Loading