All notable changes to cmdc_gateway are documented in this file.

[0.4.2] - 2026-05-18

Patch release — repository URL normalization + documentation cleanup. No code changes, no behavior changes.

  • Repository URL → https://github.com/tupleyun/cmdc_gateway
  • Module moduledoc + inline comments rewritten to neutral technical descriptions (removed internal task numbering)

[0.4.1] - 2026-05-16

Patch release — 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 兜底查询

补全 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/4get/1delete/1list_recent/1gc_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 持久化

把 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/4tick_now/0stats/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 客户端示例

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/send11CJSON-RPC 同步
POST /v1/a2a/tasks/sendSubscribe11CJSON-RPC + SSE 流式
POST /v1/a2a/tasks/sendWithWebhookv0.4.0JSON-RPC + webhook 异步
GET /v1/a2a/tasks/:task_id12M v0.4.1兜底查询(ETS 10min)

配置参考

# 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.exs14 测试(put/get/TTL/GC/ delete/list_recent + event → status 推导)
  • test/cmdc_gateway/webhook/dead_letter_test.exs5 测试 (build_entry + next_retry 指数退避)
  • test/cmdc_gateway/webhook/dead_letter_dets_test.exs8 测试 (DETS round-trip + 持久化 reload + 幂等删除)
  • test/cmdc_gateway/webhook/dispatcher_test.exs4 测试(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 模式落地, 补齐 sync / SSE / webhook 三大长任务交互入口; 依赖升级到 cmdc ~> 0.4,自动获得核心库 v0.4 全部能力(Checkpoint / Backend / Skill v2 / 3 新内置 Plugin / Telemetry)。No breaking changes against v0.3.x。

Added — A2A Webhook 异步交互模式

A2A 协议 4 种交互机制中,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 验签示例

# 客户端发起
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

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

  • 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 --strict0 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

  • CMDCGateway.AgentCard — 生成 A2A 兼容的 Agent Card JSON(capabilities + endpoints)
  • GET /.well-known/agent.json — 免认证暴露 Agent Card,供其他 A2A Agent 发现

Added — Guardrails & Safety Control Plane

  • 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 — 中段注入指令
  • 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 透传

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_switchedtool_attachedtool_detachedtool_call_unknownmemory_flushedplugin_eventsteering

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