diff --git a/roboco/runtime/orchestrator.py b/roboco/runtime/orchestrator.py index 5ebe9933..c44b968a 100644 --- a/roboco/runtime/orchestrator.py +++ b/roboco/runtime/orchestrator.py @@ -4413,7 +4413,11 @@ class AgentOrchestrator: cli_model=cli_model, api_url=api_url, agent_uuid=agent_uuid, - agent_token=issue_agent_token(agent_uuid, "secretary", ""), + agent_token=issue_agent_token( + agent_uuid, + "secretary", + AGENTS[SECRETARY_AGENT_ID].team.value, + ), provider_base_url=route.base_url, provider_auth_token=route.auth_token, provider_type=route.provider_type.value, diff --git a/tests/unit/runtime/test_secretary_spawn.py b/tests/unit/runtime/test_secretary_spawn.py index f64b5ef8..02dd5219 100644 --- a/tests/unit/runtime/test_secretary_spawn.py +++ b/tests/unit/runtime/test_secretary_spawn.py @@ -4,6 +4,12 @@ from __future__ import annotations from unittest.mock import MagicMock +from roboco.agents_config import ( + get_agent_team, + issue_agent_token, + verify_agent_token, +) +from roboco.foundation.identity import AGENTS from roboco.runtime.orchestrator import ( SECRETARY_AGENT_ID, AgentOrchestrator, @@ -57,3 +63,25 @@ def test_resolve_secretary_host_paths_has_claude_and_prompt() -> None: assert "claude" in paths assert "prompt" in paths assert SECRETARY_AGENT_ID in str(paths["prompt"]) + + +def test_secretary_token_signs_over_real_team_not_empty(monkeypatch) -> None: + """The secretary token must verify against the (id, role, team) the + secretary driver actually sends. secretary_driver._headers sends + X-Agent-Team = get_agent_team(uuid) = the secretary's real team ("board"), + so the token issued at spawn (orchestrator _spawn_secretary_container) must + be signed over that same team — not "" — or every /api/secretary/* call + 401s with "signature mismatch" under ROBOCO_AGENT_AUTH_REQUIRED. + """ + monkeypatch.setenv("ROBOCO_AGENT_AUTH_SECRET", "x" * 32) + secretary = AGENTS[SECRETARY_AGENT_ID] + agent_uuid = str(secretary.uuid) + team = secretary.team.value + assert team # the bug was signing "" — the secretary IS on a team + + token = issue_agent_token(agent_uuid, "secretary", team) # mirrors spawn line + # secretary_driver._headers sends X-Agent-ID=uuid, role=secretary, + # X-Agent-Team=get_agent_team(uuid). + assert verify_agent_token( + token, agent_uuid, "secretary", get_agent_team(agent_uuid) + )