你的 Company 已經照 Poppy 0.3 把 poppy.json 放上線。0.3 是 11 月 4 日發佈、之後不會再改的版本,可是 4 天後的今天,llms.txt 開頭寫的是「The docs are draft 0.4, which is in progress.」。這時來敲門的 agent,可能停在 0.3,也可能已經在實作 0.4。同一個網域前面,兩種版本,poppy.json 該怎麼擺?擺出來之後,選錯版本的 agent 又是怎麼被擋回去的?

先交代我手上有什麼。Poppy 是讓 personal agent 與公司合作的開放協定,它要解的處境,introduction 說得很直接:現在要嘛 agent 冒充使用者,用密碼登入、在給人看的頁面上點來點去,要嘛公司整個擋掉,什麼都不能做。專案 repo 的 README 寫明那是網站原始碼,「It documents the protocol but does not implement the APIs the specification describes.」我讀了 introduction、0.4 draft 規格、changelog、path-to-1 和幾張 GitHub issue,沒有裝過任何實作,也沒有跑過它的驗證工具。下面每一步都是照規格推演出來的,不是跑出來的。

先看規格自己走多快

版本 發佈日 狀態
0.1 2026-10-09 已發佈,凍結
0.2 2026-10-24 已發佈
0.3 2026-11-04 已發佈,凍結,被 draft 0.4 取代
0.4 尚未發佈 draft,進行中

0.1 到 0.2 隔 15 天,0.2 到 0.3 隔 11 天,從 0.1 到 0.3 一共 26 天(間隔是我用日期相減算的)。repo 沒發過任何 GitHub Release,版本以網站上的文件為準。

這個節奏再加上規格自己的聲明,事情就麻煩了。path-to-1 寫「Until 1.0, a revision can break compatibility with earlier drafts.」,0.4 規格 §1 也寫任何一部分都可能在穩定版之前改變,包括不向後相容的改法。1.0 之前每一版都可能跟上一版不相容,而它隔十一到十五天就出一版。要同時服務兩個版本的 Company,就活在這個處境裡。

poppy.json 怎麼擺

0.4 draft 的做法是這樣:poppy.json 放你偏好的那一版,其他版本寫進 supported_versions,每一版對到各自文件的 URL。規格 §3.4 給的範例長這樣:

1
2
"protocol_version": "0.4",
"supported_versions": { "0.3": "https://example.com/poppy/0.3.json" }

幾條硬限制都在 §3.4。supported_versions 裡每個 URL 必須是 HTTPS,而且在 poppy.json 同一個 host 上(重導向之後也算);agent 不准使用其他 host 的文件,也不准跟著那種文件重導向。

我覺得最值得停下來看的是下一條。同時提供的每一份文件,organization.domain 必須相同,也必須選同一個 issuer,理由規格自己講了:新版本不能改變 Company 的識別,否則 User ID 與 refresh token 會跟著斷。版本可以換,domain 與 issuer 不能動。這個限制把「升版」和「搬家」切開了。

0.3 的 agent 走進來

選版本的規則寫在同一節。兩個版本相容的條件是 major 相同,1.0 之前連 minor 也要相同。agent 從 protocol_version 與 supported_versions 的所有版本裡,挑自己能實作、而且相容的最新一版,用那一版的文件;一個都不相容,就一個都不准用。

套進開頭的情境,推論是這樣(規格沒有寫這個例子,這是我的推演):1.0 之前 0.3 與 0.4 互不相容,所以 0.3 的 agent 會略過 protocol_version 的 0.4,改用 supported_versions 裡的 0.3 文件。這裡還有個缺口:supported_versions 是 0.4 draft 才列進 changelog 的新項目,0.3 規格我沒讀,不知道 0.3 的 agent 認不認得這個欄位。

之後每個帶 Session header 的請求,agent 都要用 Poppy-Version 報上自己用的版本,格式是 major.minor。規格範例的 headers:

1
2
3
Poppy-Session: eyJ0eXAiOiJwb3BweS1zZXNzaW9uK2p3dCIs…
Poppy-Version: 0.4
Poppy-Extensions: "operations";v="2", "links";v="1"

同一個 Session 裡這些值每個請求都必須一樣,要換就開新的 Session。所以版本是 Session 的屬性,不是請求的小動作。

被擋回來的那一次

