Claude Code Harness 源码学习讲义

1. 学习目标
本讲义以 claude-code-source-code 目录为核心,目标不是学习 TypeScript 语法本身,而是理解 Claude Code 这类 agent harness 的工程结构、运行思想和可复用范式。
重点问题包括:
- 用户输入如何进入 agent 主循环。
- 模型输出如何转化为工具调用。
- 工具调用如何经过 schema 校验、权限检查、hooks 和执行管线。
- 上下文如何由 system prompt、user context、attachments、tool results 和历史消息共同组成。
- 长会话如何通过 token 预算、compact boundary 和 auto compact 保持可运行。
- REPL、slash commands、MCP、远程配置和 telemetry 如何共同组成成熟的 agent runtime。
2. 总体架构
Claude Code 不是一个简单的"CLI 调模型"程序,而是一个完整的交互式 agent runtime。核心路径可以概括为:
text
CLI 启动
-> main.tsx 初始化运行时
-> replLauncher.tsx 挂载 React/Ink REPL
-> REPL.tsx 接收用户输入
-> processUserInput 解析输入、命令和附件
-> query.ts 进入 agent loop
-> services/api/claude.ts 流式调用 Claude API
-> toolOrchestration.ts 执行工具
-> 工具结果转回消息
-> query.ts 继续下一轮推理
对应的核心文件:
src/entrypoints/cli.tsx:进程入口,处理快速路径和动态加载。src/main.tsx:CLI 主编排层,负责初始化配置、命令、运行模式和 REPL。src/replLauncher.tsx:加载App与REPL,交给 Ink 渲染。src/screens/REPL.tsx:交互式终端界面,负责输入、消息、权限 UI 和 agent turn。src/query.ts:主 agent loop,负责模型流、工具调用、上下文压缩和回合状态。
3. TypeScript 阅读基础
阅读该项目时,TypeScript 可以按 C++/Python 思维理解。
3.1 type 类似结构体或接口定义
ts
type State = {
messages: Message[]
turnCount: number
}
可以类比为 Python 的 dataclass:
python
@dataclass
class State:
messages: list[Message]
turn_count: int
也可以类比为 C++ 的结构体:
cpp
struct State {
std::vector<Message> messages;
int turnCount;
};
3.2 async function* 是异步生成器
query.ts 中的核心入口是:
ts
export async function* query(params: QueryParams): AsyncGenerator<..., Terminal> {
const terminal = yield* queryLoop(params, consumedCommandUuids)
return terminal
}
这类函数不是一次性返回结果,而是可以不断 yield 事件。Python 类比:
python
async def query(params):
async for event in query_loop(params):
yield event
这种结构非常适合 agent,因为 agent 运行过程中会不断产生流式文本、工具进度、工具结果、错误消息和最终状态。
3.3 Promise<T> 类似未来值
Promise<T> 可以类比为 Python 的 coroutine / awaitable,也可以类比为 C++ 中的 std::future<T>。
ts
async function loadConfig(): Promise<Config> {
return config
}
3.4 泛型用于表达工具输入输出
工具类型中常见:
ts
export type Tool<Input, Output, Progress> = {
call(args: Input): Promise<ToolResult<Output>>
}
这类似 C++ 模板或 Python typing 泛型,用于保证每个工具的输入、输出和进度类型一致。
4. 第一课:从入口到 Agent Loop
4.1 启动入口
src/entrypoints/cli.tsx 是最外层入口。它先处理 --version、远程模式、daemon 等快速路径,最后才动态加载完整 CLI:
ts
const { main: cliMain } = await import('../main.js')
await cliMain()
这种设计的目的是减少启动成本。简单命令不需要加载整个 REPL、工具系统和 React/Ink UI。
4.2 主编排层
src/main.tsx 的 main() 是运行时编排中心。它负责:
- Commander CLI 参数解析。
- 配置、策略、远程设置初始化。
- 首次启动、trust dialog、onboarding。
- 本地模式、远程模式、resume、direct connect 等路径分流。
- 最终调用
launchRepl()进入交互式运行。
main.tsx 的角色类似大型 C++/Python 程序中的 bootstrap layer。它不应该承载核心 agent 逻辑,而是负责把各子系统接起来。
4.3 REPL 挂载
src/replLauncher.tsx 的职责很薄:
ts
await renderAndRun(root, (
<App {...appProps}>
<REPL {...replProps} />
</App>
))
这里体现了一个成熟 harness 的分层:
App提供全局状态、主题、统计信息等上下文。REPL负责真正的人机交互。renderAndRun控制 Ink 根节点生命周期。
4.4 REPL 触发 Agent Turn
src/screens/REPL.tsx 中,用户提交输入后会进入 onQueryImpl,核心调用是:
ts
for await (const event of query({
messages: messagesIncludingNewMessages,
systemPrompt,
userContext,
systemContext,
canUseTool,
toolUseContext,
querySource: getQuerySourceForREPL(),
})) {
onQueryEvent(event)
}
这段代码说明:
- REPL 不直接调用 Claude API。
- REPL 把当前消息、系统提示、上下文、工具权限函数和工具上下文交给
query()。 query()不断 yield 事件。- REPL 根据事件更新 UI 和消息列表。
这种结构可以称为"UI 与 agent loop 之间的事件流边界"。
5. 第二课:query.ts 的 Agent Loop
5.1 query() 与 queryLoop()
query() 是对 queryLoop() 的包装。真正的状态机在 queryLoop() 中。
ts
export async function* query(params: QueryParams) {
const consumedCommandUuids: string[] = []
const terminal = yield* queryLoop(params, consumedCommandUuids)
return terminal
}
yield* 表示把另一个生成器产生的事件原样转发出去。Python 类比:
python
async for item in query_loop(params):
yield item
5.2 回合状态 State
queryLoop() 内部维护跨迭代状态:
ts
let state: State = {
messages: params.messages,
toolUseContext: params.toolUseContext,
maxOutputTokensOverride: params.maxOutputTokensOverride,
autoCompactTracking: undefined,
stopHookActive: undefined,
maxOutputTokensRecoveryCount: 0,
hasAttemptedReactiveCompact: false,
turnCount: 1,
pendingToolUseSummary: undefined,
transition: undefined,
}
这说明 agent turn 不是单次 API 请求,而是一个持续推进的状态机。一次用户输入可能触发多次模型调用和多批工具调用。
5.3 模型流式调用
模型调用发生在:
ts
for await (const message of deps.callModel({
messages: prependUserContext(messagesForQuery, userContext),
systemPrompt: fullSystemPrompt,
thinkingConfig: toolUseContext.options.thinkingConfig,
tools: toolUseContext.options.tools,
signal: toolUseContext.abortController.signal,
options: {
model: currentModel,
mcpTools: appState.mcp.tools,
queryTracking,
},
})) {
// consume streaming messages
}
关键点:
- 发送给模型的不只是用户输入,而是规范化后的 messages。
prependUserContext()会把运行时用户上下文插入消息头部。tools会以 schema 形式暴露给模型。signal支持中断。- MCP 工具、agent 定义、模型、预算等运行时信息通过
options传入。
5.4 工具调用闭环
当模型返回 tool use blocks 后,query.ts 调用:
ts
const toolUpdates = streamingToolExecutor
? streamingToolExecutor.getRemainingResults()
: runTools(toolUseBlocks, assistantMessages, canUseTool, toolUseContext)
工具执行结果会被转成消息:
ts
toolResults.push(
...normalizeMessagesForAPI(
[update.message],
toolUseContext.options.tools,
).filter(_ => _.type === 'user'),
)
这就是 agent 的核心闭环:
text
assistant 产生 tool_use
-> harness 执行工具
-> 工具结果变成 tool_result/user message
-> 下一轮模型读取工具结果
-> 模型继续推理
6. 第三课:工具系统与权限管线
6.1 Tool 是能力对象
src/Tool.ts 中的 Tool 类型定义了工具必须具备的能力:
ts
export type Tool<Input, Output, P> = {
call(args, context, canUseTool, parentMessage, onProgress): Promise<ToolResult<Output>>
description(input, options): Promise<string>
readonly inputSchema: Input
isConcurrencySafe(input): boolean
isEnabled(): boolean
isReadOnly(input): boolean
isDestructive?(input): boolean
}
一个工具不是单纯函数,而是包含:
- 名称与描述。
- 输入 schema。
- 输出 schema。
- 权限检查。
- 是否只读。
- 是否可并发。
- 是否破坏性。
- 执行进度回调。
- UI/transcript 展示信息。
Python 中可以抽象为:
python
@dataclass
class Tool:
name: str
input_schema: dict
call: Callable
is_read_only: Callable = lambda args: False
is_concurrency_safe: Callable = lambda args: False
is_destructive: Callable = lambda args: False
6.2 默认值 fail-closed
buildTool() 会给工具补默认行为:
ts
const TOOL_DEFAULTS = {
isEnabled: () => true,
isConcurrencySafe: () => false,
isReadOnly: () => false,
isDestructive: () => false,
checkPermissions: input => Promise.resolve({ behavior: 'allow', updatedInput: input }),
}
其中 isConcurrencySafe 和 isReadOnly 默认都是 false,这是保守设计。工具如果不明确声明自己安全,就按不安全处理。
6.3 工具注册
src/tools.ts 中 getAllBaseTools() 返回内置工具列表,包括:
AgentToolBashToolGlobToolGrepToolFileReadToolFileEditToolFileWriteToolNotebookEditToolWebFetchToolTodoWriteToolWebSearchToolSkillToolAskUserQuestionToolListMcpResourcesToolReadMcpResourceTool
工具注册点集中化有利于审计、过滤和测试。
6.4 工具池装配
assembleToolPool() 会把内置工具和 MCP 工具合并:
ts
export function assembleToolPool(
permissionContext: ToolPermissionContext,
mcpTools: Tools,
): Tools {
const builtInTools = getTools(permissionContext)
const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)
return uniqBy(
[...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
'name',
)
}
关键设计:
- 先按权限过滤工具。
- 内置工具和 MCP 工具统一成
Tool。 - 排序保持 prompt cache 稳定。
- 同名冲突时内置工具优先。
6.5 工具执行管线
工具执行不是直接 tool.call(),而是通过:
text
runTools
-> runToolUse
-> inputSchema.safeParse
-> runPreToolUseHooks
-> permission decision
-> tool.call
-> runPostToolUseHooks
-> normalize result
这种 middleware 风格是 harness engineering 的核心。
6.6 并发策略
runTools() 会把工具调用分批:
- 连续的并发安全工具可以并行执行。
- 非并发安全工具单独串行执行。
ts
if (isConcurrencySafe) {
runToolsConcurrently(...)
} else {
runToolsSerially(...)
}
工程意义:
- 文件读、搜索类工具可以并发。
- 文件写、shell、编辑类工具默认串行。
- 并发策略由工具声明,不由 executor 硬编码。
7. 第四课:上下文工程
7.1 System Prompt 分层
src/constants/prompts.ts 中,system prompt 被分成静态段和动态段:
ts
return [
getSimpleIntroSection(outputStyleConfig),
getSimpleSystemSection(),
getActionsSection(),
getUsingYourToolsSection(enabledTools),
getSimpleToneAndStyleSection(),
getOutputEfficiencySection(),
...(shouldUseGlobalCacheScope() ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY] : []),
...resolvedDynamicSections,
].filter(s => s !== null)
静态段适合 prompt cache。动态段包含:
- session guidance
- memory
- env info
- language
- output style
- MCP instructions
- scratchpad
- token budget guidance
7.2 User Context 不是 System Prompt
prependUserContext() 会把运行时上下文包装成 meta user message:
ts
createUserMessage({
content: `<system-reminder>
As you answer the user's questions, you can use the following context:
...
IMPORTANT: this context may or may not be relevant...
</system-reminder>`,
isMeta: true,
})
这体现出上下文分层:
- 长期稳定规则放 system prompt。
- 当前运行时环境放 meta user message。
- 文件、目录、技能、记忆等放 attachments。
- 工具结果放 tool result messages。
7.3 Attachments 是上下文管道
src/utils/attachments.ts 中定义了大量 attachment 类型:
nested_memorydynamic_skillskill_listingskill_discoveryinvoked_skillscurrent_session_memorymcp_instructions_deltachanged_filesplan_modetodotask
附件不是 UI 附属物,而是向模型注入上下文的一种结构化通道。
7.4 消息规范化
normalizeMessagesForAPI() 会在发给模型前处理消息:
- 过滤 progress message。
- 过滤 UI-only / virtual message。
- 合并连续 user messages。
- 将部分 system local command 转成 user message。
- 处理过大的图片、PDF、工具结果。
- 保证 API 看到的消息序列合法。
这说明内部消息结构和 API 消息结构不是同一个东西。成熟 harness 需要一个规范化边界。
7.5 Auto Compact
autoCompact.ts 中先计算有效上下文窗口:
ts
effectiveContextWindow = contextWindow - reservedTokensForSummary
然后设置阈值:
ts
autocompactThreshold = effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS
默认 buffer:
- auto compact buffer:
13_000tokens - warning buffer:
20_000tokens - manual compact buffer:
3_000tokens
该机制说明:agent 不能等上下文爆掉才处理,而要提前保留摘要空间和恢复空间。
8. 第五课:运行时治理
8.1 REPL 是 Runtime
REPL 负责:
- 输入框和消息列表。
- loading 状态。
- abort controller。
- 工具进度。
- permission prompt。
- MCP 状态。
- remote session。
- bridge client。
- turn complete hook。
因此 REPL 不是界面皮肤,而是 agent runtime 的前台控制面。
8.2 Slash Commands 与 Skills
src/commands.ts 中,prompt 型 commands 可以作为 skills 暴露给模型:
ts
export const getSkillToolCommands = memoize(
async (cwd: string): Promise<Command[]> => {
const allCommands = await getCommands(cwd)
return allCommands.filter(cmd =>
cmd.type === 'prompt' &&
!cmd.disableModelInvocation &&
cmd.source !== 'builtin'
)
},
)
这意味着 slash command 不只是用户命令,也可以成为模型可调用能力。
8.3 远程安全边界
远程模式只允许安全命令:
ts
export function filterCommandsForRemoteMode(commands: Command[]): Command[] {
return commands.filter(cmd => REMOTE_SAFE_COMMANDS.has(cmd))
}
bridge 模式中:
local-jsx命令默认禁止,因为会弹本地 Ink UI。prompt命令默认允许,因为只是展开成文本。local命令必须显式加入 allowlist。
这是多客户端 agent 产品必须具备的安全边界。
8.4 MCP 工具适配
MCP 工具通过远端 tools/list 获取,然后克隆成本地 Tool:
ts
return {
...MCPTool,
name: skipPrefix ? tool.name : fullyQualifiedName,
mcpInfo: { serverName: client.name, toolName: tool.name },
isMcp: true,
inputJSONSchema: tool.inputSchema,
}
设计意义:
- 外部工具和内置工具进入同一套
Tool协议。 - MCP 权限仍进入统一 permission pipeline。
- MCP 工具可以声明
readOnlyHint、destructiveHint、openWorldHint。 - agent loop 不需要知道工具来自内置还是 MCP。
8.5 Trust Boundary
启动阶段先应用安全环境变量:
ts
applySafeConfigEnvironmentVariables()
交互式 trust dialog 通过后才应用完整环境变量:
ts
applyConfigEnvironmentVariables()
setImmediate(() => initializeTelemetryAfterTrust())
这是一种重要治理范式:不可信工作区不能过早影响进程环境、遥测、MCP 审批和外部 includes。
9. Harness Engineering 方法论
Claude Code 的设计可以总结为以下范式。
9.1 Agent Loop 是状态机
不要把 agent 写成:
python
response = model(prompt)
print(response)
成熟 agent 应写成:
python
while True:
response = model(messages, tools)
if response.has_tool_calls:
results = run_tools(response.tool_calls)
messages.extend(results)
continue
return response
9.2 Tool 是声明式协议
工具必须声明:
- name
- description
- input schema
- call
- permission policy
- concurrency policy
- read/write/destructive 属性
9.3 权限应在执行管线中集中处理
不推荐:
python
def edit_file(path, text):
if not allowed(path):
raise PermissionError
...
推荐:
python
def run_tool(tool, input, context):
validate_schema(tool, input)
run_pre_hooks(tool, input)
check_permission(tool, input, context)
result = tool.call(input, context)
run_post_hooks(tool, result)
return normalize_result(result)
9.4 上下文需要结构化
上下文应分为:
- system prompt
- runtime context
- attachments
- tool results
- memory
- compact summaries
- transcript history
不要把所有内容拼成一个大字符串。
9.5 Runtime 与 UI 不可完全分离
交互式 agent 的 UI 需要参与:
- 权限询问。
- 工具进度。
- 中断。
- 远程会话同步。
- 状态恢复。
- 配置变更。
因此 REPL 是 runtime 的一部分。
9.6 外部能力统一适配
MCP、skills、plugins、subagents、slash commands 最终都应适配到统一协议。agent loop 只面对统一的工具池,而不关心能力来源。
10. 建议阅读顺序
第一轮应按执行链阅读:
src/entrypoints/cli.tsxsrc/main.tsxsrc/replLauncher.tsxsrc/screens/REPL.tsxsrc/query.ts
第二轮阅读工具系统:
src/Tool.tssrc/tools.tssrc/services/tools/toolOrchestration.tssrc/services/tools/toolExecution.tssrc/hooks/useCanUseTool.tsx
第三轮阅读上下文工程:
src/constants/prompts.tssrc/utils/api.tssrc/utils/messages.tssrc/utils/attachments.tssrc/services/compact/autoCompact.tssrc/services/compact/compact.ts
第四轮阅读运行时治理:
src/commands.tssrc/interactiveHelpers.tsxsrc/bootstrap/state.tssrc/services/mcp/client.tssrc/services/remoteManagedSettings/index.ts
11. 最小 Python Harness 草图
以下草图用于表达 Claude Code harness 的核心思想,不追求完整实现。
python
@dataclass
class Tool:
name: str
description: str
input_schema: dict
call: Callable
is_read_only: Callable = lambda args: False
is_concurrency_safe: Callable = lambda args: False
class AgentHarness:
def __init__(self, model, tools, permission_engine, context_builder):
self.model = model
self.tools = tools
self.permission_engine = permission_engine
self.context_builder = context_builder
self.messages = []
async def run_turn(self, user_input: str):
self.messages.append({"role": "user", "content": user_input})
while True:
payload = self.context_builder.build(self.messages, self.tools)
response = await self.model.stream(payload)
async for event in response:
yield event
if not response.tool_calls:
return
tool_results = await self.run_tools(response.tool_calls)
self.messages.extend(tool_results)
async def run_tools(self, tool_calls):
results = []
for call in tool_calls:
tool = self.find_tool(call.name)
validate(tool.input_schema, call.args)
await self.permission_engine.check(tool, call.args)
result = await tool.call(call.args)
results.append(self.normalize_tool_result(call, result))
return results
该草图对应 Claude Code 中的核心模块:
AgentHarness.run_turn对应query.ts。run_tools对应toolOrchestration.ts与toolExecution.ts。Tool对应Tool.ts。context_builder对应prompts.ts、api.ts、messages.ts、attachments.ts。permission_engine对应useCanUseTool.tsx与 permissions 子系统。
12. Claude Code 的 Harness 建设重点分析
Claude Code 的核心价值不只是接入 Claude 模型,也不只是提供一组读文件、改文件、执行命令的工具。它真正重要的工程成果,是围绕大模型构建了一套完整的 agent harness。
在这里,harness 可以理解为"大模型的运行时外骨骼"。模型负责推理、计划、选择下一步动作;harness 负责控制动作如何发生、能否发生、何时发生、结果如何回到模型、上下文如何维护、风险如何拦截、用户如何介入、长任务如何持续。
一个成熟 agent 系统的本质不是:
text
LLM + prompt + tools
而是:
text
LLM
+ event loop
+ tool protocol
+ permission system
+ context pipeline
+ session state
+ runtime UI
+ extension protocol
+ observability
+ policy and governance
Claude Code 的 harness 建设可以从九个层次理解。
13. 第一层:Agent Loop Harness
13.1 Agent Loop 的地位
src/query.ts 是 Claude Code 的 agent loop 核心。它不是普通 API wrapper,而是一个任务状态机。
普通聊天程序通常是:
python
messages.append(user_input)
response = model(messages)
messages.append(response)
return response
Claude Code 的 agent loop 更接近:
python
messages.append(user_input)
while True:
response = model(messages, tools, system_prompt, runtime_context)
if response.contains_tool_use:
tool_results = run_tools(response.tool_uses)
messages.extend(tool_results)
continue
if should_compact(messages):
messages = compact(messages)
continue
if should_continue_for_budget(response):
messages.append(continuation_instruction)
continue
return response
这说明 agent loop 的目标不是"回答一次",而是持续推进任务直到达到终止条件。
13.2 query() 的事件流设计
query() 是 async function*,也就是异步生成器。它的调用者不会等待整个任务结束后才得到结果,而是边运行边收到事件。
事件类型包括:
- 模型开始请求。
- 流式文本。
- thinking block。
- tool use。
- tool result。
- progress message。
- tombstone message。
- compact boundary。
- API error message。
- tool summary。
这种设计使 REPL 可以实时更新 UI,也使工具执行、流式输出和状态变化可以统一进入同一条事件流。
从 harness 角度看,这是一种"事件驱动 agent runtime"。
13.3 State 对象
queryLoop() 内部维护 State:
ts
let state: State = {
messages: params.messages,
toolUseContext: params.toolUseContext,
maxOutputTokensOverride: params.maxOutputTokensOverride,
autoCompactTracking: undefined,
stopHookActive: undefined,
maxOutputTokensRecoveryCount: 0,
hasAttemptedReactiveCompact: false,
turnCount: 1,
pendingToolUseSummary: undefined,
transition: undefined,
}
该状态对象体现了 agent loop 的复杂性:
messages:当前可见会话历史。toolUseContext:工具运行时上下文。autoCompactTracking:自动压缩状态。stopHookActive:停止 hook 状态。maxOutputTokensRecoveryCount:输出 token 恢复次数。hasAttemptedReactiveCompact:是否尝试过反应式压缩。turnCount:当前 agent turn 次数。pendingToolUseSummary:工具调用摘要。transition:上一轮继续的原因。
普通 chat app 不需要这些状态。只有具备长任务、工具调用、压缩、权限、恢复、继续执行能力的 agent harness 才需要。
13.4 Agent Loop 的终止条件
成熟 agent loop 必须明确知道什么时候停止。Claude Code 的停止条件包括:
- 模型没有再请求工具。
- 达到最大 turn 数。
- 用户中断。
- 上下文达到 blocking limit。
- stop hook 阻止继续。
- 工具调用失败且无法恢复。
- API 报错且 fallback 或 compact 失败。
- token budget 满足。
这说明 agent harness 不能只考虑"如何继续",还要精确定义"何时停止"。
14. 第二层:Tool Protocol Harness
14.1 Tool 不是函数
Claude Code 中的 Tool 是完整能力对象,而不是普通函数指针。
一个工具包含:
namedescriptionpromptinputSchemainputJSONSchemaoutputSchemacallcheckPermissionsisEnabledisReadOnlyisConcurrencySafeisDestructiveisOpenWorldrequiresUserInteractioninterruptBehaviorisSearchOrReadCommandmaxResultSizeCharsrenderResultForAssistantrenderResultForUsermapToolResultToToolResultBlockParam
这套协议表明:工具是 agent runtime 的一等公民。工具不仅负责执行,还负责描述自身能力、风险、并发属性、权限需求和展示方式。
14.2 Tool 协议解决的问题
工具协议主要解决以下问题。
14.2.1 模型如何知道工具能做什么
工具需要把自身描述转换成模型可理解的 schema 和 prompt。模型不是直接调用代码,而是根据工具名称、描述和 JSON schema 产生结构化 tool use。
14.2.2 Harness 如何校验模型输入
模型生成的工具参数不一定合法。因此每个工具必须有 inputSchema。执行前通过 schema 解析,失败则返回结构化错误,而不是让工具函数崩溃。
14.2.3 Harness 如何判断风险
工具必须声明:
- 是否只读。
- 是否破坏性。
- 是否开放世界访问。
- 是否可以并发。
- 是否需要用户交互。
这些属性会影响权限、调度和 UI。
14.2.4 Harness 如何统一外部工具
MCP 工具、内置工具、技能工具最终都转成同一个 Tool 类型。agent loop 不需要知道工具来源。
14.3 buildTool() 的默认值策略
buildTool() 给工具补默认行为:
ts
const TOOL_DEFAULTS = {
isEnabled: () => true,
isConcurrencySafe: () => false,
isReadOnly: () => false,
isDestructive: () => false,
checkPermissions: input => Promise.resolve({ behavior: 'allow', updatedInput: input }),
toAutoClassifierInput: () => '',
userFacingName: () => '',
}
这里体现了两个工程原则:
- 默认保守。
- 默认行为集中管理。
isConcurrencySafe 和 isReadOnly 默认都是 false,表示工具如果没有明确声明安全,就按不安全处理。这是 agent harness 的重要安全原则。
15. 第三层:Tool Execution Harness
15.1 工具执行不是 tool.call()
Claude Code 不会直接执行:
ts
await tool.call(input)
而是通过完整执行管线:
text
tool_use block
-> findToolByName
-> inputSchema.safeParse
-> normalize/backfill input
-> runPreToolUseHooks
-> resolveHookPermissionDecision
-> canUseTool
-> tool.checkPermissions
-> tool.call
-> runPostToolUseHooks
-> processToolResultBlock
-> create message update
这体现了成熟 harness 的核心:所有动作必须通过统一执行管线。
15.2 Schema 校验
模型生成的参数可能有类型错误、字段缺失或格式错误。执行管线先做:
ts
const parsedInput = tool.inputSchema.safeParse(input)
如果失败,则生成模型可读的错误消息,让模型有机会修正参数。
这比直接抛异常更适合 agent,因为 agent 需要从错误中恢复。
15.3 PreToolUse Hooks
pre-tool hook 可以在工具执行前介入:
- 审计即将发生的动作。
- 修改工具输入。
- 阻止工具调用。
- 触发额外权限判断。
- 记录观测数据。
这使 harness 具有横切扩展能力。权限、安全、日志、策略都可以作为 middleware 插入,而不需要写进每个工具。
15.4 Permission Decision
权限判断不是单点逻辑,而是多来源合成:
- 用户设置。
- 项目设置。
- CLI 参数。
- policy settings。
- session 临时授权。
- always allow 规则。
- always deny 规则。
- hook 决策。
- 工具自身
checkPermissions。 - 交互式用户确认。
- auto mode classifier。
最终得到一个 permission decision。该 decision 决定工具是否可以执行、输入是否被修改、是否需要用户确认。
15.5 PostToolUse Hooks
工具执行后还会进入 post hook:
- 记录执行结果。
- 触发审计。
- 做结果转换。
- 处理失败。
- 生成额外消息。
这使工具执行结果不只是返回给模型,还能影响 runtime 状态。
16. 第四层:Tool Orchestration Harness
16.1 工具调度的必要性
模型一次回复可能包含多个 tool_use。harness 必须决定这些工具如何执行。
不能简单全部并发,因为写文件、执行 shell、改状态等操作存在顺序依赖和副作用。
也不能全部串行,因为读文件、搜索、列目录等只读工具可以并发,提高效率。
16.2 分批执行
toolOrchestration.ts 的策略是:
text
连续的 concurrency-safe 工具 -> 一个并发 batch
非 concurrency-safe 工具 -> 单独串行 batch
逻辑上等价于:
python
batches = []
for tool_call in tool_calls:
if tool.is_concurrency_safe(input):
append_to_current_readonly_batch(tool_call)
else:
flush_readonly_batch()
append_single_serial_batch(tool_call)
16.3 并发安全由工具声明
判断并发安全时调用:
ts
tool.isConcurrencySafe(parsedInput.data)
这意味着调度器不需要知道每个工具的具体含义。调度器只依赖工具协议。
这是高质量架构的标志:策略由对象声明,执行器按协议工作。
16.4 Context Modifier
工具执行可能修改 ToolUseContext。并发执行时不能随便修改共享上下文,因此 Claude Code 会先收集 context modifier,再按 tool use 顺序应用。
这解决了并发工具更新共享状态的问题。
设计原则:
- 并发阶段只产生变更描述。
- 合并阶段按确定顺序应用变更。
- 避免竞态污染全局上下文。
17. 第五层:Permission Harness
17.1 权限系统的作用
Agent 最大风险是模型能调用真实世界工具。Claude Code 的权限 harness 目标是让模型"有能力但不失控"。
权限系统覆盖:
- 文件读取。
- 文件编辑。
- 文件写入。
- Bash/PowerShell。
- Notebook 编辑。
- MCP 工具。
- 子 agent。
- 任务工具。
- Web fetch/search。
17.2 工具暴露前过滤
权限不仅在执行时检查,也会影响工具是否暴露给模型。
filterToolsByDenyRules() 会将被 blanket deny 的工具从工具池中过滤掉。这样模型根本看不到这些工具。
这是第一道防线。
17.3 执行时再次检查
即使工具被暴露给模型,执行时仍然要检查权限。这是第二道防线。
双层防御的原因:
- 工具列表可能在 turn 之间变化。
- MCP 工具可能动态连接。
- 权限规则可能更新。
- 模型可能引用别名或历史工具。
- 外部调用可能绕过 UI。
成熟 harness 不能只依赖"模型看到什么",必须在执行点再次验证。
17.4 权限模式
Claude Code 中常见权限模式包括:
- default:需要时询问用户。
- bypass:跳过部分交互确认。
- strict:更严格拒绝。
- auto:结合分类器自动判断一部分操作。
权限模式不是简单布尔值,而是影响整个工具执行管线的策略上下文。
17.5 用户交互式授权
在 REPL 中,权限请求可以通过 UI 展示给用户。用户可以:
- 允许一次。
- 总是允许。
- 拒绝。
- 修改输入后允许。
- 添加规则。
这使 agent 能在高风险动作前停下来,由人类确认。
17.6 远程场景权限
远程/mobile/bridge 场景不能随意弹本地 UI。因此命令和工具需要额外安全边界:
REMOTE_SAFE_COMMANDSBRIDGE_SAFE_COMMANDSisBridgeSafeCommand
其中:
local-jsx命令默认禁止,因为会渲染本地 Ink UI。prompt命令默认安全,因为只是展开成文本。local命令必须显式加入 allowlist。
这是多客户端 agent runtime 必须具备的权限治理。
18. 第六层:Context Harness
18.1 上下文是运行时内存
Claude Code 没有把 prompt 当成一段文案,而是把上下文当成运行时内存系统。
上下文由多个区域组成:
text
system prompt static section
system prompt dynamic section
meta user context
conversation messages
attachments
tool results
memory
skills
MCP instructions
compact summary
token budget reminders
这种设计类似操作系统中的内存布局。
18.2 System Prompt 静态段
静态段包含:
- 身份说明。
- 基础行为规则。
- 工具使用原则。
- 输出风格。
- 任务执行原则。
静态段尽量稳定,这样可以命中 prompt cache,降低延迟和成本。
18.3 System Prompt 动态段
动态段包含:
- 当前 session guidance。
- memory。
- 环境信息。
- 语言设置。
- output style。
- MCP instructions。
- scratchpad。
- token budget 指令。
动态段放在 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 后面,避免污染静态缓存段。
18.4 Meta User Context
prependUserContext() 会把用户上下文包装成 meta user message。
这类消息带有明确提示:
text
this context may or may not be relevant
这是一种重要设计。运行时上下文不应强迫模型一定使用,而是提供参考。
18.5 Attachments
Attachments 是 Claude Code 上下文系统中非常重要的一层。
常见 attachment 包括:
- 打开的 IDE 文件。
- 用户选中的代码。
- 变更过的文件。
- 目录列表。
- 文件内容。
- PDF 引用。
- nested memory。
- dynamic skill。
- skill discovery。
- invoked skills。
- todo reminder。
- task list。
- MCP instructions delta。
- plan mode reminder。
- auto mode reminder。
附件系统的意义是:上下文来源很多,不能全部塞进 user input,也不能全部塞进 system prompt,必须有结构化管道。
18.6 Message Normalization
内部消息类型比 API 消息类型丰富得多。内部有:
- progress message。
- attachment message。
- tombstone。
- system local command。
- virtual message。
- hook result。
- compact boundary。
发给 API 前必须通过 normalizeMessagesForAPI() 转换。
这体现了一个重要原则:
text
内部 runtime message != 模型 API message
中间需要一个规范化边界。
18.7 Compact Boundary
长会话不能无限增长。Claude Code 通过 compact boundary 管理历史:
- 压缩前历史被总结。
- 总结后插入 compact boundary。
- 后续 API 调用只取 boundary 之后的消息。
- 必要的文件引用、技能、记忆会重新注入。
这相当于 agent 的 checkpoint 机制。
18.8 Token Budget
Claude Code 不只关注上下文 token,也关注输出 token、turn budget 和用户指定目标。
上下文过大时会触发:
- warning。
- auto compact。
- blocking limit。
- reactive compact。
输出不够时可能触发 continuation。
这说明 harness 必须同时管理输入预算和输出预算。
19. 第七层:REPL Runtime Harness
19.1 REPL 的真实角色
src/screens/REPL.tsx 不是简单界面,而是交互式 runtime。它承担:
- 接收用户输入。
- 触发 prompt submit。
- 执行 slash command。
- 调用
query()。 - 消费 query event。
- 更新消息列表。
- 管理 loading 状态。
- 管理 abort controller。
- 显示 tool progress。
- 展示 permission request。
- 处理远程 bridge。
- 处理 initial prompt。
- 处理 queued input。
- 处理 turn complete。
- 管理 MCP 状态。
- 管理 background query。
这说明 agent 产品中的 UI 层不是纯展示层。对于交互式 agent,UI 是 runtime 控制面。
19.2 QueryGuard
REPL 中存在 query guard,用于避免多次提交、取消竞态、旧 query finally 覆盖新 query 状态等问题。
这类问题在简单 demo 中不明显,但在真实交互式 agent 中非常常见:
- 用户提交后立刻取消。
- 用户取消后马上重提。
- 工具还在运行时用户输入新消息。
- remote session 和 local query 同时改变 loading。
- finally 回调晚于新 query 启动。
因此 harness 必须有显式的 query lifecycle 管理。
19.3 Abort Controller
Claude Code 使用 AbortController 管理中断。
中断不只是停止模型流,还要影响:
- 正在执行的工具。
- 工具进度状态。
- UI loading。
- bridge completion。
- turn complete hook。
- transcript。
这说明取消机制必须从 UI 贯穿到模型和工具。
19.4 Streaming UI
模型输出、thinking、工具进度、工具结果都以流式事件进入 REPL。
REPL 不应该等待完整 response 后渲染,而应持续消费事件。这对用户体验和长任务可观测性非常重要。
20. 第八层:Extension Harness
20.1 统一扩展模型
Claude Code 支持多种扩展来源:
- 内置工具。
- MCP 工具。
- slash commands。
- skills。
- plugins。
- subagents。
- tasks。
- remote resources。
这些扩展最终会被适配到统一 runtime 概念中:
- 工具进入
Tool协议。 - prompt 型命令进入 skill/tool command。
- MCP resource 进入 resource 工具。
- subagent 进入 AgentTool。
- task 进入 task tools。
20.2 MCP 适配模式
MCP 工具通过:
text
MCP server
-> tools/list
-> result.tools
-> map to Tool
-> assembleToolPool
-> expose to model
-> toolExecution
每个 MCP tool 会继承 MCPTool 原型,再覆盖:
namemcpInfodescriptionpromptinputJSONSchemaisConcurrencySafeisReadOnlyisDestructiveisOpenWorldcheckPermissionscall
这是一种典型 adapter pattern。
20.3 Skills 与 Commands
Claude Code 中 prompt 型 command 可以被模型调用。这样,用户或插件提供的命令不只是人类 slash command,也能成为模型技能。
这使 harness 具备"能力装载"机制:
text
技能文件 / 命令定义
-> Command
-> SkillToolCommands
-> SkillTool
-> 模型可调用能力
20.4 Subagent Harness
AgentTool 使模型可以启动子 agent。子 agent 不是简单函数调用,而是带有:
- 自己的工具池。
- 自己的上下文。
- 自己的权限限制。
- 自己的消息链。
- 与父 agent 的结果回传。
这体现了 agent harness 的递归结构:agent 可以调用另一个受限 agent。
21. 第九层:Governance Harness
21.1 Trust Boundary
Claude Code 在启动时区分:
- trust 前可应用的安全配置。
- trust 后才能应用的完整配置。
trust 前:
ts
applySafeConfigEnvironmentVariables()
trust 后:
ts
applyConfigEnvironmentVariables()
initializeTelemetryAfterTrust()
这说明本地 agent 必须防止不可信目录通过配置、环境变量、外部 include、MCP server 等方式影响执行。
21.2 Remote Managed Settings
远程托管设置允许产品侧下发策略、功能开关、限制和 killswitch。
从 harness 角度看,这是治理层:
- 控制功能是否启用。
- 控制策略限制。
- 控制 remote behavior。
- 控制风险功能。
- 控制 telemetry。
21.3 Feature Gates
项目中大量使用 feature gates。它们用于:
- A/B 测试。
- 灰度发布。
- 内部功能。
- 实验功能。
- kill switch。
- dead code elimination。
这说明成熟 agent runtime 不能只靠代码版本控制行为,还需要运行时 feature management。
21.4 Telemetry 和 Observability
Claude Code 在工具执行、模型调用、压缩、权限、MCP、性能等位置都有 telemetry。
原因是 agent 行为高度动态,很多问题只有运行时才能观测:
- 工具失败率。
- schema 错误率。
- permission deny 率。
- auto compact 触发率。
- token 使用。
- API fallback。
- MCP 连接失败。
- 长任务卡顿。
- 用户取消。
没有 observability,agent harness 很难产品化。
22. Harness 建设的核心分工
Claude Code 中模型和 harness 的职责边界非常清晰。
22.1 模型负责
- 理解用户意图。
- 制定局部计划。
- 选择工具。
- 生成工具参数。
- 阅读工具结果。
- 决定是否继续。
- 生成最终回答。
22.2 Harness 负责
- 构造 system prompt。
- 构造 runtime context。
- 暴露工具 schema。
- 校验工具输入。
- 判断工具权限。
- 调度工具并发。
- 执行工具。
- 处理工具结果。
- 管理消息历史。
- 控制上下文压缩。
- 处理中断。
- 管理 REPL 状态。
- 记录 session。
- 接入 MCP。
- 控制远程安全边界。
- 管理 feature gates。
- 上报 telemetry。
一句话总结:
text
模型决定"下一步想做什么",harness 决定"这一步能不能做、怎么做、做完如何反馈、失败如何恢复"。
23. 与普通 Agent Demo 的差异
普通 agent demo 通常具备:
- 一个 prompt。
- 一个 model call。
- 一个 tools list。
- 一个简单 while loop。
Claude Code harness 额外具备:
- 结构化 Tool 协议。
- 权限系统。
- 工具并发调度。
- hooks。
- MCP 动态工具。
- skill/command 统一能力系统。
- session state。
- streaming event runtime。
- compact 策略。
- prompt cache 设计。
- REPL 控制面。
- remote bridge。
- trust boundary。
- telemetry。
- policy limits。
- feature gates。
因此 Claude Code 不是 demo agent,而是产品级 agent operating environment。
24. 可复用的 Harness 建设清单
构建类似 Claude Code 的 agent harness,可以按以下清单设计。
24.1 Agent Loop
- 使用显式状态机。
- 支持多轮 tool-use continuation。
- 支持 streaming events。
- 支持 abort。
- 支持 max turns。
- 支持 fallback。
- 支持 compact。
- 支持 tool result 回填。
24.2 Tool Protocol
- 工具必须有 schema。
- 工具必须声明 read/write/destructive。
- 工具必须声明 concurrency safe。
- 工具必须支持权限检查。
- 工具结果必须规范化。
- 工具描述必须可供模型理解。
24.3 Permission Pipeline
- 工具暴露前过滤。
- 工具执行前再检查。
- 支持用户确认。
- 支持 allow/deny rules。
- 支持 session temporary grants。
- 支持 policy settings。
- 支持 hooks。
- 支持 remote mode 降级。
24.4 Context Pipeline
- system prompt 分静态和动态。
- runtime context 用 meta user message。
- 文件、技能、记忆用 attachments。
- 内部消息和 API 消息分离。
- 支持 compact boundary。
- 支持 token warning。
- 支持 prompt cache 稳定性。
24.5 Runtime UI
- 支持流式渲染。
- 支持工具进度。
- 支持权限弹窗。
- 支持中断。
- 支持输入队列。
- 支持 session resume。
- 支持远程 bridge。
- 支持背景任务。
24.6 Extension System
- 外部工具适配统一 Tool。
- MCP tools 动态加载。
- prompt commands 可转 skills。
- plugins 不绕过权限。
- subagents 有受限工具池。
24.7 Governance
- trust boundary。
- safe config before trust。
- full config after trust。
- remote managed settings。
- feature gates。
- telemetry。
- graceful shutdown。
- policy limits。
25. 最小 Harness 架构图
text
┌────────────────────┐
│ User │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ REPL Runtime │
│ input / UI / abort │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Agent Loop │
│ query state machine│
└──────┬───────┬─────┘
│ │
│ ▼
│ ┌────────────────────┐
│ │ Context Pipeline │
│ │ prompt/messages │
│ │ attachments/compact│
│ └────────────────────┘
│
▼
┌────────────────────┐
│ LLM API │
│ stream/tool_use │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Tool Orchestrator │
│ batch/serial/async │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Permission Pipeline│
│ rules/hooks/user │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Tools │
│ builtin/MCP/skills │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Tool Result Message│
└─────────┬──────────┘
│
└── back to Agent Loop
26. Python 版本 Harness 设计范式
以下代码展示了一个更接近 Claude Code 思想的 Python harness 骨架。
python
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Awaitable, Callable, AsyncIterator
Json = dict[str, Any]
@dataclass
class ToolResult:
content: Any
new_messages: list[Json] = field(default_factory=list)
metadata: Json = field(default_factory=dict)
@dataclass
class PermissionDecision:
allowed: bool
reason: str = ""
updated_input: Json | None = None
@dataclass
class Tool:
name: str
description: str
input_schema: Json
call: Callable[[Json, "ToolUseContext"], Awaitable[ToolResult]]
is_read_only: Callable[[Json], bool] = lambda args: False
is_concurrency_safe: Callable[[Json], bool] = lambda args: False
is_destructive: Callable[[Json], bool] = lambda args: False
async def check_permissions(
self,
args: Json,
context: "ToolUseContext",
) -> PermissionDecision:
return PermissionDecision(allowed=True, updated_input=args)
@dataclass
class ToolUseContext:
cwd: str
session_id: str
permission_mode: str
messages: list[Json]
runtime_state: Json = field(default_factory=dict)
class PermissionEngine:
async def check(
self,
tool: Tool,
args: Json,
context: ToolUseContext,
) -> PermissionDecision:
if context.permission_mode == "strict" and not tool.is_read_only(args):
return PermissionDecision(
allowed=False,
reason="strict mode blocks non-read-only tools",
)
if tool.is_destructive(args):
return PermissionDecision(
allowed=False,
reason="destructive operation requires explicit approval",
)
return await tool.check_permissions(args, context)
class ToolExecutor:
def __init__(self, permission_engine: PermissionEngine):
self.permission_engine = permission_engine
async def run_tool(
self,
tool: Tool,
args: Json,
context: ToolUseContext,
) -> Json:
self.validate_input(tool.input_schema, args)
decision = await self.permission_engine.check(tool, args, context)
if not decision.allowed:
return {
"role": "tool_result",
"tool_name": tool.name,
"content": f"Permission denied: {decision.reason}",
}
call_args = decision.updated_input or args
result = await tool.call(call_args, context)
return {
"role": "tool_result",
"tool_name": tool.name,
"content": result.content,
"metadata": result.metadata,
}
def validate_input(self, schema: Json, args: Json) -> None:
# 实际项目应使用 jsonschema / pydantic / msgspec 等严格校验。
required = schema.get("required", [])
for key in required:
if key not in args:
raise ValueError(f"missing required field: {key}")
class ContextBuilder:
def build(
self,
messages: list[Json],
tools: list[Tool],
context: ToolUseContext,
) -> Json:
return {
"system": self.build_system_prompt(tools, context),
"messages": self.normalize_messages(messages),
"tools": [self.tool_to_schema(tool) for tool in tools],
}
def build_system_prompt(self, tools: list[Tool], context: ToolUseContext) -> str:
static_part = (
"You are an agentic coding assistant. "
"Use tools when needed and explain final results clearly."
)
dynamic_part = f"Current working directory: {context.cwd}"
return static_part + "\n\n" + dynamic_part
def normalize_messages(self, messages: list[Json]) -> list[Json]:
return [
msg for msg in messages
if msg.get("type") not in {"progress", "ui_only"}
]
def tool_to_schema(self, tool: Tool) -> Json:
return {
"name": tool.name,
"description": tool.description,
"input_schema": tool.input_schema,
}
class AgentHarness:
def __init__(
self,
model: Any,
tools: list[Tool],
context_builder: ContextBuilder,
tool_executor: ToolExecutor,
context: ToolUseContext,
):
self.model = model
self.tools = {tool.name: tool for tool in tools}
self.context_builder = context_builder
self.tool_executor = tool_executor
self.context = context
async def run_turn(self, user_input: str) -> AsyncIterator[Json]:
self.context.messages.append({
"role": "user",
"content": user_input,
})
while True:
payload = self.context_builder.build(
self.context.messages,
list(self.tools.values()),
self.context,
)
response = await self.model.complete(payload)
for event in response.events:
yield event
if not response.tool_calls:
self.context.messages.append({
"role": "assistant",
"content": response.final_text,
})
return
tool_result_messages = []
for tool_call in response.tool_calls:
tool = self.tools[tool_call["name"]]
result_message = await self.tool_executor.run_tool(
tool,
tool_call["args"],
self.context,
)
tool_result_messages.append(result_message)
yield {
"type": "tool_result",
"message": result_message,
}
self.context.messages.extend(tool_result_messages)
该骨架体现了 Claude Code harness 的关键思想:
AgentHarness对应query.ts。Tool对应Tool.ts。ToolExecutor对应toolExecution.ts。PermissionEngine对应 permission pipeline。ContextBuilder对应 prompts/messages/attachments。run_turn()中的while True对应 agent loop。
27. Harness 建设的最终判断标准
一个 agent harness 是否成熟,可以用以下问题检查。
27.1 Agent Loop
- 是否支持多轮工具调用。
- 是否支持工具结果回填。
- 是否支持中断。
- 是否支持错误恢复。
- 是否支持上下文压缩。
- 是否支持最大 turn 限制。
27.2 Tool System
- 工具是否有 schema。
- 工具是否声明权限属性。
- 工具是否声明并发属性。
- 工具是否能统一处理内置和外部工具。
- 工具结果是否规范化。
27.3 Permission
- 工具暴露前是否过滤。
- 执行时是否再次检查。
- 是否支持用户确认。
- 是否支持规则持久化。
- 是否支持远程场景降级。
27.4 Context
- system prompt 是否分静态和动态。
- runtime context 是否结构化。
- attachments 是否可控。
- 历史是否可压缩。
- token limit 是否提前管理。
27.5 Runtime
- UI 是否能显示流式事件。
- 是否能显示工具进度。
- 是否能处理中断。
- 是否能处理权限弹窗。
- 是否能恢复 session。
- 是否能接入远程客户端。
27.6 Governance
- 是否有 trust boundary。
- 是否有 feature gates。
- 是否有 policy limits。
- 是否有 telemetry。
- 是否有 graceful shutdown。
Claude Code 在这些方面都做了系统性建设,因此它更接近"agent operating system",而不是简单 agent demo。
28. 项目目录架构与 Harness 实现的对应关系
Claude Code 的 src/ 目录并不是按"页面、接口、工具函数"这类普通应用方式组织,而是围绕 agent runtime 的职责边界拆分。理解目录结构时,应从 harness 的运行链路出发:
text
启动入口
-> 初始化运行时
-> 建立 REPL/UI 控制面
-> 解析用户输入
-> 构造上下文
-> 调用模型
-> 执行工具
-> 回填工具结果
-> 维护会话状态
-> 处理权限、扩展、远程、治理
对应到目录,可以分为以下层次:
text
src/
entrypoints/ 进程入口与启动分流
main.tsx CLI 主编排层
replLauncher.tsx REPL 挂载层
screens/ 交互式 runtime 页面
components/ REPL/TUI 组件
ink/ 终端 UI 基础设施
query.ts agent loop 核心
query/ query loop 的拆分辅助模块
Tool.ts 工具协议定义
tools.ts 工具池注册与装配
tools/ 各具体工具实现
services/tools/ 工具执行与调度管线
hooks/ React hooks 与运行时 hooks
utils/ 上下文、消息、权限、文件、git、模型等基础能力
constants/ prompt、工具名、XML tag、限制等常量
commands.ts slash command 聚合与过滤
commands/ 各 slash command 实现
services/ API、MCP、analytics、compact、policy 等服务层
context/ React context 与运行时上下文 provider
state/ AppState 与全局状态管理
bootstrap/ 会话级全局状态与启动共享状态
bridge/ 远程控制与 REPL bridge
cli/ 非交互 CLI 子命令与传输层
tasks/ 任务系统
memdir/ 记忆系统
skills/ 技能系统
plugins/ 插件系统
assistant/ 自主助手 / KAIROS 相关能力
coordinator/ 多 agent 协调模式
remote/ 远程模式相关能力
types/ 公共类型定义
从 harness 角度看,这些目录并不是平级的"功能文件夹",而是分别承担 runtime 的不同器官。
29. 顶层文件:Harness 主干
src/ 顶层文件承担主干职责,类似一个系统的骨架。
29.1 main.tsx:启动编排器
main.tsx 不是 agent loop 本身,而是把运行时组装起来的 bootstrap orchestrator。
主要职责:
- 解析 CLI 参数。
- 加载配置。
- 初始化 telemetry、policy、remote settings。
- 处理 trust dialog。
- 处理不同运行模式:REPL、print、remote、resume、SSH、direct connect。
- 装配 commands、tools、MCP、AppState。
- 调用
launchRepl()。
对应 harness 知识点:
text
main.tsx
-> runtime bootstrap
-> mode routing
-> trust boundary
-> policy initialization
-> REPL startup
它相当于 Python 项目里的:
python
def main():
config = load_config()
runtime = build_runtime(config)
if args.print:
run_headless(runtime)
else:
run_repl(runtime)
29.2 query.ts:Agent Loop 核心
query.ts 是 harness 的心脏。它负责把模型、工具、上下文、压缩、状态迁移全部连接成一个循环。
主要职责:
- 接收 messages、systemPrompt、userContext、toolUseContext。
- 构造发给模型的请求。
- 流式消费模型输出。
- 收集 tool_use blocks。
- 调用
runTools()执行工具。 - 把工具结果转成消息。
- 触发 auto compact / reactive compact。
- 控制 turn count、token budget、continuation。
- 处理 fallback、abort、stop hooks。
对应 harness 知识点:
text
query.ts
-> agent loop
-> state machine
-> streaming event source
-> tool-use continuation
-> compact control
-> model/tool boundary
29.3 Tool.ts:工具协议
Tool.ts 定义了工具必须满足的协议。它是工具系统的接口层。
主要职责:
- 定义
Tool类型。 - 定义
ToolUseContext。 - 定义
ToolPermissionContext。 - 定义
ToolResult。 - 定义工具进度类型。
- 提供
buildTool()。 - 提供
findToolByName()。
对应 harness 知识点:
text
Tool.ts
-> capability protocol
-> schema contract
-> permission context
-> execution context
-> tool result contract
29.4 tools.ts:工具池装配
tools.ts 是工具注册和工具池组装层。
主要职责:
- 注册所有内置工具。
- 根据 feature gate 和环境变量决定启用哪些工具。
- 根据权限规则过滤工具。
- 合并内置工具与 MCP 工具。
- 保持工具顺序稳定,以利于 prompt cache。
对应 harness 知识点:
text
tools.ts
-> tool registry
-> tool pool assembly
-> deny-rule prefilter
-> MCP + builtin merge
-> prompt-cache stable ordering
29.5 commands.ts:命令能力聚合
commands.ts 负责聚合 slash commands,并决定哪些 command 可以被模型当作 skill 使用。
主要职责:
- 加载所有 slash commands。
- 区分 local、local-jsx、prompt 型命令。
- 提供
getSkillToolCommands()。 - 维护 remote safe commands。
- 维护 bridge safe commands。
- 根据远程模式过滤命令。
对应 harness 知识点:
text
commands.ts
-> human command interface
-> model-invokable skills
-> remote command safety
-> bridge safety boundary
29.6 context.ts 与 history.ts
这类顶层文件负责基础运行上下文和历史。
对应 harness 知识点:
text
context.ts
-> user context
-> system context
-> runtime environment injection
history.ts
-> prompt history
-> input history
-> session UX support
30. 入口与启动目录
30.1 entrypoints/
entrypoints/ 是进程入口层。这里的代码决定程序从哪个模式启动。
常见职责:
- CLI 快路径。
--version等零依赖路径。- daemon / remote / print 模式分流。
- 初始化启动 profiling。
- 延迟加载完整 CLI,减少启动成本。
对应 harness 知识点:
text
entrypoints/
-> process entry
-> fast path
-> startup optimization
-> mode dispatch
对于 agent 产品,启动速度很重要。不能每次 --version 都加载 React、MCP、工具系统、模型配置。entrypoints/ 的作用就是在最外层减少不必要加载。
30.2 bootstrap/
bootstrap/ 保存启动后多个模块共享的全局运行状态。
典型职责:
- 当前项目根目录。
- token budget。
- 成本统计。
- channel/trust 状态。
- main loop model override。
- telemetry 句柄。
- session / agent 类型信息。
- 工具循环状态。
对应 harness 知识点:
text
bootstrap/
-> process-wide runtime state
-> session-global state
-> cross-module coordination
bootstrap/state.ts 的存在说明:agent runtime 不是纯函数系统。REPL、query、tools、telemetry、commands 之间需要共享一些会话级状态。
30.3 interactiveHelpers.tsx
该文件虽然在顶层,但从职责上属于启动 UI 和 trust boundary。
主要职责:
renderAndRun()控制 Ink 根节点生命周期。- onboarding。
- trust dialog。
- MCP approval。
- CLAUDE.md external include approval。
- trust 后应用完整环境变量。
- trust 后初始化 telemetry。
对应 harness 知识点:
text
interactiveHelpers.tsx
-> interactive startup gates
-> trust boundary
-> setup screens
-> telemetry-after-trust
31. REPL 与终端 UI 目录
31.1 screens/
screens/ 是交互式界面层,其中 REPL.tsx 是最重要文件。
REPL.tsx 承担:
- 用户输入。
- 消息列表。
- 调用
handlePromptSubmit()。 - 调用
query()。 - 消费 query events。
- 管理 loading。
- 管理 abort。
- 管理 tool progress。
- 管理 permission prompt。
- 处理 initial message。
- 处理 remote session。
- 处理 bridge。
- 处理 queue。
对应 harness 知识点:
text
screens/REPL.tsx
-> foreground runtime
-> event consumer
-> human-in-the-loop control plane
-> query lifecycle owner
REPL 不是普通 UI。它是 agent runtime 的前台控制面。
31.2 components/
components/ 包含大量 Ink/React 组件。
这些组件服务于 harness 的可观测性和人机协作:
- 输入框。
- 消息显示。
- 权限确认弹窗。
- 工具进度。
- onboarding。
- trust dialog。
- MCP 配置审批。
- agent 配置界面。
- compact summary。
- OAuth 登录。
- 自动更新提示。
对应 harness 知识点:
text
components/
-> runtime visualization
-> permission UX
-> setup UX
-> tool progress UX
-> agent configuration UX
成熟 agent harness 需要 UI,因为很多决策必须让用户介入,尤其是权限和风险操作。
31.3 ink/
ink/ 是终端 UI 基础设施。
主要职责:
- 包装 Ink 基础组件。
- 主题系统。
- 终端能力检测。
- 终端渲染工具。
- 键盘输入基础能力。
对应 harness 知识点:
text
ink/
-> terminal rendering substrate
-> TUI abstraction
-> interactive agent shell foundation
31.4 context/
context/ 主要是 React context 层,例如 modal、overlay、notification、queued message、stats、voice 等。
对应 harness 知识点:
text
context/
-> UI/runtime shared state
-> React provider layer
-> modal/overlay coordination
-> notification channel
例如当前打开的 src/context/modalContext.tsx 属于这一层。它不是 agent loop 核心,但为 REPL 的交互控制面提供基础设施。
32. Tool Harness 目录
32.1 tools/
tools/ 是具体工具实现目录。每个工具通常有自己的子目录。
典型工具包括:
BashToolPowerShellToolFileReadToolFileEditToolFileWriteToolGlobToolGrepToolNotebookEditToolWebFetchToolWebSearchToolAgentToolSkillToolTodoWriteToolAskUserQuestionToolMCPToolListMcpResourcesToolReadMcpResourceToolTaskCreateToolTaskUpdateToolTaskListToolTaskGetTool
对应 harness 知识点:
text
tools/
-> concrete capabilities
-> file system actions
-> shell actions
-> web actions
-> subagent actions
-> skill invocation
-> MCP resource access
每个工具都实现 Tool 协议。工具目录不是简单函数库,而是 agent 可行动作的集合。
32.2 services/tools/
该目录是工具执行 runtime,不是具体工具。
主要文件职责:
text
toolExecution.ts
-> 单个 tool_use 的完整执行管线
toolOrchestration.ts
-> 多个 tool_use 的批处理、并发和串行调度
StreamingToolExecutor.ts
-> 流式工具执行
toolHooks.ts
-> pre/post tool hooks
对应 harness 知识点:
text
services/tools/
-> tool execution pipeline
-> tool scheduling
-> hook middleware
-> progress event generation
-> result normalization boundary
tools/ 与 services/tools/ 的区别很重要:
text
tools/
定义"能做什么"
services/tools/
定义"如何安全、可控、可观测地执行"
这正是 harness engineering 的典型分层。
32.3 hooks/useCanUseTool.tsx
虽然位于 hooks/,但它是工具权限链路的关键节点。
职责:
- 封装工具权限判断。
- 连接 AppState 中的 permission context。
- 在交互式场景中触发 UI 授权。
- 支持 coordinator/swarm/interactive 等不同上下文。
对应 harness 知识点:
text
hooks/useCanUseTool.tsx
-> permission decision adapter
-> React UI permission bridge
-> tool execution guard
33. Context Harness 目录
33.1 constants/
constants/ 不只是常量目录,它包含很多 prompt 和协议常量。
重要文件:
text
constants/prompts.ts
-> system prompt 构造
constants/systemPromptSections.ts
-> system prompt section registry
constants/tools.ts
-> 工具名、工具白名单/限制
constants/xml.ts
-> XML tag 协议
constants/apiLimits.ts
-> API 限制
constants/toolLimits.ts
-> 工具限制
对应 harness 知识点:
text
constants/
-> prompt contract
-> tool naming contract
-> XML message protocol
-> model/API limit constants
其中 prompts.ts 是上下文工程的核心之一。
33.2 utils/messages.ts
messages.ts 是消息系统核心。
职责:
- 创建 user message。
- 创建 assistant message。
- 创建 progress message。
- 创建 tool result message。
- 创建 interruption message。
- 创建 compact boundary。
- 合并 user messages。
- 过滤 virtual messages。
- 将内部消息规范化为 API 消息。
- 将 attachment 转成模型可读消息。
对应 harness 知识点:
text
utils/messages.ts
-> internal message model
-> API message normalization
-> tool result serialization
-> compact boundary semantics
这是 agent harness 中非常关键的"消息 ABI"。内部 runtime 使用丰富消息类型,模型 API 只接受有限消息类型,因此必须有规范化层。
33.3 utils/attachments.ts
attachments.ts 是上下文注入管道。
它负责把各种环境信息转成 attachment:
- 当前目录。
- 打开的文件。
- 选中的代码。
- 修改过的文件。
- nested memory。
- dynamic skills。
- skill discovery。
- todo reminder。
- task list。
- MCP instructions delta。
- plan mode reminder。
- auto mode reminder。
对应 harness 知识点:
text
utils/attachments.ts
-> dynamic context injection
-> memory/skill/file/task surfacing
-> runtime-to-model context bridge
33.4 utils/api.ts
api.ts 是模型请求前的上下文辅助层。
职责:
prependUserContext()。appendSystemContext()。splitSysPromptPrefix()。- 构造 tool schema。
- 处理 prompt cache blocks。
- 记录 context metrics。
对应 harness 知识点:
text
utils/api.ts
-> API payload preparation
-> system prompt cache partition
-> meta user context injection
-> tool schema conversion
33.5 services/compact/
该目录处理长上下文压缩。
主要职责:
- 计算有效上下文窗口。
- 计算 auto compact 阈值。
- 判断是否需要压缩。
- 执行 compact。
- 处理 reactive compact。
- 生成 compact summary。
- 处理 compact 后文件、技能、memory 的再注入。
对应 harness 知识点:
text
services/compact/
-> long-context control
-> summarization checkpoint
-> token budget management
-> context recovery
34. Command 与 Skill Harness 目录
34.1 commands/
commands/ 是每个 slash command 的具体实现目录。
命令类型大致包括:
- 本地 UI 命令。
- 本地文本命令。
- prompt 型命令。
- 配置命令。
- session 命令。
- MCP 命令。
- memory 命令。
- skills 命令。
- agents 命令。
- context 命令。
- compact 命令。
对应 harness 知识点:
text
commands/
-> user command surface
-> local runtime control
-> model prompt expansion
-> skill source
34.2 Command 到 Skill 的转换
commands.ts 中有:
ts
getSkillToolCommands()
getSlashCommandToolSkills()
这说明 prompt 型 slash command 可以被模型当作 skill 调用。
对应 harness 知识点:
text
commands.ts + commands/
-> slash command
-> prompt command
-> SkillTool visible ability
-> model-invokable workflow
这是一种重要设计:面向人的命令系统和面向模型的技能系统共用一部分基础结构。
34.3 skills/
skills/ 存放技能相关能力,包含 bundled skills、技能加载、技能描述等。
对应 harness 知识点:
text
skills/
-> reusable task knowledge
-> model-callable procedure
-> prompt-level capability packaging
技能不是普通工具。工具更偏"执行动作",技能更偏"提供任务方法和上下文约束"。
35. Services 目录:产品级 Runtime 服务
services/ 是大量系统服务所在目录。它不是普通业务服务层,而是 agent runtime 的后端基础设施。
35.1 services/api/
职责:
- 调用 Claude API。
- 处理 streaming。
- 构造 API payload。
- 处理 fallback。
- 处理错误。
- 处理 dump prompts。
对应 harness 知识点:
text
services/api/
-> model transport
-> streaming protocol
-> API error boundary
-> fallback model support
35.2 services/mcp/
职责:
- 连接 MCP server。
- 拉取 tools/list。
- 拉取 resources/list。
- 调用 MCP tools。
- 处理 MCP auth。
- 处理 MCP URL elicitation。
- 将 MCP tool 转成 Claude Code
Tool。
对应 harness 知识点:
text
services/mcp/
-> external capability adapter
-> dynamic tool discovery
-> MCP transport
-> MCP permissions integration
35.3 services/analytics/
职责:
- 事件上报。
- GrowthBook feature gate。
- telemetry sink。
- metadata 清洗。
- 运行指标采集。
对应 harness 知识点:
text
services/analytics/
-> observability
-> experiment control
-> runtime behavior measurement
35.4 services/remoteManagedSettings/
职责:
- 远程配置拉取。
- 配置缓存。
- checksum。
- 轮询更新。
- 与环境变量和策略联动。
对应 harness 知识点:
text
services/remoteManagedSettings/
-> remote governance
-> policy delivery
-> killswitch infrastructure
35.5 services/policyLimits/
职责:
- 加载策略限制。
- 判断功能是否被允许。
- 控制某些运行行为。
对应 harness 知识点:
text
services/policyLimits/
-> enterprise policy
-> runtime constraints
35.6 services/sessionTranscript/ 与相关服务
这类服务负责会话转录、摘要、长期记录等。
对应 harness 知识点:
text
session services
-> transcript persistence
-> long-running agent memory
-> audit trail
36. State、Context 与 Hooks 的关系
Claude Code 同时使用三类状态/上下文机制。
36.1 bootstrap/state.ts
这是进程/会话级共享状态,偏非 React。
适合放:
- 当前 cwd/project root。
- token budget。
- OpenTelemetry 句柄。
- main loop model。
- agent type。
- session 级 flags。
36.2 state/AppState.ts
这是 React app state,供 REPL 和组件使用。
适合放:
- 消息列表相关状态。
- MCP clients。
- permission context。
- agent definitions。
- UI 状态。
- 工具运行状态。
36.3 context/
这是 React Context provider 层。
适合放:
- modal context。
- overlay context。
- stats context。
- voice context。
- notification context。
- queued message context。
36.4 hooks/
hooks/ 将 React 状态、runtime 行为和 side effects 连接起来。
典型职责:
useCanUseTool:工具权限。useQueueProcessor:输入队列。useMergedTools:工具池合并。useReplBridge:远程 bridge。- MCP 管理 hooks。
- UI lifecycle hooks。
对应 harness 知识点:
text
bootstrap/state.ts
-> non-React session state
state/
-> React application state
context/
-> provider-level UI/runtime state
hooks/
-> stateful runtime behavior adapter
这种分层说明:agent runtime 不可能只靠一个全局变量或一个 context 管理所有状态。不同生命周期的状态应放在不同层。
37. Remote、Bridge、CLI 与多入口 Harness
37.1 cli/
cli/ 主要处理非交互子命令和 transport。
职责:
- auth 命令。
- mcp 命令。
- plugins 命令。
- agents 命令。
- update。
- worker state upload。
- SSE/WebSocket transport。
对应 harness 知识点:
text
cli/
-> headless control plane
-> non-REPL commands
-> transport utilities
37.2 bridge/
bridge/ 是本地 REPL 与远程控制之间的桥。
职责:
- 初始化 REPL bridge。
- 接收远程消息。
- 转发本地状态。
- 处理 bridge-safe commands。
- 管理 remote control client。
对应 harness 知识点:
text
bridge/
-> remote human interface
-> multi-client session bridge
-> command safety boundary
37.3 remote/
remote/ 处理远程运行模式。
对应 harness 知识点:
text
remote/
-> remote execution mode
-> local/remote runtime split
37.4 多入口设计
Claude Code 同时支持:
- 交互式 REPL。
-p非交互 print 模式。- remote mode。
- SSH mode。
- direct connect。
- bridge/mobile。
- daemon/background。
- subagent。
这要求 harness 中的核心逻辑不能写死在 UI 里。因此:
text
query.ts
是核心 agent loop
REPL.tsx
是交互式前台
cli/
是非交互控制面
bridge/
是远程控制面
这是成熟 runtime 的多入口架构。
38. Memory、Tasks、Agents、Plugins 的扩展目录
38.1 memdir/
memdir/ 是记忆系统。
职责:
- 加载 memory。
- 查找相关 memory。
- 计算 memory age。
- 处理 memory paths。
- 将 memory 转为 prompt 或 attachment。
对应 harness 知识点:
text
memdir/
-> persistent memory
-> context retrieval
-> long-term personalization
38.2 tasks/
tasks/ 是任务系统。
职责:
- task create/update/list/get。
- background task。
- local workflow task。
- task state persistence。
对应 harness 知识点:
text
tasks/
-> long-running work unit
-> agent task state
-> background execution
38.3 tools/AgentTool/
虽然位于 tools/,但 AgentTool 是 subagent harness 的入口。
职责:
- 启动子 agent。
- 装配子 agent 工具池。
- 过滤子 agent 工具权限。
- 管理子 agent context。
- 回传子 agent 结果。
对应 harness 知识点:
text
tools/AgentTool/
-> recursive agent runtime
-> delegated execution
-> isolated tool context
38.4 plugins/
plugins/ 是插件系统。
职责:
- bundled plugins。
- plugin CLI。
- plugin lifecycle。
- plugin commands / skills / tools 扩展。
对应 harness 知识点:
text
plugins/
-> third-party extension mechanism
-> capability packaging
38.5 assistant/ 与 coordinator/
这些目录对应更高级的自主 agent / 多 agent 功能。
assistant/ 偏 KAIROS、自主助手、会话选择等。
coordinator/ 偏多 agent 协调模式。
对应 harness 知识点:
text
assistant/
-> autonomous agent mode
-> proactive assistant runtime
coordinator/
-> multi-agent orchestration
-> worker coordination
39. 目录到 Harness 知识点总表
| Harness 知识点 | 主要文件/目录 | 作用 |
|---|---|---|
| 进程入口 | entrypoints/ |
快速路径、启动分流、延迟加载 |
| 主启动编排 | main.tsx |
CLI 参数、配置、运行模式、REPL 启动 |
| 交互 UI 挂载 | replLauncher.tsx |
App + REPL 挂载到 Ink |
| 前台 runtime | screens/REPL.tsx |
输入、消息、query 调用、权限 UI、bridge |
| UI 组件 | components/ |
权限弹窗、工具进度、onboarding、消息显示 |
| 终端基础设施 | ink/ |
Ink 封装、主题、终端能力 |
| Agent loop | query.ts, query/ |
状态机、模型流、工具闭环、压缩 |
| 工具协议 | Tool.ts |
Tool 接口、ToolUseContext、ToolResult |
| 工具注册 | tools.ts |
内置工具列表、权限过滤、MCP 合并 |
| 具体工具 | tools/ |
Bash、Read、Edit、Agent、Skill、MCP 等 |
| 工具执行 | services/tools/ |
schema 校验、hooks、权限、call、结果处理 |
| 权限判断 | hooks/useCanUseTool.tsx, utils/permissions/ |
用户授权、规则、模式、拒绝追踪 |
| System Prompt | constants/prompts.ts |
静态/动态 prompt 构造 |
| Prompt section | constants/systemPromptSections.ts |
prompt section 注册和解析 |
| API 上下文 | utils/api.ts |
user context 注入、prompt cache 分块、tool schema |
| 消息系统 | utils/messages.ts |
内部消息与 API 消息转换 |
| 附件系统 | utils/attachments.ts |
文件、记忆、技能、任务等动态上下文 |
| 压缩 | services/compact/ |
auto compact、summary、token 阈值 |
| 命令聚合 | commands.ts |
slash command 注册、skill command、remote safe |
| 命令实现 | commands/ |
具体 slash command |
| MCP | services/mcp/ |
MCP 连接、工具发现、资源、调用 |
| API 调用 | services/api/ |
Claude API、streaming、fallback |
| 运行观测 | services/analytics/ |
telemetry、GrowthBook、事件 |
| 远程配置 | services/remoteManagedSettings/ |
远程策略、killswitch、配置轮询 |
| Policy | services/policyLimits/ |
组织/产品策略限制 |
| 全局启动状态 | bootstrap/ |
session/global runtime state |
| React 状态 | state/ |
AppState、状态变更 |
| React 上下文 | context/ |
modal、overlay、stats、notifications |
| Hooks | hooks/ |
状态与 runtime 行为连接 |
| 远程桥接 | bridge/ |
mobile/web/remote control 与 REPL 连接 |
| 非交互 CLI | cli/ |
headless commands、transport |
| 记忆 | memdir/ |
MEMORY、相关记忆检索、长期上下文 |
| 技能 | skills/ |
bundled skills、技能加载 |
| 插件 | plugins/ |
插件扩展 |
| 任务 | tasks/ |
task tools、background workflow |
| 子 agent | tools/AgentTool/, coordinator/ |
subagent、多 agent 协调 |
| 自主助手 | assistant/ |
KAIROS、自主 agent 模式 |
40. 按 Harness 视角重画源码架构
从源码目录可以重画出以下逻辑架构:
text
┌─────────────────────────────────────────────┐
│ Entry Layer │
│ entrypoints/ main.tsx cli/ │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Runtime UI Layer │
│ replLauncher.tsx screens/ components/ ink/│
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Agent Loop Layer │
│ query.ts query/ │
└──────────────┬──────────────┬───────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌────────────────────┐
│ Context Layer │ │ Tool Layer │
│ constants/prompts.ts │ │ Tool.ts │
│ utils/api.ts │ │ tools.ts │
│ utils/messages.ts │ │ tools/ │
│ utils/attachments.ts │ │ services/tools/ │
│ services/compact/ │ │ useCanUseTool.tsx │
└──────────┬───────────┘ └──────────┬─────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ Service Layer │
│ services/api/ mcp/ analytics/ policy/ │
│ remoteManagedSettings/ sessionTranscript/ │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Extension / State Layer │
│ commands/ skills/ plugins/ memdir/ tasks/ │
│ state/ context/ hooks/ bridge/ assistant/ │
└─────────────────────────────────────────────┘
该图说明:目录不是随机拆分,而是围绕 agent harness 的运行时层次组织。
41. 关键对应关系:从用户输入到工具执行
一次用户输入在目录中的流转路径如下:
text
screens/REPL.tsx
-> utils/handlePromptSubmit.ts
-> utils/processUserInput/
-> commands.ts / commands/
-> utils/attachments.ts
-> query.ts
-> services/api/claude.ts
-> Tool use blocks
-> services/tools/toolOrchestration.ts
-> services/tools/toolExecution.ts
-> Tool.ts / tools/
-> utils/permissions/ + hooks/useCanUseTool.tsx
-> tool.call()
-> utils/messages.ts
-> query.ts 下一轮
这条链路可以对应到 harness 的概念:
text
REPL 接收输入
-> input preprocessing
-> command routing
-> context attachment
-> agent loop
-> model sampling
-> tool planning
-> tool scheduling
-> permission gate
-> tool execution
-> result normalization
-> loop continuation
42. 关键对应关系:从目录到运行时职责
42.1 输入进入系统
相关目录:
screens/components/hooks/utils/processUserInput/commands/
职责:
- 采集用户输入。
- 判断是否 slash command。
- 解析 pasted content。
- 处理 @ mention。
- 决定是否进入模型。
- 生成新 messages。
Harness 知识点:
text
input boundary
-> clean user intent
-> separate command from model prompt
-> attach runtime context
42.2 模型看到什么
相关目录:
constants/utils/api.tsutils/messages.tsutils/attachments.tsservices/compact/tools.tsservices/mcp/
职责:
- 生成 system prompt。
- 注入 user context。
- 规范化 messages。
- 生成 tool schema。
- 合并 MCP tools。
- 控制 prompt cache。
- 控制上下文长度。
Harness 知识点:
text
model input construction
-> prompt layout
-> tool schema exposure
-> context memory management
42.3 模型能做什么
相关目录:
Tool.tstools.tstools/services/mcp/commands.tsskills/plugins/
职责:
- 定义工具协议。
- 注册内置工具。
- 动态加载外部工具。
- 将 prompt commands 暴露为 skills。
- 将插件能力接入工具池。
Harness 知识点:
text
capability surface
-> model-visible actions
-> schema-bound invocation
-> extension adapter
42.4 模型动作如何执行
相关目录:
services/tools/hooks/useCanUseTool.tsxutils/permissions/tools/utils/toolResultStorage.ts
职责:
- 校验工具输入。
- 检查权限。
- 调度并发/串行。
- 执行工具。
- 处理大结果。
- 生成 tool result message。
Harness 知识点:
text
action execution
-> safe dispatch
-> permission middleware
-> result feedback
42.5 系统如何持续运行
相关目录:
query.tsbootstrap/state/context/hooks/services/analytics/services/remoteManagedSettings/services/policyLimits/
职责:
- 管理 turn state。
- 管理 app state。
- 管理 session state。
- 处理中断和恢复。
- 上报 telemetry。
- 接收远程策略。
- 控制 feature gates。
Harness 知识点:
text
runtime lifecycle
-> stateful agent process
-> observability
-> governance
43. 与 C++/Python 项目架构的类比
如果用 Python/C++ 重建类似结构,可以按下面方式映射。
43.1 Python 包结构类比
text
agent_app/
entrypoints/
cli.py
runtime/
repl.py
app_state.py
bootstrap.py
agent/
query_loop.py
transitions.py
tools/
base.py
registry.py
builtin/
executor.py
orchestrator.py
permissions.py
context/
prompts.py
messages.py
attachments.py
compact.py
commands/
registry.py
builtin/
services/
model_api.py
mcp.py
telemetry.py
policy.py
extensions/
skills.py
plugins.py
subagents.py
43.2 C++ 模块类比
text
core/
AgentLoop
Message
ContextBuilder
tools/
ITool
ToolRegistry
ToolExecutor
PermissionEngine
runtime/
Repl
AppState
EventStream
services/
ModelClient
McpClient
Telemetry
Policy
extensions/
Skill
Plugin
SubAgent
43.3 最重要的架构原则
不要把项目组织成:
text
main.py
tools.py
prompt.py
这种结构适合 demo,不适合产品级 agent。
更合理的组织是:
text
入口层
运行时层
agent loop 层
工具协议层
工具执行层
上下文层
扩展层
治理层
Claude Code 的目录结构正是这种分层思想的体现。
44. 目录结构背后的设计原则
44.1 核心 loop 独立于 UI
query.ts 不属于 screens/,说明 agent loop 独立于 UI。REPL 可以调用它,headless CLI 也可以调用它,subagent 也可以复用类似机制。
设计原则:
text
agent loop should be UI-independent
44.2 工具协议独立于具体工具
Tool.ts 在顶层,tools/ 存放具体工具。
设计原则:
text
tool protocol should be separate from tool implementations
44.3 工具执行独立于工具定义
services/tools/ 不在 tools/ 内部,说明执行管线是 runtime service,不是某个工具自己的逻辑。
设计原则:
text
tool execution pipeline should be centralized
44.4 上下文构造独立于模型调用
constants/prompts.ts、utils/api.ts、utils/messages.ts、utils/attachments.ts 与 services/api/claude.ts 分开。
设计原则:
text
context construction should be separate from model transport
44.5 外部能力统一适配
MCP 位于 services/mcp/,但最终产物是 Tool。
设计原则:
text
external capabilities should adapt into internal protocols
44.6 治理能力独立成服务
analytics、remote settings、policy limits 都在 services/ 中。
设计原则:
text
governance should be runtime infrastructure, not business logic scattered everywhere
45. 学习源码时的定位方法
阅读 Claude Code 源码时,可以先判断文件属于哪种 harness 职责。
45.1 如果文件在 tools/
重点看:
- 该工具暴露什么能力。
- 输入 schema 如何定义。
- 权限如何检查。
- 是否只读。
- 是否可并发。
call()如何返回结果。
不要一开始陷入工具内部细节。
45.2 如果文件在 services/tools/
重点看:
- 工具执行顺序。
- hooks 何时运行。
- 权限何时运行。
- 结果如何变成 message。
- 错误如何返回给模型。
这是 tool harness 的核心。
45.3 如果文件在 utils/messages.ts
重点看:
- 内部消息类型。
- API 消息类型。
- 哪些消息会被过滤。
- tool result 如何序列化。
- attachment 如何转成 user message。
这是模型上下文边界。
45.4 如果文件在 utils/attachments.ts
重点看:
- 哪些运行时信息会被注入模型。
- 何时注入。
- 如何去重。
- 如何截断。
- 如何避免 prompt cache 被破坏。
这是动态上下文系统。
45.5 如果文件在 services/mcp/
重点看:
- MCP server 如何连接。
- 工具如何发现。
- MCP tool 如何转换为
Tool。 - MCP 结果如何处理。
- MCP 权限如何接入。
这是外部工具适配层。
45.6 如果文件在 screens/REPL.tsx
重点看:
- 用户输入如何进入
query()。 query()事件如何更新 UI。- loading、abort、queue 如何管理。
- permission UI 如何接入。
- remote bridge 如何接入。
这是交互式 runtime。
46. 小结
Claude Code 的目录结构与 harness 建设高度对应:
text
entrypoints/main/cli
管启动
screens/components/ink/context/state/hooks
管交互 runtime
query
管 agent loop
Tool/tools/services/tools
管工具协议与执行
constants/utils/services/compact
管上下文工程
commands/skills/plugins/mcp
管能力扩展
analytics/remoteManagedSettings/policyLimits/bootstrap
管治理、观测和全局运行状态
因此,学习该项目时不应按文件数量平均阅读,而应围绕 harness 主线阅读:
text
query.ts
-> Tool.ts
-> tools.ts
-> services/tools/
-> constants/prompts.ts
-> utils/api.ts
-> utils/messages.ts
-> utils/attachments.ts
-> screens/REPL.tsx
-> services/mcp/
-> commands.ts
掌握这条主线后,其他目录都可以归入某个 harness 职责层中。