Skip to content
FunCoding

Search

Search docs, Skills and MCP

Dual Output 事件与控制协议

处理握手、增量消息、审批竞争、输入排队和异常关闭。

This page has not been translated into English yet. The original Chinese version is shown below.

Dual Output 一行一个 JSON 对象,事件格式沿用无头 stream-json,且始终开启部分消息。消费者需同时理解流式增量和完成消息,避免把每种事件都当作新的独立回复。

先读会话握手

通道第一条是 system / session_start。用 session_id 关联会话,并读取 data.protocol_version、data.version、data.supported_events 做能力检测。旧版本可能没有 protocol_version,可把缺失视为 0,再决定是否接受较弱兼容。

协议版本 2 将文本 tool_result.content 限制为 JSON 字符串序列化后的 65,536 UTF-8 字节。超出时变成确定性的头尾预览。这是字段限制,不是每行 JSONL 的通用大小上限,也不代表输出中保留了完整原始工具正文。

事件分类

事件消费方式
stream_event处理 message_start、content_block_start/delta/stop、message_stop,更新正在输出的消息
user / assistant完整消息;assistant 可能带 usage,user 也用于承载 tool_result
control_request需要工具审批,保留 request_id 与工具参数
control_responseTUI 或外部通道做出的最终控制结果
result轮次结果、错误和时长等汇总
system / session_end正常会话关闭信号

不要只按 type:user 就渲染为用户刚输入的文字;其中也可能是工具结果内容块。控制请求的 request.subtype 为 can_use_tool,提供 tool_name、tool_use_id、input 等信息,供外部界面展示待批准操作。

提交与审批

输入通道接受两种记录:

{"type":"submit","text":"Explain the latest change"}
{"type":"confirmation_response","request_id":"REQUEST_ID_FROM_EVENT","allowed":false}

先用真实控制事件里的 request_id 替换占位值,再根据用户决定填写 allowed。不要根据模型文本或猜测出的 ID 自动批准。

submit 在 TUI 忙时进入队列,回到 idle 后重试提交。confirmation_response 则立即派发,不能被前面的普通提示挡住,否则正在等待批准的工具无法继续。

本地界面与远端界面竞争同一审批,先完成的决定生效。迟到响应不会再次执行工具;未知、已取消或已处理的 request_id 可收到 subtype:error 的 control_response。接入方应关闭过期审批,不要无限重发。

延迟与解析错误

输入使用 fs.watchFile,轮询间隔 500ms,因此从追加记录到被发现可能约半秒;模型响应和工具等待时间另计。输出随事件产生写出,没有同样的输入轮询间隔。

JSON 解析失败的行记录错误并跳过,不会停止整个 watcher。写入方应一次追加完整一行并以换行结束,读取方保留未完成行,避免把任意文件读取块当作完整 JSON。

正常结束与失联

只有看到 session_end 才把该流标记为正常关闭。进程退出、旁路停用或流断开时可能没有此事件;旁路异常并不意味着 TUI 已经停止,更不能据此重新提交上一次可能已执行的操作。

会话记录可用于观察和回放,但文本工具结果可能已截断。审计完整原始输出时还需原始工具记录;不要把 JSONL 旁路称为无损的全部执行证据。