# Changelog
All notable changes to `cmdc_gateway` are documented in this file.
## [0.4.1] - 2026-05-16
**Patch release** — Phase 12M v0.4.1 webhook 可靠性闭环 + A2A 协议
补全。**纯加性变更**,与 v0.4.0 完全向后兼容。
> 主库 `cmdc` 在 v0.4.1 不变(保持 0.4.0 为最新),本 patch 仅升级
> Gateway。`{:cmdc_gateway, "~> 0.4"}` 自动拉到 0.4.1。
### Added — A2A `tasks/get` 兜底查询 (Phase 12M / 12M.4)
补全 v0.4 留尾的「webhook 派发失败 / 客户端漏接收时无法查询 task 状态」
痛点:
- **`GET /v1/a2a/tasks/:task_id`** 新端点:返回 task 当前 status /
last_event / payload / updated_at / ttl_until
- **`CMDCGateway.TaskStore`** 新模块:ETS-backed 短期缓存(默认 TTL
10 分钟),由 `CMDCGateway.A2A` 在每次 webhook 派发时同步刷新
- `put/4`、`get/1`、`delete/1`、`list_recent/1`、`gc_now/0` 公共 API
- 后台 GenServer 每 60s GC 过期条目
- 不持久化(重启即清,仅做"短期兜底",与 12M.1 dead-letter 职责正交)
- A2A `dispatch_webhook/5` 内部每次派发同步 `TaskStore.put`,让 GET
端点能立即拿到 accepted / status_update / artifact_update / completed
/ failed 5 类状态
### Added — Webhook Dead-Letter 持久化 (Phase 12M / 12M.1)
把 v0.4.0 「webhook 派发失败仅 log」升级为「失败 → 持久化 → BEAM
重启后自动重试」的可靠性闭环:
- **`CMDCGateway.Webhook.DeadLetter` behaviour** — 4 callback:
`init/1` / `save_failed/2` / `list_pending/1` / `delete/2` /
`mark_retried/3`。任意 backend 实现统一接口
- **`CMDCGateway.Webhook.DeadLetter.DETS`** —— 默认 backend:DETS 文件
持久化,BEAM 重启后自动 reload;零外部依赖;`auto_save: 5_000ms` 保护
- **`CMDCGateway.Webhook.Dispatcher`** —— 调度器 GenServer:
- 启动时按 `enabled?` opt 决定是否开始调度(默认 true,可通过
`WEBHOOK_DLQ_ENABLED=false` 关闭)
- 每 `tick_interval_ms`(默认 30s)扫一遍 pending → 到时间的条目重
新调 `Webhook.dispatch`
- 派发成功 → `DeadLetter.delete` 清除
- 派发失败 → `DeadLetter.mark_retried` 推后下一次重试(指数退避,
base 30s / cap 1h / 默认 max 10 retries)
- 达到 max_retries → 永久放弃 + `Logger.error` + emit telemetry
`[:cmdc_gateway, :webhook, :dead_letter, :permanent_failure]`
- 公共 API:`enqueue/4`、`tick_now/0`、`stats/0`(含 pending_count /
total_retries / total_succeeded / total_permanent_failures)
- **`A2A.dispatch_webhook/5` 自动入队**:3 次同步重试失败后 → 调
`Dispatcher.enqueue` 入 dead-letter。Dispatcher 未启动 / 不可用时
退化为原始 error tuple(无 regression)
### Added — Webhook Receiver 客户端示例 (Phase 12M / 12M.2 + 12M.3)
`cmdc_gateway/examples/a2a/` 新增两个开箱即用的 webhook 接收端示例,
含 HMAC-SHA256 验签与 5 类 event payload 解析:
- **`webhook_receiver.exs`** —— Elixir Plug.Cowboy 实现
- 自包含 `Mix.install([{:plug_cowboy, ~> 2.7}, {:jason, ~> 1.4}])`,
单文件即可跑
- 自定义 `body_reader` 缓存 raw bytes 给签名校验用
- `:crypto.hash_equals/2` 常数时间比较(OTP 25+),老 OTP 自动降级
- **`webhook_receiver.mjs`** —— Node.js Express 实现
- `crypto.timingSafeEqual` 防 timing attack
- JSDoc 类型定义覆盖 5 类 payload
- `--self-test` 内置签名算法验证
- `examples/a2a/README.md` —— 协议演进时间线表 + 各 receiver 运行
命令 + 端到端测试 curl 示例
### Changed — `examples/a2a/README.md` 协议速查表
| 端点 | 阶段 | 说明 |
|------|------|------|
| `POST /v1/a2a/tasks/send` | 11C | JSON-RPC 同步 |
| `POST /v1/a2a/tasks/sendSubscribe` | 11C | JSON-RPC + SSE 流式 |
| `POST /v1/a2a/tasks/sendWithWebhook` | **12L NEW.5** | JSON-RPC + webhook 异步 |
| **`GET /v1/a2a/tasks/:task_id`** | **12M v0.4.1** | **兜底查询(ETS 10min)** |
### 配置参考
```elixir
# config/runtime.exs(推荐)
config :cmdc_gateway, CMDCGateway.Webhook.Dispatcher,
enabled?: true,
backend: CMDCGateway.Webhook.DeadLetter.DETS,
backend_opts: [file: ~c"/var/cmdc/webhook_dlq.dets"],
tick_interval_ms: 30_000,
max_retries: 10
```
环境变量快速配置:
- `WEBHOOK_DLQ_ENABLED=false` — 关闭 dead-letter 调度
- `WEBHOOK_DLQ_FILE=/path/to/dlq.dets` — DETS 文件路径
- 默认值:`enabled?=true`,文件位置 `System.tmp_dir!()/cmdc_gateway_webhook_dlq.dets`
### Tests
- `test/cmdc_gateway/task_store_test.exs` — **14 测试**(put/get/TTL/GC/
delete/list_recent + event → status 推导)
- `test/cmdc_gateway/webhook/dead_letter_test.exs` — **5 测试**
(build_entry + next_retry 指数退避)
- `test/cmdc_gateway/webhook/dead_letter_dets_test.exs` — **8 测试**
(DETS round-trip + 持久化 reload + 幂等删除)
- `test/cmdc_gateway/webhook/dispatcher_test.exs` — **4 测试**(init
降级 + enqueue + stats + tick 未到期跳过)
- `test/cmdc_gateway/a2a_test.exs` — **+4 新测试**(`GET tasks/:id`
TaskStore 命中 / 404 / TTL 过期 / status 推导)
- 全量 **160 tests / 0 failures**(v0.4.0 125 → v0.4.1 160,+35 新增)
- `mix compile --warnings-as-errors` / `mix format --check-formatted` /
`mix credo --strict` 路径全通
### 留给 v0.4.2 / v0.5
- webhook artifact_update(message_delta)throttle 机制(v0.4 全归并为
status_update 避免风暴;v0.4.2 可选 throttle 后开启 artifact_update)
- A2A 多轮对话 `sessionId` 默认复用(v0.4.0 起步阶段每次 task 是独立
session,v0.5 计划补 sessionId 复用语义)
- A2A `Push notifications` 完整模式(v0.5)
## [0.4.0] - 2026-05-16
**Minor release** — A2A Protocol 第 4 种交互机制 **webhook 模式**落地
(Phase 12 NEW.5 / 12L),补齐 ADP Ch.15 定义的 sync / SSE / **webhook** 三大入口;
依赖升级到 `cmdc ~> 0.4`,自动获得核心库 Phase 12 全部能力(Checkpoint /
Backend / Skill v2 / 3 新内置 Plugin / Telemetry)。No breaking changes
against v0.3.x。
### Added — A2A Webhook 异步交互模式(Phase 12 NEW.5 / 12L / ADP Ch.15)
ADP Ch.15 定义的 4 种 A2A 交互机制中,v0.3 已实现 sync (`/tasks/send`) +
SSE (`/tasks/sendSubscribe`),**唯独缺 webhook**。SSE 在家庭路由器 / NAT
环境下普遍被 60 ~ 300 秒空闲超时杀连接,对 `deep_research` / `ralph_mode`
这种 >5 分钟的长任务是硬伤。v0.4 补齐:
- **`POST /v1/a2a/tasks/sendWithWebhook`** — JSON-RPC 2.0 扩展端点:接收
`callbackUrl`(必填)+ `webhookSecret`(可选,用于 HMAC 签名)+ `id` /
`message` / `sessionId` / `timeout_ms`;**立即返回** `{taskId, status:
"accepted"}`,Agent 任务在后台 `Task` 进程内异步执行
- **`CMDCGateway.Webhook`** 新模块:HMAC-SHA256 签名 + 派发 + 指数退避重试
- `sign_payload/2` + `verify_signature/3` —— 用 `:crypto.hash_equals/2`
(OTP 25+,常数时间比较防 timing attack;老 OTP 自动降级到 byte_size
+ 逐字符)
- `build_payload/3` —— 标准 payload schema(`event` / `taskId` /
`timestamp` / `schemaVersion: 1` + extra)
- `dispatch/3` —— 同步 POST + 3 次指数退避(1s / 2s / 4s),失败 log
error;HTTP 客户端用 Erlang 内置 `:httpc`(**零额外依赖**)
- **`CMDCGateway.A2A.start_with_webhook/3`** —— Webhook lifecycle 编排:
- 立即派发 `task.accepted` webhook
- subscribe EventBus;翻译 5 类 A2A 生命周期事件回调:
- `task.accepted` —— Task 接受,session 已创建
- `task.statusUpdate` —— Agent 状态变化(`working / tool_calling` 等)
- `task.artifactUpdate` —— 流式 message_delta(可选)
- `task.completed` —— Agent 正常结束 + 完整回复
- `task.failed` —— Agent abort / timeout / error
- 派发失败:3 次指数退避后仅写 log(v0.4.0 不持久化重试,留 v0.4.1)
### Signature 验签示例
```bash
# 客户端发起
curl -X POST https://your-gateway/v1/a2a/tasks/sendWithWebhook \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "id": 1, "method": "tasks/sendWithWebhook",
"params": {
"id": "task-abc",
"callbackUrl": "https://my-app.com/webhook",
"webhookSecret": "shared-secret-123",
"message": {"role": "user", "parts": [{"type": "text", "text": "..."}]}
}
}'
# 立即返回
# {"jsonrpc": "2.0", "id": 1, "result": {"taskId": "task-abc", "status": "accepted"}}
# 后台异步派发回调(含 HMAC-SHA256 签名)
# POST https://my-app.com/webhook
# X-CMDC-Signature: sha256=<hex>
# Body: {"event": "task.completed", "taskId": "task-abc", "result": {...},
# "timestamp": "2026-05-16T...", "schemaVersion": 1}
```
### Changed — 依赖升级
- `{:cmdc, "~> 0.4"}` —— 升级到核心库 0.4.0,Gateway 自动获得 cmdc 0.4
全部能力:
- **`CMDC.Checkpoint`** —— 配 `Backend.DETS` 可让 Session 在 BEAM 重启 /
Gateway 滚动更新后无缝恢复
- **`CMDC.Backend.Composite`** —— prefix 路由后端,Gateway 单 Session
可同时挂 `Sandbox.Local` (默认) + `Memory.PG` (`/memories/*`) + ETS
(`/conversation_history/*`)
- **3 新内置 Plugin** —— `LargeResultOffload`(200KB+ tool result 写
`/large_tool_results/*` 不污染 message history)、`ContentPolicy`
(LLM-as-Judge 越狱 / 离题拦截)、`EpisodicMemory`(成功 session
自动转 few-shot 复用)
- **`CMDC.Telemetry`** —— 标准 `:telemetry` 事件契约(`[:cmdc, :llm,
:request, :start/:stop]` 等 6 个事件),无侵入接 Langfuse / LangSmith
/ Tempo
- **`CMDC.Skill` v2** —— 加 `license / compatibility / metadata`
Anthropic spec 字段;Skill 文件 10MB 上限防 DoS
### Tests
- `test/cmdc_gateway/webhook_test.exs` —— 12 个新测试:
- 签名稳定性 / 篡改检测 / secret 不匹配 / 长度差异(6)
- payload 5 种 event 字符串映射(2)
- dispatch mock 一次性成功 / 重试 3 次后失败 / 第 2 次重试成功 /
secret 触发签名 header 注入(4)
- 全量 **125 tests / 0 failures**(v0.3.0 113 → v0.4.0 125,+12 webhook)
### 留给 v0.4.1 minor 版本
- webhook 派发失败的持久化 dead-letter queue(v0.4.0 仅写 log)
- `examples/a2a/webhook_receiver.exs` + `webhook_receiver.mjs` 客户端示例
(核心服务已完整,示例补全后即可宣传 12L.3)
- `tasks/get` REST 端点 + ETS 缓存最近 10 分钟 task 状态(webhook 派发
失败时给客户端兜底查询)
## [0.3.0] - 2026-05-16
**Minor release** — A2A Protocol Task endpoints + Gateway-level Guardrails
+ alignment with cmdc 0.3.0 core. No breaking changes against v0.2.x.
### Added — A2A Protocol Task Endpoints (Phase 11C / ADP Ch.15)
Implement A2A (Agent-to-Agent) JSON-RPC 2.0 Task protocol, making CMDC
Gateway a first-class A2A-compliant Agent runtime callable from any
ADK / LangGraph / CrewAI / external Agent that speaks A2A.
- `POST /v1/a2a/tasks/send` — synchronous JSON-RPC Task invocation;
idempotent on `taskId`; returns final Task result map; goes through
Auth → RateLimit → Guardrails Plug chain
- `POST /v1/a2a/tasks/sendSubscribe` — SSE streaming variant emitting
`TaskStatusUpdateEvent` / `TaskArtifactUpdateEvent` with heartbeats
- `CMDCGateway.A2A` — new module: JSON-RPC handler + Task lifecycle
(handle_send / start_subscribe); maps A2A Task semantics onto existing
Session API
- `CMDCGateway.SSEHandler.stream_a2a/3` — A2A-flavored SSE stream that
translates CMDC events to A2A Task lifecycle event schema
### Added — Gateway Guardrails (Phase 11C / ADP Ch.18)
- `CMDCGateway.Plugs.Guardrails` — HTTP-layer input/output filter:
denylist keywords, max prompt size, sensitive content rejection;
mounted in the Router Plug chain after Auth + RateLimit, before
dispatch; pluggable via application config
### Added — A2A Client SDK Examples
Three runnable client samples proving external A2A agents can call this
gateway end-to-end (discover → send → subscribe):
- `examples/a2a/curl_examples.sh` — raw HTTP / SSE walkthrough
- `examples/a2a/elixir_send.exs` — Mix-runnable Elixir Req client
- `examples/a2a/node_send.mjs` — Node.js fetch-based client
- `examples/a2a/README.md` — quickstart with environment setup
### Added — Performance Baselines
- `benchmark/event_translator.exs` — Benchee suite for
`CMDC.Event.t()` → A2A `TaskStatusUpdateEvent` translation throughput
- `:benchee ~> 1.3` added as `:dev / :test` dependency
### Changed — Dependency Upgrade
- `{:cmdc, "~> 0.3"}` — pick up all 13 RFC items from cmdc 0.3.0 core
(most notably: `:tool_execution_metrics` event, `:after_turn` Plugin
hook, batch `attach_tools/2`, error-tuple public API #B21)
### Quality
- `mix credo --strict` — **0 issues** (clean)
- All A2A endpoints covered by `test/cmdc_gateway/a2a_test.exs`
- Existing 107 tests + new A2A coverage all green
## [0.2.0] - 2026-04-24
### Added — A2A Inter-Agent Communication(ADP Ch15)
- `CMDCGateway.AgentCard` — 生成 A2A 兼容的 Agent Card JSON(capabilities + endpoints)
- `GET /.well-known/agent.json` — 免认证暴露 Agent Card,供其他 A2A Agent 发现
### Added — Guardrails & Safety Control Plane(ADP Ch18 + CMDC v0.2 RFC)
- `POST /v1/sessions/:id/switch_model` — 运行时切模型(RFC C8,async 202)
- `POST /v1/sessions/:id/attach_tool` — 运行时加载工具(RFC C9)
- `DELETE /v1/sessions/:id/tools/:name` — 运行时卸载工具(RFC C9)
- `POST /v1/sessions/:id/steer` — 中段注入指令(Phase 10A)
- `POST /v1/sessions/:id/abort` — 结构化 abort,支持 reason/kill_tools(RFC B6,async 202)
- `GET /v1/sessions/:id/status` — 扩展 session status(pending + queue_sizes,RFC C11)
- `POST /v1/sessions` 新增 userData + promptMode 透传(RFC A1 / Phase 10B)
### Fixed — Meter 自动订阅修复(P0)
- `Meter.init/1` 启动时 `EventBus.subscribe_all/0` 全局订阅
- 自动 `record_token_usage/2` on `{:agent_end, _, %CMDC.TokenUsage{}}` 事件
(支持 cached/reasoning/cost_usd 细粒度字段)
- 移除对 `{:prompt_received}` 的订阅,避免与 Router 手动 `record_prompt/1` 重复计数
### Changed — EventTranslator 白名单扩容
- 新增 7 种事件翻译:`model_switched`、`tool_attached`、`tool_detached`、
`tool_call_unknown`、`memory_flushed`、`plugin_event`、`steering`
### Changed — 依赖与质量
- `{:cmdc, "~> 0.2"}` 依赖升级
- 新增 `excoveralls` 开发依赖
- Router 的 `CMDC.steer/2` 返回值修正(移除永不匹配的 `:ok` 子句)
- `switch_model`/`abort` 改为 202 Accepted + `async: true`(cast 语义)
- 代码质量 lift:`mix credo --strict` 零 issue(修 `with/else` 单子句 + `try/rescue` 显式 +
cyclomatic complexity + alias 字母序)
### Stats
- 107 个单元 + 集成测试,全部通过(其中 13 个 v0.2 新端点测试)
- `mix compile --warnings-as-errors` 通过
- `mix credo --strict` 0 issue
## [0.1.0] - 2026-04-08
### Added
- `CMDCGateway.Router` — Plug/Cowboy HTTP 路由,13 个 REST 端点
- `CMDCGateway.SessionStore` — ETS Session 注册表,含 tenant_id/api_key 多租户字段
- `CMDCGateway.RateLimiter` — per-api-key 令牌桶限流
- `CMDCGateway.Plugs.Auth` — API Key 认证中间件(X-API-Key / Bearer Token)
- `CMDCGateway.Plugs.RateLimitPlug` — 限流 Plug,返回标准 429 + Retry-After
- `CMDCGateway.Meter` — per-api-key 用量计量(prompt 次数 + token 统计)
- `CMDCGateway.EventTranslator` — 内部 CMDC 事件 → 对外 JSON schema(15 种事件)
- `CMDCGateway.SSEHandler` — Server-Sent Events 流式推送 + 30 秒心跳
- `CMDCGateway.WSHandler` — Cowboy WebSocket 双向通信(事件推送 + 控制消息)
- `CMDCGateway.CallbackTool` — HTTP 回调工具动态注册与代理执行
- `GET /healthz` 健康检查端点(无需认证)
- 84 个单元测试 + 集成测试,全部通过