Persistent A2A peer conversations
September 2, 2026 ยท View on GitHub
Lemon implements the A2A v1.0 JSON-RPC protocol in both directions. A Lemon
agent can receive work from Hermes or another A2A peer, and the built-in
peer tool can initiate or resume a long-running conversation with a
configured peer.
This is separate from named execution nodes. A named node moves one Lemon run to another machine; an A2A peer remains an independent agent with its own identity, memory, tools, and task lifecycle.
What persists
For every configured peer, Lemon stores a default outbound A2A context. Calling
the peer tool with action = "message" reuses that context automatically.
action = "new" is the explicit escape hatch for starting a separate chat.
Inbound A2A contextId values are mapped to stable private Lemon sessions.
The remote value is hashed before it enters the session key, so a peer cannot
select or discover an arbitrary Lemon session. Contexts, messages, task state,
run IDs, and turn counts use the canonical Lemon store and survive a runtime
restart when the configured store is durable.
Lemon configuration
Store a shared bearer token in Lemon's encrypted secret store:
lemon secrets set a2a/hermes '<random-token>'
Then add this to ~/.lemon/config.toml:
[gateway]
enable_a2a = true
[gateway.a2a]
host = "127.0.0.1"
port = 9901
public_url = "http://127.0.0.1:9901"
name = "Lemon"
agent_id = "default"
skills = ["coordination", "coding"]
reply_timeout_ms = 300000
rate_limit_per_minute = 60
max_context_turns = 100
[gateway.a2a.peers.hermes]
url = "http://127.0.0.1:9900"
token_secret = "a2a/hermes"
agent_id = "default"
capabilities = ["coordination", "coding"]
timeout_ms = 300000
token_secret is used in both directions. Use inbound_token_secret and
outbound_token_secret when the peer uses different credentials. Literal
tokens are not accepted in this section.
The listener is tokenless only when it is bound to a direct loopback address
and no inbound token secret is configured. A non-loopback listener fails
validation unless at least one inbound peer token secret is present. Across
machines, use HTTPS/WSS termination or a verified encrypted overlay and set
public_url to the URL peers can actually reach.
Hermes configuration
Hermes' A2A platform listens on port 9900 by default. Its configuration shape is:
gateway:
platforms:
a2a:
enabled: true
extra:
port: 9900
a2a_agents:
lemon:
url: "http://127.0.0.1:9901"
auth:
type: bearer
token: "<same-random-token>"
timeout: 300
capabilities:
- coordination
- coding
Configure Hermes' inbound bearer token according to its A2A plugin settings,
then restart both runtimes. The Lemon Agent Card is available at
http://127.0.0.1:9901/.well-known/agent-card.json; Hermes exposes the same
well-known path on port 9900.
Agent usage
The peer tool actions are:
list: configured peers and their current default context.discover: fetch the peer's Agent Card.message: send a message in the persistent default conversation.new: start and select a new conversation.history: read Lemon's durable local transcript for a context.status: refresh a remote task.cancel: request remote task cancellation.
For example, an agent can call:
{"action":"message","peer_id":"hermes","text":"Please investigate the failing release and report evidence here."}
The next message call to hermes resumes the same context without the model
having to retain or repeat a context ID.
Wire surface and safety
Lemon publishes canonical and legacy Agent Card paths and accepts the A2A v1.0
methods SendMessage, SendStreamingMessage, GetTask, ListTasks,
CancelTask, and SubscribeToTask, plus the standard lowercase aliases.
Streaming uses reconnectable server-sent events. Runs continue under a task
supervisor if the initiating HTTP or SSE connection closes.
Every task lookup/list/cancel operation is scoped to the authenticated peer.
Inbound peer text is wrapped as external untrusted content before model use.
Outbound responses are returned to the Lemon model with trust: :untrusted.
Inbound runs use a conservative read/coordination tool allowlist by default;
allow_tools can broaden it for one explicitly trusted peer.
The inbound A2A messageId is also the durable replay key. Resending the same
message for the same authenticated peer returns the original task, including
its current state, without starting a second Lemon run. A message ID already
owned by another peer is rejected rather than reassigned.
Push notification callbacks are not advertised in v1 because Lemon does not
currently accept callback URLs. Peers should use synchronous send, streaming,
or SubscribeToTask/GetTask reattachment.