Repository navigation
feat(deploy_agent): deliver the role to the agent or fail the step - #146
Conversation
A role the agent card could not take was filtered out silently and still recorded on DeployedAgent, so an agent believed to be an executor or a person called the env as the default role. The step now puts AgentEnv-Role into the MCP registration headers for the gateway env when the card's mcp-config add accepts headers, negotiates it through agent-config when the card lists it, sets AGENT_ENV_ROLE for the generated MCP CLI, and raises before any request when neither path can carry a requested role. The recorded role is therefore always one the agent received. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t actually receives A card that requires `name` on mcp-config add and lists `headers` as optional was refused the role header, because the capability check asked about `url` and `headers` alone while the registration also carried the gateway card's name. The check now covers the fields the body will contain. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… gateway envs What an env does with the role is the env's business: a gateway forwards it, a protocol server reads it from the header itself, anything else ignores a header it does not know. Judging deliverability by env type refused a header-capable agent on an env that would have honoured the header. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
In a7bb965 the role header goes on every MCP registration whose card takes |
…one log line _mcp_add_body already decides whether the card's `add` takes the env card name; the role header is one more field it may take, judged on the field set actually sent. execute() builds every registration body first, then asks which paths carry the role and raises if none does. The two warnings for a single path go: the one info line names the paths that carried it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Simplified in 8d9b4ae: |
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The |
Summary
DeployAgentTaskStepfiltered a requestedroleagainst the agent card's agent-configsupportedlist and dropped it silently when the card did not list it, then recorded it onDeployedAgentanyway. An agent the task believed to be an executor, or a person, called the env as the default role, andregister_env_triggerstrusted a role the agent never received. Once the role is the agent's identity in every server (companion gateway and synthetic-pipelines changes), a dropped role is a silent identity swap to the pinned user.AgentEnv-Role) of every env entry when the card's mcp-configaddacceptsheaders(card_request_accepts), merged onto the sandbox headers as a fresh dict. deploy_agent does not judge what the env does with it: a gateway forwards it, a protocol server reads it from the header itself, anything else ignores a header it does not know.A2AAgent.negotiate_agent_configinstead of the hand-rolled filter;rolereaches agent-config exactly when the card lists it.AGENT_ENV_ROLEis set in the agent's environment so the generated MCP CLI stops calling as its defaultclirole.role or Noneso an empty string is not "delivered"._mcp_add_bodygates the card name on the same capability read as the header, so a hand-writtensupportedlist behaves like a protocol-rendered card.DeployedAgent.roleis therefore always a role the agent received.Behaviour change. A task that sets a role on an agent image that cannot carry it now fails at deploy instead of running as
default. In dev,claude-code-cliandcodex-clilistrolein agent-config and acceptheaders;a2a-defaultdoes neither and will fail loudly when given a role. Ships last, after the gateway and server changes.Testing
Unit: new
tst/unit/task_step/test_deploy_agent_role_delivery.py(20; review fix: the header decision judges the fields the registration actually carries, so a card that requiresnameand listsheadersas optional takes the role): both paths, header only, config only, neither (raises before any request, agent closed, nothing recorded), an env not behind a gateway gets the header too, the header lands on every registration, sandbox headers not mutated, card-shape matrix, roleNonebyte-identical to today, empty string,AGENT_ENV_ROLEset and an explicit value kept.test_deploy_agent_mcp_alias.py+1 (hand-writtensupportedrelays the name).tst/unit/task_step+tst/unit/env/task_step: 1648 → 1667 passed, the same 8 pre-existing environment-gated failures.End to end in dev: two
claude-code-cliagents deployed with email roles into one env behind the companion gateway:Both agents were deployed with their emails as roles against the real
claude-code-clicard (which listsrolein agent-config and accepts registrationheaders), so both delivery paths carried the role and the refusal path did not trigger. The twoprompt_agentsteps ran in parallel (103 s and 101 s); every attribution check read back through role sessions passed (6 of 6): each agent's mail arrived in the other's inbox from the right sender, each primary calendar holds its own agent's event, and#generalcarries one post per agent under its own Slack user id. A first attempt with Sonnet failed at the first turn with "Prompt is too long" (270 tool schemas), unrelated to this change; Opus completed.🤖 Generated with Claude Code


Confidence Score: 5/5
No new blocking finding was established.Summary
DeployAgentTaskStepsends the requested role through MCP headers, agent-config, or both. It fails before configuration requests when neither path can carry it.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Deploy agent and read card] --> B[Build MCP registrations] B --> C[Negotiate agent-config fields] C --> D{Requested role has a delivery path?} D -->|No| E[Close agent and fail] D -->|Yes or no role requested| F[Send registrations and configuration] F --> G[Record deployed agent]Reviews (5) · Last reviewed commit: "Merge main into feat/deploy-agent-delive..." · Reviewed by Greptile