From d9a01f693b75511eaec7f0310f64a4a13f44cb16 Mon Sep 17 00:00:00 2001 From: chennyiiis Date: Wed, 14 Oct 2026 19:12:37 +0800 Subject: [PATCH] fix(intent-router): route a kernel request on the target PolicyGate admitted PolicyGate decides whether a request may be routed by its stripped ``target_agent`` (`policy/gate.py`), and the router then dispatched on the raw one. A request naming `" kernel_agent"` therefore passed admission and fell through to the unknown-target refusal: the agent it named was enabled, had a handler and was allowed to serve it. The router now reads the field the way the gate read it, so the two agree on the name. The reply kind is deliberately left as sent: the requester awaits ``_done`` built from its own string, so normalising that would answer on a key nobody is listening to. Observable effect: a kernel request whose ``target_agent`` carries surrounding whitespace now runs its handler instead of coming back failed with ``unknown_target_agent``. An operator saw the request refused and recorded in the session's failure ledger for naming an agent that does exist. Co-Authored-By: Claude Opus 5 --- .../tests/test_profile_and_kernel_handlers.py | 23 +++++++++++++++++++ .../orchestrator/loop/intent_router.py | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/hyperloom/inference_optimizer/tests/test_profile_and_kernel_handlers.py b/src/hyperloom/inference_optimizer/tests/test_profile_and_kernel_handlers.py index ff1a78d090..c2c9c98af4 100644 --- a/src/hyperloom/inference_optimizer/tests/test_profile_and_kernel_handlers.py +++ b/src/hyperloom/inference_optimizer/tests/test_profile_and_kernel_handlers.py @@ -4116,6 +4116,29 @@ async def test_coordinator_auto_reject_answers_in_one_envelope( await c.stop() +@pytest.mark.asyncio +async def test_a_padded_target_agent_reaches_its_handler(session_dir): + """PolicyGate admits the stripped target, so the router has to route on the same name it admitted.""" + c = Coordinator(session_dir, backends=_backends_silent()) + try: + c.shared_state.kernel_enabled = True + await c.router.handle_intent( + "orchestration", + Intent( + type=IntentType.REQUEST, + payload={"target_agent": " kernel_agent ", "kind": "invent_brand_new_kind"}, + ), + ) + + responses = await c.bus.tail(topic="response", to_agent="orchestration") + assert responses, "the request must be answered" + # It reaches the kernel_agent arm, so it is refused for its kind rather than for its target. + assert responses[0].payload["result"]["error_class"] == "unknown_kernel_kind" + assert responses[0].from_agent == "kernel_agent" + finally: + await c.stop() + + @pytest.mark.asyncio async def test_coordinator_request_handler_exception_recorded(session_dir): """Handler crashes → RESPONSE.status='failed' + error_class set.""" diff --git a/src/hyperloom/orchestrator/loop/intent_router.py b/src/hyperloom/orchestrator/loop/intent_router.py index 1bd1e89312..7d850e2a46 100644 --- a/src/hyperloom/orchestrator/loop/intent_router.py +++ b/src/hyperloom/orchestrator/loop/intent_router.py @@ -1130,7 +1130,7 @@ async def _auto_reject(self, request_msg: Message, result: dict[str, Any]) -> No async def _handle_request(self, source: str, intent: Intent) -> None: """Route a REQUEST intent to its programmatic handler.""" - target_agent = intent.payload["target_agent"] + target_agent = str(intent.payload["target_agent"]).strip() kind = intent.payload["kind"] denied = self._coord.dispatcher.sequence_denial_for_request(target_agent, kind) if denied is not None: