
omo-senpi init-deep-advisor 组件深入解析基于漂移检测的 AGENTS.md 知识库主动刷新机制【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent导读init-deep-advisor是 oh-my-openagent 仓库omo-senpi包中的一个会话启动顾问组件session-start advisor负责在每次会话启动时读取仓库根目录下的.omo/init-deep.json快照判断自上次生成 AGENTS.md 知识库以来仓库是否发生了实质性漂移drift并在适当时机主动向用户提议重新运行init-deep技能。本文基于 packages/omo-senpi/src/components/init-deep-advisor/AGENTS.md 展开结合该目录下的源码实现与测试用例完整讲解其触发模型、快照契约、抑制机制、生命周期以及底层 git 管道实现帮助你理解这套只提议、不执行、可抑制、无打扰的知识库保鲜机制是如何工作的。组件定位只读快照、UI-only 提议init-deep-advisor的核心设计原则可以用一句话概括它只负责提议从不自行执行任何写入操作。它读取init-deep技能生成的快照文件.omo/init-deep.json当检测到仓库在快照之后发生漂移或从未初始化过时通过 UI 弹窗询问用户是否重新运行init-deep技能用户做出的所有决定拒绝、冷却、上次提议的 HEAD都被记录在omo-state/init-deep-advisor-state/目录下。该状态目录以仓库真实路径realpath下的 git common dir 的 sha256 哈希作为键因此同一个仓库的不同 worktree 会共享同一份顾问状态——这是worktrees of one repo share state这一设计要点的具体含义实现见 git-helpers.ts 的gitCommonDirRealpath内部调用git rev-parse --git-common-dir后做realpathSync与 state.ts 的repoHash。组件解剖Anatomy原文档给出了组件内各文件的职责总览下面是完整继承并补充源码细节的对照表路径职责结合源码补充component.ts注册session_start监听仅处理reason startup的事件执行 preflighthasUI检查、omo-senpi-init-deep-advisor-disabled开关、git 仓库检查git rev-parse --is-inside-work-tree与--show-toplevel、onboarding-marker 门控运行时通过import(new URL(...))懒加载bundle 时加载omo-init-deep-advisor.js开发态加载runtime.tsruntime.ts驱动器依次执行 decline / cooldown / HEAD 门控、computeEligibility、ui.select60 秒超时选项为Run now/Skip this time/Never in this project/Never anywhere通过sendMessage发出omo-init-deep-advisor:runfollowUp triggerTurn并追加一条omo-init-deep-advisor:proposed日志条目eligibility.ts将快照状态路由到具体触发器快照缺失 → coverage 路径快照无效 →snapshot-invalid快照有效 → 漂移阈值判断每条可触发的路径恰好命名一个触发器drift.tscomputeDrift计算自快照 SHA 以来的提交数、被触碰文件占比、churn LOC 占比、经过天数shouldProposeRefresh决定是否提议刷新。快照 SHA 无法解析为 commit 时返回{ kind: stale }永远视为可提议coverage.ts候选目录遍历深度 3、以源文件为主的目录与 AGENTS.md 覆盖率计算服务于快照缺失路径state.ts快照读取与校验、仓库哈希、全局/项目级 decline、cooldown、last-proposed HEAD 的原子写入tmp rename权限 0600proposed-data.ts定义EligibilityResult/OmoInitDeepProposedData数据结构用于:proposed日志条目含trigger、coverage与drift二选一、suggestedModegit-helpers.ts基于 NUL 分隔的 git plumbing 命令HEAD、提交数、被触碰文件、churn LOC、tracked 文件总数漂移计算排除了AGENTS.md、**/AGENTS.md与.omo/init-deep.json本身git-exclude.ts以幂等方式向.git/info/exclude追加路径用于 local 模式输出管理constants.ts集中存放下述全部阈值、源文件扩展名与需要排除的目录名qa-*.sh/qa-rpc-*.mjs沙箱化端到端运行的手工 QA 工具qa-sandbox.sh、qa-snapshot.sh、qa-managed-block.sh、qa-init-deep-amendments.sh、qa-rpc-driver.mjs、qa-rpc-support.mjs从 index.ts 看该组件与 onboarding、telemetry 等一起被 extension/component-list.ts 注册进 omo-senpi 扩展体系组件内部还依赖 extension/startup-deferral.ts 的deferUntilAfterFirstPaint把整个运行推迟到首次渲染之后避免阻塞启动路径。漂移模型四个独立触发条件这是整个组件的核心决策逻辑。原文档明确强调constants.ts values, verify there before citing——所有阈值都必须以 constants.ts 为准任何其他代码不得硬编码这些数值。当前实现中的完整阈值如下常量值含义COMMIT_DISTANCE_THRESHOLD30快照以来提交数阈值TOUCHED_RATIO_THRESHOLD0.15被触碰文件 / tracked 文件占比阈值CHURN_LOC_RATIO_THRESHOLD0.25churn LOC增删行合计/ 源文件总 LOC 占比阈值DAYS_SINCE_THRESHOLD90快照时间距今的天数阈值COOLDOWN_DAYS7冷却天数MS_PER_DAY86_400_000毫秒换算当快照有效时只要满足以下ANY任一条件即提议刷新对应 eligibility.ts 的driftEligibilitycommitsSince 30且touchedRatio 0.15→ 触发器commit-and-touchchurnLocRatio 0.25→ 触发器loc-churndaysSince 90→ 触发器snapshot-age快照 SHA 无法解析为 commitgit cat-file -t sha返回非commit或快照文件无法解析 → 触发器snapshot-invaliddrift.ts中computeDrift的具体度量方式drift.tscommitsSincegit rev-list --count sha..HEADtouchedFilesgit diff --name-only -z --find-renames sha HEAD -- .外加排除规则trackedFilesgit ls-files -z的 NUL 计数touchedRatio touchedFiles / max(trackedFiles, 1)churnLocgit diff --numstat解析出的增删行之和二进制文件行以-表示时跳过totalLoc仅统计SOURCE_EXTENSIONS覆盖的 tracked 文件的换行数churnLocRatio churnLoc / max(totalLoc, 1)daysSince (Date.now() - snapshot.timestamp) / MS_PER_DAY特别地gitObjectType(root, snapshot.commitSha) ! commit时直接返回{ kind: stale }此时shouldProposeRefresh无条件返回 true在通过 HEAD/cooldown 门控的前提下——这正是不可解析的快照永远可提议的实现依据。值得强调的是 git-helpers.ts 中的DRIFT_EXCLUDES:(exclude)AGENTS.md :(exclude,glob)**/AGENTS.md :(exclude).omo/init-deep.jsontouchedFiles与churnLoc的 diff 都会带上这些 exclude 参数从而保证顾问自身产出的文件AGENTS.md 与快照永远不会成为触发漂移的因素形成自洽闭环。快照缺失路径覆盖率coverage检测当.omo/init-deep.json不存在ENOENT 被readSnapshot归类为missing时组件走覆盖率路径而非漂移路径。此时遍历仓库候选目录提议初始化init-deep的条件是missingRatio 0.5MISSING_COVERAGE_RATIO_THRESHOLD候选目录的判定规则coverage.ts 的walk函数目录深度 1..3CANDIDATE_MAX_DEPTH 3根目录 depth0 不参与候选判定满足源文件数 8CANDIDATE_MIN_FILES或 LOC 500CANDIDATE_MIN_LOC之一跳过符号链接symlink与排除目录EXCLUDED_DIR_NAMES包括node_modules、.git、dist、build、vendor、.next、__pycache__、.venv、target、coverage、third_partySOURCE_EXTENSIONS覆盖 21 种语言扩展名.ts .tsx .js .jsx .py .go .rs .java .kt .swift .rb .php .c .cpp .cs .scala .lua .ex .exs .zig .dartisCovered的判定方式是从候选目录自身沿父目录链向上查找只要任意层级存在AGENTS.md即视为被覆盖coverage.ts。最终coverage coveredDirs / candidateDirsmissingRatio 1 - coverage若没有任何候选目录则返回 null不提议。快照契约.omo/init-deep.jsonv1顾问只读取、不写快照快照由init-deep技能负责生成。其 v1 契约如下state.ts 的InitDeepSnapshotV1{ commitSha: hex, fileCount: 0, loc: 0, timestamp: 0, mode: local }字段校验规则readSnapshotstate.ts字段类型约束说明commitShastring快照时的 HEAD 提交哈希fileCount有限数字且 0tracked 文件数loc有限数字且 0源文件总行数timestamp有限数字且 0快照毫秒时间戳modelocal \| committedAGENTS.md 是否纳入 git 跟踪任何字段不符合上述约束快照整体读作invalid会触发snapshot-invalid刷新提议文件缺失ENOENT读作missing走覆盖率路径其余读取错误同样归为invalid。实际写入快照的命令在 skills/init-deep/SKILL.md 的 Phase 5 中给出通过git rev-parse HEAD、git ls-files -z的 NUL 计数、git ls-files -z过滤源文件扩展名后的wc -l汇总计算SHA/FILES/LOC/NOW然后以cat .omo/init-deep.json EOF的方式写盘。mode的判定顺序是显式USER_MODE_CHOICEcommitted/local优先否则回退到git ls-files --error-unmatch AGENTS.md是否成功成功 →committed失败 →local。在local模式下技能会向.git/info/exclude追加一段幂等的 managed block# omo-senpi init-deep local (managed)与# ...之间的/.omo/init-deep.json和AGENTS.md两行重复运行不会重复追加切回committed模式前需要用sed删除该块。仓库侧对应的程序化实现就是 git-exclude.ts 的addLocalExcludePaths/removeLocalExcludePaths/isExcluded它们通过git rev-parse --git-path info/exclude解析排除文件路径按行精确去重、文件内容无变化时不落盘。抑制机制四重门控与优先级原文档规定抑制条件的判定顺序全局拒绝 → 项目拒绝 → 7 天冷却 → 与上次提议相同 HEAD。在 runtime.ts 中这一顺序被严格实现isGloballyDeclined(stateDir)存在init-deep-advisor-declined-global文件即返回Never anywhere 的产物isProjectDeclined(stateDir, repo)init-deep-advisor-declined-projects/repoHash文件存在即返回Never in this project 的产物readCooldownUntil(stateDir, repo)Date.now() cooldownUntil时返回Skip this time 与选择超时的产物冷却时长COOLDOWN_DAYS 7天computeEligibility内部再校验currentHead lastProposedHead则返回 null同 HEAD 不重复提议writeCooldown写入的是{ until: at COOLDOWN_DAYS * MS_PER_DAY }state.tsreadCooldownUntil会校验until必须是有限非负数字否则按 0即无冷却处理。关于状态文件有一个值得注意的时序细节last-proposed HEAD 是在ui.select弹窗之前写入的runtime.ts因此即使在弹窗期间进程崩溃同一 HEAD 下也不会再次弹窗——这是对崩溃中途不重复打扰的显式设计。decline 文件没有过期机制by design一旦Never in this project或Never anywhere被写入该仓库/全局就不会再被提议。所有状态写入都经过writeAtomicstate.ts先写${dest}.${pid}.${randomUUID()}.tmp权限0o600再renameSync原子替换finally 中清理残留 tmp 文件避免并发写入产生半截 JSON。生命周期从会话启动到提议落地完整调用链如下对应原文档 Lifecycle 一节并结合 component.ts 与 runtime.tssession_start(startup) → preflight: hasUI 检查无 UI 直接退出 omo-senpi-init-deep-advisor-disabled flag 检查 git 仓库检查gitToplevel非仓库退出 onboarding-marker 门控marker mtime 必须早于进程启动时间 → deferUntilAfterFirstPaint 推迟到首帧之后 → gates: 全局拒绝 → 项目拒绝 → 冷却 → 同 HEAD → eligibility: computeEligibility 计算触发器 → 写入 last-proposed HEAD弹窗前 → ui.select(Init-deep, 4 选项, 60s 超时) → 用户选择: Run now → sendMessage 发出 omo-init-deep-advisor:run followUp triggerTurncontent 指示 Agent 读取 builtin-skills/init-deep/SKILL.md 并执行 并追加 omo-init-deep-advisor:proposed 条目 Skip this time → writeCooldown Never in this project → writeProjectDecline Never anywhere → writeGlobalDecline 超时(undefined) → writeCooldown与 Skip 等价其中 preflight 的几个细节值得展开组件只在payload.reason startup时响应component.ts会话中途的其他session_start事件不触发gitToplevel使用git rev-parse --show-toplevel失败返回 null 直接放弃保证组件只在真正的 git 仓库内运行onboarding-marker 门控只有 onboarding 标记的 mtime 早于进程启动时间即已完成 onboarding才放行避免在引导阶段打扰用户runBundledAdvisorAfterPreflight依据OMO_SENPI_BUNDLED编译常量决定懒加载omo-init-deep-advisor.jsbundle 产物还是runtime.ts开发态见 component.ts。omo-init-deep-advisor:run消息的 content 是Read the init-deep skill atskillsRoot/init-deep/SKILL.mdwith the read tool and follow it.即以 followUp 消息的形式让 Agent 读取技能文件并遵循执行runtime.ts。suggestedMode的推导则通过git ls-files --error-unmatch AGENTS.md判断命令成功AGENTS.md 已被跟踪→committed抛错 →local。提议数据形状与日志条目每次提议都会追加一条omo-init-deep-advisor:proposed条目通过pi.appendEntry其数据结构由 proposed-data.ts 定义。三种形态如下// 形态一coverage-gap快照缺失 { repo, trigger: coverage-gap, coverage: { missingRatio, candidateDirs, coveredDirs }, drift: null, suggestedMode } // 形态二漂移类触发器 { repo, trigger: commit-and-touch | loc-churn | snapshot-age, coverage: null, drift: { commitsSince, touchedRatio, churnLocRatio, daysSince }, suggestedMode } // 形态三快照无效 { repo, trigger: snapshot-invalid, coverage: null, drift: { stale: true }, suggestedMode }可见coverage与drift是互斥的xorEligibilityResult保证每条可触发的路径恰好对应一个触发器eligibility.ts 末尾的throw new Error(eligible drift has no trigger)是对可提议但无触发器这一不可能状态的防御性兜底。事件 schema 同时被 telemetry/event-schemas.ts 引用说明提议事件会进入遥测链路。组件测试与 QA 覆盖该组件拥有相当完整的测试矩阵可交叉印证上述行为component.test.ts 与 component.test-support.tspreflight 与注册逻辑的注入式测试drift.test.ts漂移度量与阈值判定coverage.test.ts候选目录、覆盖率、排除规则state.test.ts快照校验、原子写入、decline/cooldown 读写constants.test.ts阈值常量回归保护git-helpers.test.ts 与 git-exclude.test.tsgit plumbing 与 exclude 幂等性。手工 QA 方面qa-sandbox.sh负责搭建沙箱仓库、qa-snapshot.sh验证快照读写、qa-managed-block.sh验证.git/info/excludemanaged block、qa-rpc-driver.mjs/qa-rpc-support.mjs通过 RPC 驱动端到端运行另有 fixtures.test-support.ts 与 init-deep-pre-amendment.md 记录功能演进前后的契约基线。与 init-deep 技能的分工顾问与技能的分工边界清晰顾问只负责何时提议技能负责如何生成。init-deep技能skills/init-deep/SKILL.md生成层级化的 AGENTS.md 知识库根文件 按复杂度评分的子目录文件其执行路径为尺寸评估与节点公式计算N_quick ceil(S / CHUNK)CHUNK 400 * 1024字节N_high ceil(N_quick / 12)→quick扫描节点 map 提取事实 →unspecified-high写入节点 reduce 打分并写 AGENTS.md → 根文件与验证节点收敛main session 只读验证结论遵循 always-reduce 规则→ 主会话写快照与模式判定。技能支持/init-deep更新模式、/init-deep --create-new全量重建、/init-deep --max-depth2默认 3三种用法并在完成后清理.omo/init-deep/临时目录只保留.omo/init-deep.json作为长期产物——这正是顾问读取的唯一输入。反模式清单Anti-patterns原文档明确列出的四条开发红线值得所有参与该模块的开发者遵守不要在别处硬编码阈值数字所有阈值必须从constants.ts导入避免数字漂移导致行为不一致constants.test.ts为此提供回归保护不要把AGENTS.md或.omo/init-deep.json的变更算作漂移DRIFT_EXCLUDES存在的意义就是让顾问自身的输出永远无法触发它自己防止自我实现的循环提议不要以非原子方式写 decline/cooldown 文件writeAtomictmp rename0600是唯一允许的写入路径防止并发/崩溃导致半截状态文件不要不先记录 HEAD 就提议也不要以编程方式解除已拒绝的仓库HEAD 先写保证崩溃安全decline 无过期是设计决策尊重用户永不的选择。小结init-deep-advisor用一套紧凑的快照 漂移 覆盖率 多级抑制模型解决了仓库知识库何时需要重新生成这一运维痛点四个独立的漂移触发器覆盖了提交量、文件波及面、代码改动量、时间老化四种漂移形态覆盖率路径兜底从未初始化的场景四重门控 原子状态写入保证用户体验不被打扰、状态不损坏而DRIFT_EXCLUDES则让整个机制对自身输出免疫。对于想要扩展该机制例如新增触发条件、调整阈值或接入其他技能的开发者建议从 constants.ts 与 eligibility.ts 入手并保持只读快照、只提建议、原子写状态的三条设计底线。【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考