SSE 背压与慢客户端
区分帧数、字节、重放预算和 MCP App 文本降级。
EventBus 为每个 session 保留 ring,为每个 subscriber 维护独立 live queue。一个客户端消费慢,不应使其他客户端或 agent 执行无限等待。
默认限制
| 项目 | 默认或范围 |
|---|---|
| 每 session subscriber | 64 |
| 每 subscriber live backlog | 256 帧 |
| 每 subscriber live bytes | 2 MiB 序列化字节 |
| HTTP maxQueued | 16–2048,可在支持 slow_client_warning 时使用 |
| warning threshold | 帧数或字节达到 75% |
| warning 重新准备 | 两个维度都降到严格低于 37.5% |
maxQueuedBytes 是内部 constructor 配置,不提供 HTTP query、SDK 参数或 CLI 开关让远端调用者提高内存预算。
直接交付与缓冲
已有消费者等待 next() 时直接交付,不形成 live backlog。需要入队时检查帧数和字节;空 live queue 允许第一条超大帧原样进入,所以 2 MiB 不是单事件硬上限。
replay 和 synthetic 警告用 forced 标记,不计入 live 队列上限。但 replay 有独立字节预算,不能由“forcePush 绕过 live 限制”推断无限回放内存。
MCP App 的文本回退
非空队列将超过字节限制时,符合条件的 MCP App tool_call/tool_call_update 可把完整 rawOutput.html 清空为字符串,并保留非空 fallbackText。只有缩小后能放入才继续连接;无有效文本回退或仍超限会驱逐 subscriber。
直接交付和 ring 保持原 frame,排队客户端可能收到同 id 的降级 payload。之后从该 id 恢复不会重发它;需要完整文档时应显式查询持久 transcript,并仍遵守分页预算。
Warning 与 eviction
slow_client_warning 是 subscriber 私有、无 id 的提示。live 帧数或字节再溢出时发送 client_evicted,reason 分别为 queue_overflow 或 queue_bytes_overflow,再排空至该终止帧并结束订阅。
响应中 queueSize、maxQueued、queuedBytes、maxQueuedBytes 便于定位哪个维度超限;字节溢出还可带 eventBytes。
client_evicted 不是 session_closed。应用可以重连并按replay 契约恢复,但应该先减少同步渲染、阻塞处理或过重的订阅工作。
订阅与取消
内部 EventBus.subscribe 返回时同步登记 subscriber,避免首次 next() 之前的 publish 丢失。这个保证属于内部 bus,不表示客户端刚取得 AsyncIterable 就已完成远端 HTTP/SSE 握手。
AbortSignal 取消会立即丢弃该订阅的缓冲、移除 listener 和登记;bus close 后 publish 返回 undefined。不要用并发 next() 驱动同一个 queue iterator,应顺序消费。
超过 subscriber 上限时,route 发 stream_error,而不是成功返回永远空的流。客户端应把该错误与会话真正死亡区分。