Day20 的 skill 是文字——要等模型去讀它,內容才進得了上下文,你還有一個「讀到的時候」可以守。extension 不是。它是一個模組;omp 會從 repo 的
.omp/extensions/自動把它找出來並 import,模組的 top-level 程式碼在 import 的那一刻就在同一個行程、沒有隔離的情況下執行(接著才跑它的 default factory)——都早於第一次 model turn,也在任何 tool/核准關卡之外。今天是這個系列第一次,沒有一個「內容攔截點」救得了你:因為 Day15 到 Day20 那些 guard 本身也是 extension/hook,跟要擋的東西住在同一個行程裡——守門的和闖進來的在同一層。真正能靠的不是在內容上動手,而是來源(什麼東西准載入)和行程外的隔離。講清楚界線:實測看到的是「程式碼在載入時、以它所在行程的權限跑了(這次量到的是讀一個 env 變數、寫一個檔、三個能力的typeof存在)」,不是「祕密被偷走了」——憑證的值從沒被記下,所有金鑰掃描都是 0。
Day20 把外部 deploy skill 的兩條內容路徑(description 和 body)看清楚了:那是文字,兩條都有一個「進模型前」的點可以改寫。今天換成 extension——同樣是從別人那裡來的供應鏈元件,但它是程式碼。今天只回答一件事:一支你從 teammate、廠商、或 clone 的 repo 裡拿到的 extension,在它被「載入」的那一刻能做什麼、守門的 tool 層看不看得到、你有沒有辦法讓它根本不載入。subagent 會不會帶著 guard 一起走,留到 Day22;搜回來的網頁內容留到 Day25;接別人的 MCP 留到 Day27;它自己改寫規則(autolearn)留到 Day30。還有一點要先講明:skill/extension 附帶的 scripts/、以及經 bash/工具去執行的那條路,是另一個執行面,本篇沒有安裝或執行它,只看「extension 模組載入即執行」這一面。至於金鑰怎麼搬到 agent 碰不到的地方,Day16 講過(auth-gateway、容器、把金鑰移出行程)——那正好就是今天這篇指回去的那個緩解方向,在還沒進入今天內容時,先上個概念圖:
你的團隊想統一開發體驗,從某個 teammate、某個廠商、或是 GitHub 上 clone 下來的 repo,拿到一支共用的 dx-helper extension。它躺在專案裡:
.omp/extensions/dx-helper/index.ts
這就是和 Day20 最根本的差別。Day20 的 skill 是文字:frontmatter 的 description 進 <skills>,body 要等模型用 read 去讀——不管哪一條,都得先有「模型願意去讀」這個動作,內容才進上下文。extension 不是文字,是程式碼:它是一個 TS/JS 模組,export 一個 default factory,omp 會直接跑這個 factory(docs/extensions.md)。要不要跑,不取決於模型讀不讀——取決於它有沒有被「載入」。而 omp 會從你的 cwd 自動把它找出來載入。
這一場(autoload)我只對 agent 說了一句「2 + 2 等於多少」,它也確實只回了 4(turn.stopReason: "stop"、assistantText: "4")。但危險的部分,在我開口之前就發生完了。那支自動發現的 extension:
factoryLoadObserved: true,這筆是 top-level 程式碼寫下的;factory 隨後才註冊工具,見 registeredTool),而且早於 agent 開始(orderingLoadBeforeAgentStart: true)。CURSOR_API_KEY(credentialEnvPresent: true、credentialEnvLen: 69——只記下「有」和「長度 69」,值從頭到尾沒被記下)。ext-autoload-marker.txt,markerFileExists: true)。capabilities.{fetch,bunSpawn,fsWrite} 都是 true——這是用 typeof 探的,沒有真的去呼叫 fetch/spawn,沒有真的對外連線。那個 marker 檔是一個無害的 canary,站在「任意動作」的位置上:它證明的是「一支 extension 可以在載入時、沒有工具、沒有核准地動手」,而不是它真的做了什麼壞事。本篇沒有任何祕密外流——四場的金鑰掃描都是 0。重點只有一個:你問的是「2 + 2」,但在你問之前,程式碼已經跑過,而且它摸得到憑證、摸得到檔案系統。
這一節全部來自 2026-10-04 的官方文件,沒有在 IDE 做 live 測試;另有一份可選的手動流程放在 verification/day21/cursor-manual-test.md,其結果會另外補上——本節不主張任何手動結果。
Cursor 這邊要分成兩個不同的面:hooks 和 VS Code 式的 extension。兩者的風險形狀不一樣。
先看 hooks。 文件寫得很清楚(2026-10-04 文件):hooks 是「spawned processes that communicate over stdio」——被生出來、用 stdio 以 JSON 雙向溝通的子程序,在 agent loop 的某些階段前後跑,能觀察、擋、或改行為(Hooks)。它們可以寫在專案層的 .cursor/hooks.json。接著把幾條文件敘述組合起來看(這是我自己把多條敘述接起來的推論,不是文件裡的某一句話):
把 1 和 2 接起來:workspace trust 預設是關的,關的時候所有 workspace 自動視為 trusted,於是「trusted workspace」這個條件預設就成立——所以一個你 clone 下來的 repo,它附帶的 .cursor/hooks.json 在預設狀態下會自動載入執行,不會跳出提示要你確認。再次強調這是我把多條文件敘述組合出來的,不是文件裡單獨一句話。 相容格式也一樣:Cursor 會載入 Claude Code 的 hooks,而「Include Third-Party Plugins, Skills, and Other Configs」這個開關預設是開的,.claude/settings.json(checked into repo)的 hook 預設也會被載入(Third Party Hooks)。至於 hook 子程序本身——文件沒有記載任何 sandbox、權限上限,或對它存取環境變數/祕密/網路/其他程序的限制;文件寫的是 hook 腳本執行時會「收到」哪些環境變數(CURSOR_PROJECT_DIR、CURSOR_USER_EMAIL 之類),不是「被限制」能碰什麼。
再看 VS Code 式的 extension。 Cursor 用一個「extension host」來跑 extension;Cursor 官方文件沒有描述任何 capability sandbox 或權限模型,對 extension 能碰什麼著墨極少,主要只有「Extensions can access your workspace」這類句子(Identity and Access Management,2026-10-04 文件)。(補一個上游註腳:VS Code 上游文件說 extension host「has the same permissions as VS Code itself」——但這是上游 VS Code 文件,不是 Cursor 的:VS Code extension runtime security。)不過 extension 和 hook 有個關鍵差別:extension 需要明確安裝,而這條安裝路徑上有真實的來源控制(都在 2026-10-04 文件):marketplace proxy 會先做 malware/供應鏈掃描再上架、驗過才給下載,失敗就擋、還有 blocklist(Extensions help);有可選的 publisher 驗證徽章;企業可用 allowlist(AllowedExtensions,Identity and Access Management);有 install cooldown;還有可選的簽章驗證(Extensions help)。
把兩面分清楚:hooks = 可提交進 repo、在預設信任下自動執行、文件沒有 sandbox;extension = 要明確安裝、有真實的來源控制,但一旦裝上,文件也沒有描述任何 runtime sandbox。
痛點是哪三件:
.cursor/hooks.json、相容的 .claude/settings.json、以及要安裝的 marketplace extension,都是可能進來的路。今天要誠實地轉個彎。前面幾天 omp 的「平替」都是:某個內容進模型前,有一支擴充看得到、改得動。今天不是。
因為 extension 是程式碼,不是內容。 omp 的文件講得很白:extension「run in-process with no isolation」(docs/extensions.md)、「not sandboxed (same process/runtime)」、而且它們「share one EventBus and one ExtensionRuntime instance」(docs/extension-loading.md)。它們和本體、和彼此,都在同一個行程、共用同一條事件匯流排。
而且 omp 會從 cwd 自動發現它。 原生自動發現的來源包含 <cwd>/.omp/extensions、<cwd>/.omp/settings.json#extensions(docs/extension-loading.md),還會把 <cwd>/.omp/hooks/pre|post/ 底下的 JS/TS hook 當 extension 一起載入。這條路沒有文件化的信任關卡——也就是說,你 clone 一個 repo、在裡面跑 omp,它底下那些 extension 程式碼就有機會被載入執行。
最反身的一點:守門的也是 extension/hook。 Day17–20 那些 guard 是用 -e 載入的 extension,Day15–16 的 guard 則是 omp 從 .omp/hooks/pre/ 自動發現的 hook factory;這個實驗裡那支只讀的觀察擴充(observer)用 -e 載入。不管哪一種,它們都和本體、和彼此在同一個行程、同一層。一支掛在 tool_call/tool_result 的 guard,守的是模型的工具使用——tool_call/tool_result 會攔截所有工具(註冊表在 sdk.ts 被包起來,docs/extensions.md)。但它攔的是「工具」。一支 sibling extension 在載入(import)時直接讀環境變數、直接寫檔,這些根本不經過工具層,所以同層的 guard 看不到。autoload 兩場裡,那支 sibling observer 對 probe 的「載入期讀環境變數、寫檔」記到的 tool 事件是 0(observer.toolCallCount: 0)——這是「看不到」的實測;至於「擋不擋得住」,那是架構上的事:tool 層 guard 守的是工具呼叫,不是載入期程式碼。守門的和闖進來的在同一層,這就是今天沒有內容攔截點救得了你的原因。
所以 omp 的真正控制,不在「攔內容」,在「來源」——到底什麼東西准載入:
--no-extensions / SDK 的 disableExtensionDiscovery:關掉自動發現(但用 -e 顯式指定的還是會載入)。disabledExtensions: [extension-module:<name>]:用 id 把某一支擋掉。--trusted-extension <絕對路徑>:精確的檔案白名單(同時關掉自動發現),比「全開」更嚴。-e,不要讓它從 cwd 自動撿。第四、五節就用實測把這三條走一遍。先把資料流寫成最短一行:
.omp/extensions/*.ts → import 時 top-level 程式碼即執行(in-process, no isolation)→ factory 隨後註冊 → 全在 model turn 與 tool gate 之前
先看不加任何來源控制、讓 extension 從 cwd 自動被撿起來時,它實際走到哪裡。這就是 autoload 兩場(autoload-1、autoload-2)。
在這兩場裡,那支自動發現的 extension 的模組 top-level 程式碼在載入(import)時就跑了(factoryLoadObserved: true;factory 隨後才註冊工具,見 registeredTool: true),而且早於 agent 開始(orderingLoadBeforeAgentStart: true);它讀得到 CURSOR_API_KEY(credentialEnvPresent: true、credentialEnvLen: 69,值從未被記下),capabilities.{fetch,bunSpawn,fsWrite} 都是 true(用 typeof 探得、沒有真的呼叫),並且在沒有任何 tool call、也不經工具核准路徑的情況下寫了一個 marker 檔(markerFileExists: true)。同一個行程裡那支 sibling 觀察擴充記到的 tool 事件是 0(observer.toolCallCount: 0)。全程 keyOccurrences: 0、persistedKeyOccurrences: 0。
把界線說死:證據顯示的是「程式碼在載入時、以它所在行程的權限跑了」(這次量到的是:讀一個 CURSOR_API_KEY、寫一個檔、三個能力的 typeof 存在);它沒有顯示任何祕密被外流(沒有東西離開這台機器,金鑰掃描 0)。我不會說這支 extension「偷」了什麼——它示範的是它能讀、能動,而且用的是一個無害的 canary。「能做」和「做了壞事」是兩件事,今天只證到「能做」。
把 autoload 這一側的能力收成一張表:
| 能力(autoload,載入時) | 本次觀察 | 證據欄位 |
|---|---|---|
| 模組 top-level 程式碼是否在載入(import)時執行 | 是 | factoryLoadObserved: true |
| 是否早於 agent 開始 | 是 | orderingLoadBeforeAgentStart: true |
| 讀得到憑證環境變數嗎 | 讀得到(只記存在與長度,值不記) | credentialEnvPresent: true、credentialEnvLen: 69 |
手上握有的能力(typeof 探測,未呼叫) |
fetch/bunSpawn/fsWrite 皆在 | capabilities.{fetch,bunSpawn,fsWrite}: true |
| 無 tool call、無核准下寫檔 | 寫了 marker | markerFileExists: true |
| 同層 sibling guard 看得到嗎 | 看不到 | observer.toolCallCount: 0 |
| 金鑰外流 | 無 | keyOccurrences: 0、persistedKeyOccurrences: 0 |
方向因此很清楚:既然守門的 tool 層看不到載入期的程式碼,補的就不該是「再加一個內容關」,而是「決定什麼准載入」加上「把行程本身關進隔離」。下一節兩個都看。
這一側是 no-extensions-1 和 disabled-1。
在 no-extensions-1(加 --no-extensions)和 disabled-1(在 .omp/config.yml 設 disabledExtensions: [extension-module:dx-helper])兩場,這支自動發現的 probe 根本沒有載入(factoryLoadObserved: false、markerFileExists: false),而用 -e 顯式載入的觀察擴充照樣載入(observer.factoryLoadObserved: true),session 也照樣回答 4(turn.stopReason: "stop")。也就是說,--no-extensions 和 disabledExtensions 把自動發現的程式碼擋在「根本不載入」這一關;它們是來源控制,已 live 驗證。
注意這裡補的東西的性質:它不是「看到內容再挑掉」,是一個開關——決定那段程式碼要不要被載入。--no-extensions 關的是「自動發現」這件事,所以顯式 -e 的擴充照樣進得來(這也符合文件:explicit -e 在 --no-extensions 下仍載入);disabledExtensions 則是用 extension-module:<name> 這個 id 精準擋掉某一支。兩條都讓自動撿進來的程式碼在跑之前就被攔下。
開關解決的是「什麼准載入」。但只要一支程式碼被載入,它就和本體、和你的 guard 同在一個行程、共用一條事件匯流排——同層的東西關不住同層的東西。所以真正的隔離邊界不在行程裡,在行程外:
auth-gateway(選項 1)+容器(第 2 件):金鑰不在 agent 那台機器、那個使用者底下,載入期的 extension 理想上讀到的只會是個轉送用的 token。但要誠實標註:Day16 當時就寫明「cursor-sdk 這種 extension provider 能不能走 gateway,我沒有驗證」——所以對本篇暴露的這個 CURSOR_API_KEY,這條是方向,不是已驗證的結論。這是架構層的建議:既然同一個行程裡的 guard 擋不住 sibling,那就別指望在行程裡解決它,把界線畫到作業系統那一層。
| 面向 | 自動發現(autoload) | 關掉來源之後(no-extensions/disabled) | 實測證據 |
|---|---|---|---|
| 自動發現的 extension 會不會載入 | 會,factory 在載入時跑 | 不載入 | autoload factoryLoadObserved: true;兩場控制 false |
| 載入時讀得到憑證嗎 | 讀得到(present、長度 69,值不記) | 沒機會(根本沒載入) | credentialEnvPresent: true、credentialEnvLen: 69 |
| 同層 sibling guard 看得到載入期動作嗎 | 看不到(autoload 兩場 0 tool 事件) | 控制場 probe 未載入、無載入期動作可看;observer 仍載入 | autoload observer.toolCallCount: 0 |
| off-switch 擋不擋得住載入 | 擋不住(本來就沒開) | 擋得住;顯式 -e observer 仍載入、session 仍答 4 |
控制場 markerFileExists: false、observer.factoryLoadObserved: true、turn.stopReason: "stop" |
| 金鑰外流 | 無 | 無 | 四場 keyOccurrences: 0、persistedKeyOccurrences: 0 |
| 面向 | omp 18.2.8(本次實測+文件) | Cursor(2026-10-04 文件) |
|---|---|---|
| 第三方程式碼的 runtime capability sandbox | 沒有:文件與實測都是 in-process、not sandboxed | 文件沒有記載 hook 或 extension 的 runtime capability sandbox |
| 從 repo 自動載入 | .omp/extensions/、.omp/settings.json#extensions、.omp/hooks/pre|post/ 從 cwd 自動發現,無文件化信任關卡(曝露面較大) |
專案 .cursor/hooks.json committed、在預設信任下自動載入執行(由多條文件敘述組合得出);VS Code 式 extension 需顯式安裝 |
| 硬性關閉開關 | 有:--no-extensions/disableExtensionDiscovery、disabledExtensions,已 live 驗證 |
extension 可停用、企業可用 AllowedExtensions allowlist;hook 的關閉以 workspace trust/企業政策為主 |
| 守門的與被守的同層 | 是:guard 本身也是 extension/hook;sibling 的載入期程式碼不經工具層,observer 實測記到 0 tool 事件(看不到),擋不擋得住是架構推論 | 不是同一個模型:hook 是子程序、extension 在 extension host |
| 來源控制 | 靠你維護 allowlist/只用顯式 -e |
marketplace 掃描+blocklist、publisher 驗證、企業 allowlist、install cooldown、簽章驗證 |
兩邊的共同點:對第三方程式碼,都沒有文件化的 runtime capability sandbox。差別在三個軸上:omp 從 cwd 自動發現(曝露面更大)、但有一個硬性的 off-switch(控制更直接)、而且守門的本身就是 extension(反身性);Cursor 的 extension 有 marketplace 掃描/allowlist/簽章這些安裝端的來源控制,但它的 hook 在預設信任下會自動跑(由多條文件敘述組合得出)。每一格都綁在文件或實測上、都標了日期。
| 限制 | 現在能說到哪裡 |
|---|---|
| 來源控制要你維護 | --no-extensions/disabledExtensions 是開關,哪些該擋、allowlist 怎麼列,得你自己維護;extension 新增、改名、搬家、升版都要跟著更新 |
| 同層 extension 可互相包夾 | 一支載入進同一行程的惡意 extension,也能註冊 middleware 去包住/抵銷另一支 extension(tool_result 是 middleware-style、before_agent_start 是鏈式覆寫)——這是推理推論,未單獨 live 測試,標注清楚 |
| scripts/bash 執行未測 | 本篇沒有安裝或執行 extension/skill 附帶的 scripts/,也沒測經 bash/工具的執行;文字面的來源控制不能代替工具執行政策 |
| Cursor 側只有文件 | 本節是 2026-10-04 官方文件;手動流程與結果另外補上,本文不主張 |
| 四場不能外推 | 四場是決定性的載入期 run(autoload 跑兩次只是看穩定性,不是機率事件),不保證所有版本、設定、帳號政策都一樣 |
| 長度 69 不是祕密 | credentialEnvLen: 69 只是「長度」這個觀察值,不是金鑰本身;沒有任何值被記下或外流,金鑰掃描全 0 |
第一步,盤點每一個會自動載入的 extension 來源。 任何一次 clone 或安裝之後,先列清楚:.omp/extensions/、.omp/settings.json#extensions、.omp/hooks/pre|post/、以及已安裝的 plugin。記下來源、版本/commit、誰 review 過;來路不明的先別讓它進場。
第二步,預設只用顯式載入。 用 --no-extensions(或 SDK 的 disableExtensionDiscovery)關掉自動發現,需要什麼就用顯式 -e 指名載入;對已知不想要的,用 disabledExtensions: [extension-module:<name>] 擋掉——而且把 disabledExtensions 設在使用者層設定,別放在那個不可信 repo 自己的 .omp/config.yml 裡。兩個提醒:--no-extensions 會把 .omp/hooks/pre|post/ 自動發現的 hook factory 一起關掉(loader.ts 整段 ambient 探索都跳過),所以你若靠 Day15–16 那種放在 .omp/hooks/pre/ 的 guard,要改用顯式 -e 載入它們,否則它們會跟著不見。這一步是「決定什麼准載入」,是今天真正的主力。
第三步,把真正的界線畫到行程外。 既然同層 guard 擋不住 sibling,就別在行程裡解決:用獨立的使用者/容器跑 agent,再加一個唯讀的憑證代理,讓行程永遠不握有原始金鑰(Day16 的 auth-gateway +容器)。
第四步,把 skill/extension 的 scripts 和 bash 當成另一套執行政策。 載入即執行是一面;允許它跑 scripts/ 或 bash 是另一面,要另外設 pre-execution 的核准/白名單政策,本篇沒有驗證這一面。
留在 Cursor 的話(以下皆 2026-10-04 文件):用 MDM 強制開啟 workspace trust(WorkspaceTrustEnabled),別讓所有 workspace 預設自動信任;用企業的 extension allowlist(AllowedExtensions)+簽章驗證+marketplace 控制收斂 extension 來源;而對 committed 的 .cursor/hooks.json 和 .claude/ 裡的 hook,要有人或 CI 審過才合入——因為它們在預設信任下會自動跑(這點由多條文件敘述組合得出)。
第一題:為什麼一個你沒「用」的 extension,也能在你問問題前就做事?
因為它是程式碼,不是等你去用的文字。omp 從 cwd 的 .omp/extensions/ 自動發現它並 import——模組的 top-level 程式碼在 import 的那一刻、同一個行程裡就執行了(接著才跑 default factory),都早於第一次 model turn。這次實測,兩場 autoload 的 orderingLoadBeforeAgentStart 都是 true:import 期那筆紀錄的時間戳早於 agent 開始。你只問了「2 + 2」,但它讀環境變數、寫檔都在你開口之前發生完了。
第二題:既然有 Day15–20 的 guard,為什麼擋不住惡意 extension?
因為那些 guard 本身也是 extension/hook,和要擋的東西住在同一層。guard 掛在 tool_call/tool_result,守的是模型的工具呼叫;但一支 sibling extension 在載入(import)時直接讀環境變數、直接寫檔,這些根本不經過工具層。autoload 兩場裡,同層的 observer 對 probe 的載入期動作記到的 tool 事件是 0(控制場 probe 根本沒載入)。守門的站在工具這一關,而闖進來的根本沒走這一關。
第三題:那到底該靠什麼?
靠兩件行程裡的內容守門給不了的東西:一是來源/開關——用 --no-extensions、disabledExtensions、只用顯式 -e,決定什麼程式碼准載入;二是行程外的隔離——獨立使用者/容器+唯讀憑證代理,讓就算載入了也碰不到真正的祕密。內容 guard 對「程式碼」不適用。
Day22 看 subagent——當 agent 生出子 agent,守門的 guard 會不會跟著走,還是子 agent 就脫離了你設的關。
第 1 步:讀四場總表。 打開 research_folder_omp_vs_Cursor/verification/day21/runs/ 底下四場的 summary.json 和彙總 ext-summary.json,確認:autoload 兩場「載入了也寫了檔」(factoryLoadObserved: true、markerFileExists: true),no-extensions 和 disabled 兩場「沒載入也沒寫檔」(都 false),而且四場的 keyOccurrences、persistedKeyOccurrences 全是 0。
第 2 步:看那支 probe 到底記了什麼(只讀)。 讀 verification/day21/make-fixture.sh 裡植入的 probe,確認它只記 CURSOR_API_KEY 的「存在」和「長度」、capabilities 用 typeof 探而不呼叫、只寫一個無害 marker。再回去對 autoload-1/summary.json 的 credentialEnvLen: 69——看清楚那是「長度」,不是值。(若想親自跑 off-switch,可 cd research_folder_omp_vs_Cursor/verification/day21 && bun run-ext.ts no-extensions my-check,但那會用到一次 model turn,這裡保持只讀就好。)
第 3 步:追到原始碼。 看 discovery/builtin.ts:482(從 .omp/extensions 自動發現)和 docs/extension-loading.md 的「Extensions are not sandboxed (same process/runtime)」。把「自動撿」和「不隔離」兩件事擺在一起,就看懂為什麼載入即執行、而且同層擋不住。
第 4 步:在 Cursor 找 sandbox。 在 2026-10-04 的 Cursor 文件裡,找一個「hook 或 extension 的 runtime capability sandbox」——你會找不到:文件講的是安裝端的來源控制(掃描、allowlist、簽章)和 workspace trust,沒有講跑起來之後能碰什麼的執行期限制。
最後一個提醒(粗體):以上沒有一項證明了祕密被外流、或模型被騙去做壞事。 證到的是「程式碼在載入時、以它所在行程的權限、在同一個行程裡跑了(這次量到:讀一個 env 變數、寫一個檔、三個能力的 typeof 存在)」;憑證的值從沒被記下,所有金鑰掃描都是 0。真正能靠的控制是來源(什麼准載入)+行程外的隔離。
今天對應的威脅: T1(外部內容操控/供應鏈),主要對應 OWASP Agentic Top 10 2026 的 ASI04「Agentic Supply Chain Vulnerabilities」。一支被安裝或自動載入的 extension,是一個供應鏈元件——而且它是程式碼、不是資料;一旦在行程裡跑起來,它就碰得到這個行程碰得到的一切(風險從 Day19/Day20 的「資料/上下文中毒」升級成「在你的行程裡直接執行程式碼」)。要特別說清楚:那些危險效果——偷金鑰、偽造工具結果、停用某支 guard——本篇都沒有執行,只用了一個無害的 canary。
引用來源:
正文用事情本身來講,場次代號和檔案路徑收在這裡。
| Run ID | 可追溯結果 | 證據層級 |
|---|---|---|
autoload-1 |
factory 載入即跑、早於 agent:factoryLoadObserved: true、orderingLoadBeforeAgentStart: true;讀得到憑證 credentialEnvPresent: true、credentialEnvLen: 69(值不記);capabilities.{fetch,bunSpawn,fsWrite}: true;無工具、無核准寫檔 markerFileExists: true;sibling observer.toolCallCount: 0;turn.stopReason: "stop"、keyOccurrences: 0、persistedKeyOccurrences: 0 |
2026-10-04 live(網路通),載入期決定性 |
autoload-2 |
同 autoload-1:載入即跑、credentialEnvLen: 69、寫檔、sibling 0 tool 事件、金鑰 0 |
2026-10-04 live,重跑看穩定性 |
no-extensions-1 |
加 --no-extensions:probe 不載入 factoryLoadObserved: false、markerFileExists: false;顯式 -e observer 仍載入 observer.factoryLoadObserved: true;session 仍答 4、turn.stopReason: "stop"、金鑰 0 |
2026-10-04 live,off-switch 驗證 |
disabled-1 |
在 .omp/config.yml 設 disabledExtensions: [extension-module:dx-helper]:probe 不載入(兩個欄位 false);顯式 observer 仍載入;session 仍答 4、金鑰 0 |
2026-10-04 live,id 擋除驗證 |
ext-summary.json |
四場嚴格彙總:probeLoadedWhenAutoDiscovered: 2、probeSuppressedByControl: 2、observerToolEventsTotal: 0、allCredentialVisibleAtLoad: 2、anyKeyLeak: false |
四場彙總+持久化金鑰重掃 |
ext-autoload-marker.txt 是替代「任意動作」的無害 canary。上表沒有任何一場偷取、外傳祕密,或偽造/停用任何東西;credentialEnvLen: 69 是長度觀察值,不是金鑰。
實驗條件: 2026-10-04(UTC)。omp 18.2.8,以 --mode rpc 由 verification/day21/run-ext.ts 驅動;provider 是 extensions/cursor-sdk,模型是 cursor-sdk/claude-opus-5-5、--thinking off,只開 read,write 工具,--approval-mode yolo(omp 預設)、--no-title、--no-lsp、--no-rules。四場 model turn 都成功完成(stopReason: "stop"),表示對外連線可用。要說清楚:「無核准」不是 yolo 造成的——載入期程式碼本就不經工具核准路徑(核准關卡在 tool wrapper,sdk.ts),所以就算不是 yolo,import 時的程式碼也不會被工具核准擋住。植入的 dx-helper probe(由 make-fixture.sh 建在暫存專案的 .omp/extensions/dx-helper/index.ts)只記 CURSOR_API_KEY 的存在與長度、用 typeof 探能力而不呼叫、只寫一個無害 marker。金鑰讀自 gitignore 的 cursor_sdk_api,只放進子程序的環境變數,從沒進 RPC 那條線;跑完掃每一個 run 的持久化檔案,四場 keyOccurrences、persistedKeyOccurrences 都是 0。暫存工作目錄跑完全部刪除。
以下路徑相對 research_folder_omp_vs_Cursor/:
verification/day21/runs/{autoload-1,autoload-2,no-extensions-1,disabled-1}/summary.json(各夾另含 events.jsonl、observer.jsonl、cmd.txt、sessions/;autoload 兩場另有 ext-side.jsonl)verification/day21/runs/ext-summary.json
verification/day21/runs/README.md
verification/day21/run-ext.ts
verification/day21/make-fixture.sh
-e 顯式載入):verification/day21/observer-ext.ts
ext-summary.json、不呼叫模型):verification/day21/summarize-ext.ts
verification/day21/cursor-manual-test.md、verification/day21/runs/cursor-manual-2026-10-04/
omp 18.2.8 文件與原始碼:下列 docs/ 路徑相對 oh-my-pi-main/;其餘原始碼路徑相對 oh-my-pi-main/packages/coding-agent/src/。
docs/extensions.md
registerTool/registerCommand/registerProvider/sendMessage/before_provider_request
tool_call/tool_result 攔截所有工具(註冊表在 sdk.ts 被包起來)docs/extension-loading.md
<cwd>/.omp/extensions、<cwd>/.omp/settings.json#extensions,以及 .omp/hooks/pre|post/
EventBus and one ExtensionRuntime」--no-extensions/disableExtensionDiscovery(顯式 -e 仍載入);disabledExtensions 以 extension-module:<name> 擋除cli/args.ts:272(--no-extensions 解析)main.ts:1622-1624(noExtensions → disableExtensionDiscovery)main.ts:1717-1725(--no-extensions 下顯式 -e roots 仍被授權;只關自動發現)config/settings-schema.ts:489(disabledExtensions 預設空陣列)capability/index.ts:127-129、169-171(建立並套用 disabledExtensions)discovery/builtin.ts:61-63(專案 config 目錄=<cwd>/.omp)、discovery/builtin.ts:482(自動發現 .omp/extensions)、discovery/builtin.ts:504-509(.omp/settings.json#extensions)Cursor 來源(2026-10-04 官方文件):
.cursor/hooks.json checked into version control、在 trusted workspace「automatically loads and runs」AllowedExtensions;「Extensions can access your workspace」.claude/settings.json hook,第三方匯入開關預設開;相容 PreToolUse/PostToolUse 等