對外協議是投影
IDE 看見的是 Thread / Turn / Item,不是內核 EventMsg。一次 turn/start 先回響應,再推事件流;審批是反向請求,不回包這一輪就停住。
turn/start 的回包只表示請求被接受,真正開跑看 turn/started;Python SDK 和 TypeScript SDK 走的不是同一條協議面。
turn/start 的 params。回車即播放。
turn/start 的響應立刻回來,只表示請求被接受。真正開始轉圈,要等後面那條 turn/started 通知。你在給編輯器寫插件。調試器裏已經能看到內核往外拋事件:turn_started、exec_command_begin,字段是 snake_case。第一包數據過來,對不上。方法名是 turn/started,中間是斜槓。字段是 threadId、startedAt。
命令開始時你等的 exec_command_begin 沒出現,來的是 item/started,裏面塞着一個 type: "commandExecution" 的 item。審批更怪:服務端反向發來一條 request,你得回 response,否則這一輪卡在那兒。
如果編輯器按 81 種 EventMsg 寫 switch,每加一種內部事件都是一次客戶端升級。deprecated 別名也會從倉內相容問題變成對外合同。
調度函式 apply_bespoke_event_handling 吃一條內核 Event,按四條規則收成對外消息。
EventMsg 的 snake_case type 變成 turn/started、item/agentMessage/delta 這種資源路徑,字段改成 camelCase。
delta 和工具生命週期被收進 ThreadItem,再塞進 item/started 或 item/completed。IDE 按 item 的 type 畫卡片。
ExecCommandBegin、ViewImageToolCall、以及 match 末尾的通配臂,綫上沒有對應通知。舊事件還在給 rollout 扇出。
一條 ItemStarted(DynamicToolCall) 既發通知,又發 item/tool/call 這條 ServerRequest,等客戶端執行。
出處:codex-rs/app-server/src/bespoke_event_handling.rs 第 159 至 188 行;codex-rs/app-server/src/bespoke_event_handling.rs 第 880 至 918 行;codex-rs/app-server/src/bespoke_event_handling.rs 第 996 至 1036 行
item_event_to_server_notification 只覆蓋一對一、無狀態的投影。函式名像總入口,調用點才知道它是助手。ExecCommandBegin 在助手裏還能變成 item/started,在調度裏卻走進 deprecated 空分支。現場命令卡片來自後面的 ItemStarted。以調度為準。
出處:codex-rs/app-server-protocol/src/protocol/event_mapping.rs 第 25 至 37 行;codex-rs/app-server-protocol/src/protocol/item_builders.rs 第 1 至 11 行
內核按發生了什麼命名,對外按用戶看見什麼命名。內部還可以繼續發 deprecated 事件給 rollout,調度寫一句註釋丟掉即可。換語言重寫,這張表還在:左邊內部 type,右邊寫清留下、改名、丟掉還是拆開。
未知行必須失敗。空預設等於通配臂,新事件能通過編譯,IDE 的 stdout 上什麼都沒有。
出處:codex-rs/app-server/src/bespoke_event_handling.rs 第 1238 至 1245 行
同事把 turn/start 的響應當成一輪已經開始。響應立刻回來,裏面是一份空 items 的 turn。模型還沒開口。真正開跑是後面那條 turn/started 通知。
出處:codex-rs/app-server/README.md 第 81 至 81 行
審批做成普通 notification,客戶端可以不理。turn 會停在等待上,直到超時或中斷。
綫上能解出來的對象只有四種:帶 id 的 request、不帶 id 的 notification、成功 response、錯誤 response。看起來像 JSON-RPC,結構體裏沒有 jsonrpc 字段。常量 JSONRPC_VERSION 還在,綫上不帶這個鍵。
出處:codex-rs/app-server-protocol/src/rpc.rs 第 1 至 11 行;codex-rs/app-server-protocol/src/rpc.rs 第 34 至 72 行
對外消息是四套,而且不對稱。
1. ClientRequest:客戶端問,等人回包。initialize、turn/start 是穩定面主幹。
2. ServerNotification:服務端推,不等回包。turn/started、item/started 在這裏。
3. ServerRequest:服務端問人。第一條穩定方法是 item/commandExecution/requestApproval。
4. ClientNotification:展開之後只有 Initialized。
出處:codex-rs/app-server-protocol/src/protocol/common.rs 第 1663 至 1670 行;codex-rs/app-server-protocol/src/protocol/common.rs 第 1954 至 1956 行
請求要回執,通知是廣播,反向請求把人拉進環。這三件事混成一種,編輯器要麼空轉等開跑,要麼漏畫審批按鈕。id 對得上,過載時還能把 request 失敗回給調用方,避免審批懸掛。
實驗方法有 57 個方法級標記。如果靠第二端口,穩定客戶和冒險客戶要連兩個地方。TUI 如果因為同進程就改收 EventMsg,現場通知和遠端 IDE 會各寫一份 item。
實驗面靠 initialize 時一個布爾 experimentalApi,缺省 false。再 initialize 會收到 Already initialized。沒開開關就打 server/diagnostics,錯誤碼 -32600,句子是固定的 server/diagnostics requires experimentalApi capability。Python SDK 把這個預設改成 True,官方腳本已經站在實驗合同上。
出處:codex-rs/app-server/src/message_processor.rs 第 891 至 895 行;sdk/python/src/openai_codex/client.py 第 209 至 209 行
TUI 不直連 core。內嵌只換載體:socket 和 stdio 換成記憶體通道,MessageProcessor 還在。請求仍是 ClientRequest,響應仍走同一套 envelope。進程內是 transport-local,不是 protocol-free。
出處:codex-rs/app-server/src/in_process.rs 第 1 至 24 行
TypeScript SDK 不走這條路。它拼的是 exec --experimental-json,事件 type 是點號,字段是 snake_case,完整枚舉只有 8 個變體。沒有 initialize,沒有審批 request。能力差在協議面,不差在語言。
出處:sdk/typescript/src/exec.ts 第 89 至 90 行;codex-rs/exec/src/exec_events.rs 第 8 至 37 行
遠程和本機的差別應落在網絡,不落在語義。實驗面用 capability,比文檔裏寫一句實驗更硬。一個布爾把穩定面和實驗面切開,schema 生成出兩份,預設那份不含實驗字段。
DeepSeek Harness:內核類型就是協議類型
DSH 五個入口共用同一棵插件樹。headless 的入口配置把自己寫成 composition base:負責拼插件,不另寫一套事件類型。跨進程時 Typert 從 TypeScript 類型圖生成 stub,@Remote('create') 返回的是 identity,不是另一套展示模型。
改一個事件字段,五張臉一起變。收益是不會出現 Python 看見 thread/started、TypeScript 看見 thread.started 這種分裂。Codex 反過來,內部可以標 deprecated 繼續扇給 rollout,對外合同按投影層凍結。漏改投影,客戶也看不見,只是功能丟了。
Claude Code:入口標記,沒有第二協議面
還原源碼裏能找到的是入口判斷:CLAUDE_CODE_ENTRYPOINT === 'claude-vscode' 時返回 claude-vscode。沒有對位的對外 IDE 協議 crate。擴展靠 MCP 和進程入口嵌進來,第三方 IDE 沒有一份帶 schema 的雙向 RPC 可以對。
Codex 付了投影層的維護成本,換來 VS Code 擴展、Python SDK 和本機 TUI 共用同一份 v2。
已核對源碼 · 2026-08-22 · restored-src/src/main.tsx 第 823 至 823 行回包到了,該不該轉圈
turn/start 的響應已經回來,items 是空的。編輯器現在該轉圈,還是該等 turn/started?如果內核新加一個 EventMsg 變體,投影沒跟上,stdout 上會出現什麼?
進階一問:同一輪對話裏模型要跑一條需要提問的命令。Python 客戶端可以彈窗並回包,TypeScript 的 Thread.run() 為什麼做不到?