- 人工智能
- AI Agent
- 代码智能体
- 多智能体
- MCP Clients
- Agent 编排
【免费下载链接】oh-my-openagent
OmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.
导读
本文以 packages/omo-senpi/src/AGENTS.md 为主体,系统讲解 oh-my-openagent 中 Senpi 原生 TypeScript 扩展适配器(@oh-my-opencode/omo-senpi)的源码根目录组织方式:包 barrel 的最小导出策略、六大代码区域的定位、十项根级审计测试门禁的职责,以及测试命名、组件工厂、宿主端口等工程约定。读者读完可掌握该适配器"源码入口极简、扩展面走 extension 与组件 barrel"的架构取舍,并能独立执行包级测试、类型检查与完整门禁命令。
源码根:一个刻意保持"薄"的入口
packages/omo-senpi/src/是整个 Senpi 适配器的源码根。整个src/index.ts只有两行导出:
export const omoSenpiAdapterPackageName = "@oh-my-opencode/omo-senpi" export * from "./install"也就是说,包 barrel(index.ts)只导出包名常量与安装/卸载入口,其余全部能力都通过extension/index.ts(扩展入口)和各组件自己的 barrel 被消费。这是一种"入口最小化"设计:包的公共面由package.json的exports字段进一步收窄——只开放.、./agent-home、./install、./extension四个子路径(见 package.json),其中.与./extension都指向src/index.ts。
WHERE TO LOOK:六大代码区域速查
源码根 AGENTS.md 用一张表给出了"任务 → 位置"的索引,这是深入该包的第一张地图:
| 任务 | 位置 | 说明 |
|---|---|---|
| 扩展入口 / 组件组装 | src/extension/ | index.ts(源码入口,eager task)、bundled-index.ts(构建产物入口,lazy task 运行时),拥有独立 AGENTS.md |
| 安装 / 卸载 | src/install/ | runSenpiInstaller/runSenpiUninstaller、本地 launcher、原子化 settings 写入 |
| 测试用真实宿主模块 | src/senpi-test-runtime.ts | 加载时解析已安装的@code-yeongyu/senpidist,import 真实 Theme/ModelRegistry/ModelRuntime |
| 深组件 | src/components/{task,memory,lsp,telemetry,init-deep-advisor}/ | 每个都有独立 AGENTS.md;memory/额外记录了worker/、commands/、palace/ |
| 内置远程 MCP | src/components/builtin-mcps/ | context7+grep_appHTTP 声明、CONTEXT7_API_KEYbearer 门控与禁用开关 |
| X 搜索 | src/components/x-search/ | 凭据门控的x_search工具与条件技能 |
| 小型组件 | src/components/* | 单一职责工厂(ulw-loop、config-watch、onboarding、fallback-architect 等) |
extension:组合层是真正的"主入口"
src/extension/是 Senpi ExtensionAPI 组合层,职责在 extension/AGENTS.md 中有完整说明:
types.ts定义了结构化的宿主端口(SenpiExtensionAPI、ComponentContext、ComponentLogger、OmoSenpiComponent),组件只 import 这些类型,绝不引用具体 senpi 类型;compose.ts的composeOmoSenpiExtension定义了激活顺序:先发布OMO_DAG_SDK_ROOT与OMO_AGENT_TOOLKIT_SDK_ROOT环境变量,再注册任何组件;能力不匹配时只记一条警告并禁用扩展(绝不 throw);注册全局omo-senpi-disabled标志与每个组件各自的omo-senpi-<name>-disabled标志;每个register单独 try/caught,单个组件失败不阻塞其余组件;component-list.ts的createOmoSenpiComponents(taskComponent)返回 18+ 个组件的注册数组,task 组件由入口文件注入。注册顺序是承重的(load-bearing),例如 config-startup 必须排在前面以便在配置诊断输出前就绪,memory 注册在 task 之后、config-watch 之前。
值得注意的细节:源码/开发入口index.ts使用 eager 的createTaskComponent()直接组合;而构建产物入口bundled-index.ts换用 lazy task shim,通过await import("#omo-task-runtime")延迟加载,该别名由plugin/scripts/build-extension.mjs创建——这也是 extension-node-runtime-audit.test.ts 要审计的目标。
install:本地 launcher 与原子写入
src/install/提供runSenpiInstaller/runSenpiUninstaller,其插件路径解析逻辑见父级文档(packages/omo-senpi/AGENTS.md):解析resolveAgentHome(omo 安装默认~/.omo/agent,显式设置OMO_/SENPI_/PI_CODING_AGENT_DIR时优先,仅作为回退检测扁平~/.omo与~/.senpi/agent布局),添加/移除绝对插件路径,并生成指向同一目录的本地 launcher。settings 的写入是原子化的,相关实现与测试位于 src/install/。
senpi-test-runtime:无宿主测试的真模块加载器
senpi-test-runtime.ts 是"真实宿主模块"的测试桥:它在模块加载时通过import.meta.resolve("@code-yeongyu/senpi")定位已安装的 Senpi dist 目录,然后分别 importmodes/interactive/theme/theme.js、core/model-registry.js、core/model-runtime.js、core/sdk.js,导出Theme、ModelRegistry、ModelRuntime、createAgentSession。这样组件单元测试就能在完全没有真实宿主进程的情况下驱动真实的 Senpi 模型注册表与运行时。
组件注册的真相:component-list.ts
虽然 src/AGENTS.md 将组件分为"深组件"与"小组件",但实际注册顺序统一由 component-list.ts 决定:
config-startup → model-profile → bundled-skills → native-badge → onboarding → init-deep-advisor → telemetry → ultrawork → skill-pointers → ulw-execute-continuation → ulw-loop → todo-fanout-reminder → git-master → fallback-architect → ast-grep → builtin-mcps → lsp → x-search → comment-checker → task(注入)→ thread → memory → config-watch从源码注释可以读出排序依据:
- config-startup 排第一:配置诊断必须先于 profile 提示输出;
- bundled-skills 在启动 UI 组件之前:技能可用性在 native-badge → onboarding → advisor 的会话启动邻接区之前解析,
session-start-ordering.test.ts钉死了这一顺序; - task 由入口注入:
createOmoSenpiComponents(taskComponent)接收 task 组件参数,而不是自行构造。
根级审计门禁:可执行的包契约测试
源码根 AGENTS.md 明确强调:这些是可执行的包契约测试(executable package-contract tests),不是文档。逐项说明如下(均为src/根下已确认存在的测试文件):
| 测试 | 契约内容 |
|---|---|
bundle-purity.test.ts | 扩展 bundle 的 import 白名单;必须与plugin/scripts/build-extension.mjs中的SENPI_LOADER_ALIASES保持对齐(peer-external 规则)。实测它钉死了 19 个 peer 别名:@earendil-works/pi-*、@mariozechner/pi-*系列、@code-yeongyu/senpi、typebox 系列与@sinclair/typebox系列(见 bundle-purity.test.ts),并扫描构建产物,断言除 node 内建与白名单 peer 外没有任何外部 import |
bundle-size.test.ts | bundle 体积预算 |
package-shape.test.ts | 适配器 manifest 契约;license/notice 文件必须随生成的产物一起发布 |
plugin-manifest.test.ts | 打包后的插件 manifest |
runtime-dependency-resolution.test.ts | 一个 symlink 安装的插件在没有宿主提升(host hoisting)的情况下,仍能从真实路径解析运行时依赖 |
runtime-package.test.ts | LSP daemon 运行时 staging:manifest 钉死排序后的输出;篡改输出会被拒绝 |
extension-node-runtime-audit.test.ts | 扩展在纯 Node/jiti 下可加载:模块作用域内不得出现仅 Bun 支持的属性(import.meta.dir/import.meta.file)——这是 v5.0.0-beta.1 的回归修复 |
senpi-main-runtime-import-audit.test.ts | 主运行时 import 面审计 |
omo-native-capture-path.audit.test.ts | capture path 面审计 |
skills-sync.test.ts | 同步的技能不得携带外来 harness 令牌(codex、multi_agent、spawn_agent,大小写不敏感) |
bundle-purity.test.ts的实现值得一提:它用collectStaticImportSpecifiers正则扫描 minified bundle 的静态 import/export,且特意做"空白容忍"处理——import{x}from"y"与import { x } from "y"两种形态都覆盖,避免门禁在压缩产物上空通过。测试还校验了plugin/runtime/agent-toolkit-sdk/sdk.js的 import 全部为node:前缀,且构建输入不含任何node_modules/路径。
工程约定(CONVENTIONS)
- 测试就近放置:
*.test.ts与被测源码同目录,使用 Bun runner,命名规范为#given ... #when ... #then ...(如#given the senpi loader aliases #when tested #then the shared build constant pins all 19 peers);FakeExtensionAPI在没有宿主的情况下驱动注册(见 test-support/fake-extension-api.ts)。 - 组件即工厂对象:组件是
create*Component()工厂返回的对象,带register(pi, ctx)方法;注册顺序统一在 extension/component-list.ts 中维护,组件间依赖决定顺序,重排前必须检查交叉依赖。 - 宿主面只通过结构化端口消费:
extension/types.ts的SenpiExtensionAPI/ComponentContext是宿主面的唯一权威描述,extension/之外禁止使用具体 senpi 类型。SenpiExtensionAPI中rpc、events、cwd、appendEntry、registerMcpServer等成员都是可选(optional)的,以便旧版宿主仍能加载扩展(见 types.ts)。
常用命令
bun test packages/omo-senpi # 包级测试套件 tsgo --noEmit -p packages/omo-senpi/tsconfig.json # 类型检查 bun run test:senpi # 完整门禁:build + stage + typecheck + tests父级文档(packages/omo-senpi/AGENTS.md)补充了更深层的 QA 命令:真实宿主的端到端驱动(SENPI_BIN="$(command -v senpi)" node packages/omo-senpi/scripts/qa/task-e2e.mjs与team-e2e.mjs),以及bun run test:senpi作为包门禁、scripts/qa/作为真实 harness 证明的分层原则。若 Senpi 二进制不可用,这些驱动会报告SKIP/FAIL,而不是触碰真实的~/.senpi/agent。
反模式(ANTI-PATTERNS):三条红线
- 不要为通过构建而放松审计门禁——要么修复违规,要么有意变更契约。门禁测试(bundle-purity、runtime-package 等)是包契约的"活文档",放松即违约。
- 不要在不保持
runtime-dependency-resolution.test.ts/runtime-package.test.ts绿灯的情况下新增运行时依赖——尤其是从 symlink 安装场景验证依赖解析与 LSP daemon staging 仍成立。新增运行时依赖的同时,必须确保这两项契约在 symlink 安装下依然通过。 - 不要扩张包 barrel——新能力必须走
extension/或组件自己的 barrel。这正是本文开头"源码根保持薄入口"设计的延续:src/index.ts只导出包名与 install,任何试图往 barrel 里堆导出的做法都被视为反模式。
延伸:从源码根到整体包
源码根的 AGENTS.md 刻意把"包解剖、构建与 QA"上移到父级 packages/omo-senpi/AGENTS.md 讲述,二者构成完整视图:父级文档给出了二十余个注册组件的逐个职责说明(ultrawork 指令注入、skill-pointers 关键词表、ulw-loop 延续、fallback-architect 降级提示、builtin-mcps 远程 MCP、memory 长期记忆适配等),以及构建管线(build-extension.mjs生成六个扩展产物、sync-skills.mjs同步技能、peer-external 规则)。在动手修改该包前,建议按"源码根 → extension → 目标组件"的层次顺序阅读各自的 AGENTS.md,并始终以bun run test:senpi作为提交前门禁。
- 人工智能
- AI Agent
- 代码智能体
- 多智能体
- MCP Clients
- Agent 编排
【免费下载链接】oh-my-openagent
OmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.
相关推荐
oh-my-openagent OmO Native 遥测波次计划合规审计:从 REJECT 到 APPROVE 的源码级复盘
oh my openagent OmO Native 遥测波次计划合规审计:从 REJECT 到 APPROVE 的源码级复盘 导读 :本篇文章以仓库内 .om
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排oh-my-openagent omo-senpi 适配器回归 QA 实录:从 Issue 5317 证据记录解读隔离认证驱动器
oh my openagent omo senpi 适配器回归 QA 实录:从 Issue 5317 证据记录解读隔离认证驱动器 在 oh my openage
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排Slint 单片机裸机 GUI 开发指南:Rust no_std 环境下的平台抽象、事件循环与软件渲染
Slint 单片机裸机 GUI 开发指南:Rust no_std 环境下的平台抽象、事件循环与软件渲染 本指南基于 api/rs/slint/mcu.md ht
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考