mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-26 01:01:40 +00:00
feat(codex-bridge): notify origin on async completion
This commit is contained in:
parent
f27b32d6b0
commit
da25c6e163
8 changed files with 614 additions and 7 deletions
|
|
@ -75,6 +75,19 @@ def test_start_task_uses_app_server_thread_turn_without_mailbox(tmp_path, monkey
|
|||
assert "inbox" not in json.dumps(client.requests).lower()
|
||||
|
||||
|
||||
def test_start_task_records_notify_target(tmp_path, monkeypatch):
|
||||
manager = make_manager(tmp_path, monkeypatch)
|
||||
|
||||
result = manager.start_task("Analyze tests", cwd=str(tmp_path), notify_target="feishu:chat-1")
|
||||
task_id = result["task"]["hermes_task_id"]
|
||||
|
||||
assert result["task"]["notify_target"] == "feishu:chat-1"
|
||||
assert result["task"]["notification_status"] == "pending"
|
||||
persisted = manager.status(task_id)["task"]
|
||||
assert persisted["notify_target"] == "feishu:chat-1"
|
||||
assert persisted["notification_status"] == "pending"
|
||||
|
||||
|
||||
def test_server_approval_request_can_be_reported_and_resolved(tmp_path, monkeypatch):
|
||||
manager = make_manager(tmp_path, monkeypatch)
|
||||
started = manager.start_task("Run a safe command", cwd=str(tmp_path))
|
||||
|
|
@ -143,8 +156,76 @@ def test_steer_and_interrupt_call_codex_turn_methods(tmp_path, monkeypatch):
|
|||
assert client.requests[-1][0] == "turn/interrupt"
|
||||
|
||||
|
||||
def test_notify_completed_sends_once_for_targeted_completed_task(tmp_path, monkeypatch):
|
||||
manager = make_manager(tmp_path, monkeypatch)
|
||||
started = manager.start_task("Summarize a bug", cwd=str(tmp_path), notify_target="feishu:chat-1")
|
||||
task_id = started["task"]["hermes_task_id"]
|
||||
deliveries = []
|
||||
|
||||
manager.record_event(
|
||||
task_id,
|
||||
"turn/completed",
|
||||
{"turn": {"id": "turn-1", "status": "completed"}, "message": "Done fixing it."},
|
||||
)
|
||||
|
||||
first = manager.notify_completed(notifier=lambda target, message: deliveries.append((target, message)) or {"ok": True})
|
||||
second = manager.notify_completed(notifier=lambda target, message: deliveries.append((target, message)) or {"ok": True})
|
||||
|
||||
assert first["processed"] == 1
|
||||
assert first["notifications"][0]["notification_status"] == "sent"
|
||||
assert first["notifications"][0]["sent"] is True
|
||||
assert second["processed"] == 0
|
||||
assert len(deliveries) == 1
|
||||
assert deliveries[0][0] == "feishu:chat-1"
|
||||
assert task_id in deliveries[0][1]
|
||||
assert manager.status(task_id)["task"]["notification_status"] == "sent"
|
||||
|
||||
|
||||
def test_notify_completed_marks_no_target_without_sending(tmp_path, monkeypatch):
|
||||
manager = make_manager(tmp_path, monkeypatch)
|
||||
started = manager.start_task("No callback needed", cwd=str(tmp_path))
|
||||
task_id = started["task"]["hermes_task_id"]
|
||||
|
||||
manager.record_event(
|
||||
task_id,
|
||||
"turn/completed",
|
||||
{"turn": {"id": "turn-1", "status": "completed"}, "message": "Done."},
|
||||
)
|
||||
|
||||
result = manager.notify_completed(notifier=lambda _target, _message: (_ for _ in ()).throw(AssertionError("sent")))
|
||||
|
||||
assert result["processed"] == 1
|
||||
assert result["notifications"][0]["notification_status"] == "no_target"
|
||||
assert result["notifications"][0]["sent"] is False
|
||||
assert manager.status(task_id)["task"]["notification_status"] == "no_target"
|
||||
|
||||
|
||||
def test_notify_completed_dry_run_does_not_send_or_mark(tmp_path, monkeypatch):
|
||||
manager = make_manager(tmp_path, monkeypatch)
|
||||
started = manager.start_task("Preview callback", cwd=str(tmp_path), notify_target="local")
|
||||
task_id = started["task"]["hermes_task_id"]
|
||||
|
||||
manager.record_event(
|
||||
task_id,
|
||||
"turn/completed",
|
||||
{"turn": {"id": "turn-1", "status": "completed"}, "message": "Done."},
|
||||
)
|
||||
|
||||
result = manager.notify_completed(
|
||||
dry_run=True,
|
||||
notifier=lambda _target, _message: (_ for _ in ()).throw(AssertionError("sent")),
|
||||
)
|
||||
|
||||
assert result["processed"] == 1
|
||||
assert result["notifications"][0]["notification_status"] == "dry_run"
|
||||
assert result["notifications"][0]["sent"] is False
|
||||
assert manager.status(task_id)["task"]["notification_status"] == "pending"
|
||||
|
||||
|
||||
def test_tool_schema_refuses_danger_full_access():
|
||||
props = bridge.CODEX_BRIDGE_SCHEMA["parameters"]["properties"]
|
||||
|
||||
assert "danger-full-access" not in props["sandbox"]["enum"]
|
||||
assert "never" not in props["approval_policy"]["enum"]
|
||||
assert "notify_completed" in props["action"]["enum"]
|
||||
assert "notify_target" in props
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue