The planner had no idea which tools the tenant actually has installed, so
``tool`` nodes were generated with hallucinated provider/tool names that
failed at draft sync. Add a tenant-scoped catalogue helper and pipe its
output into both planner and builder prompts.
- core/workflow/generator/tool_catalogue.py: enumerate hardcoded built-in
providers plus plugin providers (via ToolManager.list_builtin_providers
which already covers both), pull provider_name + tool_name + label +
llm-facing description, cap to 80 entries to keep the prompt bounded,
and render as a compact one-tool-per-line block. Per-provider failures
are logged and skipped so one bad plugin can't kill generation.
- prompts/planner_prompts.py + prompts/builder_prompts.py: new optional
catalogue section. Planner is told to pick concrete provider/tool
identifiers from the list; builder is told to set provider_id /
provider_name / tool_name to entries that actually exist.
- runner.py: thread the catalogue text through generate_workflow_graph
→ _run_planner / _run_builder. New param defaults to "" so unit tests
and tool-less tenants still work unchanged.
- services/workflow_generator_service.py: build + format the catalogue
for the calling tenant. Catalogue build failures (plugin daemon down,
etc.) are logged and downgraded to "no catalogue" — never block
generation outright.
- 5 new unit tests cover the catalogue formatter (empty/short/long
descriptions, label dedupe, newline stripping); the existing 7 runner
tests keep passing thanks to the default-empty param.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>