對外協議是投影
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() 為什麼做不到?