跳转到主要内容
BeeOS 公开 OpenAPI 契约backend/openapi/beeos-platform-v1.yaml)以及从中生成的 SDK (TypeScript 的 @beeos-ai/sdk、Go 的 github.com/beeos-ai/sdk-go)所有值得注意的变更都记录在本文件。 本文件是规范变更日志
  • 终端用户升级 SDK 版本时看的是它,
  • 发布工作流打 tag 时查的也是它(SDK 生成器在 sdks/openapi-sdk/generate.sh 里盖版本戳)。
每服务的 backend 变更(cluster、runtime、a2a 等)见各服务自己的 README 与 backend/docs/adr/ 下的 ADR。 格式基于 Keep a Changelog, 契约版本号遵循 Semantic Versioning

版本号策略

Bump触发条件
MAJOR移除路由、改必填字段名、改已有响应形状、改鉴权语义(例如收紧所有权模型)、改 error code 语义。
MINOR加新路由、加可选请求字段、加可选响应字段、加新 code 值。
PATCH文档、示例、OpenAPI description 打磨、不影响生成代码的 generator 配置调整。
TypeScript 和 Go SDK package 共享同一条版本线。它们总是从同一次 generator run 一起切,不能放出版本不一致的对子。

发布流程 —— 一个版本怎么真正发出去

  1. 改 spec。 所有 wire 变更先落在 backend/openapi/beeos-platform-v1.yaml。 openapi-gateway 服务通过 go:embed 消费此文件,所以 backend/services/openapi-gatewaygo build 就是第一道 lint。
  2. 同步 vendor 副本。sdks/openapi-sdk/ 下:
    make sync-spec     # copy → spec/beeos-platform-v1.yaml
    make validate      # openapi-generator-cli validate
    make gen           # regenerate both SDKs
    
  3. 更新 generator 版本戳。 同一个 commit 里把 sdks/openapi-sdk/generate.sh 88-95 行的 npmVersion=packageVersion= 一起升上去。两个 SDK 共享版本号。
  4. 写下面的条目。[Unreleased] 块顶部加新段;只描述 wire 变更。不影响任何消费方字节的实现侧重构,放对应服务的 README,不放这里。
  5. 开 PR。 CI 跑 redocly lint + make gen + backend/services/openapi-gateway/internal/dto/contract_test.go 里的契约漂移测试,并对生成的 Go SDK 跑 go build ./...
  6. 合并后,发布工作流: 用今天的日期戳变更日志头、给 meta repo 打 sdk-v<version> tag、把 @beeos-ai/sdk 发布到 npm、把生成的 Go SDK 推到 github.com/beeos-ai/sdk-go(那是个 release-tagged repo,所以 go get 能解析到新 tag)。
不要手动改 sdks/beeos-ai-sdk/sdks/beeos-ai-sdk-go/ —— 它们每次发布都被重新生成。仅有的手工资产在 sdks/openapi-sdk/assets/{beeos-ai-sdk,beeos-ai-sdk-go}/ 下,每次盖在 generator 输出之上(README、LICENSE)。

[Unreleased]

新增(Message Envelope v3 — MessageDTO 对齐)

  • MessageDTO 现在引用平台级 v3 union schema。 以前 state / stop_reason / partsMessageDTO 内部 各自 inline 一份 enum / array-of-object;现在统一 $refMessageState / StopReason / Part —— 跟 TaskSSEMessage 和流式 envelope 已经在用的 union 完全一致。生成的 SDK 现在 自动带类型化的 MessageState / StopReason / Part 导出; 裸 JSON 调用方 wire 上看不到差异(union 字符串值集合本就一样, 这次只是 contract 层去重)。
  • MessageDTO.updated_at(RFC3339Nano,UTC)。 v3 envelope 的 服务端最后一次 PATCH 时间戳。仅当与 created_at 不同才下发 —— 新 POST 的 reply 没有;streaming envelope 每次 PATCH 会改写; 终态行在状态转换那一刻冻结。SSE 重放客户端用它来 ACK 进度 checkpoint;聊天记录 UI 忽略。Legacy v1 行上 字段缺失。

修复(SDK 指南 typo)

  • TypeScript 指南:reply.data?.reply 改为 reply.data?.texttask.data?.status === "completed""succeeded"TaskStatus 真实的终态值);conv.data!.idconv.data!.conversationId(匹配生成的 ConversationResponseAllOfData 字段);删除 PresignUploadRequest 上并不存在的 size_bytes 字段。
  • Go 指南:reply.GetData().GetReply()GetText()status == "completed""succeeded"; 从 PresignUploadRequest 去掉 SizeBytes
  • Quickstart:TypeScript + Go 示例同步把 replytext

