* fix(gateway): forward assistant_id as agent_name in build_run_config
Fixes#1644
When the LangGraph Platform-compatible /runs endpoint receives a custom
assistant_id (e.g. 'finalis'), the Gateway's build_run_config() silently
ignored it — configurable['agent_name'] was never set, so make_lead_agent
fell through to the default lead agent and SOUL.md was never loaded.
Root cause (introduced in #1403):
resolve_agent_factory() correctly falls back to make_lead_agent for all
assistant_id values, but build_run_config() had no assistant_id parameter
and never injected configurable['agent_name']. The full call chain:
POST /runs (assistant_id='finalis')
→ resolve_agent_factory('finalis') # returns make_lead_agent ✓
→ build_run_config(thread_id, ...) # no agent_name injected ✗
→ make_lead_agent(config)
→ cfg.get('agent_name') → None
→ load_agent_soul(None) → base SOUL.md (doesn't exist) → None
Fix:
- Add keyword-only parameter to build_run_config().
- When assistant_id is set and differs from 'lead_agent', inject it as
configurable['agent_name'] (matching the channel manager's existing
_resolve_run_params() logic for IM channels).
- Honour an explicit configurable['agent_name'] in the request body;
assistant_id mapping only fills the gap when it is absent.
- Remove stale log-only branch from resolve_agent_factory(); update
docstring to explain the factory/configurable split.
Tests added (test_gateway_services.py):
- Custom assistant_id injects configurable['agent_name']
- 'lead_agent' assistant_id does NOT inject agent_name
- None assistant_id does NOT inject agent_name
- Explicit configurable['agent_name'] in request is not overwritten
- resolve_agent_factory returns make_lead_agent for all inputs
* style: format with ruff
* fix: validate and normalize assistant_id to prevent path traversal
Addresses Copilot review: strip/lowercase/replace underscores and
reject names that don't match [a-z0-9-]+, consistent with
ChannelManager._normalize_custom_agent_name().
---------
Co-authored-by: voidborne-d <voidborne-d@users.noreply.github.com>