The agent registry¶
The registry is the workbench's list of agents it can call. The conductor, the CLI, the transports and the viewer all read the agent list from it. None of them names an agent in code. The decision behind this design is Agents. It replaced the entry-point discovery of Agents.
The code is in harness/src/dd_harness/registry.py. The A2A client it uses is in remote.py.
Where the list comes from¶
Each agent is a separate service. The workbench finds them through one configuration file,
../agents.yaml:
agents:
- name: hello # optional; labels the agent if it is unavailable
url: http://127.0.0.1:8101 # required; the agent's base URL
timeout_s: 30 # optional; seconds per call, default 60
Set DD_AGENTS_CONFIG to use a different file, for example one per deployment.
The file holds only addresses. Everything else about an agent comes from the agent itself:
its identifier, version, the input classes it reads, its payload class, its grounding mode, its
action class, its requirement identifiers, how it derives each payload field (derivations) and
the models it sends data to (models, Policy and identity).
What happens at start-up¶
The workbench builds the registry once, when it starts. For each entry in agents.yaml it:
- fetches the agent card from
<url>/.well-known/agent-card.json; - finds the Data Director extension,
https://w3id.org/data-director/a2a/agent-spec/v0, in the card's capabilities; - rebuilds the agent's
AgentSpecfrom the extension's parameters: the class schemas the card carries, each checked against its digest, and the contract version, checked for compatibility (Contract); - registers the agent under the
agent_idfrom the spec.
The agent_id comes from the card, not from name in agents.yaml. Requests, profiles and
envelopes use the agent_id, for example example.hello.
The workbench does not read the file again while it runs. If an agent starts after the workbench, or its URL changes, restart the workbench to pick it up. Each CLI command is a new process, so it always reads the current state.
When something is wrong¶
Some problems stop the workbench from starting. Others affect only one agent.
| Problem | What happens |
|---|---|
agents.yaml is missing, has no top-level agents: list, or an entry has no url |
RegistryError. The workbench does not start. |
Two cards declare the same agent_id |
RegistryError. The workbench does not start. |
| The card cannot be fetched (the service is down, the URL is wrong, or the request times out) | The agent is recorded as unavailable, with the reason. The other agents still load. |
The card has no Data Director extension; names a class it carries no schema for; carries a schema that does not match its digest; or declares a payload class that does not mix in Grounded |
The agent is recorded as unavailable, with the reason. A class the harness has not seen before is not a reason: the card carries its schema (Contract). |
The card declares a contract_version this harness cannot govern, or none |
The agent is recorded as incompatible, with both versions. The other agents still load (Contract). |
| A request names an agent that is not registered | The conductor raises UnknownAgent. The message lists the registered agents and the reason each unavailable or incompatible one is missing. No envelope is written, because no agent ran. |
| A registered agent cannot be reached when a request is sent, times out, or answers outside the contract | The run ends failed, with problem type agent-error, and the envelope is stored as usual. |
An unavailable or incompatible agent is listed under its name, or under its URL if it has no
name. The registry does not know its agent_id, because it could not read the card, or could not
trust what the card said.
An agent is compatible when its card's contract_version has the harness's major version and,
while the major version is 0, its minor version as well. The contract version is the version of
the core schema, sdk/src/dd_sdk/schema/data_director.yaml.
Registration is not permission¶
The registry says which agents can be reached. The policy profile named in each request decides
which of them may run (Policy and identity). A registered agent that the
profile does not enable is refused at the policy gate, and the refusal is recorded in an
envelope. See conductor.md.
Where the registry is shown¶
Every view below uses the same manifest, describe(spec) from dd_sdk.agent. The views
therefore always agree with one another.
| Where | What it shows |
|---|---|
uv run workbench agents |
A table of registered agents, then each unavailable or incompatible agent with its reason. Add --json for the raw manifest. |
GET /agents |
{"agents": [...manifest...], "unavailable": {name: reason}, "incompatible": {name: reason}}. The viewer reads this. |
| The workbench's own A2A card | One skill per registered agent, tagged with its grounding mode, the classes it accepts and its requirement identifiers. |
| The viewer | The agent picker, grouped by the prefix of each agent_id. Unavailable and incompatible agents appear with their reason and cannot be run. The payload view badges fields from the agent's derivations in the manifest. |
Adding or removing an agent¶
To add an agent, serve it and add one entry to agents.yaml. Then enable it in the profiles
that should run it. The full steps are in ../../agents/README.md.
To remove an agent, delete its entry. No other part of the workbench is edited.
For local development, scripts/run-agents.sh at the repository root starts every agent on the
port agents.yaml expects.
In tests¶
Tests do not read agents.yaml. Registry.from_agents takes agent objects directly, and
dd_harness.testing.make_conductor passes each agent through in_process unless called with
remote=False. This serves the agent
with dd_sdk.serve on an in-memory ASGI transport and builds a RemoteAgent from its card.
Tests therefore use the same card, the same extension and the same A2A messages as a deployed
agent, without a network.
Not yet decided¶
- Authentication between the workbench and its agents. TODO(#17) (Agents). Until it exists, do not expose agent ports beyond the host or a private network.
- Reloading the registry, or checking agent health, while the workbench runs. TODO(#251).
- Container images and a
compose.yamlfor running the agents. TODO(#223, #224) (Agents).