大家好,我是程序員魚皮 ?。
堪稱全球最強的 AI 編程工具 Claude Code 的 50 多萬行源碼泄露了!一家以 AI 安全著稱的公司,自己卻在代碼發布環節翻了車。
![]()
針對這件事,我只想說兩個字:
感謝!
![]()
這份源碼對學技術的同學來說簡直是寶藏資源啊,過年了過年了!
那 Claude Code 源碼具體是怎么泄露的?源碼里有哪些精妙的設計?為什么它能做到 AI 編程工具的天花板?
下面我利用 AI 帶大家一起學習,學會它優秀的設計后,你可以改進自己的 AI 項目、或者面試的時候吊打一下面試官。
?? 本文對應視頻版:https://bilibili.com/video/BV1ZB9EBmEAU
完整源碼我存了一份,想要獲取完整源碼的同學可以點我頭像,私信「claude」獲取,隨時可能被下掉,速速速
到底是怎么泄露的
Claude Code 是通過 npm 發布的,你可以把 npm 理解為程序員的「應用商店」,開發者把寫好的工具打包上傳到這里,別人一行命令就能安裝。
![]()
正常情況下,代碼上線之前會經過「壓縮混淆」處理,變成一坨人類看不懂的東西。
![]()
但在開發的時候,為了方便定位 Bug,代碼打包工具會自動生成一個叫Source Map的文件,它相當于一張「翻譯對照表」,能把加密后的代碼還原成原始版本。但是這個文件只能在開發環境用,上線時必須刪掉!
結果 Anthropic 的工程師在發布 Claude Code 2.1.88 版本的時候,打包工具 Bun 默認生成了 Source Map 文件,而他們忘了在發布配置里把.map文件排除掉。于是一個 59.8 MB 的cli.js.map文件就這么被直接發到了公開的 npm 倉庫。
![]()
這個 map 文件本質上是一個 JSON,里面有兩個關鍵數組,sources存著所有文件的路徑,sourcesContent存著每個文件的完整源碼,兩者一一對應。只要寫個腳本解析一下這個 JSON,按路徑把內容寫出來,所有源碼就原樣還原了。
而且據說 map 文件里甚至還引用了一個 Cloudflare R2 存儲桶的公開地址,點進去直接就能下載打包好的完整源碼目錄,連腳本都不用寫。
![]()
安全研究員 Chaofan Shou 最先發現了這個問題并發到了 X 上:
![]()
然后幾個小時之內 GitHub 上就冒出了好幾個鏡像倉庫,源碼傳遍了整個互聯網,其中有個倉庫不到 1 天就快 10w Star 了(而且倉庫里早就沒有 Claude Code 源碼了)……
更搞笑的是,去年 2 月 Claude Code 剛發布的時候就出過一模一樣的事故,同樣的錯誤犯了兩次!
怕不是 Claude Code 團隊的開發者 Vibe Coding 上癮,連基本操作都忘了?
源碼里都有什么
這次泄露的是 Claude Code 客戶端的源碼,包括 1906 個 TypeScript 源文件、大約 51.2 萬行代碼。涵蓋了 Agent 循環引擎、40 多個內置工具的完整實現、系統提示詞的組裝邏輯、記憶系統、上下文壓縮、權限管控機制,還有一堆沒上線的隱藏功能。但是不包括服務端的模型訓練代碼和 API 后端邏輯。
![]()
Claude Code 完整架構
整個項目用的是 React Ink 框架,簡單說就是用 React 來寫命令行界面,你可以理解為「在終端里寫網頁」。所以 Claude Code 的命令行交互才會比很多傳統工具絲滑。
![]()
我用 AI 把它的架構從上到下梳理了一遍,大致分為 6 層:
1)最上面是 CLI 和界面層,你在終端里看到的所有交互都是這一層負責的
2)往下是 Agent 循環引擎,你可以把它理解為 Claude Code 的大腦,所有的決策都從這里發出
3)再往下是工具系統,40 多個內置工具加上 MCP 擴展
4)然后是記憶系統,解決 AI 聊著聊著就斷片兒的老毛病
5)上下文壓縮系統,解決 token 越來越貴、窗口越來越滿的問題
6)最底層是權限和安全系統
![]()
Claude Code 分層架構圖
接下來咱們結合源碼,看看每個模塊具體是怎么實現的,Claude Code 是怎么做到這么好用的?
一、Agent 循環
大家都知道現在的 AI 編程工具早已經不是簡單的「一問一答」了,而是能連續執行、自主完成任務的 AI Agent。
可能有同學會覺得,這背后一定是什么特別復雜的架構,用了什么 AI Agent 開發框架、什么多 Agent 協作之類的吧?
但我打開負責核心對話循環的query.ts文件一看,好家伙,最核心的代碼其實就是一個while(true)無限循環!
// query.ts
asyncfunction*queryLoop(params: QueryParams, consumedCommandUuids:string[]){
letstate: State = {
messages: params.messages,
toolUseContext: params.toolUseContext,
autoCompactTracking:undefined,
maxOutputTokensRecoveryCount:0,
hasAttemptedReactiveCompact:false,
turnCount:1,
// ...
}// eslint-disable-next-line no-constant-condition
while(true) {
// 1. 上下文壓縮(防止 token 爆炸)
// 2. 調用大模型,流式獲取響應
// 3. 解析模型返回的工具調用
// 4. 執行工具,獲取結果
// 5. 把結果追加到對話歷史
// 6. 如果沒有新的工具調用,結束;否則繼續循環
}
}
就是這么樸素的一個循環。每一輪迭代里,程序先做上下文壓縮、再調用大模型拿到響應,如果模型說「我要用某個工具」就去執行,把結果追加到對話歷史里,然后進入下一輪,一直循環工作直到活兒干完了才停下來。
這在 AI 領域叫ReAct 機制(推理 + 執行),讓 AI 自己形成「思考 → 行動 → 觀察 → 再思考」的閉環。Claude Code 的源碼就是這個理論最接地氣的工程實現了。
![]()
每次循環中,程序會通過services/api/claude.ts里的queryModel函數來跟大模型溝通。這個函數負責發送請求、接收 SSE 流式輸出、累計 token 用量這些事情。模型返回的結果里如果包含tool_use類型的內容,說明 AI 還想調用工具繼續干活,循環就接著轉;如果沒有tool_use了,說明任務做完了,循環就結束。
// services/api/claude.ts — 模型調用核心函數
asyncfunction*queryModel(
messages: Message[],
systemPrompt: SystemPrompt,
thinkingConfig: ThinkingConfig,
tools: Tools,
signal: AbortSignal,
options: Options,
):AsyncGenerator{
// 解析模型(含 Bedrock inference profile)
// 合并 Beta headers
// 構造 anthropic.beta.messages 流式請求
// 累計 token 用量
// yield 流式事件
}
在這個循環定義的上方,還有一段 Anthropic 工程師留下的注釋,叫什么「巫師守則」:
// query.ts — queryLoop 函數上方
/**
* The rules of thinking are lengthy and fortuitous...
*
* The rules follow:
* 1. A message that contains a thinking block must be part of a query
* whose max_thinking_length > 0
* 2. A thinking block may not be the last message in a block
* 3. Thinking blocks must be preserved for the duration of an assistant
* trajectory
*
* Heed these rules well, young wizard. For they are the rules of thinking,
* and the rules of thinking are the rules of the universe. If ye does not
* heed these rules, ye will be punished with an entire day of debugging
* and hair pulling.
*/
這三條規則是關于模型思考過程(thinking block)的處理約束,大意就是「年輕的巫師啊,好好遵守這三條關于 thinking block 的規則,不然你將受到整整一天 debug 和拔頭發的懲罰」。
我一時間分不清,這是程序員的浪漫,還是 AI 的浪漫?
二、工具設計
Claude Code 內置了 40 多個工具,在負責工具注冊和管理的tools.ts文件里,有個getAllBaseTools()函數列出了所有內置工具:
// tools.ts — 工具注冊文件
/**
*NOTE:This MUST stay in sync with https://console.statsig.com/...
* in order to cache the system prompt across users.
*/
exportfunctiongetAllBaseTools():Tools{
return[
AgentTool, // 子 Agent 生成
TaskOutputTool, // 任務輸出
BashTool, // 執行終端命令
...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
FileReadTool, // 讀文件
FileEditTool, // 編輯文件
FileWriteTool, // 寫文件
NotebookEditTool, // 編輯 Notebook
WebFetchTool, // 網絡請求
WebSearchTool, // 網絡搜索
TodoWriteTool, // 待辦事項
SkillTool, // 技能調用
EnterPlanModeTool,// 進入規劃模式
...(process.env.USER_TYPE ==='ant'? [ConfigTool] : []),
// ... 還有幾十個,根據環境和 Feature Flag 動態加載
...(isToolSearchEnabledOptimistic() ? [ToolSearchTool] : []),
]
}
注意開頭那段注釋,意思是這份工具清單必須和他們的 A/B 測試配置中心保持同步,否則全球用戶共享的系統提示詞緩存就廢了。看來工具注冊不只是功能問題,還直接關系到成本和性能。
還有最后一行的ToolSearchTool,當用戶接了很多 MCP 插件、工具數量特別多的時候,Claude Code 不會把所有工具的完整描述都塞進系統提示詞里,而是先給模型一份「工具名 + 一句話介紹」的精簡清單,讓模型自己選需要哪些,再按需加載完整定義,從而節省了大量 token。
代碼里還能看到process.env.USER_TYPE === 'ant'這個判斷,ant 是 Anthropic 內部員工的代號(螞蟻),這說明 Anthropic 自己也在重度用 Claude Code 來開發 Claude Code,怪不得這源碼一股 AI 味兒。
每個具體的工具都是通過Tool.ts里的buildTool工廠函數創建的,這個函數有個很巧妙的默認值設計:
// Tool.ts — 工具類型定義和工廠函數
// Defaults (fail-closed where it matters):
// - isConcurrencySafe → false (assume not safe)
// - isReadOnly → false (assume writes)
// - isDestructive → false
// - toAutoClassifierInput → '' (skip classifier — security-relevant tools must override)
constTOOL_DEFAULTS = {
isEnabled:()=>true,
isConcurrencySafe:(_input?: unknown) =>false,
isReadOnly:(_input?: unknown) =>false,
isDestructive:(_input?: unknown) =>false,
toAutoClassifierInput:()=>'',
// ...
}
注釋里寫的 「fail-closed where it matters」 是什么意思呢?
可以看到isConcurrencySafe和isReadOnly都默認為false,也就是說如果某個工具的開發者忘了聲明「我只是讀個文件,不會改東西」,系統就自動把它當成危險操作處理,不讓它并發執行。
這種思路叫做fail-closed(失敗時關閉),打個比方就像公司的門禁系統,你沒刷卡默認進不去;跟它相反的 fail-open 就像小區大門,誰來都讓進。
對于 AI 應用,你不知道它下一步會調用什么工具。在這種不確定性下,「默認禁止」比「默認允許」安全太多了。畢竟 Claude Code 直接操作你整個代碼庫,一旦出事兒可能是你半個月的代碼白寫了。
還有toAutoClassifierInput默認返回空字符串,意味著默認跳過自動分類器檢查,但注釋專門強調了安全相關的工具必須自己重寫這個方法。這也是 fail-closed 的思路。
三、讀寫分離的工具并發
假設 AI 同時想讀 3 個文件、再改 1 個文件,這 4 個操作是一個一個排隊跑、還是一起并發執行呢?
Claude Code 在負責工具執行編排的toolOrchestration.ts里做了一套很聰明的讀寫分離機制。
先看并發上限,默認最多 10 個工具同時并發,還可以通過環境變量調整:
// services/tools/toolOrchestration.ts — 工具執行編排
functiongetMaxToolUseConcurrency():number{
returnparseInt(process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY ||'',10) ||10
}
然后是核心的分批邏輯:
// services/tools/toolOrchestration.ts
functionpartitionToolCalls(toolUseMessages, toolUseContext):Batch[]{
returntoolUseMessages.reduce((acc, toolUse) =>{
consttool = findToolByName(toolUseContext.options.tools, toolUse.name)
constparsedInput = tool?.inputSchema.safeParse(toolUse.input)
constisConcurrencySafe = parsedInput?.success
?(() => {
try{
returnBoolean(tool?.isConcurrencySafe(parsedInput.data))
}catch{
returnfalse// 拋異常也當不安全,又是 fail-closed
}
})()
:false // 解析失敗也當不安全if(isConcurrencySafe && acc[acc.length - 1]?.isConcurrencySafe) {
acc[acc.length- 1].blocks.push(toolUse) // 合并到并發批次
}else{
acc.push({ isConcurrencySafe, blocks: [toolUse] }) // 新開一個批次
}
returnacc
}, [])
}
簡單說就是連續幾個只讀工具可以一起跑,但一旦遇到寫操作,就得排隊等前面的都完成了才行。而且注意那個 try-catch,如果判斷方法本身拋了異常(比如參數解析出錯),也直接當不安全處理。
![]()
并發執行完之后,產生的上下文修改contextModifier也不會立刻生效,而是先排隊存著,等一個批次的所有工具都跑完了再按順序依次應用。
在工具執行編排文件的runTools函數里能看到這個邏輯:
// services/tools/toolOrchestration.ts — runTools 函數
if(isConcurrencySafe) {
constqueuedContextModifiers = {}
forawait(constupdate of runToolsConcurrently(blocks, ...)) {
if(update.contextModifier) {
// 先排隊,不立即應用
queuedContextModifiers[toolUseID].push(modifyContext)
}
}
// 全部跑完后,按工具調用順序依次應用
for(constblock of blocks) {
for(constmodifier of queuedContextModifiers[block.id]) {
currentContext = modifier(currentContext)
}
}
}
學過數據庫的同學肯定覺得眼熟,這不就是「讀讀并行、讀寫互斥」的簡化版嘛。很多計算機底層的設計思想,換個場景照樣管用,這就是基礎知識的魅力。
四、System Prompt 的緩存分裂
Anthropic 的 API 支持 Prompt Cache 提示詞緩存,如果你每次發給模型的系統提示詞前半部分都不變,API 就能緩存這部分內容,后續請求直接復用,不用重新處理。
打開負責系統提示詞組裝的constants/prompts.ts文件,你會看到提示詞被一個叫DYNAMIC_BOUNDARY的標記分成了上下兩部分:
// constants/prompts.ts — 系統提示詞組裝
exportconstSYSTEM_PROMPT_DYNAMIC_BOUNDARY =
'__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'sections = [
// ...靜態部分:角色定義、行為規范、工具說明、語氣要求等
// === BOUNDARY MARKER - DO NOT MOVE OR REMOVE ===
...(shouldUseGlobalCacheScope() ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY] : []),
// --- Dynamic content (registry-managed) ---
...resolvedDynamicSections, // 動態部分:當前時間、git 狀態、CLAUDE.md 配置、MCP 工具等
]
Claude Code 用這條分界線精確地把提示詞切成兩半:上面是靜態部分,全球幾百萬用戶共享同一份緩存;下面是動態部分,比如你當前的時間、你的 Git 倉庫狀態、你自己配的 CLAUDE.md 規則等等,每個用戶各不相同,獨立加載。
源碼注釋里還專門警告,如果把動態內容不小心混到了靜態部分里,每個用戶的提示詞都不一樣了,緩存就全廢了。
// constants/prompts.ts
// WARNING: Do not remove or reorder this marker without updating cache logic in:
// - src/utils/api.ts (splitSysPromptPrefix)
// - src/services/api/claude.ts (buildSystemPromptBlocks)
如果你也在做 AI 應用,調用量大的話,這個優化技巧能幫你省不少錢。
五、內容檢索策略
AI 模型本身沒有你項目的代碼記憶,所以需要一種方式讓它「看到」相關的代碼和文檔。
現在業界最流行的做法叫 RAG 檢索增強生成,簡單說就是先把項目數據做 Embedding 存到向量數據庫里,用戶提問時先從數據庫里檢索出相關內容,再連同問題一起發給模型回答。
但沒想到,Claude Code 壓根不用這套!
不管是搜記憶文件還是搜歷史對話,它用的都是最樸素的 Grep 文本搜索。
在負責記憶系統的memdir/memdir.ts文件里有個buildSearchingPastContextSection函數,寫得很明確:
// memdir/memdir.ts — 記憶搜索指令
constmemSearch =`grep -rn "
"
${autoMemDir}--include="*.md"`
consttranscriptSearch =`grep -rn "
"
${projectDir}/ --include="*.jsonl"`
Claude Code 的創始人 Boris Cherny 在播客里說過:俺們試過 RAG,但發現讓 AI 自己決定搜什么、怎么搜,效果反而遠遠好于 RAG。
我的理解是這樣的,RAG 相當于你幫實習生把相關資料都整理好了打包給他看;而 Agentic Search 是直接給他公司文檔庫的權限,讓他自己去找。模型能力越強,后者的優勢就越大,因為 AI 比你更知道自己需要什么信息。而且 Grep 這種方案簡單、沒有索引過期問題、不用維護向量數據庫,工程復雜度直接降了一個量級。
所以做 AI 應用的時候,哪些能力該交給工程系統、哪些該留給 AI 模型,這個邊界值得好好想想。模型越來越強,很多以前需要復雜工程方案解決的問題,現在可能直接讓 AI 自己搞定就行了。
六、三層記憶架構
這是我覺得整份源碼里設計最精妙的部分了,也是現在 Context Engineering 上下文工程領域最值得學的案例。
用過 AI 編程工具的朋友應該都體驗過:同一個對話框里聊久了,AI 就斷片兒了,忘了之前說過什么,甚至自相矛盾。
Claude Code 為了解決這個問題,搞了一套分層記憶系統,網上有人管它叫 Self-Healing Memory 自愈記憶。
![]()
第一層、MEMORY.md(熱數據,常駐加載)
MEMORY.md 就像一本書的目錄,每次對話都會加載到上下文里。
看看memdir/memdir.ts文件里是怎么限制它的大小的:
// memdir/memdir.ts — 記憶索引文件
exportconstENTRYPOINT_NAME ='MEMORY.md'
exportconstMAX_ENTRYPOINT_LINES =200
// ~125 chars/line at 200 lines. At p97 today; catches long-line indexes that
// slip past the line cap (p100 observed: 197KB under 200 lines).
exportconstMAX_ENTRYPOINT_BYTES =25_000
最多 200 行、25KB,而且只存指針不存內容,每行不超 150 字符。因為它每次都要占上下文窗口的位置,如果太大就會把有用的對話內容擠掉。
注釋里提到,25KB 那個字節限制是后來加的。因為他們發現有用戶雖然沒超 200 行,但每行寫了一大堆,200 行居然能寫到 197KB。所以不得不加個字節數的兜底保護。
如果內容超出限制了怎么辦?
在這個文件里,還有一段詳細的截斷邏輯:
// memdir/memdir.ts — 截斷邏輯
exportfunctiontruncateEntrypointContent(raw:string):EntrypointTruncation{
// 先按行數截斷
lettruncated = wasLineTruncated
? contentLines.slice(0, MAX_ENTRYPOINT_LINES).join('\n')
: trimmed
// 再按字節數截斷,在最后一個換行符處切,避免截斷到一半
if(truncated.length > MAX_ENTRYPOINT_BYTES) {
constcutAt = truncated.lastIndexOf('\n', MAX_ENTRYPOINT_BYTES)
truncated = truncated.slice(0, cutAt >0? cutAt : MAX_ENTRYPOINT_BYTES)
}// 截斷后追加 WARNING,讓 AI 知道這個文件沒加載完
return{
content: truncated +
`\n\n> WARNING:${ENTRYPOINT_NAME}is${reason}. Only part of it was loaded.`,
}
}
采用行數截斷 + 字節截斷雙保險,截斷時還會在最后一個換行符處切開,不會切到一行的中間。截斷之后還追加了一條 WARNING,讓 AI 知道「這個索引沒加載完整」。這種細節才是 Claude Code 體驗好的原因。
第二層、話題文件(溫數據,按需加載)
你的編碼偏好、項目的架構約定、之前踩過的坑這些細節信息,都存在單獨的話題文件里,比如user_role.md、feedback_testing.md。
新對話開始時,Claude Code 會用 Sonnet 小模型來挑選最多 5 個跟當前對話相關的文件加載進來。在負責記憶召回的findRelevantMemories.ts文件中,可以看到它挑選記憶的提示詞:
// memdir/findRelevantMemories.ts — 記憶召回
constSELECT_MEMORIES_SYSTEM_PROMPT =`You are selecting memories that will be
useful to Claude Code as it processes a user's query.
Return a list of filenames for the memories that will clearly be useful (up to 5).
- If a list of recently-used tools is provided, do not select memories that are
usage reference or API documentation for those tools (Claude Code is already
exercising them). DO still select memories containing warnings, gotchas, or
known issues about those tools — active use is exactly when those matter.`
最后那條規則我覺得是 punchline:如果某個工具正在被使用,就不加載它的使用文檔(你都在用了說明你會用),但一定要加載它的已知問題和坑。想想也對,你正在用一個東西的時候,最怕的不就是不知道有什么坑嘛。
而且 Claude Code 的記憶不記代碼。因為代碼會發生變化,但記憶并不會自動更新。舉個例子,如果記憶里說「函數 X 在第 30 行」,你后來重構了,這條記憶就變成誤導了。所以它只記人的偏好和判斷,代碼的事實永遠去源碼里實時讀取。
做過后端開發的朋友都知道,緩存和數據庫不一致是最坑的 Bug 之一。Claude Code 的做法等于從根源上消滅了不一致的可能性。
第三層、歷史對話(冷數據,Grep 搜索)
更早之前的歷史對話會被存成.jsonl格式的文件,需要的時候用 Grep 搜關鍵詞就能找到。
總結一下,不同溫度的數據用不同方式管理。熱的常駐、溫的按需、冷的搜索。
如果你面試被問到 “AI 應用怎么做長期記憶”,這套方案絕對能讓面試官眼前一亮,畢竟 Claude Code 這種級別的產品都在用。
七、五級上下文壓縮
上一節講的是記憶怎么存和取,這一節講的是上下文怎么「瘦身」。大模型的上下文窗口是有上限的,隨著對話越來越長、工具調用結果越攢越多,token 用量會快速膨脹,不僅費錢,還可能直接超出窗口限制導致請求失敗。
Claude Code 為了應對這個問題,設計了五級壓縮策略,像漏斗一樣層層過濾:
Snip 剪裁:最輕的一刀,把舊的工具調用結果只保留結構,不保留內容
Microcompact 微壓縮:把體積大的工具執行結果卸載到緩存里。注意是卸載到緩存而不是直接丟掉,因為子智能體后續可能還需要這些結果
Context Collapse 折疊:對中間的對話做折疊摘要,只保留關鍵信息
Autocompact 自動壓縮:當上下文占用超過閾值時,觸發全量摘要壓縮
Reactive Compact 應急壓縮:最后的兜底,當 API 返回 413 “提示詞太長” 錯誤時緊急觸發
![]()
這五級從輕到重依次觸發,能裁掉的內容先裁掉,實在不夠了再上更重的方案。
從query.ts文件的開頭可以看到其中三級壓縮模塊通過 Feature Flag 按需引入(另外兩級在其他文件里):
// query.ts — 壓縮模塊按需加載
constreactiveCompact = feature('REACTIVE_COMPACT')
?require('./services/compact/reactiveCompact.js') :null
constcontextCollapse = feature('CONTEXT_COLLAPSE')
?require('./services/contextCollapse/index.js') :null
constsnipModule = feature('HISTORY_SNIP')
?require('./services/compact/snipCompact.js') :null
這里面還有個「斷路器」機制讓我印象深刻,在負責自動壓縮的services/compact/autoCompact.ts文件里:
// services/compact/autoCompact.ts — 斷路器
// Stop trying autocompact after this many consecutive failures.
// BQ 2026-03-10: 1,279 sessions had 50+ consecutive failures
// (up to 3,272) in a single session, wasting ~250K API calls/day globally.
constMAX_CONSECUTIVE_AUTOCOMPACT_FAILURES =3
看到注釋中的那串數字了嗎?
2026 年 3 月 10 號他們統計發現有 1279 個會話連續壓縮失敗了 50 次以上,最夸張的一個會話連續失敗了 3272 次還在不停重試,全球每天浪費 25 萬次 API 調用!
要是讓我這種老倒霉蛋遇上了,光 token 費就夠我吃一個月的了,這要是曝光出來公關團隊怕是有的忙了……
所以他們加了這個斷路器,連續失敗 3 次就自動停下來。
八、安全審查
用 Claude Code 的時候你可以開啟--dangerously-skip-permissions模式(也叫 YOLO 模式),跳過所有權限確認,讓 AI 自己干活。
我之前一直以為這模式就是完全不設防了,看了源碼才知道,其實背后還偷偷跑著一個「影子 AI」在幫你把關。
源碼里有個文件叫utils/permissions/yoloClassifier.ts,每次主 AI 要執行操作,這個獨立的 AI 分類器都會過一遍:
// utils/permissions/yoloClassifier.ts — YOLO 安全分類器
exportasyncfunctionclassifyYoloAction(
toolName:string,
toolInput: Record,
// ...
):Promise<'allow' | 'soft_deny' | 'hard_deny'>{
// 用一個獨立的模型來判斷這個操作安不安全
// allow: 安全的,直接放行
// soft_deny: 有風險,降級成需要手動確認
// hard_deny: 直接攔截,沒得商量
}
而且權限系統不只有 YOLO Classifier 一個檢查點。一次工具調用要過的關卡至少包括:
你當前的運行模式(Plan / Auto / Bypass)
用戶在 hooks 里設的自定義規則
YOLO Classifier 的模型分析
Bash 命令危險度分類(像 rm -rf 這種直接攔截)
配置文件里的規則引擎
多個來源的權限結果還有競爭關系,最終取最嚴格的那個。
![]()
其中第 4 點 Bash 命令安全檢查的嚴格程度遠超我的預期。在tools/BashTool/bashSecurity.ts這個文件里,定義了23 種安全檢查規則:
// tools/BashTool/bashSecurity.ts — Bash 安全檢查 ID
constBASH_SECURITY_CHECK_IDS = {
INCOMPLETE_COMMANDS:1, // 殘缺命令(以 tab 或 - 開頭)
JQ_SYSTEM_FUNCTION:2, // jq 的 system() 函數調用
OBFUSCATED_FLAGS:4, // 混淆的命令行參數
SHELL_METACHARACTERS:5, // 危險的 shell 元字符
DANGEROUS_VARIABLES:6, // 危險環境變量注入
IFS_INJECTION:11, // IFS 變量注入
PROC_ENVIRON_ACCESS:13, // /proc/environ 訪問
CONTROL_CHARACTERS:17, // 控制字符
UNICODE_WHITESPACE:18, // Unicode 空白字符欺騙
ZSH_DANGEROUS_COMMANDS:20, // Zsh 危險命令(zmodload 等)
COMMENT_QUOTE_DESYNC:22, // 注釋與引號狀態不同步
QUOTED_NEWLINE:23, // 引號內換行
// ... 共 23 種
}
比如第 18 條的UNICODE_WHITESPACE,說明 Anthropic 的安全團隊考慮過用 Unicode 零寬空格來混淆命令的攻擊手法,讓安全檢查器看到的和 shell 實際執行的不是同一條命令。還有第 20 條針對 Zsh 的zmodload命令,這個命令能加載模塊繞過常規的文件和網絡檢查,總之防護做的還是很周到的。
怪不得我開著全部放行模式用了這么久,從來沒出過什么嚴重事故,原來是有一套多層機制在背后替我負重前行。
九、Feature Flag 和未來功能
在 Claude Code 的源碼中,你會經常看到feature('XXX')的判斷:
// tools.ts — Feature Flag 條件加載
constSleepTool = feature('PROACTIVE') || feature('KAIROS')
?require('./tools/SleepTool/SleepTool.js').SleepTool :nullconstWebBrowserTool = feature('WEB_BROWSER_TOOL')
?require('./tools/WebBrowserTool/WebBrowserTool.js').WebBrowserTool :null
這就是 Feature Flag 功能開關,做開發的同學應該都不陌生。每個實驗功能都藏在開關后面,可以按用戶、按環境灰度發布。這樣萬一出了問題,就不用回滾代碼,直接關掉開關就行。通過條件require,關掉的功能在打包時還能被 tree-shaking 掉,不增加體積。
有趣的是,這些 Feature Flag 等于無意中泄露了 Claude Code 的產品路線圖:
1)KAIROS:長期助手模式,AI 可以 24 小時持續運行不結束。里面還有個 AutoDream 自動做夢功能,名字起得很是浪漫,說白了就是 AI 白天干活記筆記,晚上自動整理記憶。
2)COORDINATOR_MODE:多 Agent 協作,一個 AI 指揮多個 AI 干活。
從coordinatorMode.ts能看到,它的工作流分四個階段:
// coordinator/coordinatorMode.ts — 協調者工作流定義
| Phase | Who | Purpose |
|----------------|--------------------|---------------------------------------------------------|
| Research | Workers (parallel) | Investigate codebase, find files, understand problem |
| Synthesis | You (coordinator) | Read findings, craft implementation specs |
| Implementation | Workers | Make targeted changes per spec, commit |
| Verification | Workers | Test changes work |
子智能體之間通過utils/mailbox.ts里的 Mailbox(一個基于文件的消息隊列)來通信。
還有 VOICE_MODE 語音模式、WEB_BROWSER_TOOL 瀏覽器操作工具等等。
十、反蒸餾和臥底模式
Anthropic 為了防止競爭對手竊取 Claude Code 的能力、以及防止內部信息通過開源貢獻泄露,在源碼里埋了兩套防御機制。(但沒想到這波泄露了個大的 )
先說反蒸餾。有些競爭對手可能會通過錄制 Claude Code 的 API 流量來「蒸餾」它的能力,就是把大模型的輸入輸出錄下來,用來訓練自己的小模型。
Anthropic 的應對策略簡直絕了,直接往 API 請求里注入假工具定義:
// services/api/claude.ts — 負責模型 API 調用
// Anti-distillation: send fake_tools opt-in for 1P CLI only
if(feature('ANTI_DISTILLATION_CC')
&& process.env.CLAUDE_CODE_ENTRYPOINT ==='cli'
&& shouldIncludeFirstPartyOnlyBetas()
) {
result.anti_distillation = ['fake_tools']
}
做防御不是單純加密或者限速,而是主動給你喂假數據,讓你拿去訓練的模型越訓越差,有點兒反間計的意思。
而且它只在 Anthropic 官方的命令行客戶端,也就是你在終端里直接用claude命令的場景中才生效,通過 SDK 或其他方式接入的不受影響。
再說臥底模式,Anthropic 內部員工用 Claude Code 往開源項目提交代碼時,會自動啟用這個模式:
// utils/undercover.ts — 負責隱藏 AI 貢獻痕跡
/**
* Undercover mode — safety utilities for contributing to public repos.
* When active, Claude Code strips all attribution to avoid leaking internal
* model codenames, project names, or other Anthropic-internal information.
* The model is not told what model it is.
*
* There is NO force-OFF. This guards against model codename leaks.
*/exportfunctionisUndercover():boolean{
if(process.env.USER_TYPE ==='ant') {
if(isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER))returntrue
// Auto: active unless we've confirmed we're in an internal repo.
returngetRepoClassCached() !=='internal'
}
returnfalse
}
注釋里寫得很明確:「There is NO force-OFF」,不能強制關閉。而且從代碼邏輯看,默認就是開啟的,只有當倉庫被確認為內部倉庫時才會關掉。說明 Anthropic 非常擔心內部模型代號通過開源貢獻泄露出去。
十一、其他細節
最后分享幾個源碼里藏著的有趣細節。
1)在buddy/目錄下,Anthropic 的工程師居然偷偷藏了一套數字寵物系統!
一共 18 種物種,從鴨子到仙人掌應有盡有:
// buddy/types.ts — 虛擬寵物物種定義
exportconstSPECIES = [
duck, goose, blob, cat, dragon, octopus, owl, penguin,
turtle, snail, ghost, axolotl, capybara, cactus, robot,
rabbit, mushroom, chonk,
]asconst
這個功能原本計劃 4 月份作為彩蛋預熱、5 月正式上線,結果源碼泄露了提前被大家發現了。AI 寫代碼寫累了,然后擼擼 AI 寵物,Anthropic 的工程師也太會整活了。
2)還有個小細節,因為 Claude 之前老是浪費一輪對話去執行mkdir檢查目錄存不存在,工程師們直接在memdir/memdir.ts里硬編碼了一行提示,告訴 AI “這個目錄已經存在了,直接用 Write 工具往里寫就行,不要再跑 mkdir 或者檢查目錄是否存在了!”
// memdir/memdir.ts
// Shipped because Claude was burning turns on `ls`/`mkdir -p` before writing.
exportconstDIR_EXISTS_GUIDANCE =
'This directory already exists — write to it directly with the Write tool
(do not run mkdir or check for its existence).'
3)Claude Code 對啟動速度有一種近乎偏執的追求。
比如在入口文件cli.tsx里,--version這種最簡單的請求一個模塊都不加載,直接返回;其他路徑全部用await import()動態加載,按需引入。
更絕的是,在加載主模塊的這幾百毫秒里,它會同時啟動一個「早期輸入捕獲器」(utils/earlyInput.ts),把你在等待時敲的鍵都緩存起來,模塊加載完之后再回放,讓你感覺 “剛敲完就有反應了”。
初始化階段還會偷偷發一個 TCP 預連接(utils/apiPreconnect.ts),讓 TCP+TLS 握手和初始化工作并行跑,省掉 100ms 左右的等待時間。
// utils/apiPreconnect.ts — TCP 預連接最后聊幾句
/**
* Preconnect to the Anthropic API to overlap TCP+TLS handshake with startup.
* The TCP+TLS handshake is ~100-200ms that normally blocks inside the first
* API call. Kicking a fire-and-forget fetch during init lets the handshake
* happen in parallel with action-handler work.
*/
看完這份源碼,你會發現 Claude Code 里其實沒什么驚天動地的新算法,用的基本上都是程序員接觸過的基礎知識,比如并發控制、讀寫分離、分層緩存、斷路器、功能開關等等……
但是 Claude Code 團隊把這些東西組合到了 AI 場景里,打造出了一個極其優秀的產品,所以說計算機的基礎知識和設計思想很重要。
這份源碼也是目前最好的 AI 應用架構教材了。像 Agent 循環怎么寫、工具系統怎么設計、記憶怎么管理、安全怎么做,答案全在里面。簡歷上如果能寫上 “深度閱讀了 Claude Code 源碼并應用于自己的項目”,那絕對是加分項。
至于說這次泄露對 Claude Code 有多大影響嘛…… 我覺得其他做 AI 編程工具的競爭對手們肯定開心壞了,大家也學到知識(吃到瓜)了,我也有流量了,用 Claude Code 的人更多了,可以說是皆大歡喜。
而且就算有人家的源碼,也很難跟人家這種有資源的專業團隊去競爭呀,你就像我開源出來一個 yupi-code 估計也沒人用吧?
話說我最近正好在自己的 魚皮 AI 導航網站 上更新 Claude Code 相關的教程,萬萬沒想到竟然從源碼開始講起了。如果你想更系統地學習 AI 編程工具的使用方法、項目實戰、經驗技巧和原理,可以關注一波,后續持續更新。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.