內建代理工具
這些是內建代理執行框架直接註冊、綁定伺服器端資源的工具——語言伺服器、結構化搜尋、除錯器、核心、提問與工作程式控制——有別於它橋接進來的 MCP 工具,以及執行框架所提供的通用檔案、搜尋與 shell 工具。它們不在 MCP 註冊表上:外部 MCP 用戶端看不到它們,因為每一個都綁定着一項伺服器端的即時資源——一個語言伺服器行程、一個除錯配接器、一個直譯器——而該資源只屬於一次執行。
這裏每一個工具,只有在它的支撐資源為本次執行接入之後才會註冊——語言伺服器或除錯監督程式、一個 tree-sitter 解析器、一個核心工廠、一個提問連接埠、一份 vibe 名冊。對這次檢出的語言伺服器或除錯配接器所做的偵測,是另一項更靠後的檢查(status / adapters):那些工具會一直留在工具介面上,好讓代理能問清楚為甚麼甚麼都用不了,而不是靠猜。
核准層級
標題為「核准層級」的章節每個工具都會宣告一個層級,而層級決定的是哪些介面能看到它——而不只是它是否會提示。計劃模式與唯讀子代理都限制在 read,因此 write 或 exec 層級的工具根本不會出現在它們的 schema 中。
| 層級 | 含義 | 這裏的工具 |
|---|---|---|
read |
無副作用 | lsp、ast_grep、ask_user,以及五個 vibe_* |
write |
會改動檔案 | lsp_rename、ast_edit、resolve |
exec |
啟動或控制一個行程 | debug、eval |
lsp — 語言伺服器查詢
標題為「lsp — 語言伺服器查詢」的章節當一次執行擁有語言伺服器監督程式與工作目錄時才會註冊。伺服器能否真正作答,是在呼叫時按交集來偵測的:根標記必須存在,並且伺服器二進位檔必須能被解析到。只有其中一項成立都不會啟動任何伺服器——status 會如實說明——而不是悄悄靠猜。
定位是由 line 加一個 symbol 子串指定的,而絕不靠列號。被要求給出列號的模型只能靠猜,而猜錯會解析成同一行上的另一個符號——一個看起來正確、說的卻是別的東西的答案。在導覽類操作中省略 symbol 是一種錯誤,而不是默認猜第一列。
| 操作 | 引數 | 傳回 |
|---|---|---|
diagnostics |
file(或 "*") |
錯誤與警告;"*" 改為執行專案檢查器 |
definition |
file, line, symbol |
符號宣告的位置 |
type_definition |
file, line, symbol |
其型別宣告的位置 |
implementation |
file, line, symbol |
具體的實作 |
references |
file, line, symbol |
每一處使用,上限 50 處 |
hover |
file, line, symbol |
簽章與文件註解 |
symbols |
query 或 file |
以 query 做工作區搜尋(上限 100);以 file 取文件符號(上限 200) |
code_actions |
file, line, symbol |
伺服器提供的快速修正 |
status |
— | 哪些伺服器正在執行,以及各自對應哪些根 |
reload |
— | 為這個根重新啟動伺服器 |
寫入時的診斷
標題為「寫入時的診斷」的章節真正的回報不在工具本身。write、edit 與 apply_patch 都被包了一層:寫入之後,檔案會經語言伺服器同步並儲存,而只有新引入的診斷會被併入工具結果。代理在弄壞組建的那一刻就知道自己弄壞了,毋須記得去問。
已經報告過的診斷,會由一份按執行維護的帳簿抑制,該帳簿以嚴重程度、代碼與訊息為鍵——刻意排除了位置,因此僅僅向下移動了四行的診斷,不會被當作新增而重新報告。
lsp_rename — 全專案重新命名
標題為「lsp_rename — 全專案重新命名」的章節它是一個獨立的 write 層級工具,理由只有一個:層級決定可見性。把重新命名併入 lsp,要不是把改寫程式庫的能力交給一個唯讀的探索者,就是讓每一次 hover 都要先過一道核准提示。
| 引數 | 含義 |
|---|---|
file |
符號所在的位置 |
line |
由 1 開始計數的行號 |
symbol |
在該行上標識它的子串 |
new_name |
改成甚麼名稱 |
ast_grep — 結構化搜尋
標題為「ast_grep — 結構化搜尋」的章節它按形狀而不是按文字尋找程式碼。$NAME 匹配任意節點,$$$NAME 匹配一串同層節點,$_ 則是萬用字元,不綁定任何東西。
令它成為一個匹配器、而不只是一條多繞幾步的正則表達式的特性在於:同一個模式中重複出現的元變數,必須捕獲相同的文字。
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")它會匹配處置自己所檢查對象的守衛,而不會匹配處置其他東西的守衛。
| 引數 | 含義 |
|---|---|
pattern |
一段帶元變數的程式碼片段 |
path |
要搜尋的檔案或目錄;預設為整個工作區 |
language |
隨附的語法 id(dart、python、rust、c 等)。當 path 指向單一檔案時會被推斷出來 |
max_results |
預設 50 |
ast_edit — 結構化重寫,先暫存
標題為「ast_edit — 結構化重寫,先暫存」的章節匹配方式相同,另外還接受一個 rewrite 模板,把元變數代回其中。不會寫入任何內容。 改動會被暫存起來,而結果中會給出一個 edit_id。
ast_edit(pattern: "dispose($X)", rewrite: "$X.dispose()", language: "dart")→ (proposed) 12 replacements in 4 files … call resolve with edit_id "edit_1"| 引數 | 含義 |
|---|---|
pattern |
要匹配甚麼 |
rewrite |
換成甚麼,其中 $NAME 會被代回 |
path |
作用範圍;預設為整個工作區 |
language |
語言 id |
resolve — 提交或丟棄一次暫存的改動
標題為「resolve — 提交或丟棄一次暫存的改動」的章節| 引數 | 含義 |
|---|---|
edit_id |
來自 (proposed) 結果 |
action |
accept 或 discard |
每個檔案都會與暫存時擷取的內容比對,任何不一致都會拒絕整項改動。只套用了一半的結構化重寫,會留下一棵在舊形狀與新形狀下都編譯不過的樹。
debug — 驅動除錯器
標題為「debug — 驅動除錯器」的章節當一次執行擁有除錯工作階段監督程式、工作目錄與對話 id 時才會註冊。配接器能否真正啟動,是在呼叫時按與語言伺服器相同的交集規則偵測的(根標記並且二進位檔)。屬於 exec 層級;非平行安全(工作階段是跨呼叫累積的狀態,因此 continue 搶在 stack 前面,會讀到一個已經移動過的堆疊框)。
每個對話只允許一個工作階段,並帶一個硬性 TTL(30 分鐘)。第二次 launch 會被拒絕,而不是悄悄取代第一個。
| 操作 | 引數 | 說明 |
|---|---|---|
adapters |
— | 這裏有哪些配接器可用,以及為甚麼不可用 |
status |
— | 是否有工作階段正在執行,以及它停在哪裏 |
breakpoints |
file, lines[] |
lines 為空會清除該檔案的斷點。可以在啟動之前設定 |
launch |
program, args[] |
啟動程式,並停在你的斷點處 |
attach |
port |
連接到一個已在執行的被除錯程式 |
continue |
thread_id? |
執行至下一個停止點 |
pause |
thread_id? |
讓它就地停止 |
step_over / step_in / step_out |
thread_id? |
移動一步 |
threads |
— | 執行緒 id 與名稱 |
stack |
thread_id? |
堆疊框,上限 30 個 |
scopes |
frame_id |
某個堆疊框上的作用域,每個都帶一個 reference |
variables |
reference |
子項,上限 50 個;值上限 400 個字元 |
evaluate |
expression, frame_id? |
在該堆疊框自身的作用域中求值 |
output |
— | 被除錯程式的 stdout/stderr,有上限 |
terminate |
— | 結束工作階段並終止配接器 |
配接器
標題為「配接器」的章節| Id | 指令 | 根標記 |
|---|---|---|
dart |
dart debug_adapter |
pubspec.yaml |
debugpy |
python3 -m debugpy.adapter |
pyproject.toml, setup.py, requirements.txt |
lldb |
lldb-dap |
Cargo.toml, CMakeLists.txt |
delve |
dlv dap |
go.mod |
debugpy 會優先選擇專案本地的 .venv/bin 或 venv/bin,而不是 $PATH:$PATH 裏的 python 是另一個環境,而從錯誤的那個附加時,失敗的樣子讀起來像程式碼壞了。
eval — 持久直譯器
標題為「eval — 持久直譯器」的章節一個 Python 或 JavaScript 儲存格,其變數、匯入與已載入的資料在多次呼叫之間都會保留下來。屬於 exec 層級;非平行安全。
| 引數 | 含義 |
|---|---|
code |
儲存格 |
description |
必填。5–10 個詞,用作逐字稿標籤 |
language |
python(預設)或 javascript |
reset |
先丟棄直譯器,從乾淨狀態重新開始 |
- 最後一個運算式會像筆記本那樣被回顯。
display()可用,pandas 資料框會渲染成表格,而 matplotlib 圖形會以逐字稿會顯示的影像形式傳回。- 人們下意識會敲的那些 IPython 慣用寫法——
%pip、%cd、!cmd、%%bash、%%timeit——都會被改寫成普通程式碼。%pip還會逐出sys.modules,讓下一次import看到剛剛安裝的東西。
在儲存格內呼叫你自己的工具
標題為「在儲存格內呼叫你自己的工具」的章節contents = tool("read", {"path": "lib/auth.dart"})這座橋會重新進入執行框架的工具註冊表,因此護欄、核准以及每一層包裝,都以與模型發出的呼叫完全相同的方式生效。它走的是核心自己的管道,而不是回送通訊端:沒有連接埠、沒有密鑰、沒有出網規則。
儲存格的逾時是一份無活動預算(預設 5 分鐘),而在一次橋接呼叫尚未完成期間會被暫停,因此一個扇出到子代理的儲存格,不會在扇出途中被殺掉。
ask_user — 向人提問
標題為「ask_user — 向人提問」的章節屬於 read 層級。這是一次執行向操作者提問的唯一方式。它會在對話中渲染一張編號卡片,並一直阻塞,直到有人回答、跳過,或提問逾時。絕不要把問題寫成一則聊天訊息——那會結束當前回合。
| 引數 | 含義 |
|---|---|
question |
要問甚麼 |
context |
選填:你為甚麼問,以及答案會決定甚麼 |
options[] |
可選項,每一項為 {label, description?} |
allow_free_text |
是否接受手動輸入的答案 |
multi_select |
是否可以選擇多項 |
跳過即成功:基於一個明確說明的假設繼續推進,並且不再詢問。逾時則是一種錯誤,附帶同樣的指示。權限提示(「我可以這樣做嗎?」)是另一道閘門,在對話中渲染為同樣的卡片。
只有在提問連接埠與目標空間都存在時才會註冊。在沒有真人接入的情況下,這次呼叫會失敗,而不是永遠阻塞下去——這與護欄 prompt 在沒有連接任何核准人時所採取的失效關閉姿態相同。
vibe_* — 指揮背景工作程式
標題為「vibe_* — 指揮背景工作程式」的章節只有在 Vibe 模式 開啟期間才會註冊。這五個都屬於 read 層級:閘門應當設在工作程式的那些工具上,因為編輯真正發生的地方在那裏。
| 工具 | 引數 | 說明 |
|---|---|---|
vibe_spawn |
brief, label, tier? |
立即傳回;工作程式繼續執行 |
vibe_send |
worker_id, message |
追加到簡報中並重新執行 |
vibe_wait |
worker_id, timeout_seconds? |
阻塞,直到它結束 |
vibe_kill |
worker_id |
停止它 |
vibe_list |
— | 層級、狀態、已用時間與最近一次結果 |
tier 可以是 fast(規格明確的機械式工作)或 good(設計、判斷,或審查一個 fast 工作程式的產出)。