diff --git a/agent/outbound_webhooks.py b/agent/outbound_webhooks.py index 0f29d5e50f..cd600832cb 100644 --- a/agent/outbound_webhooks.py +++ b/agent/outbound_webhooks.py @@ -445,8 +445,13 @@ def _serialize_payload( cwd = str(Path.cwd()) except OSError: cwd = "" + # Resolved at fire time from the bound home so a multiplexed gateway's + # receivers can tell which profile emitted the event (#92674). + from hermes_cli.profiles import get_active_profile_name + payload = { "hook_event_name": event, + "profile": get_active_profile_name(), "tool_name": kwargs.get("tool_name"), "tool_input": kwargs.get("args") if isinstance(kwargs.get("args"), dict) else None, "session_id": kwargs.get("session_id") or kwargs.get("parent_session_id") or "", diff --git a/tests/agent/test_outbound_webhooks.py b/tests/agent/test_outbound_webhooks.py index 8b02970e50..39c8a25221 100644 --- a/tests/agent/test_outbound_webhooks.py +++ b/tests/agent/test_outbound_webhooks.py @@ -301,6 +301,23 @@ class TestPayload: assert payload["delivery_id"] == "did_1234" assert payload["timestamp"].endswith("Z") + def test_profile_field_reflects_bound_profile_home(self, tmp_path, monkeypatch): + """Receivers behind a multiplexed gateway need to know which profile + fired (#92674): ``profile`` follows the bound home at fire time.""" + from hermes_constants import reset_hermes_home_override, set_hermes_home_override + + monkeypatch.setenv("HERMES_HOME", str(tmp_path)) + profile_home = tmp_path / "profiles" / "b" + profile_home.mkdir(parents=True) + token = set_hermes_home_override(profile_home) + try: + body = outbound_webhooks._serialize_payload("on_session_end", {}, "did_1") + finally: + reset_hermes_home_override(token) + assert json.loads(body)["profile"] == "b" + body = outbound_webhooks._serialize_payload("on_session_end", {}, "did_2") + assert json.loads(body)["profile"] == "default" + def test_unserialisable_values_stringified(self): body = outbound_webhooks._serialize_payload( "on_session_end", {"weird": object()}, "did_1" diff --git a/website/docs/user-guide/features/hooks.md b/website/docs/user-guide/features/hooks.md index 8ef9e7b180..e30b3b0d39 100644 --- a/website/docs/user-guide/features/hooks.md +++ b/website/docs/user-guide/features/hooks.md @@ -1876,11 +1876,12 @@ Secrets: prefer `secret_env` (the name of an environment variable, typically set ### Wire format -Each firing POSTs a JSON body with the same top-level shape as shell hooks' stdin, plus delivery metadata: +Each firing POSTs a JSON body with the same top-level shape as shell hooks' stdin, plus delivery metadata. `profile` names the Hermes profile that emitted the event (`"default"` outside profiles), so receivers behind a multiplexed gateway can tell profiles apart: ```json { "hook_event_name": "on_session_end", + "profile": "default", "tool_name": null, "tool_input": null, "session_id": "sess_abc123",