@genee/omp-opsx-addon
Pi Extension: OpenSpec workflow orchestration - coder/reviewer/planner agents, session title & progress
Package details
Install @genee/omp-opsx-addon from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@genee/omp-opsx-addon- Package
@genee/omp-opsx-addon- Version
0.9.0- Published
- Sep 16, 2026
- Downloads
- 1,108/mo · 519/wk
- Author
- iamfat
- License
- MIT
- Types
- extension
- Size
- 899.2 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
omp-opsx-addon
Pi Extension: OpenSpec workflow orchestration — coder/reviewer/planner agents, session title & progress.
架构
┌─────────────────────────────────────────────────────────┐
│ 主 agent (Orchestrator) │
│ system prompt 由 plugin 注入: model 选择决策 + 路由表 │
│ │
│ 用户说 "@coder 实现 X" → transform 成 task 委派指令 │
└───────────────────────┬─────────────────────────────────┘
│
┌───────▼────────┐
│ omp 内置 task │
│ (所有 role) │
│ 原生 subagent │
└───────┬────────┘
│
┌────────┴──────────┐
│ .omp/agents/ │
│ coder / planner │
│ code-reviewer / │
│ proposal-reviewer │
└─────────┬─────────┘
│ autoloadSkills
┌─────────▼─────────┐
│ skills/ │
│ opsx-orchestration│
│ -protocol(随包) │
└───────────────────┘
核心机制:主 agent 用 omp 内置 task 工具委派所有 4 个角色。model 选择由插件自动完成,不再有 acpx(外部 agent)路径。每个角色的有效指令集 = .omp/agents/*.md(角色专属模板)+ 随包技能 skills/opsx-orchestration-protocol(四角色共享编排协议,经 frontmatter autoloadSkills 注入)——详见下文「Agent 指令集」。
Agent 指令集:模板 + 协议技能
四个 dispatch agent 的有效指令集 = 角色专属模板(.omp/agents/*.md)+ 共享协议技能(随包):
| 角色 | 模板承载的角色专属内容 | autoloadSkills 注入的技能 |
|---|---|---|
| coder | 工作边界(不改提案文件 / 不二次委派 task / 通用编码请求免 scratchpad)、scoped 自验证门槛、ACTION 报告块 | opsx-orchestration-protocol + openspec-apply-change(两个) |
| planner | 提案拆分规则、Budget 估算、输出说明 | opsx-orchestration-protocol + openspec-propose(两个) |
| code-reviewer | 审查维度、全局验证三级定级(微改级 / 全流程级,Tier 1/2/3)、审查输出块 |
opsx-orchestration-protocol(一个) |
| proposal-reviewer | 审查维度、提案审查输出块 | opsx-orchestration-protocol(一个) |
共享协议技能 skills/opsx-orchestration-protocol/SKILL.md 是四角色共享编排协议的单一真源:scratchpad 四区共享缓存与角色读写矩阵(append-only、planner 建+写 / coder 读+写 / 两个 reviewer 只读)、supersede 权威语义与两档客观判据(档一继续 + supersede 修正,档二 STATUS: blocked 交主 agent 裁决)、无关状态硬栅栏(节内首行角色门——仅 coder 适用,reviewer / planner / proposal-reviewer MUST NOT 受本节限制)、P0/P1 评审闭环与轮次上限(Loop 1 ≤2 轮、Loop 2 最多 3 轮含首次实现与修复)、报告契约、生效与兜底。模板不再复述这些正文,每处只留指向技能的一行指针。
- 位置与发现:技能在包根
skills/,由宿主对扩展根的技能发现通道自动收录,无需安装动作——installAgents()的写入集合仍只有 4 个.omp/agents/*.md与.addon-version,不落项目目录、不建.omp/skills/,技能也不参与 MARKER / 版本 / conflict 状态机。 - 生效方式:frontmatter
autoloadSkills在子 agent 首个 prompt 之前把技能正文注入其上下文,等价于/skill:opsx-orchestration-protocol(coder / planner 另各注入一个官方技能)。 - frontmatter:
name/description/tools/autoloadSkills(+ 可选model)。原skill:字段是宿主 agent frontmatter 白名单不解析的死配置,已移除。 - 兜底:
autoloadSkills解析失败是静默的,故每份模板对每个技能各留一行skill://<name>指针(Skill tool 手动读取,逐行同构、不合并);技能名不可解析且skill://<name>亦不可读时视为前提缺失,子 agent 输出STATUS: blocked(无 SESSION)并报缺失技能名交由主 agent 决策,不凭记忆复述其流程。 - 官方技能的边界:
openspec-apply-change/openspec-propose正文面向主会话撰写;对子 agent 只有流程与产物定义适用(选变更、读 tasks/proposal/design/specs、逐 task 实现、- [ ]→- [x]、阻塞时不猜测),announce进度播报、「ask the user / 询问用户」、「pause / 等待用户输入」、progress / pause 报告块不适用(子 agent 无用户);需用户决策时走STATUS: blocked上报主 agent。该边界由协议技能单点声明,官方技能文件本身不改动。 - 升级路径:技能文件与生成模板的代码同处一个 npm 包版本(
.npmignore未排除skills/),升级插件即同时在场,不存在「模板已瘦身而技能未就位」的中间态;存量.omp/agents/*.md仍由既有installAgents状态机处理——含 MARKER 的判updated并被覆盖,不含 MARKER 的判conflict且不覆盖。
Dispatch 配置 (opsx.yml)
两层 opsx.yml(全局 ~/.omp/agent/opsx.yml + 项目 <cwd>/.omp/opsx.yml,项目逐键覆盖全局)。
# agents.* 的值可以是:
# 'auto' — 插件按 tier + usage 自动选 model
# 'smol' / 'slow' / 'default' — OMP role alias(由 OMP modelRoles 解析)
# 'gpt-5-pro' — 明确 model id
# 'anthropic/claude-opus-4-6' — provider/model 完全限定
agents:
coder: auto
reviewer: slow
planner: slow
proposal-reviewer: default
# 可选:限制自动选择时使用的 model 池
model_allowlist:
- "anthropic/claude-opus-*"
- "openai/gpt-5*"
# 可选:自定义 tier 评分
tiers:
- pattern: "*opus*"
tier: top
- pattern: "openai/gpt-4o-mini"
tier: low
# 可选:dispatch agent → tier 期望
agent_tiers:
planner: top
# 可选:OMP model role → tier 重定向(或 skip 排除)。
# 写入作用域 = 最小写入集 {smol, default, slow, vision}:合法 tier 值重定向该 role 的
# 写入档位(如 slow: high);skip 使该 role 不写覆盖。写入集之外 role 的条目对写入
# 惰化(保留解析校验);skip 在非写入 role 上等价 no-op。
model_role_tiers:
slow: high # 例:slow 改写 high 档选中(默认 top 档补选)
vision: skip # 不覆盖 vision(vision 只经能力门控写入,仅 skip 有效)
# 可选:月计划覆盖的 provider(追加到内置默认集;条目规范化、去重)
plan_providers:
- ArkCodingPlan
# 可选:从生效 plan 集合剔除((默认集 ∪ plan_providers) − plan_providers_remove)
plan_providers_remove:
- cursor
# 可选:PAYG 兜底末位(缺省 true);false 折叠覆盖类别键,PAYG 回到常规排序
payg_last_resort: true
# 可选:会话启动自动应用一次选择(缺省 true);false 时新会话不自动应用,
# 行为与本键引入前一致(选择待手动 /pick-model 应用)
autoselect_on_start: true
旧键已废弃(仍生效):顶层的
coder:/reviewer:/planner:/proposal-reviewer:与role_tiers:是上面的旧写法,仍照常生效,但读到时会按层文件各 warn 一条(单文件多个旧键合并为 一条、列出全部旧键名),并计划一个版本周期后移除。迁移:顶层 role 键挪进agents:mapping、role_tiers:改名agent_tiers:(schema 不变)。同一文件内新旧同现时新键胜出(agents.*> 顶层旧键、agent_tiers>role_tiers);跨层合并语义不变(agent 值逐键覆盖、tier 表整键覆盖)。
agents / agent_tiers 与 model_role_tiers 消歧义
两者都是 opsx.yml 里的映射键、形似「role → tier」,但管理的对象与生效层完全不同:
agents / agent_tiers |
model_role_tiers |
|
|---|---|---|
| 管的对象 | opsx 插件的 4 个 dispatch agent(coder / reviewer / planner / proposal-reviewer,落 .omp/agents/*.md、经 task 工具委派) |
OMP 宿主的 model role(10 个内置 default/smol/slow/vision/plan/designer/commit/tiny/task/advisor + 自定义 role) |
| 键空间 | 固定 4 个 agent 名(未知子键 warn 丢弃) | 任意 OMP role 名(内置 + 自定义,宽松键空间) |
| 值 | agents.*:role 值(auto / OMP alias / model id / provider-model);agent_tiers:tier 名 |
tier 名或 skip |
| 生效层 | 插件委派 dispatch agent 时的 model 决策(agent → 用哪个 model) | /pick-model 后写入 session 覆盖(overrideModelRoles)的 role → model 映射 |
model_role_tiers 与 agents / agent_tiers 互不兜底、互不影响:前者不参与 dispatch agent 的
model 选择,后者也不影响 session 覆盖写入哪些 role。
覆盖类别与 PAYG 兜底(自动选择排序首位)
自动选择先按覆盖类别硬分区、再比 tier:① 月计划覆盖 → ② 其它可用 → ③ 按量计费(PAYG)兜底。
③类仅在①②类无可用候选时参与选择(有别的用,就不用 PAYG)。/pick-model <selector> 收窄池内仍按此
排序、仅剩③类时正常选中;pinned role 不经选择器,分区不影响显式钉值。
- 归类通用、代码零写死:
plan_providers成员(规范化后)→ ①类;quota policy 为balance(REQUIRED_WINDOWS注册表,如 deepseek)→ ③类;其余 → ②类。未在REQUIRED_WINDOWS登记 的按量计费 provider 落②类,登记即入③类——注册表是按量计费的既有登记扩展点。 plan_providers:追加语义,条目规范化(ArkCodingPlan/arkcodingplan同源到ark-coding-plan)、去重,非法条目 warn 丢弃。plan_providers_remove:从生效集合剔除默认或追加的成员,条目同样规范化。payg_last_resort:缺省true,③类保持兜底末位;设为false折叠整个覆盖类别键—— ③类回到常规排序(可按 tierGap 胜出),同时取消①类 plan 相对②类 standard 的位次偏好 (类别键整体失效,选择回到 tier / 用量 / 价格轴)。
Model 选择
插件启动时从 ModelRegistry.getAvailable() 拿所有有鉴权的 model,从 authStorage.fetchUsageReports() 拉用量报告,然后按 role → tier 期望 + 用量健康度自动选最优 model。
决策日志打印到 stderr:[omp-opsx-addon] planner → anthropic/claude-opus-4-6 (tier=top, gap=0, remaining=0.80)
会话启动自动应用一次选择(autoselect_on_start,缺省 true)
配好 china / model_allowlist / tier 期望后,新会话不再等手动命令:session_start 时插件自动应用一次当前
选择——经 ensureSelection 计算(china/allowlist 约束照常生效),按与 /pick-model 相同的应用路径写入
modelRoles(最小写入集 {smol, default, slow, vision} + per-agent 运行时中和),并把主会话模型设为
reviewer pick(high 档选中模型;与当前模型一致则不调用 setModel)。应用在首 turn 前 await 完成、每个会话
实例只应用一次,之后用户显式 /model 切换保持权威——启动路径不会再次触发、不回写主会话模型。pinned role
(agents.* 显式钉值)照常优先于自动 pick(钉值旁路选择器,不被自动写入覆盖)。启动应用路径的任何错误
(选择计算抛错、settings 不可用、setModel 失败)只 warn([omp-opsx-addon] session-start autoselect error: ...),不阻塞会话启动。autoselect_on_start: false(任一层 opsx.yml)完全退出,行为与本键引入前一致。
手动 /pick-model 命令照常可用、立即生效;task 子 agent 不触发启动应用。
/pick-model:selector 约束与 role 定向([roleList:])
语法:/pick-model [roleList:]<selector> [--china];子命令 choices / update / refresh / reset 不变。
无前缀 = 全局约束(全部 role 一起收窄,行为与引入 role 定向之前完全一致);/pick-model reset
清除约束(含 role 定向),一切重算路径(refresh / 约束期 tick / ensureSelection)都遵守当前约束。
roleList 词表(逗号分隔、大小写不敏感,与宿主 MODEL_ROLE_IDS 同源、零硬编码):
- 内置 model role 10 个:
default/smol/slow/vision/plan/designer/commit/tiny/task/advisor; - opsx agent 名作糖:
coder→smol,reviewer/code-reviewer/planner/proposal-reviewer→default(解析期映射为 canonical model role); all:等价无前缀的全局约束。
示例:/pick-model smol:glm*(只收窄 smol)、/pick-model smol,default:zhipu*/glm* --china、/pick-model coder:glm qwen --china。
粒度 = tier 槽位:约束把 scope role 集映射到的每个 tier 槽位(mid/high 槽位、top 补选与
vision 能力候选集)切到约束池,其余槽位照常走全池。映射到同槽位的其它 role 自然随动——默认映射下
task 与 smol 同为 mid,/pick-model smol:glm* 换掉 smol 写入后 task 经其枚举回退链
(["tiny","smol"] 等)跟着解析到新 mid 模型;这不是 bug,是 OMP 原生链的必然结果,命令输出会标注
scope(如 scope=smol)使其可见。model_role_tiers 参与 scope→槽位解析(与写入集同一张有效映射表
OMP_ROLE_TO_TIER);有效档位解析为 skip 的 scope role(如配了 model_role_tiers: { task: skip }
再执行 /pick-model task:glm*)直接报用法错误并指名该 role——skip 的 role 不落任何槽位,池约束对它
不可表达。vision 只出现在 scope 时仅约束其图像能力候选集,不收窄 high 槽位。
scoped pinned 交互:定向约束只把 scope 内 role 置 auto(显式覆盖 pinned);scope 外 role 保留
agents: 配置值(pinned 继续生效、omp-alias 语义不变),不经任何重算路径进入约束池,也不触发空选
回滚;reset 后全部恢复 opsx.yml 语义。agents: / agent_tiers: 是 agent-config-rename 改名后的
键名(改名前为顶层 coder: / code-reviewer: / planner: / proposal-reviewer: 四键与 role_tiers:,语义相同,旧键仍被兼容读取)。
冒号是保留字符:裸词匹配按非字母数字分词,含冒号的 token 若回落 selector 会被拆成多个子词按 OR
静默施加全局约束。因此含 : 但不是 roleList 前缀的 token(如 zhipu:glm、zhipu:glm*、拼错的
smlo:glm,不论是否带 *、位于命令中何处)一律报用法错误,并把合法词表全文内联在错误信息里——
不会静默施加任何约束。
choices 展示:紧凑行式输出(见下文「/pick-model 输出格式」),状态行携带约束段,如
当前选择 · constraint: selectors=glm* · scope=smol,default(scope 段为解析后的 canonical model
role;全局约束无 scope 段)。
china 配置键(缺省 false):opsx.yml 顶层布尔键,为 true 时 CHINA_EXCLUDE_FAMILIES
(gpt/claude/openai/anthropic 家族,裸词 token 双侧匹配,语义与 --china 一致)成为一切选择
路径的缺省排除——无约束的默认选择(ensureSelection)、refresh、reset 之后的重选、selector
约束池全部生效,无需每次敲 --china。优先级为命令显式 > 配置缺省:显式 --china 写入 session
约束(约束期内持续,fingerprint 化),省略旗标时配置缺省层兜底;china: false 或缺省键 = 与引入前
完全一致(仅显式 --china 生效)。关断手段 = 改配置(无 --no-china 旗标);改 opsx.yml 后新会话
生效(配置 session 启动读一次,无热加载)。scoped(role 定向)约束下缺省排除只作用于约束池
(scope 内槽位);scope 外槽位保持不含 china 排除的全池(显式 scope 划分 > 缺省叠加)。
配置为 true 时 choices/报告头部显示 china=on(无 selector 约束时也显示,如
constraint: china=on);reset 只清 session 约束、不清配置缺省。两层合并(project 逐键覆盖
global):project 显式 china: false 压过 global china: true;非法值 warn 后回落 false。
同账号区域变体去重(zai ↔ zhipu-coding-plan)
zai 与 zhipu-coding-plan 是智谱同一账号的两个区域入口。两者同时在候选池且两扇门解析出的 API
key 严格相等(同账号凭据证明)时,插件按区域偏好收敛到一侧,池与展示都不再重复;key 不同、缺失
或解析失败则保守并存(两扇门都保留、usage 栏两行都显示,靠「智谱 / Z.AI」标签区分)。同账号
(key 相等)才收窄;多 key 同账号场景请保持单一 key(两扇门配同一把 key)以获得收窄,或接受双行
显示。两扇门共享同一份额度,收窄只影响走哪扇门,不损失额度:
- 常量表:内置单行
[{ domestic: 'zhipu-coding-plan', intl: 'zai' }](lib/provider-variants.ts的REGION_VARIANT_GROUPS)。刻意不建通用配置表;未来出现新的区域入口对时扩展该常量即可。 - 判定与前提:双侧在池且 key 严格相等才收窄(key 相等 = 同账号证明;key 不等/缺失/解析失败
= 保守并存);池中只剩单侧(allowlist/reachability/静态排除/
provider_models已滤掉一侧)= 零 行为。收窄方向由china驱动:china: true保留zhipu-coding-plan、收起zai;china: false/缺省保留zai、收起zhipu-coding-plan。 - provider 级整体收窄:被收侧整个 provider 退出候选池(不参与评分),其独有模型一并让位—— 同账号一个入口足够。收窄作用于账号级基础池(与 selector 约束正交,scoped 约束的 scope 外槽位同样 去重);池构成或 key 状态变化(可达性/allowlist/静态排除翻转、重新登录换发 key 使收窄集翻转)纳入 selectionHealthKey 指纹,正确失效重算缓存。
- 旁路:pinned role(
agents:钉值)与显式 selector 不受去重影响——显式指令 > 去重。显式 selector 指定被收侧 provider 时该侧已不在池中,走既有「无命中」错误路径(provider 计数中不含被收 侧);出路 = 改china或调整 allowlist。 - 诊断:去重激活时 choices 附录追加标注行,如
**区域变体** zai → 已收起(同账号区域变体,已按区域偏好收起;保留 zhipu-coding-plan);zai的 family 标签显示为 Z.AI(显示名常量映射),与zhipu-coding-plan恒可区分。 - usage 栏同判定收窄:usage 栏的 provider 行应用同一收窄判定(含 key 严格相等门;在场的判定 =
有 usage 数据或凭据即可,不要求模型候选)——收窄激活时
china: true不渲染 zai 行;缺省 /china: false不渲染zhipu-coding-plan行、zai 行显示 Z.AI。key 不等/缺失时不收窄,两行 都渲染、以「智谱 / Z.AI」标签区分。两扇门共享同一份额度,保留侧行即权威展示,信息不丢;单侧在场 照常渲染。后台状态探测不停探被收侧(可达性记录保持新鲜)。 - 与 model_allowlist 的叠加:allowlist 是 provider 全串 glob 白名单(未列出即排除),china 是
家族黑名单,两者叠加生效。注意区域偏好切换时 allowlist 不会自动映射——想让两个区域入口都
可用就把两区域条目都写上(如
zhipu-coding-plan/*之外补zai/glm-5.3);若 allowlist 只列了 单侧条目,另一侧根本不进池、去重不触发(只写zhipu-coding-plan条目的现有配置即此形态,行为 不变)。国内用户「不要家族排除但要国内门」(china=false 偏好国内入口)也可用单侧 allowlist 化解。 - 与
provider_models(逐 provider 模型约束)的关系:两者同为池过滤阶段的合取输入、互不替代; 变体去重收窄整个 provider,provider_models收窄 provider 内的模型集合,同配时按交集生效(见下节)。
per-provider 模型目标(provider_auto / provider_auto_tiers / provider_models)
三个 provider 键控的顶层键(键均经规范化,ArkCodingPlan / arkcodingplan 与 ark-coding-plan
同源收敛),共同表达「每个 provider 的模型走什么」:
# 服务端路由哨兵:provider → 该家「Auto」目录模型 id(须与 catalog id 一致,
# 大小写不敏感)。命中的候选豁免本地启发式评分,按声明档位参选。
provider_auto:
ark-coding-plan: ark-code-latest
cursor: default
# 哨兵声明档位(tiny|low|mid|high|top);非法值 warn 丢弃,未声明的哨兵按 mid。
provider_auto_tiers:
ark-coding-plan: top
cursor: top
# 逐 provider 模型约束:列出的 provider 收窄到命中模型,未列出的 provider 不受限。
# 裸词按 id 全串锚定(`deepseek-flash` 精确命中,不会过匹配 deepseek-v4-flash);
# `*` 通配族(`glm-5.3*` 命中 5.3 族);含 `/` 的模式按 selector 三级语法求值。
provider_models:
deepseek: [deepseek-flash]
zhipu-coding-plan: [glm-5.3, glm-5.3-flash]
- 哨兵语义:catalog 的全 0 成本不构成比价优势——哨兵在同覆盖类、同档内让位于一切有真实
成本的候选,仅在对手耗尽或无竞争时胜出(备份定位);声明档位是哨兵的合法竞争力(如
cursor/default 声明 top 后,对启发式落档更低的 cursor 具体模型形成 tierGap 优势)。哨兵豁免
per-(provider, tier) 去重(无版本号 id 否则必被驱逐),但不豁免覆盖类别分区、配额(含
cursor bucket)、可达性排除与 allowlist。选中哨兵时 pickedReason / decision log 标注
auto与 declared tier。role pin 到哨兵(如agents: {coder: ark-coding-plan/ark-code-latest}) 走既有 pinned 路径直接生效。 - **「provider 只走哨兵」**用两键组合表达:
provider_models: {ark-coding-plan: [ark-code-latest], cursor: [default]}——收窄后该 provider 池内只剩哨兵。没有「provider 内哨兵优先于具体模型」的 隐式规则;想要这个效果就显式收窄。 provider_modelsvsmodel_allowlist:provider_models是追加快照式收窄——只约束列出 的 provider,未列出的全量参选;model_allowlist是全局白名单——未列出全排除。2026-09-12 撤销全局model_allowlist的反例:白名单无法表达「deepseek 只用 flash、zhipu 只用 5.3 族、 其余 provider 不受限」,会误伤 opencode-go 等未列 provider。两者可并存(合取)。- 两层合并陷阱:三键与
concurrency_ceiling_by_provider同为整键覆盖——项目层.omp/opsx.yml声明任一键即完整替换全局同名键(不做 per-key 叠加);只想在项目层追加 provider 时,把全局条目一并抄过来。 - cursor 区域风险:cursor 区域外会返回
Model not available in your region(不可重试,可达性 探测发现不了);excluded_providers仍是唯一的用户级排除开关,撞墙自行加回 (如excluded_providers: [cursor])。 - 哨兵 id 漂移:provider 改名其 Auto 目录条目后声明静默失效(哨兵回落普通启发式评分,无崩溃); 失效时启动/重算日志会 warn 一行指明该 provider 的哨兵 id 不在存活候选中。
速度感知选择(speed_aware)
自动选择排序中预留的 speed 槽位由此键驱动。数据源是宿主 ~/.omp/agent/agent.db 的 model_perf
表(只读:readonly 打开、整表一次 SELECT、零写入、零后台任务——仅在选择路径事件驱动读取,
模块级 TTL 缓存 300s;读失败静默回退空索引并以同 TTL 负缓存,不污染选择路径日志)。
# 速度感知选择。enabled 缺省 true(缺数据时零漂移,开启无风险面);
# mode 缺省 tie-break;min_samples 缺省 20(低于门槛的行视为未测度)。
speed_aware:
enabled: true
mode: tie-break # tie-break | aggressive
min_samples: 20
排序语义(tie-break,缺省):cost band 量化进既有的 cost 槽位(槽位次序不动)——
band(cost) = max(0, floor(log2(cost)) + 1)(cost > 0),边界为 2 的幂(band 1 = [$1,$2)…); 免费与缺价、以及 clamp 收编的 <$1 正价同落 band 0,带内按 cost 原值升序(免费先于一切正价)。 band 对 cost 全域单调,故跨带候选的相对序与纯 cost 序一致(quota-aware 基线零回归)。 同带候选再比实测 tok/s(sum(output_tokens)/sum(gen_ms)×1000)——band 即「同样经费」的 操作化定义(ratio-2 ≈ 同一价格档);speed 相等回落 cost 原值。aggressive(opt-in):speedKey 提到 band 之前——同覆盖类别、同 tier 轴内速度越过价带 (快而贵胜过慢而便宜)。类别分区与 tier 位次仍然不动。风险:系统性偏向高价快模型,故缺省关闭。
未测度候选(无数据 / 样本 <
min_samples/ provider_auto 哨兵)speedKey 恒为 -Infinity: 带内排在已测度候选之后,其相互之间仍按 band/cost 定序。选择器不做探索——想试新模型走 显式 role pin 或/pick-model <selector>;20 样本门槛在正常流量下数小时即达成。零漂移保证:
enabled: false时装配层零 IO(不打开 agent.db),速度索引为空时 band 退化 为 cost 原值序、speed 恒等——全序与引入本特性之前逐字节一致。可观测:选中候选已测度时,其 pickedReason / decision log 追加
speed=<tok/s>tok/s段 (数据层标注;/pick-model 渲染层不展示)。手工核查数据源:sqlite3 ~/.omp/agent/agent.db 'SELECT model_key, samples, output_tokens / gen_ms * 1000 AS tok_s, CASE WHEN ttft_samples > 0 THEN ttft_ms / ttft_samples END AS ttft_ms FROM model_perf ORDER BY tok_s DESC LIMIT 10;'两层合并:
speed_aware为对象键,与provider_*键同款整键覆盖——项目层声明即整份 替换全局,不做字段级叠加。
难度路由与质量带(difficulty_routing / stall_escalation / continuity_guard / quality_preference)
「分类在前、选择在后」的结构改造(纲领 W4,全键 opt-in):硬约束过滤(既有池过滤,零改动)→ prompt 难度分档 → 带内选择(W1-W3 冻结全序在偏移后目标下照常运行)→ stall 升档与连续性守门 → 质量带旋钮。
# 三开关全部缺省 false(结构改造 opt-in,W1-W3 的收益不依赖它们);
# quality_preference 缺省 balanced(独立于三开关的顶层旋钮)。
difficulty_routing:
enabled: false
stall_escalation:
enabled: false
window: 6 # 滑窗大小(LiteLLM 同款)
threshold: 3 # 同签名重复阈值(锚定最新一次调用)
continuity_guard:
enabled: false
weight: 0.6 # 换模所需最低分类置信,(0,1],越高越保守
quality_preference: balanced # balanced(缺省,逐字节现状)| cost | quality
- 难度分档 = 写入集 tier 目标偏移:纯函数
classifyTask(确定性、无 LLM、无状态)按启发式 信号族加权打分(推理标记 / 代码存在 / 技术术语 / 简单指令负向 / 多步模式 / 疑问复杂度 / 长度对数项),边界 0.15 / 0.35 / 0.60 映射simple / standard / complex / reasoning;偏移表simple −1 / standard 0 / complex +1 / reasoning +2(clamp [tiny, top]),只作用于写入集{smol, default, slow, vision}——非写入集 role 的目标恒不触碰(不复活全量覆盖)。分档是 选择输入的预修正:偏移后目标的 gap-0 组即「带内」,组内仍由冻结链既有键(成本/速度)决胜。 - 失败语义 = 安全侧强档:分类器异常或置信低于地板(0.35)一律落
complex——绝不因分类失败 或歧义静默降到最便宜档(反向规避 LiteLLM「未命中得 0.0 → 默认最便宜」的已证失效模式)。 plan-mode 感知基于 prompt 文本标记(宿主 plan-mode 状态对插件不可见)。 - stall 升档:会话域滑窗记录工具调用签名(
toolName + 键排序稳定序列化(args),长串截断、 序列化失败回退裸签名);锚定最新一次调用的签名在窗内重复 ≥threshold→ stall 生效, default role 目标 +1(clamp top),单 episode 只升一档、绝不自动降档;签名变化结束 episode (其后目标由分类常规接管,非降档动作)。数据源 =tool_execution_start事件(主源)+sessionManager.getEntries()只读转录回溯(降级源);两源皆不可得 → 惰化(无 stall 状态、 不升档、零 warn),维持现状即安全侧。采样只在主实例生效,子 agent binding 不采样。 - 连续性守门:自动重算点上,写入集 role 的在选模型(incumbent)仍在候选池时钉住不复换
(
guard=pin标注),速度/价格 tie-break 压不过钉住。换模仅当:①新 band 严格升级且分类置信 ≥weight;②stall 升档生效;③incumbent 被硬过滤出局(可用性优先)。incumbent band 未知时以standard中性档参与比较(simple 不触发升级换模)。显式命令(/pick-modelrefresh / reset / selector / choices、role-scope)绕过守门且不做新分类——显式指令 > 守门;重算沿用当前会话级 band。 quality_preference三态(顶层独立键,路由关/开均可独立生效):balanced(缺省):既有冻结全序逐字节执行——零漂移的结构保证是「不进入」旋钮分支;cost:tier 轴槽位内部容差带量化(槽位不移动)——同覆盖类内以最小有效 tier 轴值为锚,relGap ≤ 1(容差常量 T=1,不可配置)为带 0;带 0 内去轴化(tierGap/surge/rank 不参与, 成本/速度决胜,Azure「带内选最便宜」的字面落地,代价是带内放弃档位/声誉区分——知情选择); 带 1 内全链自洽,带 0 恒先于带 1(跨带质量序保持);provider_auto 哨兵按声明档参与量化(无特判);quality:质量分前缀分层(覆盖类, tier 轴, rank, reputation)——成本/速度无法越过一个轴位或 声誉差距(Azure Quality「无视成本」);哨兵 reputation=0 自然让位已知声誉模型(无特判)。
- T4 边界:路由重算只更新选择缓存(choices / dispatch 上下文 / decision 消费面),不新增
applyRoleModel/setModel调用点——无任何会话中途模型身份切换;role 模型应用仍只发生在既有 应用点(显式命令、autoselect 会话启动)。stall 采样与守门均为只读观测/选择后处理。 - 零漂移与关闭保证:三开关全 false +
balanced时,路由段整体跳过、偏移不施加、守门不进入、 比较器走既有分支——选择输出、事件订阅副作用与 W3 落地态逐字节一致(回归用例逐字节断言)。 逐键开启独立生效、互不依赖;关闭任一开关即回到关闭前行为。 - 可观测(数据层):路由启用且命中时
pickedReason/decision追加band=<band>、stall+1、guard=pin标注段;未命中/关闭时与既有格式逐字节一致。choices/widget 渲染层零编辑(标注仅 数据层可得,经 dispatch 提示的「原因」行可见)。 - 两层合并:
difficulty_routing/stall_escalation/continuity_guard为对象键,与speed_aware同款整键覆盖;quality_preference为标量键同??惯例。
子 agent 模型落点:session-scoped modelRoles(最小写入)
/pick-model <selector> / refresh / reset 应用选择结果时,插件写 OMP session 运行时
覆盖 settings.overrideModelRoles,但只写最小写入集 {smol, default, slow, vision}。
本节取代已归档 pick-model-batch-all-roles 的「覆盖宿主全部已知 role(10 内置 + 自定义)」
语义——其动机(scout 等内置 agent 跟随切换)经宿主源码实证由 OMP 原生解析链覆盖,全量写入
属过度供给:
smol← mid 档选中(coder pick)、default← high 档选中(reviewer pick)、slow← top 档补选(唯一保留的 supplemental 补选)、vision← 能力门控 pick(见下)。写入集之外的 role 一律不写,由 OMP 原生解析链接管:
role 不写时的解析去向 designer经 default 继承(继承集合 {smol, slow, designer})→ high 档 pick(与原显式写入等价) task@task未配置时回落主会话模型(主会话已被设为 high pick)commit/tiny消费方走枚举回退链 ["commit","smol",…]/["tiny","commit","smol"]/["tiny","smol"],终止于 smol 写入(mid 档)plan未配置时 plan mode 优雅保持当前模型(no-op) advisor静态 slow priority 链(固定候选表,不读写入、不继承 default) 自定义 role config 层原样保留,插件不再接管(也不再产生兜底告警) PAYG 红线(收窄口径):存在①类(月计划)候选时,写入集四 role 以及经写入/继承/枚举 终止的原生链(designer/task/commit/tiny)的最终解析不会落入 PAYG——四 role 的 pick 全部经覆盖类别分区排序产出,vision 能力门控写入同时防住 inspect_image 原生「任一图像模型」 兜底打到 PAYG 图像模型。advisor 静态链是明确的 opt-in 豁免(OMP 原生行为)。如需让 advisor 恢复跟随选择,在 OMP config 配
modelRoles.advisor: "@slow"自钉(复用写入集 slow 链随切换;model_role_tiers只能重定向所复用写入 role 的档位,无法让 advisor 本身 被写入);plan 同理可经modelRoles.plan自钉恢复跟随。model_role_tiers(opsx.yml)作用域:写入集四 role 的档位重定向(如slow: high) 与skip哨兵(该 role 不写覆盖、保留你的 config 值、不产生告警)。写入集之外 role 的 条目对写入惰化(解析校验与非法值告警保留;skip在非写入 role 上等价 no-op)。 例外:visionrole 固定按 high 档在图像模型中挑选,model_role_tiers.vision仅skip生效——其他 tier 值被静默接受但不改选档。vision 能力门控:vision 候选只从声明
input含image的模型中挑选;当前选择里没有 图像模型时,vision 不写覆盖(warn 汇总为vision(capability),绝不写入纯文本模型), vision 功能回落 config/default。恢复手段:换一个含图像模型的 selector 或用model_allowlist纳入图像模型;改model_role_tiers.vision的 tier 绕不过门控、也不改选档。auto role 的 agent 定义在
.omp/agents/*.mdfrontmatter 注入带引号的 tier alias (codermodel: "@smol",其余三者model: "@default");task 工具经 modelRoles 解析。 frontmatter 另含autoloadSkills(见「Agent 指令集」)——原skill:字段是宿主不解析的 死配置,已移除。因此所有经 role 解析的 agent 都跟随切换——包括插件未枚举的 OMP 内置/bundled/ project agent:scout(@smol)直读 smol 写入;commit/title/classifier 经枚举链落 smol; designer/@slow 经 slow 写入或 default 继承;task 继承主会话。
auto role 与 omp-alias(如
slow,值无/)的 dispatch 提示词不传model=, 由 frontmatter alias 声明式接管;pinned 具体 provider/model 仍显式传 model,行为不变。top 补选没有可用模型(候选耗尽/allowlist 全过滤)时,slow 不写覆盖、warn 汇总 (
no-pick),命令不中断;无任何模型时计划为空,与既有「模型注册表不可用」分支一致。覆盖为 session 作用域、不写盘:
overrideModelRoles只写 runtime overlay, session 结束即还原;不会修改你的~/.omp/agent/config.yml。每次应用前先clearOverride('modelRoles'),避免上一 selector 的旧 role 值残留;reset重算无约束 默认并覆盖(不清空,否则回落 config 硬钉)。
手动清理建议:若全局
~/.omp/agent/config.yml里有modelRoles.smol钉在会耗尽的 套餐模型(scout 会一直打它直到被 session 覆盖压过),或task.agentModelOverrides残留 已删除的tester等 key,可手动删除这些钉值;插件运行期间会以 session 空串中和 per-agent 钉值,但不改动你的配置文件。
/pick-model 输出格式(pick-model-ux)
四种命令(choices / selector 切换 / refresh / reset)与错误路径共用一套紧凑行式渲染:
状态行(符号 + 动作短语 + 约束段)→ 明细区(变更行,或 role 行 + agent 清单)→ 候选概览 →
脚注。无 markdown 表格、无 emoji、无分割线。符号每次渲染时读宿主 symbolPreset 设置
(unicode/nerd/ascii,缺省或非法回退 unicode),从 OMP SYMBOL_PRESETS 字形表按
SymbolKey 解析,插件零硬编码字形。内部量在展示层翻译为人话:覆盖类别 → 计划内 /
按量直连 / 按量兜底;tier → 轻量/低档/中档/高档/旗舰;role 配置 → 已指定/自动/
role 链;tier=/gap=/class= 等原始字段不再出现在输出中(数据层 pickedReason/
decision 原样保留)。
unicode preset 下 choices 实拍(合成数据):
ⓘ 当前选择 · constraint: selectors=glm* · scope=smol
主会话 zhipu-coding-plan/glm-5.3 — 计划内 · 旗舰
coder → zhipu-coding-plan/glm-5.3-flash — 计划内 · 中档 · 自动
code-reviewer → zhipu-coding-plan/glm-5.2 — 计划内 · 高档 · 自动
planner → zhipu-coding-plan/glm-5.2 — 计划内 · 高档 · 自动
proposal-reviewer → zhipu-coding-plan/glm-5.2 — 计划内 · 高档 · 自动
── agent 清单(10)──
项目
⏳ code-reviewer — 继承主会话
⏳ coder — 跟随切换 · role 链 @smol
⏳ designer — 跟随切换 · role 链 @designer(经 default 继承)
⏳ task — 继承主会话(@task 未配置回落主会话)
○ researcher — 固定 · 钉值 anthropic/claude-opus-4-6
内置
○ commit-title — OMP 自动链 @tiny(静态 priority 模式)
⏳ scout — 跟随切换 · role 链 @smol
── 候选 ──
deepseek [按量兜底]: 中档→deepseek-v4-flash
zhipu-coding-plan [计划内]: 旗舰→glm-5.3 高档→glm-5.2 中档→glm-5.3-flash
4 个候选参与评分 · 分区: 计划内 3 · 按量直连 0 · 按量兜底 1(兜底未参与)
/pick-model refresh 切换耗尽模型 · /pick-model update 更新 tier 数据
symbolPreset: nerd 时同一结构,状态符号换 Nerd Font 字形(如 status.info → U+F129 等
PUA 码位,普通日志里显示为空白)。切换 / 刷新 / 重置输出同构,状态行分别为
ⓘ 已切换 selector 约束 (selectors=… · scope=…) / ⓘ 已刷新 /
ⓘ 已清除 selector 约束,恢复默认选择,变更行形如
主会话 → provider/id(计划内 · 高档)、coder → provider/id(计划内 · 中档 · 自动);
跨 provider 歧义提示以脚注保留(提示: glm* 命中 zhipu-coding-plan(12) deepseek(3);用 zhipu-coding-plan*/… 可锁定单一 provider)。
全量 agent 清单:数据源为宿主 discoverAgents(project .omp/agents(含本插件
installAgents 落盘的 4 个 agent)→ user → 扩展/插件 → bundled 全量合并,沿用宿主优先级,
插件不自扫目录),按 项目 / 用户 / 内置 分组、组内按名排序。每行标注模型解析来源:
缺省 / @default / @task(未配置时)→ 继承主会话;@smol / @slow → role 链(跟随
切换);@designer → role 链(经 default 继承);@tiny / @advisor(未配置时)→ OMP
自动链(静态 priority 模式);字面 pattern → 钉值;其他自定义 @role → 自定义 role。
config modelRoles.* 自钉优先于清单标注(「未配置时」的分类才适用)。discoverAgents 失败
时降级为省略该段并 warn,不阻断命令。
开发
npm run typecheck
bun test