選錯的時候會怎樣?驗 Session header 的端點,也必須檢查 Poppy-Version。缺了,或者版本與 Company 提供的任何一版都不相容,端點回 HTTP 400:

1
{ "error": "version_unsupported" }

而且規格禁止 Company 偷偷改用別的版本處理那個請求。接下來輪到 agent:不用快取,重新抓一次 poppy.json,重新選版本。選到另一個版本,就開新 Session,把請求重送,只重送一次。選不到,就停止。

「只重送一次」是我讀到這段最在意的細節。它把重試上限寫進協定,我的解讀是要讓兩邊不會因為版本對不上而一直來回。至於真實的 client 是不是照做,我沒跑過,不知道。

還有兩個相關的出口。對話(conversation)可以被不同版本的 Session 接續,但規格說版本不一定帶得過去,Company 可以拒絕,回 version_mismatch(HTTP 409),並告知那段對話起始的 protocol_version。想下架某個版本的 Company,可以在該版本的回應上帶 Deprecation(RFC 9745)與 Sunset(RFC 8594)header 預告。

規格自己說不用大改,changelog 標了 Breaking

path-to-1 對 0.4 的說法很樂觀:「It’s much smaller than 0.3: mostly additions, clarifications, and fixes to 0.3. We don’t expect Companies that implement 0.3 to need significant work to adopt 0.4.」

changelog 的 0.4 區段,至少有三項標了 Breaking:多版本並行本身(上面整套 supported_versions);擴充改成每個請求在 Poppy-Extensions 宣告,原本放在 client metadata document 的 extensions 被移除;以及 poppy:browser-sign-in,已登入瀏覽器加入時需要帶這個 scope,而且它是協定定義的唯一一個 scope。

兩句話不矛盾。改動量小,跟相容是兩件事,0.3 與 0.4 在 1.0 之前照樣互不相容,所以才需要上面那一整套並行機制。

登入流程有個對照。issue #66 在 11 月 2 日開出來,標題是讓 Direct Sign-In 成為唯一必備的登入流程,並把 Device Sign-In 搬成 extension。issue 已 closed,我沒讀它為什麼關。changelog 裡 0.4 的寫法比提案軟:Direct Sign-In 是「preferred」,Device Sign-In 仍留在規格裡。這兩件事有沒有關係,我沒查。

我的立場是這樣:0.4 發佈之前,poppy.json 先維持 0.3,把工夫花在 version_unsupported 之後那一次重抓、重選、重送上。理由很單純,draft 可以不相容地改,你把它放上主文件,等於替每個 0.4 agent 背書一份隨時會變的東西。如果你的 agent 全由自己控制、能同一天一起升版,連 supported_versions 都可以先不寫;而 0.4 一旦標成 published、凍結,我就會改口,把它掛上去,0.3 退到 supported_versions。

這個立場還有一個支撐,來自規格的另一個承諾。§3.4 說從 1.0 起,poppy.json 的位置與抓取方式、protocol_version、supported_versions、Poppy-Version 和 version_unsupported 在每個版本都維持原意,讓實作不同 major 的雙方找得到共同版本,或確定沒有。版本協商這一批欄位,是規格特別承諾要維持原意的。

還開著的一個洞

信任單位是另一個問題。規格 §3 說 organization.domain 是提供那份文件的 host,這讓複製到別的網域的文件冒充不了原公司。issue #73(作者 jdenes,open,0 則留言)問的是下一步:這證明文件代表那個網域,不證明那個網域屬於 organization.name 寫的公司。他舉的例子是 lookalike 網域也能發布合規的 poppy.json、跑合規的 OAuth issuer,通過規格裡的每一項檢查。他提議選用的 organization.legal_entity,加上不可逆動作前的 counterparty check。這是一位社群成員的提案,不是規格內容,我也沒看到任何回應。

所以回到開頭那個網域。0.3 的 poppy.json 不動,domain 與 issuer 也不動;等 0.4 凍結,再把它掛上去,舊版退進 supported_versions。我沒驗證的有三件事:真實 client 的重送行為,Stacktree 在 issue #75 自稱 11 月 5 日起上線 0.3 的實況(我只讀了標題),還有 0.4 到底哪一天發佈。這三件事只要有一件的答案跟我推演的不同,上面的立場就得跟著改。