English · 中文
This page defines the vocabulary the rest of the manual uses. It describes the
current behavior of 0.4.0, including places where the implementation is
narrower than the protocol.
Agent2Agent (A2A) is an HTTP + JSON-RPC protocol for one agent to call another.
A server publishes an AgentCard describing its identity, endpoint, and
skills; a client reads the card, sends a message to the endpoint, and waits
for a task to settle (or subscribes to streaming updates). This plugin
implements the A2A 1.0.1 JSON-RPC binding and declares protocol version
1.0. It does not implement the gRPC or HTTP+JSON bindings.
| Direction | Who calls whom | Plugin surface |
|---|---|---|
| Inbound | A remote A2A client calls this DSH | One inbound server per published agent |
| Outbound | This DSH calls a remote A2A agent | One outbound connection per remote card |
The directions are independent. A profile can have zero or more of each, and they can point at other DSH instances or at non-DSH A2A agents.
An inbound server instance is a published agent. It owns:
/a2a/<id>;/a2a/<id>/agent-card.json;Instances are created, edited, enabled, disabled, and deleted from the panel
or the /a2a command. They are persisted; a restart re-assembles them and
re-registers the enabled ones.
An outbound connection instance is one remote agent this DSH can call. It owns:
On connect, the plugin fetches the card, imports its skills, and registers one
tool per skill. A failed connection stays in the panel with its lastError
and can be refreshed or edited.
A preset is a DSH agent preset: a named composition of instructions, tools, skills, and model defaults. The inbound server binds one preset and composes a fresh DSH session from it for each A2A context.
Skills are derived, not typed. At create/update/boot, the plugin:
If the skills service or preset mount is unavailable, the card falls back to a
single built-in chat skill. The same chat skill is the default routing
target when a request omits message.metadata.skill.
A request whose metadata.skill is not in the advertised set is rejected by
the skill gate before execution.
An inbound SendMessage creates a task:
a2a-<uuid>;States follow the A2A vocabulary: TASK_STATE_SUBMITTED,
TASK_STATE_WORKING, TASK_STATE_INPUT_REQUIRED,
TASK_STATE_AUTH_REQUIRED, TASK_STATE_COMPLETED, TASK_STATE_FAILED,
TASK_STATE_CANCELED, TASK_STATE_REJECTED. The last four are terminal;
INPUT_REQUIRED and AUTH_REQUIRED are interrupted but not terminal.
A context is one conversation. The plugin keeps one live DSH session per
context, with the deterministic id a2a-<contextId>. Reusing a contextId
reuses that session; a different contextId opens a different one.
Session handles are memory-only. The panel’s Close action cancels the
context’s active tasks and releases the live handle; the next task on the same
contextId opens a fresh handle. Tasks and context→session bindings are
persisted, so a restart keeps task history but does not resurrect sessions.
The inbound half has an executor seam with two implementations:
ctx.subagents is mounted, but the
current default skill→executor map binds only chat to session; unknown
skills also default to session. There is no public configuration that
selects subagent today.On the outbound side, the preset field is persisted and shown in the panel,
but current tools call the remote directly: there is no local hand-off session
composition behind it yet. Treat it as forward-looking metadata, not an active
routing rule.
The card is a pure function of the instance record plus the advertised base URL and endpoint path:
name, description, version from the instance;provider fixed to DeepSeek;capabilities: streaming true, push notifications false, extended card
true, state-transition history false;defaultInputModes / defaultOutputModes: text/plain;skills: the derived skill list;supportedInterfaces: one JSON-RPC interface at the instance endpoint,
protocol version 1.0;bearer security scheme and requirement,
with no secret value.The card is served as JSON at the instance’s cardPath. The well-known
/.well-known/agent-card.json path is not used for instances; use the full
per-instance URL printed by the panel.
Durable state lives in the a2a storage domain:
| Table | Contents |
|---|---|
tasks |
Task records, including source instance id |
contexts |
contextId → sessionId bindings |
agents |
Per-outbound-instance agent records |
identity |
Legacy service identity record |
inbound_servers |
Inbound instance records |
outbound_servers |
Outbound instance records |
The default backend is the profile’s JSON storage; the a2a domain can be
routed to SQLite. See Operations.
Inbound session working directories live under $DSH_HOME/a2a-sessions.
Tasks and instances survive a restart; live sessions and stream subscriptions
do not.
/a2a/api accepts only 127.0.0.1,
::1, or ::ffff:127.0.0.1; any other peer gets 403. A remote A2A client
can call the protocol endpoint but can never drive the control panel.A2A_INBOUND_<id>, A2A_OUTBOUND_<id>). The instance record stores
only that name; the AgentCard stores only the scheme.Authorization: Bearer <token> and returns 401 with
WWW-Authenticate: Bearer otherwise. Streaming requests are authorized
before the 200 SSE headers are written.