API、SDK、JSON-RPC 与 ACP
同一个 Agent runtime 如何被浏览器、自动化和外部客户端驱动
结论:这里不是“一套 API”,而是四种所有权完全不同的协议
DeepSeek Harness 同时存在浏览器 Host API、生成式 Typert Remote、私有 SDK JSON-RPC,以及标准 ACP adapter。它们共享一部分 Agent 与 Session 能力,却不共享稳定性、认证、取消、重连或生命周期承诺。最危险的读法,是看到它们都使用 JSON 就把它们当成可互换的远程 API。
最简洁的判断是:浏览器 API 服务同版本 UI;Remote 把编译期 descriptor 投影到同一浏览器载体;SDK 服务由调用方拥有的本地子进程;ACP 服务懂标准协议的自动化客户端。边界选错后,最先失真的通常不是类型,而是“谁拥有进程、谁可以取消、断线后谁负责恢复”。
1. 四张协议面:先按消费者和生命周期分开
| 协议面 | 典型消费者 | 物理载体 | 会话所有者 | 定位 |
|---|---|---|---|---|
| Browser ApiProxy | 随宿主发布的 Web 客户端 | HTTP POST 上行 + 两条 WebSocket 下行 | 宿主进程 | 完整产品 UI 合约 |
| Typert Remote | 编译时选定 contribution 的客户端插件 | 复用 /api HTTP 与 Host event stream | 宿主 Service / Context | 严格生成的 BFF 调用面 |
| SDK JSON-RPC | TypeScript、Python 自动化调用方 | 子进程 stdio,JSONL | SDK 实例 | 极小的私有 runtime wire |
| ACP | 标准 ACP 客户端或进程外 child backend | stdio 上的标准 JSON-RPC | ACP connection | 最小自动化 adapter |
“都能发送 prompt”不是兼容性。兼容性至少还包括握手顺序、会话恢复、错误分类、取消传播、凭据边界和断线后的状态重建。
2. Browser API 的逻辑语法:四象限消息,而不是一组随意的 fetch
ApiProxy 把消息与载体分离成四种:client-request、server-response、server-request、client-response。发起者生成 rpcId,响应者原样回显;服务端请求既可表示需要回答的 approval/question,也可表示纯 push,是否需要回应由 method 静态决定。客户端回答通过 POST /api/respond 回送,late 或 duplicate response 只得到 carrier receipt。API-RPC-MODEL
解析采用两层 discipline:第一层只判断四种信封、rpcId 与错误联合,payload/value 保持 unknown;第二层才根据 method 解析具体业务 schema。这样 transport 能报告可关联的坏信封,业务 contract 又不会被一个宽泛 JSON schema 稀释。API-RPC-SCHEMAS
业务失败不是异常,而是 RpcResult<T> 的错误分支;transport exception 可以折叠成 internal。因此 HTTP status、RPC envelope 和业务结果是三层不同语义。
3. Browser API 的版本立场:共同发布,不做协议协商
HostApi 明确写着客户端与宿主共同发布,所以现在没有 protocolVersion;host.describe.version 被定义为宿主应用版本,而不是 wire 版本。只有出现独立发布的客户端时,才计划增加协议版本协商。API-HOST-VERSION-CONTRACT
当前实现没有返回真实应用版本:源码 TODO 仍固定返回 0.0.1。因此它既不能用来协商协议,也暂时不能可靠判断宿主构建版本。API-HOST-VERSION-PLACEHOLDER
4. Browser 物理载体:HTTP 上行、WebSocket 下行,错误分层并不对称
通用 Remote/Connection RPC handler 只接受 POST 与 application/json,要求 URL endpoint 与信封 method 一致。非法信封和不匹配 method 返回 typed bad-request;业务 handler 未预期抛错则直接成为 HTTP 500,浏览器 carrier 再把非 2xx 视为 transport failure。这个限定不涵盖同一 /api 面上的 event WebSocket upgrade 与 session export route。API-HTTP-HANDLERAPI-HTTP-CLIENT
所有 /api 请求先经过 Host authority、Fetch Metadata 与 Origin fence。它防 DNS rebinding 和 cross-site browser request,但源码明确说它不是认证层;配置 trusted host 只允许携带该 authority 的请求通过这道 fence,不证明网络可达性或请求者身份。API-TRUST-FENCE
Trusted-host LAN 面也并非让全部方法拥有相同权限:settings、credentials、native dialog、agent-preset management 与 draft-credential model discovery 等 privileged method 另用空 trusted-host 集合锁到 loopback authority,并继续经过共享的 Fetch Metadata/Origin-authority 检查;HTTP 与两条 WebSocket upgrade 都经过基础 trust fence。这里不是完整的 scheme/host/port same-origin 判定,因为 Origin 可缺省,存在时只比较 authority。API-PRIVILEGED-LOOPBACK
HTTP bridge 会把整个请求体缓冲进内存,默认上限 160 MiB;浏览器断开通过 response close 转成 Fetch AbortSignal,响应流按 socket drain 做背压。这个 signal 能通知下游,却不能强制停止忽略取消的进程内业务。API-HTTP-BRIDGE
5. 两条 WebSocket 是只下行流;畸形 frame 与断线是两种不同故障
Host 为 mux 与 host event 各开一条只下行 WebSocket。客户端发送任何业务消息都会以 policy violation 关闭;source 抛错时 Host 尝试先发 stream/error,关闭时 abort source,并等待所有 pump 收敛。API-WS-HOST
浏览器端对 binary、非法 JSON、非法信封或非法业务 frame 的策略是记录并丢弃,不让它们终止 generation。换言之,schema corruption 不自动触发 reconnect;只有 socket 结束才结束流。API-WS-CLIENT
丢弃单个坏 frame 可以避免一次局部扩展不兼容拖垮连接,但也意味着关键增量若被丢弃,客户端只能依靠后续 reconnect/resync 修复,当前 generation 没有逐 frame 重放。
6. Browser 重连单位是完整 generation,不是单 socket 补洞
每一代并发启动 mux、host 和 host.describe。正常 readiness 需要两条流 open 且 describe 成功;若代理从不发 open,3 秒 guard 允许降级为 connected。在 describe/handshake 能收敛的前提下,任一流结束会 abort 整代并同时重建两条流;退避从 500ms 指数增长到 10s,延迟在 cap 的一半到 cap 之间抖动。成功握手把 attempt 清零;业务 sink 抛错只记录,不会杀 pump。API-CONNECTION-LOOP
载体没有 resume token 或逐帧 offset。onConnected 是上层重新读取 list/snapshot 的时机;连接层只恢复可达性,不恢复业务状态。
7. Typert Remote:编译期 descriptor 变成运行期 live Service 调用
Remote request 的载体无关形状只有 namespace、method、精确命名 args,以及只对声明 cancellation 的方法注入的 signal;Gateway 还声明一组稳定的 boundary failure category。REMOTE-GATEWAY-TYPES
Host 每次调用都重新读取当前严格 descriptor、验证字段集合、解析 lookup 或 Context identity、绑定当前 Service,并在返回前再次验证结果。若一个严格 endpoint 曾出现后又撤回,Gateway 会 fail closed,拒绝退回弱 SRC 推导;这避免热卸载后同名方法悄悄换成更宽的 contract。REMOTE-HOST-DISPATCH
取消并非普遍能力:没有 cancellation descriptor 的方法不会收到 signal。声明了取消的方法若在 signal 已 abort 后抛错,wire 映射为 cancelled;lookup policy 的既有 RpcError 保留,其余 Gateway 或业务异常折叠成 internal,所以同进程丰富错误分类并不全部穿过 wire。REMOTE-HOST-ERRORS
8. Remote 的挂载、撤回与事件面都是显式 capability
客户端 contribution 只接受严格 codec,在公开方法前检查重复 descriptor、namespace 和 Service 冲突;部分安装失败逆序回滚。每个 mount token 自带 AbortController,撤回会先标记 inactive、abort 在途调用,再卸载方法。REMOTE-CLIENT-MOUNT
调用端把位置参数转换为 descriptor 命名字段,并把 caller signal 与 mount lifetime 融合;返回值在客户端再次 parse。调用时、进入 carrier try 前若没有 active Connection,仍会直接抛异常;Connection 已建立后,RPC rejection、abort 或坏 response 才折叠进 RemoteResult 错误分支。REMOTE-CLIENT-INVOKE
事件也不是自动透传:当前只有一个 11 项 allowlist,沿 wire 保持原 Cordis event name 与 argument list,不做 projection、rename 或 redaction。REMOTE-EVENT-ALLOWLIST Host 在入队前只证明参数 JSON-safe,再包装成 host/remote-event。REMOTE-EVENT-FORWARD
Agent/Session identity lookup 属于 BFF 生命周期策略:live Agent 直接复用,普通冷 Session 的并发 resume 按 ID 合并;cold resume 前在入口、durable inspection 后和调用 resume 前多点检查 subagent ownership。若 publication race 使 resume 失败,catch 再重查已发布 identity,并把相应冲突映射为 agent-busy,避免通用 API 接管另一个路由所有的 child。REMOTE-AGENT-RESOLVE
9. SDK JSONL:出站写 JSON-RPC 2.0,入站宽松分类
SDK transport 的出站 request/response/notification 都写入 "jsonrpc":"2.0",一行一个紧凑 frame;入站则只按 id/method 组合分类,不校验 jsonrpc 字段或完整 JSON-RPC envelope。StringDecoder 防止多字节 UTF-8 被 chunk 切坏。非法 JSON 行、非对象与未知 response id 被忽略;handler 缺失返回 -32601,handler rejection 返回 -32603。SDK-JSONL-TRANSPORT
应用 vocabulary 只有三个 client request:initialize、session/prompt、shutdown;以及四个 server notification:session.event、session.status、subagent.started、subagent.finished。prompt response 只是 durable queue receipt 的 messageId;完整 SessionEvent 穿 wire,使 session event vocabulary 也成为兼容面。SDK-WIRE-METHODS
initialize ───────────────→ serverInfo
session/prompt ───────────→ messageId (只证明入队)
├─ session.event ──→ durable facts
├─ session.status ─→ whole-agent running / idle
└─ subagent.* ─────→ 进程内 child lineage / completion
shutdown ─────────────────→ {}
10. SDK 兼容与安全立场:无版本协商、无 wire auth、无 prompt cancel
公开限制直接说明:serverInfo.version 固定为 0.0.1,客户端不与期望版本比较;没有 prompt cancel 或 session close;服务端到客户端 request 当前没有生产者,只留下未来 approval flow 的 transport 能力。SDK-WIRE-LIMITS
SDK 的安全边界是调用方启动并拥有的本地子进程与 stdio,不是线上身份协议。wire 没有 authenticate 方法。模型凭据通过子进程环境或 runtime 自身配置进入,因而环境构造方式是 credential policy 的一部分。
在 pre-release 阶段,包版本相同仍不足以证明 wire 兼容;最安全做法是 SDK 与 runtime 同版本发布、同生命周期升级,并把 serverInfo 仅当诊断信息,而不是协商结果。
11. SDK server:全局事件源,客户端才负责 session-tree 过滤
Server 构造时订阅 context 中每一个 Session event 与 Agent status,而不是只订阅 SDK 创建的 Session;child created 同样全局上报。subagent finished 只有 service snapshot 的 local 为真才发送,不能凭相同 ID 或 parent lineage 猜测 locality。SDK-SERVER-EVENTS
initialize 解析 cwd,保存 provider/model/maxTokens,并仅在没有 owner 时自动挂载 DeepSeek fallback。类注释声称重复初始化不支持,但实现没有握手状态字段;handleRequest 也没有阻止 pre-initialize prompt。SDK-SERVER-INIT
Session 在首次 prompt 时惰性创建,同 ID 的并发创建被 promise map 合并;每次 prompt 都确认 retained handle 仍等于 live registry instance。shutdown 封闭新 admission、等待创建收敛、移除订阅,并 allSettled 所有 agent 与 fallback adapter,多个失败聚合报告。SDK-SERVER-SESSIONS
协议 shutdown 的 response 写出后,下一事件循环执行 transport flush、root fiber disposal 与 exit(0)。只卸载插件的成功路径先 shutdown server、再 close transport 且不退出整个进程;但 disposer 没有 finally,若 server teardown reject,transport close 会被跳过。SDK-SERVER-SHUTDOWN
12. TypeScript SDK:run 是 receipt-to-idle 区间,不是一一对应的 turn result
低层 HarnessClient 惰性 spawn runtime。initialize 只要求返回的 name/version 是字符串,不校验预期值;request timeout 只从本地 pending map 放弃等待,服务端工作继续到 runtime 关闭。JSON-RPC error 保留 code/data,transport failure 添加 exit code 和最多 400 行 stderr tail。SDK-TS-CLIENT
高层 run() 在发送 prompt 前订阅 session tree,拿到 messageId 后丢弃匹配 durable inbox receipt 之前的通知,再收集到根 Agent 下一次 idle。finalResponse 是这个区间最后一个已提交 assistant 文本;其他 producer 在 idle 前加入的工作也可能贡献,因此它不是由该 prompt 唯一因果归属的响应。SDK-TS-RUN
关闭是本地权威终止:先 bounded best-effort shutdown,再走 stdin EOF → POSIX SIGTERM → SIGKILL,并只在观察到 process exit 后 resolve;Windows 直接进入强制终止层。SDK-TS-DISPOSE
13. Python SDK 说同一 wire,但并非 TypeScript API 的机械翻译
Python HarnessClient 是同步客户端,发送相同的三种 request,并额外公开 next_request()、respond() 与 respond_error(),可以消费未来的 server request;当前 server 不会发送这种 request。Python 的 env 默认复制父环境后 merge override,与 TypeScript 的显式 env“完整替换”不同。其 initialize response model 也允许 serverInfo、name 与 version 缺省。SDK-PY-CLIENTSDK-PY-MODELS
reader thread 按 id 把 response 送入 waiter queue,把 notification fan-out 到 subscription,并把带 id+method 的 frame 送到 server-request queue。timeout 删除 waiter、附加 exit/stderr 诊断,但同样没有向 runtime 发送 cancellation。filter 抛错只终止自己的 subscription。SDK-PY-READER
高层 Python Session.run() 也采用 receipt-to-idle 区间,但比 TypeScript 多提取最后一个 turn/end.reason.kind 作为 finish_reason;缺少字符串 kind 会抛 SdkProtocolError。这仍是区间摘要,不是 prompt-specific verdict。SDK-PY-RUN
| 差异 | TypeScript | Python | 影响 |
|---|---|---|---|
| 显式 env | 替换完整 child env | 复制 parent 后 merge | 凭据继承策略不同 |
| serverInfo 校验 | 要求 name/version 为 string | 字段都可缺省 | 坏 runtime 的接受范围不同 |
| RunResult | 无 finish reason | 含 finish_reason、session_root | 高层 API 不同构 |
| server request | 没有 consumer API | generic queue/respond API | 当前为 dead capability,未来扩展姿态不同 |
| close/复用 | 默认 6 秒(可配置)EOF grace 后再升级终止;low-level client 永久关闭 | shutdown response 后若仍存活就立即 terminate;同一实例可再次 start,但旧 generation 留在 global notification/server-request queue 的 close sentinel 不会被清空 | Python 可能在 runtime 正做 root/persistence disposal 时发送 TERM;重启后的首次 low-level queue read 也可能先读到旧 sentinel |
TypeScript 的公开类型固定了 env replacement 与四字段 RunResult。SDK-TS-RESULT Python 的配置则额外提供 session root、base URL、API key convenience,并返回额外字段。SDK-PY-RESULT
14. SDK 漂移与 enforcement gap:文档意图不等于 wire 状态机
SdkProtocolError 的 JSDoc 仍以“prompt response 缺少 accepted:true”为例,但当前 wire 和实现已经要求 messageId。这是局部注释遗留,不是双格式兼容。SDK-DOC-STALE-ACCEPTED
重复 initialize “不支持”只写在类注释里;源码没有拒绝第二次 initialize,也没有拒绝 initialize 前的 prompt。官方 SDK wrapper 会按顺序握手,因此正常路径隐藏了这个缺口;任意 raw JSON-RPC client 则能触达它。
第二次 initialize 会改写后续新 Session 的 cwd/provider/model/maxTokens,而既有 Session 保持旧配置,形成同一 connection 内的混合 generation。修复应是显式 connection state machine,而不是让客户端“约定不要这样做”。
15. ACP:标准协议版本,刻意缩小到 automation baseline
Adapter 自身是公开 0.1.0-rc.5 包,并 pin @agentclientprotocol/sdk 0.25.1。ACP-PACKAGE运行时直接从该 SDK 导入 PROTOCOL_VERSION、ndJsonStream 与 AgentSideConnection,再以标准 stream 构造 connection;framing、method machinery 与协议常量因此来自 SDK,而非自创 wire。ACP-SDK-IMPORTSACP-SDK-CONNECTION
initialize 无论客户端请求哪个版本,都返回该 build 支持的 PROTOCOL_VERSION,同时只广告 text/resource-link baseline 所需能力,authMethods 为空,authenticate 为 no-op。session/new 只创建 fresh Session,要求绝对 cwd,并拒绝非空 additional directories 与 MCP servers。ACP-HANDSHAKE
ACP 的“标准兼容”是标准 envelope 与 baseline method compatibility,不代表实现了 ACP 的全部可选能力。客户端必须读取 capability 和返回的协议版本,不能按协议名字推断 resume、filesystem、terminal 或 MCP 已存在。
16. ACP 输出与错误:只发 committed answer,牺牲 token latency 换清洁结果
bridge 不转发 raw delta、reasoning、tool trace、plan 或 title;它只把已提交 assistant/message 的文本发成 agent_message_chunk,image 变成显式 attachment placeholder。sessionUpdate() 的 transport/write rejection 会被捕获并告警;JSON-RPC notification 没有 response,因此客户端 handler failure 不能经 notification 反向反馈。对自有 tool-call approval,bridge 发 one-shot allow/reject permission request,取消或未知选择不授予权限。ACP-OUTPUT-PERMISSION
每个 Session 只允许一个在途 prompt。bridge 先以 user messageId 建 slot,在 agent/inbox/claimed 时捕获 turn;该 captured turn 的 turn/end 会被精确关联,正常 ending 仍等 whole-agent idle 才返回。另一路 agent/error fallback 反而只在 turn 尚未捕获或 turn 不同的时候 reject 当前 slot,没有 message-level causality:先前 autonomous turn,或 prompt turn 后、idle 前的 competing turn failure,都可能被误归给等待中的 ACP prompt。输出同样覆盖该 Session 到 idle 的全部 committed assistant message,而非只限 prompt turn。session/cancel 对未知 ID 是 no-op;已知 ID 只 cancel 对应 Agent 并立即将该 prompt 结算为 cancelled。ACP-PROMPT-CANCEL
内容 codec 原样拼接 text,把 resource link 渲染成带 name/URI 的方括号引用,拒绝 richer block。turn end 到 ACP stop reason 的映射是有损的;codec 本身把 max-token 映射为 max_tokens,但 prompt wrapper 在 whole-agent idle 结算时另将它降为 end_turn,因为它不声称 prompt-specific turn outcome。ACP-CODECACP-PROMPT-CANCEL
17. ACP 生命周期:一个 connection 可有多 Session,但没有单 Session close
connection close 与插件 disposal 共享一次 memoized quiesce:同步封闭 admission、清空 map、cancel Agent 并结算所有 prompt;之后只 drain 这些 top-level Agent 下的 continuable descendants,再并行 dispose handles。Descendant-drain failure 只 warning 并继续;只有 handle-disposal failures 被聚合抛出。其他 frontend 在同一 context 下拥有的森林不会被误清。ACP-QUIESCE
这提供了 connection-owned isolation,以及成功路径上的确定 ownership 与 teardown ordering;即使 descendant drain 失败,top-level handle disposal 仍继续,但不能证明所有 descendant 已释放。它没有 session/load、resume、fork 或 per-session close。长寿命客户端需要把 connection 本身视为资源容器,而不是把 Session 当可独立回收的远程对象。
18. ACP 还是一个 child backend:每次 run 都是 fresh process transaction
进程外 provider 明确广告不支持 output schema、depth limit、tool filter、persona,也不继承 parent conversation。child cwd 必须来自显式配置或 delegating parent Session;缺少 workspace 时 fail loud,绝不退回 server process cwd。ACP-CHILD-CAPABILITIES
每次 run 启动一个 fresh process。parent 生成独立 lifecycle id,避免不同 child 内相同 ACP session id 冲突;只有 spawn、initialize 与 session/new 全部成功才发布 handle,之前任一错误或取消都先回收私有进程。环境经共享 subprocess policy 清除 credential-shaped 与 DSH_* ambient names,再由 local spawn layer merge 显式 child env。ACP-CHILD-STARTACP-CHILD-ENV-POLICYACP-CHILD-ENV-MERGE
caller abort 可以立刻以当前已收集文本返回 aborted,并 best-effort 发送 ACP cancel;这不证明 child 已停止。dispose() 才是权威 quiescence:它确保尚未请求时发出一次 best-effort cancel,再执行 stdin EOF → terminate escalation → whole-tree exit proof。ACP-CHILD-CANCEL
19. 版本、认证、取消、重连与恢复矩阵
| 维度 | Browser API / Remote | SDK JSON-RPC | ACP adapter |
|---|---|---|---|
| 版本 | 共同发布;无 protocolVersion;describe 版本仍是占位 | serverInfo 0.0.1;无协商、无期望值比较 | 标准 SDK PROTOCOL_VERSION;capability 子集 |
| 认证 | Host/Origin trust fence,不是身份认证 | 无 wire auth;依赖本地子进程所有权 | 空 authMethods;可信 stdio |
| 取消 | HTTP disconnect/caller signal;只有声明方法注入;协作式 | timeout 仅本地 abandon;终止要 close runtime | session/cancel 路由到目标 Agent并结算 prompt |
| 重连 | readiness 收敛后,双流整代重建 + 上层 snapshot resync;hung describe 会卡住收敛 | 成功握手后 transport death 为终结;不自动 reconnect | 没有 connection resume;断开释放自有 Session |
| Session 恢复 | BFF 可冷 resume 普通持久 Session | 同 runtime 按 sessionId 复用;wire 无 load/resume | fresh only |
| 进程 teardown | 宿主拥有 | SDK shutdown + EOF/TERM/KILL | server connection quiesce;child backend 另做 whole-tree 回收 |
| 结果语义 | typed RPC result + event streams | messageId receipt;高层 receipt-to-idle 区间 | committed chunks + stopReason,但不声称 prompt-causal turn |
20. 与 Codex App Server 的公开合约比较
| 维度 | DeepSeek Harness(本章源码) | Codex(仅公开文档) |
|---|---|---|
| 目标 | 浏览器面、窄 SDK 与 ACP adapter 分开 | App Server 用于 rich client;SDK 用于自动化/CI |
| JSON-RPC framing | SDK 出站带 2.0 header、入站仅宽松分类;ACP 使用标准 SDK framing;浏览器 API 自有信封 | 双向 JSON-RPC 2.0,但 wire 省略 jsonrpc 字段;stdio 为 JSONL,另有实验 WebSocket 与 Unix socket |
| 握手 | SDK server 缺少强制状态机;ACP 返回标准版本 | 每 connection 只允许一次 initialize,再发 initialized;握手前请求与重复 initialize 均拒绝 |
| 会话 | SDK 隐式 get/create;ACP fresh only;Browser BFF 可 cold resume | 公开 thread start/resume/fork/read/list 与 turn start/steer/interrupt |
| 取消与结果 | SDK 无 wire cancel;ACP 有 session/cancel;Browser signal 按 descriptor 注入 | turn/interrupt 后以 turn/completed 的 final status 收敛 |
| 远程认证 | 本章这些 adapter 没有可公开网络身份认证 | 远程 WebSocket 可配置 capability token 或 signed bearer,且在 initialize 前认证 |
| 兼容策略 | Browser 同步发布;SDK pre-release 无协商;ACP capability 子集 | 可为所运行的精确 Codex 版本生成 TypeScript/JSON Schema;实验 method/field 需 capability opt-in |
| 背压 | Browser HTTP/WS 与 SDK 各自处理;SDK 无 overload code | WebSocket ingress 满时返回 -32001,公开建议 exponential backoff + jitter |
| 服务端请求 | Browser approval 可回应;ACP permission;SDK 当前 dead capability | 公开 approval 与其他 server-initiated request,客户端返回 decision |
以上 Codex 信息来自 Codex App Server 官方文档(检索于 2026-08-13)。这里只比较公开协议能力,不推断其私有实现。
Codex App Server 更像一个有握手状态、版本配套 schema、长寿命 thread/turn 与显式 approval 的 rich-client protocol;DeepSeek 的 SDK 更像最小 subprocess automation pipe,而 ACP adapter 用标准兼容换取更小的产品面。这不是简单的“功能多少”差异,而是面向的客户端寿命与独立发布程度不同。
21. 如何安全选择:按需要的保证,而不是按最熟悉的格式
| 需求 | 优先选择 | 必须自行补齐 |
|---|---|---|
| 随产品发布的完整 Web UI | Browser ApiProxy + Remote | 真实身份认证、跨版本部署纪律、reconnect 后 snapshot resync |
| 本地脚本、CI、批量自动化 | TypeScript/Python SDK | 同版本 runtime pin、进程关闭、超时后是否终止整个 runtime 的策略 |
| 接入通用 agent client | ACP | 只依赖 advertised capability;接受 fresh-only、committed-only 与 connection-owned lifetime |
| 进程外 child Agent | ACP child backend | 明确 cwd、显式凭据、permission policy、始终 dispose |
| 独立发布的 rich client | 当前没有等价的统一 DeepSeek protocol | 版本协商、认证、resume、schema generation、overload/replay contract |
最小协议降低实现与 token 表面成本,却把状态归属、升级和恢复责任推给客户端;丰富协议提供更强的独立客户端生命周期,但必须承担 capability negotiation、鉴权、backpressure、replay 与更大的兼容测试矩阵。
22. 验证范围与可复现结果
本章以提交 47f943859bef60e4160492346772ded9b24f765a 为固定基线。focused Vitest 覆盖 SDK transport/server/client、ACP bridge/codec/permission/multi-session/disposal、ACP child backend、Typert Gateway、Remote identity resolver,以及 browser trust/HTTP bridge/WebSocket/reconnect。SDK transport suite 固定 framing 与错误语义。TEST-SDK-PROTOCOL ACP bridge suite 固定版本、auth、fresh session 与输入边界。TEST-ACP
Connection suite 固定双流 generation 与 reconnect。TEST-CONNECTION Remote client suite 固定 strict mount、withdraw cancellation、scope 与错误折叠。TEST-REMOTE
pnpm exec vitest run \
packages/sdk/protocol/tests/transport.spec.ts \
packages/sdk/server/tests/{server,plugin-apply,plugin-shape}.spec.ts \
packages/sdk/client/tests/{sdk-client,dispose}.spec.ts \
packages/acp/acp/tests/{approval,bridge,codec,dispose,edges,multi-session,turns}.spec.ts \
packages/subagent/subagent-acp/tests/subagent-acp.spec.ts \
packages/api/gateway/tests/{gateway.client,gateway.host}.spec.ts \
packages/api/remotes/tests/agent-lookup.spec.ts \
packages/client/connection/tests/{api-request-trust.host,http-bridge.host,websocket-downlink.host,connection.client}.spec.ts \
--reporter=verbose
结果:21 个 test files 全部通过,313/313 tests 通过。
耗时:21.01s(Vitest 报告)。
我的学习体会
内容仅自动保存到当前浏览器,不上传、不进入仓库。你可以导出 Markdown 自行归档。