@aramisfa/openclaw-a2a-出行
OpenClaw出局A2A代表团插件 零配置本地登入CLI.
openclaw plugins install @aramisfa/openclaw-a2a-outbound
openclaw a2a demo run
openclaw a2a demo run 启动已设置的本地演示端口, 配置临时 local-demo 内存中的目标,练习 list_targets,包含任务 send, (中文(简体) ), watch, (中文(简体) ), status继续重放,然后打印返回的 summary.continuation。 。 。 .
此包注册 :
- 页:1
openclaw a2aCLI 演示和诊断家庭 - 一个可选的 OpenClaw 工具,
remote_agent,有动作list_targets, (中文(简体) ),send, (中文(简体) ),watch, (中文(简体) ),status,以及cancel
对于一个原始的有效载荷走过,看 文件/快速启动.md。 。 。 .
安装
openclaw plugins install @aramisfa/openclaw-a2a-outbound
如果您想要再生产安装, 请按下已公布的版本 :
openclaw plugins install @aramisfa/openclaw-a2a-outbound --pin
可供选择的引导设置助手 :
clawhub install a2a-delegation-setup
ClawHub 技能是用于安装、启用、配置、核实、更新和排除出故障的可选导设置助手 @aramisfa/openclaw-a2a-outbound。插件本身仍然通过 openclaw plugins install @aramisfa/openclaw-a2a-outbound。 。 。 .
运行边界
@aramisfa/openclaw-a2a-outbound 是此寄存器中唯一一个输出的 A2A 继续表面.
summary.continuation用于remote_agent仅限后续行动.- 不重播
summary.continuation返回通过进入频道的频道发送a2a。 。 。 . - 不要治疗进取的 A2A 通道转弯,
OriginatingChannel, (中文(简体) ),OriginatingTo,或输入capabilities.reply作为通用频道级别队列后续支持的证明. - 如果你看见
A2A_OUTBOUND_DELIVERY_UNSUPPORTED, OpenClaw 试图通过队列跟踪路径openclaw-a2a-inbound; 将工作流程切换为持续summary.continuation加号remote_agent。 。 。 .
所需资源
- 节点.js
>=24.16.0 <25 || >=26.1.0 - 打开Claw
2026.9.6
国 际
openclaw a2a demo run [--json] [--write-continuation <path>]
openclaw a2a demo serve [--port <port>] [--json]
openclaw a2a doctor [--alias <alias>] [--json]
demo run:自成先入后入道.demo serve: 打印精确配置命令和原始的长寿本地对等器remote_agent载荷.doctor: 真实目标的只读安装/配置/目标/卡诊断.
OpenClaw 插件配置
openclaw plugins install 允许在 OpenClaw 系统级别自动启用插件 - 不单独 openclaw plugins enable 需要步骤。 然而 remote_agent 工具由插件自己打开 "enabled" 标记在其配置对象内。 设定 "enabled": true 在插件 id 下 openclaw-a2a-outbound 用于激活工具的 OpenClaw 插件配置 :
{
"enabled": true,
"defaults": {
"timeoutMs": 120000,
"cardPath": "/.well-known/agent-card.json",
"preferredTransports": ["JSONRPC", "HTTP+JSON"],
"serviceParameters": {}
},
"targets": [
{
"alias": "support",
"baseUrl": "https://support.example",
"description": "Primary support lane",
"tags": ["support"],
"examples": ["Summarize this incident and propose immediate actions."],
"default": true
}
],
"taskHandles": {
"ttlMs": 86400000,
"maxEntries": 1000
},
"policy": {
"acceptedOutputModes": [],
"normalizeBaseUrl": true,
"enforceSupportedTransports": true,
"allowTargetUrlOverride": false
}
}
打电话 list_targets 首先发现配置的别名并刷新对等卡元数据。 偏爱 target_alias 结束 target_url; 使用 target_url 仅当策略允许直接 URL 路由时.
统一工具合同
remote_agent 接受具有顶级字段的平面化请求对象:
action:每次请求都需要.target_alias: 首选路由键send, (中文(简体) ),watch, (中文(简体) ),status,以及cancel。 。 。 .target_url: 当策略允许或匹配所配置的目标时, 明确远程基础 URL .parts: 需要的非空数组send; 每一部分为text, (中文(简体) ),file,或data。 。 。 .message_id: 可选客户端提供的消息 idsend。 。 。 .task_handle:不透明的委托任务手柄.send, (中文(简体) ),watch, (中文(简体) ),status,以及cancel所有人都接受这个.task_id: 用于send,在无时继续现有的远程任务task_handle备有watch编号 :status编号 :cancel后退后退键task_handle已提供.context_id: 可选远程对话上下文 id forsend只有这个 也用这个task_id或者自己在现有对话中开始新的任务。 平调context_id不得用于watch, (中文(简体) ),status,或cancel。 。 。 .reference_task_ids: 可选的相关任务编号send。 。 。 .task_id继续执行现有任务;reference_task_ids引用以前的任务而不继续.task_requirement: 可选send合同。 默认为"optional"; 设置task_requirement="required"需要真正的任务.follow_updates: 流出开头send。 。 。 .follow_updates=true意思是“流出初始发送”;它不保证任务的创建,除非task_requirement="required"。 。 。 .accepted_output_modes: 可选的每个调用输出模式覆盖send。 。 。 .blocking: 可选非流sendno. 被拒绝时follow_updates=true。 。 。 .history_length: 可选历史窗口send财务报告和审定财务报表status。 。 。 .push_notification_config: 可选回调配置send。 。 。 .timeout_ms: 每个请求超时.service_parameters:可选出站服务参数.metadata: 可选元数据有效载荷send。 。 。 .
Snake case工具字段在内部被翻译为A2A SDK CameCase请求的有效载荷. 特定行动验证拒绝对每项行动不支持的字段,因此保持请求的平整并注重行动.
行动
list_targets: 发现已配置的目标,别名,例和水合对等卡元数据.send: 向远程代理发送一个或多个消息部件, 要么作为新转会, 要么作为对现有任务/ 对话的后续转会.watch: 重新订阅运行中的授权任务和流更新.status: 获取最新的任务快照 .cancel:请求取消授权的任务.
失败 send 财务报告和审定财务报表 sendStream 电话包括 error.details.capability_diagnostics 因此远程验证或内容类型的拒绝可以和存储的对等卡进行比较,而不会阻碍允许的运行时间发送.
继续规则
此软件包返回继续元数据 summary.continuation. 坚持树下逐字,并用它来选择下一个动作.
summary.continuation.target: Cononical坚持了机器跟踪的路线合同. 持久性target_url, (中文(简体) ),card_path,以及preferred_transports逐字记录;target_alias是可选描述性元数据.summary.continuation.task:唯一一个存在真正远程任务的机器可读信号. 读取task_handle, (中文(简体) ),task_id, (中文(简体) ),status, (中文(简体) ),can_resume_send, (中文(简体) ),can_watch,以及被贬损的别名can_send从这里开始,然后用它来跟进send, (中文(简体) ),watch, (中文(简体) ),status,以及cancel。 。 。 .summary.continuation.conversation:只发送对话连续性. 读取context_id从这里开始,只用于后续send。 。 。 .response_kind:只有描述性的线形分类.response_kind="message"表示端点返回 aMessage· ;response_kind="task"是指出现任务响应或事件.response_kind不替换summary.continuation。 。 。 .summary.target_*和其他顶级相容性别名只是描述性的. 不从平地推断生命周期连续性task_id平顶context_id,或其他顶级字段.watch, (中文(简体) ),status,以及cancel需求summary.continuation.task。 。 。 .- 仅限对话的后续用途
context_id,而不是任务动作. summary.continuation无法通过频道跟踪后续a2a这是长期合同remote_agent只有这个.
支助 send 模式 :
- 新任务 :
send与target_alias编号 :target_url或配置的默认目标,没有继续字段 - 现有任务续作:
send坚持不懈continuation; 平调task_handle,或task_id加号target_alias编号 :target_url,仅保留手动兼容输入 - 相关的新任务:
send与reference_task_ids可选加context_id附加时target_alias编号 :target_url或配置的默认目标 - 现有对话中的新任务 :
send只有对话才持续continuation; 平调context_id加号target_alias编号 :target_url仅保留手动兼容性
当授权任务暂停时 input-required 或批准工作流程, 恢复它与 send 再来一次 如果筑巢的话 task_handle 已过期或无法使用,插件将自动回到嵌入合同中 continuation.target + 键 continuation.task.task_id呼叫者不应将持续的后续状态降为 target_alias编号 :task_id。 。 。 .
根据延续形状选择下一个动作 :
const continuation = result.summary.continuation
const task = continuation?.task
const conversation = continuation?.conversation
if (task) {
const followUp = { action: "status", continuation }
} else if (conversation) {
const followUp = {
action: "send",
continuation,
parts: [{ kind: "text", text: "Start a related task in the same conversation." }],
}
}
实例
发现可用目标
{ "action": "list_targets" }
{
"ok": true,
"operation": "remote_agent",
"action": "list_targets",
"summary": {
"targets": [
{
"target_alias": "support",
"target_url": "https://support.example/",
"default": true,
"tags": ["support"],
"examples": ["Summarize this incident and propose immediate actions."],
"target_name": "Support Agent",
"description": "Primary support lane",
"streaming_supported": true,
"peer_card": {
"preferred_transport": "JSONRPC",
"additional_interfaces": [
{
"transport": "JSONRPC",
"url": "https://support.example/a2a/jsonrpc"
},
{
"transport": "HTTP+JSON",
"url": "https://support.example/a2a/rest"
}
],
"capabilities": {
"streaming": true,
"push_notifications": true,
"state_transition_history": true
},
"default_input_modes": ["text/plain"],
"default_output_modes": ["text/plain"],
"skills": [
{
"id": "triage",
"name": "Incident Triage",
"description": "Summarize incidents and propose next actions.",
"tags": ["support"],
"examples": ["Summarize this incident and propose immediate actions."],
"input_modes": ["application/json"],
"output_modes": ["application/pdf"]
}
]
}
}
]
},
"raw": [
{
"target": {
"baseUrl": "https://support.example/",
"cardPath": "/.well-known/agent-card.json",
"preferredTransports": ["JSONRPC", "HTTP+JSON"],
"alias": "support",
"displayName": "Support Agent",
"description": "Primary support lane",
"streamingSupported": true
},
"configuredDescription": "Primary support lane",
"default": true,
"tags": ["support"],
"examples": ["Summarize this incident and propose immediate actions."],
"card": {
"displayName": "Support Agent",
"description": "Summarize incidents and propose next actions.",
"preferredTransport": "JSONRPC",
"additionalInterfaces": [
{
"transport": "JSONRPC",
"url": "https://support.example/a2a/jsonrpc"
},
{
"transport": "HTTP+JSON",
"url": "https://support.example/a2a/rest"
}
],
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": true
},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "triage",
"name": "Incident Triage",
"description": "Summarize incidents and propose next actions.",
"tags": ["support"],
"examples": ["Summarize this incident and propose immediate actions."],
"inputModes": ["application/json"],
"outputModes": ["application/pdf"]
}
],
"lastRefreshedAt": "2026-03-12T10:00:00.000Z"
}
}
]
}
发送到一个明确的 target_alias
{
"action": "send",
"target_alias": "support",
"parts": [
{
"kind": "text",
"text": "Summarize this bug report for triage."
}
],
"metadata": {
"ticket_id": "INC-42"
}
}
{
"ok": true,
"operation": "remote_agent",
"action": "send",
"summary": {
"target_alias": "support",
"target_url": "https://support.example/",
"response_kind": "message",
"message_text": "Triage summary: reproduce, collect logs, and notify the on-call engineer."
},
"raw": {
"kind": "message"
}
}
需要持久的任务
{
"action": "send",
"target_alias": "support",
"task_requirement": "required",
"parts": [
{
"kind": "text",
"text": "Start a trackable task and return immediately."
}
]
}
在不继续任务的情况下开始相关工作
{
"action": "send",
"target_alias": "support",
"context_id": "ctx-123",
"reference_task_ids": ["task-101", "task-102"],
"parts": [
{
"kind": "text",
"text": "Start a related task in the same conversation."
}
]
}
使用配置的默认目标发送
如果有一个目标被标记 "default": true, (中文(简体) ), send 可省略 target_alias数字 :
{
"action": "send",
"parts": [
{
"kind": "text",
"text": "Draft a reply to the customer update."
}
],
"follow_updates": true
}
{
"ok": true,
"operation": "remote_agent",
"action": "send",
"summary": {
"target_alias": "support",
"target_url": "https://support.example/",
"response_kind": "task",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"task": {
"task_handle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0",
"task_id": "task-456",
"status": "completed",
"can_resume_send": false,
"can_send": false,
"can_status": true,
"can_cancel": false,
"can_watch": false
},
"conversation": {
"context_id": "ctx-456",
"can_send": true
}
}
},
"raw": {
"events": [
{
"kind": "task",
"id": "task-456",
"contextId": "ctx-456",
"status": {
"state": "submitted"
}
},
{
"kind": "status-update",
"taskId": "task-456",
"contextId": "ctx-456",
"status": {
"state": "completed"
},
"final": true
}
],
"finalEvent": {
"kind": "status-update",
"taskId": "task-456",
"contextId": "ctx-456",
"status": {
"state": "completed"
},
"final": true
}
}
}
const task = result.summary.continuation?.task
const conversation = result.summary.continuation?.conversation
if (!task) {
throw new Error("expected a trackable delegated task")
}
if (task.can_watch) {
// `watch` is valid here.
}
const followUpContext = conversation?.context_id
继续现有的远程任务 task_handle
重新发送持续的子树 :
{
"action": "send",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"task": {
"task_handle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0",
"task_id": "task-456"
},
"conversation": {
"context_id": "ctx-456",
"can_send": true
}
},
"parts": [
{
"kind": "text",
"text": "Approved. Continue with the task and finish the reply."
}
]
}
继续现有的远程任务 task_id
不使用 task_handle数字 :
{
"action": "send",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"task": {
"task_id": "task-456"
},
"conversation": {
"context_id": "ctx-456",
"can_send": true
}
},
"parts": [
{
"kind": "text",
"text": "Continue the prior conversation and draft the final reply."
}
]
}
在现有对话中启动新任务
使用 summary.continuation.conversation.context_id 当仅存在对话连续性时:
{
"action": "send",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"conversation": {
"context_id": "ctx-456",
"can_send": true
}
},
"parts": [
{
"kind": "text",
"text": "Start a new side task, but keep it in the same conversation."
}
]
}
{
"ok": true,
"operation": "remote_agent",
"action": "send",
"summary": {
"target_alias": "support",
"target_url": "https://support.example/",
"response_kind": "message",
"message_text": "Conversation continued. Start the next task when ready.",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"conversation": {
"context_id": "ctx-456",
"can_send": true
}
}
},
"raw": {
"kind": "message"
}
}
const task = result.summary.continuation?.task
const conversation = result.summary.continuation?.conversation
if (task) {
throw new Error("expected a context-only continuation")
}
if (conversation) {
const followUp = {
action: "send",
continuation: result.summary.continuation,
parts: [{ kind: "text", text: "Start the next task in this conversation." }],
}
}
对话的连续性是只发送. watch, (中文(简体) ), status,以及 cancel 需求 summary.continuation.task。 。 。 .
检查任务状态 task_handle
{
"action": "status",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"task": {
"task_handle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0",
"task_id": "task-456"
},
"conversation": {
"context_id": "ctx-456",
"can_send": true
}
},
"history_length": 2
}
{
"ok": true,
"operation": "remote_agent",
"action": "status",
"summary": {
"target_alias": "support",
"target_url": "https://support.example/",
"response_kind": "task",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"task": {
"task_handle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0",
"task_id": "task-456",
"status": "completed",
"can_resume_send": false,
"can_send": false,
"can_status": true,
"can_cancel": false,
"can_watch": false
},
"conversation": {
"context_id": "ctx-456",
"can_send": true
}
}
},
"raw": {
"kind": "task",
"id": "task-456",
"status": {
"state": "completed"
}
}
}
const task = result.summary.continuation?.task
const conversation = result.summary.continuation?.conversation
if (!task) {
throw new Error("status requires summary.continuation.task")
}
const nextStatus = task.task_handle
? { action: "status", continuation: result.summary.continuation }
: { action: "status", continuation: result.summary.continuation }
const nextSend = conversation
? { action: "send", continuation: result.summary.continuation }
: undefined
若为 summary.continuation.task.task_handle 已过期或无法使用, 重复使用相同的持续尝试 continuation数字 :
{
"action": "status",
"continuation": {
"target": {
"target_url": "https://support.example/",
"card_path": "/.well-known/agent-card.json",
"preferred_transports": ["JSONRPC", "HTTP+JSON"],
"target_alias": "support"
},
"task": {
"task_handle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0",
"task_id": "task-456"
}
}
}
watch 财务报告和审定财务报表 cancel 需求 summary.continuation.task 以往结果:
{ "action": "watch", "continuation": { "target": { "target_url": "https://support.example/", "card_path": "/.well-known/agent-card.json", "preferred_transports": ["JSONRPC", "HTTP+JSON"], "target_alias": "support" }, "task": { "task_handle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0", "task_id": "task-456" } } }
{ "action": "cancel", "continuation": { "target": { "target_url": "https://support.example/", "card_path": "/.well-known/agent-card.json", "preferred_transports": ["JSONRPC", "HTTP+JSON"], "target_alias": "support" }, "task": { "task_handle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0", "task_id": "task-456" } } }
校验和可操作错误
工具输入验证在严格模式下使用Ajv. 验证失败使用 operation: "remote_agent" 并包含本地风格的 Ajv 错误对象 :
{
"ok": false,
"operation": "remote_agent",
"action": "send",
"error": {
"code": "VALIDATION_ERROR",
"message": "remote_agent input validation failed",
"details": {
"source": "ajv",
"tool": "remote_agent",
"errors": [
{
"keyword": "anyOf",
"instancePath": "",
"message": "send requires task_handle, target_alias, target_url, or a configured default target"
}
]
}
}
}
过期的手柄返回可操作的回收信封 :
{
"ok": false,
"operation": "remote_agent",
"action": "status",
"error": {
"code": "EXPIRED_TASK_HANDLE",
"message": "task handle \"rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0\" has expired",
"details": {
"taskHandle": "rah_0a3ff8c2-4a6d-48cb-a57d-4ae6f3c589d0",
"retryHint": "Retry with the same nested continuation so the plugin can fall back to the persisted target plus taskId, or resend the original request after a restart to obtain a new handle.",
"restartInvalidatesHandles": true,
"suggested_actions": ["status", "send"],
"hint": "Retry with the same nested continuation, or use flat target_alias + task_id only as manual compatibility."
}
}
}
发展
pnpm build
pnpm test
许可证
麻省理工学院