Breaking(scope 词汇下线 — v1.1.0)

  • oag_ User API Key 的 per-route scope 已删除。 集合 agents:readagents:writetasks:readtasks:writefiles:readfiles:writeinstances:readinstances:write 以及通配符 admin:* 不再被识别。原先需要 scope 的 37 条 路由现在完全由 owner-ACL 守门 —— 每个 oag_ key 自动获得其 owner 名下全部资源的访问权,与 JWT 等价。
  • 不再下发 403 insufficient_scope 删掉所有按 error.code === "insufficient_scope" 分支的代码;合并到通用 forbidden(403)处理。
  • POST /api/v1/api-keys 忽略 body 里的 scopes,响应也 不再带 scopes。SDK 调用方应从 createAPIKey(...) 删该参数 —— 滚动发布窗口期 wire 上该字段会被静默丢弃,下一个 contract 版本会从请求 schema 整体删除。
  • 不需要数据迁移。 已签发的 oag_ key 自动获得 owner 级 全部权限继续工作;服务端 api_keys.scopes 列已 DROP。

Breaking(ADR-0022 — 双层消息存储)

  • GET /api/v1/agents/{agentId}/conversations/{convId}/messagesGET /api/v1/agents/{agentId}/tasks/{taskId}/messages 现在默认过滤掉临时流式 chunk(agent_reply_deltaagent_thought_chunkagent_message_chunk)。加 ?include_deltas=true 可恢复旧行为。latest_offset 仍然 反映真实服务端最大 offset(同时覆盖被过滤行和未被过滤行), 所以 since= / cursor 分页不受影响。SSE /events 不受影响 —— 实时消费者照常拿到每条信封。迁移建议:原来用轮询 /messages 重建流式记录的客户端代码,要么 (a) 传 include_deltas=true 保留旧行为,要么 (b) 切到 SSE /events 流,那才是消费实时 token chunk 的官方姿势。
  • SSE /events 现在可能发新的 backfill_truncated 事件帧 —— 当客户端用 Last-Event-ID 指向已老化失踪的临时 chunk 时触发。 老 SDK 不识别此帧应视为提示,跳过中间临时 chunk 改用 latest_offset(在 replay_complete 中返回)续传。按 ADR-0022 §6 默认保留窗口是 24h / 10k 条。
  • SSE 事件 offset 契约澄清:单调但不保证连续。 Producer 端写 存储失败可能在 offset 序列里留小空洞(例如观察到 … 40, 42, …41 缺失)。客户端必须offset > since 作为续传不变量 —— 断言 offset == since + 1 的代码本来就是未定义行为,现在明确 记作 bug。续传配方见 guides/streaming.md

