青雲的博客
深入浅出 Pi 第一部:先把 Pi 跑起来 第 04 章

配置、信任与资源从哪里进入

追踪全局与项目 settings、ProjectTrustStore、DefaultResourceLoader 的两阶段加载,分清配置合并、项目代码执行与实际资源发现的边界。

源码版本
v0.83.0
验证日期
Commit
845d6ff1f6643aba440341cce877ce1c43ebbc39

Pi 的“配置”至少有三条入口:全局 agentDir/settings.json,项目 cwd/.pi/settings.json,以及 CLI 当次传入的模型、工具和资源路径。它们没有被塞进一个万能对象。SettingsManager 负责前两份持久设置,main() 保留 CLI options,DefaultResourceLoader 再把设置与当次资源参数解析成可用对象。

项目设置只有在 trusted 时才参与合并

文件存储把全局路径指向 agentDir/settings.json,把项目路径指向 cwd/.pi/settings.json。合并时以全局为 base、项目为 overrides;普通值和数组由项目覆盖,嵌套对象做一层字段合并。

信任状态不是合并后的一个布尔字段,而是读取项目文件的前置条件。projectTrusted 为 false 时,项目 scope 直接返回空对象;从 trusted 切回 untrusted,也会清空已加载的项目设置并重新计算有效配置。

trust 管的是项目代码与资源,不是工具沙箱

Pi 只在 cwd 出现需要信任的项目资源时触发判断:.pi 下的相关条目,或 cwd 及祖先目录中的 .agents/skills。用户级 ~/.agents/skills 被视为全局资源,不算项目触发项。决策保存在 agentDir/trust.json,并按最近的路径记录查找。

提示文案说得更具体:信任允许 Pi 加载 .pi 设置和资源、安装缺失的项目 package、执行项目 extension。它没有承诺限制 bash,也不是工具审批或操作系统 sandbox。把“未信任项目”理解成“所有工具都无法改文件”,会得到错误的安全结论。

ResourceLoader 为什么要加载两次

需要询问信任时,DefaultResourceLoader.reload() 先强制 projectTrusted=false。这一遍排除项目 settings、项目 package 和项目 extension,但仍可加载用户级资源与调用方显式给出的临时 CLI extension,用它们参与 trust UI。得到决定以后,loader 更新信任状态,再 reload settings 并解析最终资源集。

最终资源不只有 extension。loader 分别解析 enabled extensions、skills、prompt templates、themes,再收集 AGENTS.md / CLAUDE.md 上下文文件,以及 system prompt 和 append system prompt。CLI 传入的额外路径带 temporary 元数据;它们是本次调用的显式输入,不应被误写成项目目录自动发现。

下面的实验只从固定 tag 抽取三段关键实现,不读取你机器上的 ~/.pi/agent

repo="${PI_SOURCE_DIR:-/tmp/pi-handbook-qbQTcA}"
git -C "$repo" show v0.83.0:packages/coding-agent/src/core/settings-manager.ts |
  nl -ba | sed -n '188,197p;308,376p'
git -C "$repo" show v0.83.0:packages/coding-agent/src/core/resource-loader.ts |
  nl -ba | sed -n '379,406p;446,545p'

到这里,SettingsManagerResourceLoaderModelRuntimeSessionManager 都已经有了明确来源。它们仍只是一组服务。下一章看 createAgentSession() 怎样把这些服务、模型状态和工具集合收拢成一个可以接收 prompt 的会话。