feat(webhooks): stamp the emitting profile on outbound webhook payloads
Under a multiplexed gateway every profile's outbound webhooks share one
delivery worker, so receivers could not tell which profile fired an
event. Add a top-level `profile` field to the payload, resolved at fire
time from the bound Hermes home via get_active_profile_name() ("default"
outside profiles). Documents the field in the wire-format section.
Reported by @vszgdcn8cj-ctrl.
Fixes #92674
This commit is contained in:
@@ -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 "",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user