新增

  • POST /api/v1/agents/{agentId}/tasks/{taskId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver —— 手动重新入队单次 webhook 投递。返回新的 pending WebhookDeliveryResponse。当接收方挂了你想立刻重放、不愿等下一个指数 退避窗口时有用。
  • GET /api/v1/agents/{agentId}/tasks/{taskId}/webhooks/{webhookId}/deliveries —— webhook 绑定的逐次投递审计日志。返回最近 N 次(默认 50、最大 100) 尝试,含 statusattempt_numlast_response_statuslast_errornext_attempt_at
  • secret(可选、只写)字段 —— 加在 RegisterTaskWebhookRequest 和底层 A2A pushNotificationConfig/set 上。提供后每次投递携带 X-BeeOS-Signature: sha256=<hex>,按 secret HMAC-SHA256 计算自 JSON body。wire 响应只回显 has_secret: true;原值从不返回。 Python 和 Node.js 验证示例见 docs/guides/webhooks.md
  • Webhook 重试与死信。 失败投递(网络错、超时、5xx 响应)按指数退避 重试:1m / 5m / 30m / 2h / 12h。第 6 次失败后行变 dead_letter。 4xx 响应(除 408 / 429 外)重试 —— 视为永久客户端配置错。 由 backend/services/a2a 里的后台 worker 驱动,轮询持久化的 webhook_deliveries 表。
  • GET /api/v1/tasks —— 跨智能体任务列表。返回认证调用方通过 OpenAPI 面创建的所有任务,可按 agent_id 过滤。limit + since 分页。多智能体 UI 有用(一个组织所有智能体的统一收件箱)。
  • POST /api/v1/files/presign-upload —— 申请短时效 S3 风格 PUT URL 上传文件。返回 file_id,可嵌入后续 invoke / tasks.create / conversations.send body 的 attachments[].file_id
  • GET /api/v1/files/{fileId} —— 回读已上传文件的元数据 + 签名 GET URL。
  • attachments[].file_id —— 加在 InvokeAgentRequestCreateTaskRequest 和会话发送 body 上。智能体看到的是普通 multi-part chat_messagefile part。
  • PATCH /api/v1/agents/{agentId} —— 仅 owner 更新智能体的 visibilitymcp_enabled 标志。其他智能体字段(namedescription 等)仍由智能体进程的 POST /api/v1/agents/sync 同步、 不能在这里 patch(改了下次 sync 会被静默覆盖)。
  • metrics 端点GET /metrics)—— Prometheus pull 端点, 暴露标准 process 指标加 beeos.openapi.* 命名空间。OTLP push 仍可用 通过 OTEL_EXPORTER_OTLP_* env 变量。
  • 探针路由的 OpenAPI 级限流。 /healthz/version/spec/*/metrics 现在按 IP 限流,防未鉴权抓取 DoS。
  • MaxBytesReader body 大小强制。 每请求默认 1 MiB,catalog / list 端点 64 KiB。超限返回 413 payload_too_large
  • POST /agents/{id}/invoketimeout_ms 钳制。 调用方传入值钳 在 [100, 115_000](上游端到端预算 120s)。超过钳值会暴露 service_timeout,不会无声挂住连接。
  • OpenAPI 示例 —— 给 top 5 operations (listInstanceslistAgentsinvokeAgentcreateTaskgetTask)加示例 + 全 4xx/5xx 响应形状。

变更

  • 所有错误响应 现在遵循规范信封:
    { "error": { "type": "…", "code": "…", "message": "…",
                "param": "…", "request_id": "…" } }
    
    code 取自 docs/reference/errors.md 的表。 4xx 和 5xx 共享同一形状;只检查 HTTP 状态的老客户端仍然有效。
  • Webhook 当前限制 段在 docs/guides/webhooks.md 已重写 —— 原先的 “限制”(无签名、无重试、无审计日志)现在都是一等公民特性。 唯一遗留限制是死信前 6 次尝试上限。

文档(无 wire 变更)


[0.4.0] — 2026-04-30

Breaking

  • SendMessageResponse.offset 移除。 该字段是误导性占位;真正的 offset 不透明地在 cursor 里。需要 offset 做 SSE resume 的调用方必须 走 GET /messages?since=<cursor>(SSE 消费方不受影响)。

新增

  • InvokeAgentRequest.idempotency_keyInvokeAgentRequest.metadata(都可选;向后兼容)。同 key 端到端 贯穿 L0 → MS。
  • TaskResponse.truncated(可选布尔)—— 表示任务事件日志已扫到 1000 条上限、返回事件可能不全。老客户端可忽略此字段。
  • PushNotificationConfig.protocol_filter —— gateway 在注册时盖 起源 renderer key(openapi / a2a / mcp)。让同一个底层 webhook 能按触发面过滤。

[0.3.0] — 2026-03-22

新增

  • 首个公开级契约:
    • POST /api/v1/agents/{id}/invoke(sync + SSE)。
    • POST /api/v1/agents/{id}/tasks + GET .../tasks/{id} + GET .../events(SSE)+ POST .../cancel
    • POST /api/v1/agents/{id}/conversations + send + delete + 带 since cursor 的 list。
    • 任务 webhooks:POST .../webhooks + GET + DELETE
    • 文件 presign(上传)。
    • invoke body 上的 idempotency_key
这是统一路线图之前大多数第三方文档引用的版本。SDK @beeos-ai/sdk@0.3.0github.com/beeos-ai/sdk-go@v0.3.0 仍可从 npm / proxy.golang.org 解析,并在重叠路由集上与当前 openapi-gateway wire 兼容。

更早的版本

0.2.x 及更早预日期 OpenAPI Gateway BFF 解耦(ADR-001)—— 它们直接 对接 main Gateway,不再是受支持的 SDK 契约的一部分。卡在那些版本 的消费方应直接升到 0.4.x 并照 docs/guides/sdk-migration.md 操作。