omnilane 0.42.9 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.zh-CN.md CHANGED
@@ -43,35 +43,133 @@ Gemini CLI** 之类。每一个都只接一个模型家族,所以你交代的每
43
43
 
44
44
  ## ⚡ 60 秒上手
45
45
 
46
- **最快的方式——用 npm 装:**
46
+ 你本人坐在终端前,现在就可以派工。
47
+
48
+ **1. 安装。**
47
49
 
48
50
  ```bash
49
- npm i -g omnilane # 装 CLI
50
- export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1 # 你是操作者本人,不是模型
51
- omnilane route hardest-coding "修掉会间歇失败的 auth token 更新测试"
52
- omnilane doctor # 看你手上有哪些 AI CLI / 金钥
53
- omnilane ui start # 选配:在浏览器即时看派工
51
+ npm i -g omnilane
54
52
  ```
55
53
 
56
- **或 clone 整包**(拿到路由表与可自订的技能):
54
+ 或者把仓库克隆下来,顺便拿到可以自定义的路由表和技能文件:
57
55
 
58
56
  ```bash
59
57
  git clone https://github.com/Seraphim0916/omnilane && cd omnilane
60
- ./install.sh # 侦测你的 CLI、接好技能、说你的语言
61
- export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1 # 你是操作者本人,不是模型
62
- omnilane route hardest-coding "修掉会间歇失败的 auth token 更新测试"
58
+ ./install.sh # finds your CLIs, links the skill, speaks your language
59
+ ```
60
+
61
+ **2. 看看手上有什么。** `doctor` 会列出 omnilane 能找到哪些模型 CLI 和 API 密钥,让你知道实际会运行哪一个。它不会改动任何东西。
62
+
63
+ ```bash
64
+ omnilane doctor
65
+ omnilane list # the routing table this machine resolves
66
+ ```
67
+
68
+ **3. 表明你是操作者,然后派工。**
69
+
70
+ ```bash
71
+ export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1
72
+ omnilane route hardest-coding "fix the flaky auth token refresh"
73
+ omnilane ui start # optional: watch jobs live in your browser
74
+ ```
75
+
76
+ > **为什么要那行 export?** omnilane 每次派工都会拿“提问者的能力分数”去比对,所以派工时必须说明是谁在问。真人说一次就够:`OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1`(或每次带 `--operator-asserted-human`)。模型不能替自己这样声明,它的身份是从启动它的 CLI 读出来的。两者都没有时,派工会在创建任何任务之前就被拒绝,代码是 `missing-caller-context`。
77
+
78
+ 真人用到这里就够了。下面这一节讲更实用的用法:让你的 AI 助手自己派工。
79
+
80
+ ## 🤖 让你的 AI 助手来驾驶 omnilane
81
+
82
+ 助手(Claude Code、Codex、Grok Build 或 Antigravity)会读取一份技能文件,里面教它怎么选通道、怎么派工。每台机器做一次,共四步。
83
+
84
+ ### 第 1 步:把技能交给助手
85
+
86
+ `./install.sh` 会为它找到的每个 CLI 建好链接。手动做法:
87
+
88
+ | 助手 | 做法 |
89
+ |---|---|
90
+ | Claude Code | `claude plugin marketplace add <本仓库路径>`,再 `claude plugin install omnilane@omnilane`(同时提供 `/route`、`/route-jobs` 和完工收件箱);或把 `skills/omnilane` 链接到 `~/.claude/skills/` |
91
+ | Codex | 把 `skills/omnilane` 链接到 `~/.codex/skills/` |
92
+ | Grok Build | `grok plugin install <本仓库路径> --trust` |
93
+ | Antigravity | `agy plugin install <本仓库路径>`(先用 `agy plugin validate <本仓库路径>` 检查) |
94
+
95
+ ### 第 2 步:证明一次“每个 CLI 真的会选到你指定的模型”
96
+
97
+ 模型来派工时,只能派给这台机器**证明过**的目标:例如 `codex -m gpt-5.6-sol` 真的运行的是 Sol。这份证明是一个本地文件,叫**传输覆盖文件(transport overlay)**。安装包里不会附带。没有它,每条通道都会用 `runtime-mapping-unverified` 拒绝模型调用者,`omnilane doctor` 也会警告 `no overlay configured`。
98
+
99
+ 请在普通的桌面终端里创建。(通过 ssh 登录的会话读不到 CLI 登录用的钥匙串,会把每一家都报告为未登录。)
100
+
101
+ ```bash
102
+ cd "$(npm root -g)/omnilane" # or your clone
103
+ ROOT=~/.omnilane/transport-evidence/first-sweep
104
+ python3 scripts/lib/probe_sweep.py --root "$ROOT" # one tiny prompt per selector, about 55 calls
105
+ python3 scripts/lib/build_overlay.py --root "$ROOT"
106
+ cp "$ROOT/transport-contracts.local.json" ~/.omnilane/transport-contracts.local.json
107
+ echo 'export OMNILANE_AA_TRANSPORT_OVERLAY="$HOME/.omnilane/transport-contracts.local.json"' >> ~/.omnilane/local.sh
108
+ omnilane doctor | grep transport-overlay # PASS, with a count per vendor
109
+ ```
110
+
111
+ 未登录的那一家会被标为 `unprobeable`,只是保持未验证,其他家照常可用。
112
+
113
+ ### 第 3 步:让这份证明自己保持最新,不用你动手
114
+
115
+ 覆盖文件用哈希值钉住每个 CLI 可执行文件,而**这些 CLI 会自己更新**,常常一周一次。更新之后,那一家的通道就会被拒绝,直到覆盖文件重新签署为止。`omnilane resign` 一条命令做完全部:找出哪里变了、只重新探测那一家、检查结果、换上新文件、发一笔真实派工确认,失败就自动恢复旧文件。
116
+
117
+ 它不是什么都签。变动过的 CLI 只有在“签署者与记录一致、并且装在同一类位置”时,才会**无人值守**地重新签署。所以先告诉它一次你接受哪些签署者:
118
+
119
+ ```bash
120
+ omnilane resign --record-signers # once, right after Step 2
121
+ ```
122
+
123
+ 如果你每次更新后都会自己修补某家 CLI、再用 adhoc 重新签署,也要告诉它一次(每家各一次);之后同一个安装目录里的 adhoc 新版也会无人值守地重新签署:
124
+
125
+ ```bash
126
+ omnilane resign --trust-adhoc claude # 只有你自己会把 claude 签成 adhoc 时才需要
127
+ ```
128
+
129
+ 然后让它每天运行一次。用什么调度器都行,但必须运行在**你的桌面登录会话里**(CLI 需要钥匙串)。macOS 可以用 LaunchAgent:
130
+
131
+ ```bash
132
+ cat > ~/Library/LaunchAgents/dev.omnilane.resign.plist <<'EOF'
133
+ <?xml version="1.0" encoding="UTF-8"?>
134
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
135
+ <plist version="1.0"><dict>
136
+ <key>Label</key><string>dev.omnilane.resign</string>
137
+ <key>ProgramArguments</key><array><string>/bin/zsh</string><string>-lc</string><string>omnilane resign</string></array>
138
+ <key>StartCalendarInterval</key><dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
139
+ <key>StandardOutPath</key><string>/tmp/omnilane-resign.log</string>
140
+ <key>StandardErrorPath</key><string>/tmp/omnilane-resign.log</string>
141
+ </dict></plist>
142
+ EOF
143
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.omnilane.resign.plist
63
144
  ```
64
145
 
65
- > **那个 export 是做什么的?** omnilane 会用调用者自己的能力分数来把关每一次派工,
66
- > 所以派工必须表明「是谁在问」。人类在终端前只要设一次
67
- > `OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1`,或每次带 `--operator-asserted-human`。
68
- > 模型驱动 omnilane 时**不能替自己主张**这个标志。它的身份会从启动它的 CLI 标志
69
- > (模型与强度)自动读取,一般 session 什么都不用带;`omnilane whoami` 会把这个身份
70
- > 打印成 `--caller-context FILE`。既没主张、又读不到身份的话,派工会在创建作业前就被
71
- > `missing-caller-context` 拒绝。
146
+ 本版实测过从桌面终端执行 `omnilane resign`,包括一次真实的、无人值守的 Codex 自动升级重签;上面这个 LaunchAgent 包装只是示例,本身没有实测过。请在你的机器上用 `launchctl kickstart gui/$(id -u)/dev.omnilane.resign` 确认。
147
+
148
+ `omnilane resign` 的退出码:
72
149
 
73
- > 第一次用?先跑 `omnilane doctor`——它会告诉你 omnilane 现在能接到哪些模型 CLI 与
74
- > API 金钥,你就知道实际会跑什么。
150
+ | 退出码 | 含义 | 你要做的事 |
151
+ |---|---|---|
152
+ | 0 | 没有东西变动,或变动的都已重签 | 不用做 |
153
+ | 10 | 只有 `--check` 会出现:有东西变了 | 执行 `omnilane resign` |
154
+ | 20 | 有一家需要你:签署者是新的或没有记录、可执行文件没有签名或在本地被改过、换了安装目录,或者供应商这次拒绝了上次通过的探测 | 读消息。它会打印“稍后重试”,或打印出你看过之后应执行的那一行 `omnilane resign --vendor V --approve V` |
155
+ | 30 | 重签后的覆盖文件没通过真实派工,已恢复为前一份 | 没有东西损坏,读日志即可 |
156
+ | 2 | 没有配置覆盖文件 | 做第 2 步 |
157
+
158
+ 有两个限制要知道。签署者检查用的是 macOS 的代码签名,所以在 Linux 上,每次 CLI 变动都会停在退出码 20 等你 `--approve`。另外,没有真正签名的可执行文件(例如在本地打过补丁的 CLI)一定会停下来等批准:没有任何东西能证明它来自原厂,而这正是这项检查存在的理由。
159
+
160
+ ### 第 4 步:在助手里面试试看
161
+
162
+ 请你的助手执行 `omnilane whoami`。它应该报告自己是哪个模型、哪个强度,以及一个分数。接着请它派一件小事:“用 omnilane 让 triage 通道数一数这个项目里有几个 TODO 注释”。
163
+
164
+ 如果被拒绝,拒绝消息会说明是哪一关没过、该怎么办:
165
+
166
+ | `failed_gate` | 白话 | 怎么修 |
167
+ |---|---|---|
168
+ | `caller-identity` | omnilane 看不出是哪个模型在问 | 让它把 `omnilane whoami` 作为那次工具调用里的**唯一一条命令**。Codex 尤其如此:后面接了 `; echo $?`、`&&` 或管道就读不到 |
169
+ | `target-transport` | 这台机器还没证明过那个目标,或 CLI 之后更新过 | `omnilane resign`(第 3 步) |
170
+ | `downward-ceiling` | 目标模型的分数比提问的模型高;模型只能平派或向下派 | 从拒绝消息列出的 `eligible_lanes` 里挑一条,或用更高的强度启动助手 |
171
+
172
+ Codex 的定时任务唤醒已有会话时不会记录强度。omnilane 这时会把该调用者限制在它那个模型的最低分,而不是直接拒绝:便宜的通道照常可用,贵的通道会告诉你需要哪个强度才派得动。
75
173
 
76
174
  ## 🧭 工作原理
77
175
 
@@ -159,6 +257,34 @@ flowchart LR
159
257
 
160
258
  </details>
161
259
 
260
+ ## 使用助手自己的子代理
261
+
262
+ omnilane 默认把工作交给厂商的命令行工具。当该做这件事的模型就是助手**自己那一家**的时候,再绕出去调用另一个 CLI 是多走一趟:多一次登录、多一个进程,那个 CLI 一更新又多一个会坏的地方。多数助手自己就能启动子代理,omnilane 可以改为替它规划这种工作。有两种做法。
263
+
264
+ **与助手运行完全相同内容的工人(`--inherit`)。** 助手启动子代理时*不指定模型、也不指定强度*,工人就是调用者的分身。分身不可能比本体强,而 omnilane 的分数检查要防的就只有这件事,所以这条路不需要厂商 CLI、不需要传输覆盖文件;调用者的强度读不到、或覆盖文件过期时,它照样能用。
265
+
266
+ ```sh
267
+ omnilane native-context --workdir /absolute/repo --inherits-caller-runtime # prints a capability file
268
+ omnilane route --inherit --native-context /path/printed/above --workdir /absolute/repo triage "Count the TODO comments"
269
+ # -> a PENDING handoff (JSON). The assistant now starts its own sub-agent with no
270
+ # model argument, checks the result, and records it:
271
+ omnilane jobs --json complete-native JOB_ID /absolute/completion.json
272
+ omnilane jobs --json status JOB_ID
273
+ ```
274
+
275
+ 诚实的部分:交接单上标着 `satisfies_lane_target: false`。通道在这里只是“这是哪一类工作”的标签。这样做出来的结果是“助手自己的子代理”做的,绝不是“hardest-coding 那个模型”做的;需要比调用者更强模型的通道,照样会被拒绝。`--inherits-caller-runtime` 是助手自己声明“我的子代理工具就是这样运作”,omnilane 观察不到。各家目前已知的情况:
276
+
277
+ | 助手 | 不带模型参数的子代理 |
278
+ |---|---|
279
+ | Claude Code | 官方文档写明会使用主会话的模型,强度沿用会话(除非代理定义另有设置)。内置 general-purpose 代理、且未设置 `CLAUDE_CODE_SUBAGENT_MODEL` 时成立。本版完整跑通过 |
280
+ | Codex | `collaboration.spawn_agent` 不带模型、不带强度。本版完整跑通过 |
281
+ | Grok Build | 文档写明沿用上层的模型(内置 `general-purpose` 代理是 `model: inherit`);强度没有文档说明。本版没有跑过 |
282
+ | Antigravity | `agy` 1.2.7 中找不到子代理工具。不适用 |
283
+
284
+ **助手的工具能指定的特定模型。** 把工具真正接受的内容写进能力声明文件(从 `omnilane native-context` 生成的文件开始,每组确切的模型与强度加一行),再用普通的 `omnilane route` 带上 `--native-context FILE`。只有某一行完全匹配时 omnilane 才会使用子代理:模型、强度、模式、工作目录、工具、隔离方式、生命周期都要对上。同一家厂商不等于同一个模型,也不会根据你装了哪些 CLI 去猜。`--executor native` 不匹配就失败、不回退;`--executor cli` 强制走外部 CLI。如果同厂商的目标只因为没给文件而走了 CLI,派工现在会明说。
285
+
286
+ 无论哪一种,子代理都共用助手的工具与文件系统:没有操作系统层面的沙箱,`advise`/`work` 是意图,不是强制。后台、常驻、具名会话、多轮、投票与 `sysops` 工作仍走 CLI。协议处理需要 Python 3.9 以上。结构定义、完成文件、代理复用与取消:见 [docs/native-executor.md](docs/native-executor.md)。
287
+
162
288
  ## 🖥️ Live Board
163
289
 
164
290
  每一次派发——无论前台还是 `--background`——都是落盘的一条 job。Live Board
@@ -556,6 +682,30 @@ codex 记在 session rollout,agy 写进 `cli.log`。这是 CLI 自己抄的订
556
682
 
557
683
  ## 📜 版本历程
558
684
 
685
+ ## v0.44.0 新功能
686
+
687
+ - **你自己修补过的 CLI 也能无人值守重新签署。** 如果本机有个步骤会在每次更新后修补某家 CLI、再用 adhoc 重新签署,以前签署者检查会把每一次这种更新都拦下来等 `--approve`。现在对该家运行一次 `omnilane resign --trust-adhoc VENDOR`,同一个安装目录里的 adhoc 新版就会和同签署者的更新一样,走每日 `omnilane resign` 自动重签。未签名的可执行文件、换了目录的 adhoc、其他厂商,仍然会停下来等你。信任记录在覆盖文件上,之后任何一家重签都会保留,而且是操作者动作,模型不会执行。四家都适用。
688
+ - **登录过期会提示“先登录”,不再说“稍后重试”。** `Failed to authenticate`、`OAuth session expired`、`Invalid API key`、`Unauthorized`、`401` 现在都会把该家标为无法探测,被拦下的消息会提示先登录。
689
+ - 升级:`npm i -g omnilane@0.44.0`。若从 0.42.x 升级,另外运行一次 `omnilane resign --record-signers`(见 0.43.0 说明)。
690
+
691
+ ## v0.43.1 新功能
692
+
693
+ 请安装这一版,不要安装 0.43.0。0.43.0 的 `build_overlay.py` 与 `probe.py` 在 Python 3.9 上一导入就会出错,导致该版本上首次安装的覆盖文件创建步骤与 `omnilane resign` 无法执行。其余没有变动,下面 0.43.0 的说明全部适用。升级:`npm i -g omnilane@0.43.1`,然后执行一次 `omnilane resign --record-signers`。
694
+
695
+ ## v0.43.0 新功能
696
+
697
+ 十天之内,0.42.x 有四次把所有模型调用者全部拒绝,每一次都是因为 omnilane 管不到的事实:启动器改了名、runner 脚本改了却没重签、四家厂商 CLI 在同一周各自更新、Codex 定时任务不记录强度。每一件都变成“什么都派不出去”。这一版把它们各自缩小成讲得清楚的结果,最常见的那一种还会自己修好。
698
+
699
+ - **厂商 CLI 更新了?`omnilane resign`。** 它会找出哪里变了、只重新探测那一家、检查结果、换上去、用一笔真实派工确认,失败就恢复旧的覆盖文件。只有新可执行文件的签署者相同、位置也相同时,才会**无人值守**地重签;其他情况会停下来,打印出你应执行的那一行 `--approve` 命令。先运行一次 `omnilane resign --record-signers`,再把 `omnilane resign` 安排为每天执行,之后 CLI 更新就不再是你的事。已用一次真实的 Codex 自动升级(0.155.0 → 0.155.1)验证:无需批准、映射全部保留、退出码 0。
700
+ - **拒绝消息会告诉模型该怎么办。** 每一笔被拒的派工都带着 `failed_gate`、`reason`、`next_command`、`required_caller_effort`,以及 `eligible_lanes`(这个调用者现在*派得动*的通道)。
701
+ - **没有记录强度改为缩小范围,不再全拒。** Codex 心跳定时任务会被限制在它那个模型的最低分,而不是每条通道都拒绝。便宜的通道照常可用,贵的会说明需要哪个强度。
702
+ - **助手自己的子代理。** `omnilane native-context` 会写出以前必须手写的能力声明文件;`omnilane route --inherit` 规划一个“调用者分身”工人:不经外部 CLI、不看覆盖文件、连调用者身份读不到时也能用,并且诚实标明*不是*该通道的目标模型。已在 Claude Code 与 Codex 桌面版完整跑通。
703
+ - **Codex:每次工具调用只下一条 omnilane 命令。** `omnilane whoami; echo $?` 读不到身份,单独的 `omnilane whoami` 读得到。拒绝消息现在会直接这样提示。
704
+ - **doctor 能看到搬了家的 CLI**(新版装在旧文件旁边),完全没有覆盖文件时会警告并附上步骤。
705
+ - **技能文件与教程重写。** 技能文件现在是模型照着走的五个步骤;这份 README 一步步带你让助手来驾驶 omnilane。
706
+ - **限制。** 无人值守重签依赖 macOS 代码签名;在 Linux 上,以及任何没有签名或在本地改过的 CLI,每次更新都会要求你 `--approve`。`--inherit` 还没有在 Grok Build 里跑过,Antigravity 没有提供子代理工具。完整细节见 [CHANGELOG](CHANGELOG.md)。
707
+ - **升级。** `npm i -g omnilane@0.43.0`,然后执行一次:`omnilane resign --record-signers`。
708
+
559
709
  ## v0.42.9 新功能
560
710
 
561
711
  - **经启动器带起的 Codex 桌面版。** ChatGPT.app 若通过 codex-profile-switch 启动 app-server,
package/README.zh-TW.md CHANGED
@@ -43,35 +43,133 @@ Gemini CLI** 之類。每一個都只接一個模型家族,所以你交代的每
43
43
 
44
44
  ## ⚡ 60 秒上手
45
45
 
46
- **最快的方式——用 npm 裝:**
46
+ 你本人坐在終端機前,現在就可以派工。
47
+
48
+ **1. 安裝。**
47
49
 
48
50
  ```bash
49
- npm i -g omnilane # 裝 CLI
50
- export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1 # 你是操作者本人,不是模型
51
- omnilane route hardest-coding "修掉會間歇失敗的 auth token 更新測試"
52
- omnilane doctor # 看你手上有哪些 AI CLI / 金鑰
53
- omnilane ui start # 選配:在瀏覽器即時看派工
51
+ npm i -g omnilane
54
52
  ```
55
53
 
56
- **或 clone 整包**(拿到路由表與可自訂的技能):
54
+ 或是把專案抓下來,順便拿到可以自訂的路由表與技能檔:
57
55
 
58
56
  ```bash
59
57
  git clone https://github.com/Seraphim0916/omnilane && cd omnilane
60
- ./install.sh # 偵測你的 CLI、接好技能、說你的語言
61
- export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1 # 你是操作者本人,不是模型
62
- omnilane route hardest-coding "修掉會間歇失敗的 auth token 更新測試"
58
+ ./install.sh # finds your CLIs, links the skill, speaks your language
59
+ ```
60
+
61
+ **2. 看看手上有什麼。** `doctor` 會列出 omnilane 找得到哪些模型 CLI 與 API 金鑰,讓你知道實際會跑哪一個。它不會改任何東西。
62
+
63
+ ```bash
64
+ omnilane doctor
65
+ omnilane list # the routing table this machine resolves
66
+ ```
67
+
68
+ **3. 表明你是操作者,然後派工。**
69
+
70
+ ```bash
71
+ export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1
72
+ omnilane route hardest-coding "fix the flaky auth token refresh"
73
+ omnilane ui start # optional: watch jobs live in your browser
74
+ ```
75
+
76
+ > **為什麼要那行 export?** omnilane 每次派工都會拿「發問者的能力分數」去比對,所以派工時得說清楚是誰在問。真人說一次就好:`OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1`(或每次帶 `--operator-asserted-human`)。模型不能替自己這樣宣稱,它的身分是從啟動它的 CLI 讀出來的。兩者都沒有的話,派工會在建立任何工作之前就被拒絕,代碼是 `missing-caller-context`。
77
+
78
+ 真人用到這裡就夠了。下面這一節講更實用的用法:讓你的 AI 助理自己派工。
79
+
80
+ ## 🤖 讓你的 AI 助理來開 omnilane
81
+
82
+ 助理(Claude Code、Codex、Grok Build 或 Antigravity)會讀一份技能檔,裡面教它怎麼選通道、怎麼派工。每台機器做一次,共四步。
83
+
84
+ ### 第 1 步:把技能交給助理
85
+
86
+ `./install.sh` 會替它找到的每個 CLI 建好連結。要手動的話:
87
+
88
+ | 助理 | 做法 |
89
+ |---|---|
90
+ | Claude Code | `claude plugin marketplace add <本專案路徑>`,再 `claude plugin install omnilane@omnilane`(同時附上 `/route`、`/route-jobs` 與完工收件匣);或把 `skills/omnilane` 連結進 `~/.claude/skills/` |
91
+ | Codex | 把 `skills/omnilane` 連結進 `~/.codex/skills/` |
92
+ | Grok Build | `grok plugin install <本專案路徑> --trust` |
93
+ | Antigravity | `agy plugin install <本專案路徑>`(先用 `agy plugin validate <本專案路徑>` 檢查) |
94
+
95
+ ### 第 2 步:證明一次「每個 CLI 真的會選到你指定的模型」
96
+
97
+ 模型來派工時,只能派給這台機器**證明過**的目標:例如 `codex -m gpt-5.6-sol` 真的跑的是 Sol。這份證明是一個本機檔案,叫**傳輸覆蓋檔(transport overlay)**。安裝包裡不會附。沒有它,每條通道都會用 `runtime-mapping-unverified` 拒絕模型呼叫者,`omnilane doctor` 也會警告 `no overlay configured`。
98
+
99
+ 請在一般的桌面終端機裡建立。(用 ssh 登入的工作階段讀不到 CLI 登入用的鑰匙圈,會把每一家都報成沒登入。)
100
+
101
+ ```bash
102
+ cd "$(npm root -g)/omnilane" # or your clone
103
+ ROOT=~/.omnilane/transport-evidence/first-sweep
104
+ python3 scripts/lib/probe_sweep.py --root "$ROOT" # one tiny prompt per selector, about 55 calls
105
+ python3 scripts/lib/build_overlay.py --root "$ROOT"
106
+ cp "$ROOT/transport-contracts.local.json" ~/.omnilane/transport-contracts.local.json
107
+ echo 'export OMNILANE_AA_TRANSPORT_OVERLAY="$HOME/.omnilane/transport-contracts.local.json"' >> ~/.omnilane/local.sh
108
+ omnilane doctor | grep transport-overlay # PASS, with a count per vendor
109
+ ```
110
+
111
+ 沒登入的那一家會被標成 `unprobeable`,就只是維持未驗證,其他家照常可用。
112
+
113
+ ### 第 3 步:讓這份證明自己保持最新,不用你動手
114
+
115
+ 覆蓋檔是用雜湊值釘住每個 CLI 執行檔的,而**這些 CLI 會自己更新**,常常一週一次。更新之後,那一家的通道就會被拒,直到覆蓋檔重簽為止。`omnilane resign` 一個指令做完全部:找出哪裡變了、只重新探測那一家、檢查結果、換上新檔、送一筆真實派工確認,失敗就自動還原舊檔。
116
+
117
+ 它不是什麼都簽。變動過的 CLI 只有在「簽署者跟紀錄上的相同、而且裝在同一類位置」時,才會**無人值守**地重簽。所以先告訴它一次你接受哪些簽署者:
118
+
119
+ ```bash
120
+ omnilane resign --record-signers # once, right after Step 2
121
+ ```
122
+
123
+ 如果你每次更新後都會自己修補某家 CLI、再用 adhoc 重新簽署,也要告訴它一次(每家各一次);之後同一個安裝目錄裡的 adhoc 新版也會無人值守地重簽:
124
+
125
+ ```bash
126
+ omnilane resign --trust-adhoc claude # 只有你自己會把 claude 簽成 adhoc 時才需要
127
+ ```
128
+
129
+ 接著讓它每天跑一次。用什麼排程器都行,但一定要跑在**你的桌面登入工作階段裡**(CLI 需要鑰匙圈)。macOS 可以用 LaunchAgent:
130
+
131
+ ```bash
132
+ cat > ~/Library/LaunchAgents/dev.omnilane.resign.plist <<'EOF'
133
+ <?xml version="1.0" encoding="UTF-8"?>
134
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
135
+ <plist version="1.0"><dict>
136
+ <key>Label</key><string>dev.omnilane.resign</string>
137
+ <key>ProgramArguments</key><array><string>/bin/zsh</string><string>-lc</string><string>omnilane resign</string></array>
138
+ <key>StartCalendarInterval</key><dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
139
+ <key>StandardOutPath</key><string>/tmp/omnilane-resign.log</string>
140
+ <key>StandardErrorPath</key><string>/tmp/omnilane-resign.log</string>
141
+ </dict></plist>
142
+ EOF
143
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.omnilane.resign.plist
63
144
  ```
64
145
 
65
- > **那個 export 是做什麼的?** omnilane 會用呼叫者自己的能力分數來把關每一次派工,
66
- > 所以派工必須表明「是誰在問」。人類在終端機前只要設一次
67
- > `OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1`,或每次帶 `--operator-asserted-human`。
68
- > 模型驅動 omnilane 時**不能替自己主張**這個旗標。它的身分會從啟動它的 CLI 旗標
69
- > (模型與強度)自動讀取,一般 session 什麼都不用帶;`omnilane whoami` 會把這個身分
70
- > 印成 `--caller-context FILE`。既沒主張、又讀不到身分的話,派工會在建立工作前就被
71
- > `missing-caller-context` 拒絕。
146
+ 本版實測過從桌面終端機執行 `omnilane resign`,包含一次真實的、無人值守的 Codex 自動升級重簽;上面這個 LaunchAgent 包裝只是範例,本身沒有實測過。請在你的機器上用 `launchctl kickstart gui/$(id -u)/dev.omnilane.resign` 確認。
147
+
148
+ `omnilane resign` 的結束碼:
149
+
150
+ | 結束碼 | 意思 | 你要做的事 |
151
+ |---|---|---|
152
+ | 0 | 沒有東西變動,或變動的都已重簽 | 不用做 |
153
+ | 10 | 只有 `--check` 會出現:有東西變了 | 執行 `omnilane resign` |
154
+ | 20 | 有一家需要你:簽署者是新的或沒紀錄、執行檔沒簽章或在本機被改過、換了安裝目錄,或供應商這次拒絕了上次通過的探測 | 讀訊息。它會印「稍後重試」,或印出你看過之後該執行的那一行 `omnilane resign --vendor V --approve V` |
155
+ | 30 | 重簽後的覆蓋檔沒通過真實派工,已還原成前一份 | 沒有東西壞掉,讀紀錄即可 |
156
+ | 2 | 沒有設定覆蓋檔 | 做第 2 步 |
157
+
158
+ 兩個限制要知道。簽署者檢查用的是 macOS 的程式碼簽章,所以在 Linux 上,每次 CLI 變動都會停在結束碼 20 等你 `--approve`。另外,沒有真正簽章的執行檔(例如在本機修補過的 CLI)一定會停下來等核准:沒有任何東西能證明它來自原廠,而這正是這項檢查存在的理由。
159
+
160
+ ### 第 4 步:在助理裡面試試看
72
161
 
73
- > 第一次用?先跑 `omnilane doctor`——它會告訴你 omnilane 現在能接到哪些模型 CLI 與
74
- > API 金鑰,你就知道實際會跑什麼。
162
+ 請你的助理執行 `omnilane whoami`。它應該回報自己是哪個模型、哪個強度,以及一個分數。接著請它派一件小事:「用 omnilane 讓 triage 通道數一數這個專案裡有幾個 TODO 註解」。
163
+
164
+ 如果被拒絕,拒絕訊息會說是哪一關沒過、該怎麼辦:
165
+
166
+ | `failed_gate` | 白話 | 怎麼修 |
167
+ |---|---|---|
168
+ | `caller-identity` | omnilane 看不出是哪個模型在問 | 讓它把 `omnilane whoami` 當成那次工具呼叫裡的**唯一一條指令**。Codex 尤其如此:後面接了 `; echo $?`、`&&` 或管線就讀不到 |
169
+ | `target-transport` | 這台機器還沒證明過那個目標,或 CLI 之後更新過 | `omnilane resign`(第 3 步) |
170
+ | `downward-ceiling` | 目標模型的分數比發問的模型高;模型只能平派或往下派 | 從拒絕訊息列出的 `eligible_lanes` 挑一條,或用更高的強度啟動助理 |
171
+
172
+ Codex 的排程喚醒既有對話串時不會記錄強度。omnilane 這時會把該呼叫者限制在它那個模型的最低分,而不是直接拒絕:便宜的通道照常可用,貴的通道會告訴你要哪個強度才派得動。
75
173
 
76
174
  ## 🧭 運作方式
77
175
 
@@ -141,37 +239,33 @@ flowchart LR
141
239
  - 點標準模型別名(例如 Opus)時,會鎖定技能表裡的確切模型家族。明確目標
142
240
  不存在或 CLI 不可用時會清楚失敗,不會暗中換廠商或模型家族。
143
241
 
144
- ## 原生優先派工,保留終端相容性
242
+ ## 用助理自己的子代理
145
243
 
146
- 模型路由與執行器(executor)分開判定。`--executor auto` 是預設:只有呼叫端明示的
147
- 結構化能力全部相符,才選原生子代理(native agent);一般終端沒有能力脈絡,
148
- 就保留既有 CLI。`--executor cli` 強制原流程;`--executor native` 遇到能力缺漏或不符就報錯。
149
- 同廠商不代表同模型;明確指定的廠商、模型及推理強度都保留。原生不符時,
150
- 自動模式會說明 CLI 原因,只使用同一個已解析目標,不換廠商或模型。
244
+ omnilane 預設把工作交給廠商的命令列工具。當該做這件事的模型就是助理**自己那一家**的時候,再繞出去叫另一個 CLI 是多走一趟:多一次登入、多一個程序,那個 CLI 一更新又多一個會壞的地方。多數助理自己就能開子代理,omnilane 可以改成替它規劃這種工作。有兩種做法。
151
245
 
152
- ```sh
153
- # 一般終端的預覽:不建立工作,也不呼叫供應商。
154
- omnilane route --executor auto --dry-run hardest-coding "檢查這次變更"
246
+ **跟助理跑一模一樣東西的工人(`--inherit`)。** 助理開子代理時*不指定模型、也不指定強度*,工人就是呼叫者的分身。分身不可能比本尊強,而 omnilane 的分數檢查要防的就只有這件事,所以這條路不需要廠商 CLI、不需要傳輸覆蓋檔;呼叫者的強度讀不到、或覆蓋檔過期時,它照樣能用。
155
247
 
156
- # 呼叫端依工具契約準備共享繼承能力 JSON;完整格式見下方文件。
157
- omnilane route --executor native --native-context /absolute/capability.json --workdir /absolute/repo hardest-coding "檢查這次變更"
158
- # 接著由呼叫端啟動原生代理、等待結果,再登錄真實證據。
248
+ ```sh
249
+ omnilane native-context --workdir /absolute/repo --inherits-caller-runtime # prints a capability file
250
+ omnilane route --inherit --native-context /path/printed/above --workdir /absolute/repo triage "Count the TODO comments"
251
+ # -> a PENDING handoff (JSON). The assistant now starts its own sub-agent with no
252
+ # model argument, checks the result, and records it:
159
253
  omnilane jobs --json complete-native JOB_ID /absolute/completion.json
160
254
  omnilane jobs --json status JOB_ID
161
- omnilane jobs --json result JOB_ID
162
- omnilane jobs --json list --status pending
163
255
  ```
164
256
 
165
- 原生路由輸出的是「等待執行」交接 JSON,不會從 shell 啟動原生代理,也不代表任務成功。
166
- Codex `collaboration.spawn_agent` 沒有沙箱、工具或工作目錄限制參數,會繼承父代理的工具與檔案系統權限。要求與同一能力列都必須明示 `shared-inherited`,工具陣列留空;`advise`/`work` 與工作目錄只是任務意圖,不是作業系統隔離。要求硬隔離時,自動模式保留同模型 CLI,強制原生則失敗。
257
+ 老實的部分:交接單上標著 `satisfies_lane_target: false`。通道在這裡只是「這是哪一類工作」的標籤。這樣做出來的結果是「助理自己的子代理」做的,絕不是「hardest-coding 那個模型」做的;需要比呼叫者更強模型的通道,照樣會被拒。`--inherits-caller-runtime` 是助理自己聲明「我的子代理工具就是這樣運作」,omnilane 觀察不到。各家目前已知的狀況:
258
+
259
+ | 助理 | 不帶模型參數的子代理 |
260
+ |---|---|
261
+ | Claude Code | 官方文件寫明會用主對話的模型,強度沿用工作階段(除非代理定義另有設定)。內建 general-purpose 代理、且沒設 `CLAUDE_CODE_SUBAGENT_MODEL` 時成立。本版完整跑通過 |
262
+ | Codex | `collaboration.spawn_agent` 不帶模型、不帶強度。本版完整跑通過 |
263
+ | Grok Build | 文件寫明沿用上層的模型(內建 `general-purpose` 代理是 `model: inherit`);強度沒有文件說明。本版沒跑過 |
264
+ | Antigravity | `agy` 1.2.7 找不到子代理工具。不適用 |
167
265
 
168
- 呼叫端以精確模型與推理強度啟動工作,最後登錄實際代理 ID、模型/推理強度/廠商/框架/後端、成敗、公開結果與證據。明示模型覆寫時使用 `fork_turns: "none"` 或有限的正整數歷史,不得搭配 `fork_turns: "all"`。路由已明示選中能力列中的精確模型時,可省略未知的呼叫端目前模型。重複登錄會被擋下;原生取消只改工作狀態,不發程序訊號,已啟動的代理由呼叫端另外停止。
266
+ **助理的工具能指定的特定模型。** 把工具真正接受的東西寫進能力聲明檔(從 `omnilane native-context` 產生的檔開始,每組確切的模型與強度加一列),再用一般的 `omnilane route` 帶上 `--native-context FILE`。只有某一列完全相符時 omnilane 才會用子代理:模型、強度、模式、工作目錄、工具、隔離方式、生命週期都要對上。同一家廠商不等於同一個模型,也不會從你裝了哪些 CLI 去猜。`--executor native` 不符就失敗、不回退;`--executor cli` 強制走外部 CLI。如果同廠商的目標只因為沒給檔案而走了 CLI,派工現在會明說。
169
267
 
170
- 背景、持久、即時、具名 CLI 工作階段、sysops、不支援的隔離、投票/仲裁及多輪路徑
171
- 仍走 CLI。原生只整合清單、狀態、結果、取消與完成登錄,未接 CLI 等待、重試、
172
- 信箱或目標迴圈。原生協定需要 Python 3.9+;一般終端 CLI 保留相容。
173
- 測試替身不等於真實原生驗收;主機 AGENTS 管理區塊只由父代理審查後同步。
174
- 詳見[能力與完成格式、完整範例及限制](docs/native-executor.md)。
268
+ 不管哪一種,子代理都共用助理的工具與檔案系統:沒有作業系統層級的沙箱,`advise`/`work` 是意圖,不是強制。背景、常駐、具名對話串、多輪、投票與 `sysops` 工作仍走 CLI。協定處理需要 Python 3.9 以上。結構定義、完成檔、代理重用與取消:見 [docs/native-executor.md](docs/native-executor.md)。
175
269
 
176
270
  <details>
177
271
  <summary><b>模型角色指引:仍須派工</b></summary>
@@ -597,6 +691,30 @@ codex 記在 session rollout,agy 寫進 `cli.log`。這是 CLI 自己抄的訂
597
691
 
598
692
  ## 📜 版本歷程
599
693
 
694
+ ## v0.44.0 新功能
695
+
696
+ - **你自己修補過的 CLI 也能無人值守重簽。** 如果本機有個步驟會在每次更新後修補某家 CLI、再用 adhoc 重新簽署,以前簽署者檢查會把每一次這種更新都擋下來等 `--approve`。現在對該家跑一次 `omnilane resign --trust-adhoc VENDOR`,同一個安裝目錄裡的 adhoc 新版就會跟同簽署者的更新一樣,走每日 `omnilane resign` 自動重簽。未簽章的執行檔、換了目錄的 adhoc、其他廠商,仍然會停下來等你。信任記在覆蓋檔上,之後任何一家重簽都會保留,而且是操作者動作,模型不會執行。四家都適用。
697
+ - **登入過期會叫你「先登入」,不再說「稍後重試」。** `Failed to authenticate`、`OAuth session expired`、`Invalid API key`、`Unauthorized`、`401` 現在都會把該家標成無法探測,被擋下來的訊息會叫你先登入。
698
+ - 升級:`npm i -g omnilane@0.44.0`。若是從 0.42.x 升上來,另外跑一次 `omnilane resign --record-signers`(見 0.43.0 說明)。
699
+
700
+ ## v0.43.1 新功能
701
+
702
+ 請裝這一版,不要裝 0.43.0。0.43.0 的 `build_overlay.py` 與 `probe.py` 在 Python 3.9 上一匯入就會出錯,導致該版本上首次安裝的覆蓋檔建立步驟與 `omnilane resign` 無法執行。其餘沒有變動,下面 0.43.0 的說明全部適用。升級:`npm i -g omnilane@0.43.1`,然後執行一次 `omnilane resign --record-signers`。
703
+
704
+ ## v0.43.0 新功能
705
+
706
+ 十天之內,0.42.x 有四次把所有模型呼叫者全部拒絕,每一次都是因為 omnilane 管不到的事實:啟動器改了名、runner 腳本改了卻沒重簽、四家廠商 CLI 在同一週各自更新、Codex 排程不記錄強度。每一件都變成「什麼都派不出去」。這一版把它們各自縮小成講得清楚的結果,最常見的那一種還會自己修好。
707
+
708
+ - **廠商 CLI 更新了?`omnilane resign`。** 它會找出哪裡變了、只重探那一家、檢查結果、換上去、用一筆真實派工確認,失敗就還原舊的覆蓋檔。只有新執行檔的簽署者相同、位置也相同時,才會**無人值守**地重簽;其他情況會停下來,印出你該執行的那一行 `--approve` 指令。先跑一次 `omnilane resign --record-signers`,再把 `omnilane resign` 排成每天執行,之後 CLI 更新就不再是你的事。已用一次真實的 Codex 自動升級(0.155.0 → 0.155.1)驗證:不需核准、映射全數保留、結束碼 0。
709
+ - **拒絕訊息會告訴模型該怎麼辦。** 每一筆被拒的派工都帶著 `failed_gate`、`reason`、`next_command`、`required_caller_effort`,以及 `eligible_lanes`(這個呼叫者現在*派得動*的通道)。
710
+ - **沒記錄強度改成縮小範圍,不再全拒。** Codex 心跳排程會被限制在它那個模型的最低分,而不是每條通道都拒。便宜的通道照常可用,貴的會說需要哪個強度。
711
+ - **助理自己的子代理。** `omnilane native-context` 會寫出以前得手寫的能力聲明檔;`omnilane route --inherit` 規劃一個「呼叫者分身」工人:不經外部 CLI、不看覆蓋檔、連呼叫者身分讀不到時也能用,而且老實標明*不是*該通道的目標模型。已在 Claude Code 與 Codex 桌面版完整跑通。
712
+ - **Codex:每次工具呼叫只下一條 omnilane 指令。** `omnilane whoami; echo $?` 讀不到身分,單獨的 `omnilane whoami` 讀得到。拒絕訊息現在會直接這樣提示。
713
+ - **doctor 看得到搬了家的 CLI**(新版裝在舊檔旁邊),完全沒有覆蓋檔時會警告並附上步驟。
714
+ - **技能檔與教學重寫。** 技能檔現在是模型照著走的五個步驟;這份 README 一步步帶你讓助理來開 omnilane。
715
+ - **限制。** 無人值守重簽靠的是 macOS 程式碼簽章;在 Linux 上,以及任何沒簽章或在本機改過的 CLI,每次更新都會要你 `--approve`。`--inherit` 還沒在 Grok Build 裡跑過,Antigravity 沒有提供子代理工具。完整細節見 [CHANGELOG](CHANGELOG.md)。
716
+ - **升級。** `npm i -g omnilane@0.43.0`,然後執行一次:`omnilane resign --record-signers`。
717
+
600
718
  ## v0.42.9 新功能
601
719
 
602
720
  - **經啟動器帶起的 Codex 桌面版。** ChatGPT.app 若透過 codex-profile-switch 啟動 app-server,
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.42.9
1
+ 0.44.0
package/bin/omnilane CHANGED
@@ -60,6 +60,30 @@ case "$cmd" in
60
60
  }
61
61
  exec python3 "$REPO/scripts/lib/caller_identity.py" "$@"
62
62
  ;;
63
+ native-context)
64
+ command -v python3 >/dev/null 2>&1 || {
65
+ echo "omnilane: native-context requires Python 3.9 or newer" >&2
66
+ exit 1
67
+ }
68
+ exec python3 "$REPO/scripts/lib/native_context.py" "$@"
69
+ ;;
70
+ resign)
71
+ command -v python3 >/dev/null 2>&1 || {
72
+ echo "omnilane: resign requires Python 3.9 or newer" >&2
73
+ exit 1
74
+ }
75
+ if [[ -z "${OMNILANE_AA_TRANSPORT_OVERLAY:-}" ]]; then
76
+ # local.sh is where a host records its overlay; dispatch reads it the same way.
77
+ OMNILANE_AA_TRANSPORT_OVERLAY="$(
78
+ set +u
79
+ home="${OMNILANE_HOME:-$HOME/.omnilane}"
80
+ [[ -f "$home/local.sh" ]] && . "$home/local.sh" 2>/dev/null
81
+ printf '%s' "${OMNILANE_AA_TRANSPORT_OVERLAY:-}"
82
+ )"
83
+ export OMNILANE_AA_TRANSPORT_OVERLAY
84
+ fi
85
+ exec python3 "$REPO/scripts/lib/resign.py" "$@"
86
+ ;;
63
87
  benchmark)
64
88
  command -v python3 >/dev/null 2>&1 || {
65
89
  echo "omnilane: benchmark requires Python 3.9 or newer" >&2
@@ -97,6 +121,11 @@ omnilane — one routing table, every harness
97
121
  omnilane release-audit [flags] offline, read-only release gate
98
122
  omnilane doctor [--json] [--strict] [--probe V] local health / opt-in live probe
99
123
  omnilane whoami caller-context file for the CLI this runs under
124
+ omnilane native-context [--workdir DIR] [--mode M] [--inherits-caller-runtime]
125
+ capability file that lets dispatch use this harness's own sub-agents
126
+ (--vendor V --model M: the host's statement when whoami cannot read it)
127
+ omnilane resign [--check] [--vendor V] [--approve V] [--record-signers] [--trust-adhoc V]
128
+ re-probe and re-sign the transport overlay after a CLI update
100
129
  omnilane benchmark [--json] [--run] [--vendor V] fixed quality/cost comparison
101
130
  omnilane ui start|status|url|stop
102
131
  omnilane configure interactive lane menu
@@ -53,7 +53,7 @@ _omnilane() {
53
53
  local command="${words[2]:-}" sub sub_index=3
54
54
  local -a lanes ids
55
55
  if (( CURRENT == 2 )); then
56
- _values 'command' version list route dispatch jobs doctor whoami benchmark release-audit ui configure completion help
56
+ _values 'command' version list route dispatch jobs doctor whoami native-context resign benchmark release-audit ui configure completion help
57
57
  return
58
58
  fi
59
59
  case "$command" in
@@ -58,7 +58,7 @@ _omnilane() {
58
58
  prev="${COMP_WORDS[COMP_CWORD-1]:-}"
59
59
  command="${COMP_WORDS[1]:-}"
60
60
  if [[ "$COMP_CWORD" -eq 1 ]]; then
61
- words="version list route dispatch jobs doctor whoami benchmark release-audit ui configure completion help"
61
+ words="version list route dispatch jobs doctor whoami native-context resign benchmark release-audit ui configure completion help"
62
62
  else
63
63
  case "$command" in
64
64
  route|dispatch)
@@ -11,6 +11,8 @@ complete -c omnilane -f -n __fish_use_subcommand -a jobs -d 'inspect ba
11
11
  complete -c omnilane -f -n __fish_use_subcommand -a mcp -d 'MCP stdio server'
12
12
  complete -c omnilane -f -n __fish_use_subcommand -a doctor -d 'read-only health report'
13
13
  complete -c omnilane -f -n __fish_use_subcommand -a whoami -d 'caller-context file for the launching CLI'
14
+ complete -c omnilane -f -n __fish_use_subcommand -a resign -d 're-probe and re-sign the transport overlay'
15
+ complete -c omnilane -f -n __fish_use_subcommand -a native-context -d 'capability file for this harness sub-agents'
14
16
  complete -c omnilane -f -n __fish_use_subcommand -a benchmark -d 'fixed quality/cost comparison'
15
17
  complete -c omnilane -f -n __fish_use_subcommand -a release-audit -d 'offline release gate'
16
18
  complete -c omnilane -f -n __fish_use_subcommand -a ui -d 'Live Board server'
@@ -88,6 +88,65 @@ workdir is task context, not an OS boundary. Requests for hard `read-only`,
88
88
  native jobs: `auto` stays on the same resolved model through CLI, while forced
89
89
  native fails closed.
90
90
 
91
+ ### Generating the capability file
92
+
93
+ `omnilane native-context [--workdir DIR]... [--mode advise|work]... [--harness NAME]
94
+ [--inherits-caller-runtime] [--out FILE]` writes a capability file for the
95
+ harness it runs under and prints the path. Vendor, current model and current
96
+ effort are read the way `omnilane whoami` reads them; nothing is inferred from
97
+ installed CLIs. It emits one capability row: the caller's own model at its own
98
+ effort. A caller whose effort is unrecorded gets `efforts: ["unverified"]`, which
99
+ matches no lane target, so that file serves `--inherit` only. Rows for other
100
+ models are the host's to add, from its real agent-tool contract.
101
+
102
+ `inherits_caller_runtime: true` is written only when the host passes
103
+ `--inherits-caller-runtime`. It is the host's statement that its sub-agent tool,
104
+ given no model override, runs the caller's model and effort.
105
+
106
+ What this release has and has not verified about each harness's sub-agent tool:
107
+
108
+ | Harness | Sub-agent tool | Status |
109
+ |---|---|---|
110
+ | Codex | `collaboration.spawn_agent`; a model override requires `fork_turns: "none"` or a bounded count | documented above from the tool contract; inheritance without an override is host-asserted |
111
+ | Claude Code | `Agent` tool; optional `model` override | documented (code.claude.com/docs/en/sub-agents, read 2026-09-20). Model resolves in this order: the per-invocation `model` parameter, the agent definition's `model` frontmatter, `CLAUDE_CODE_SUBAGENT_MODEL`, then the main conversation's model. Effort: the definition's `effort` frontmatter overrides the session level and its default is "inherits from session". So a host may pass `--inherits-caller-runtime` only when it spawns with no `model` argument, the agent type's definition sets neither `model` (other than `inherit`) nor `effort`, and `CLAUDE_CODE_SUBAGENT_MODEL` is unset; the built-in general-purpose agent meets the definition part. Two full `--inherit` cycles on 2026-09-20 under those conditions reported the parent's exact model. The worker cannot see its own effort, so completion reports `runtime.effort: "unknown"`; effort inheritance rests on the documentation, not on an observation |
112
+ | Grok Build | `spawn_subagent`; agent types `general-purpose`, `explore`, `plan` and user-defined ones | model: documented in the subagent guide embedded in grok 1.0.34 — "By default a subagent inherits the parent session's model"; only `[subagents.models].<agent>` in the config (highest priority) or the agent definition's `model` overrides that, and the bundled `general-purpose` definition says `model: inherit`. Effort: a definition may carry an `effort` override; no statement of the default was found, so effort inheritance is not verified. No `--inherit` cycle has been run in Grok |
113
+ | Antigravity | — | `agy` 1.2.7 exposes `--agent`, `--model` and `--effort` for the session and its help names no sub-agent spawning surface; not verified, and no `--inherit` cycle has been run |
114
+
115
+ ### Inherited worker (`--inherit`)
116
+
117
+ `dispatch.sh --inherit --native-context FILE <lane> "<task>"` plans a native
118
+ worker spawned with no model override. `aa_policy.decide_inherited` allows it
119
+ with code `native-inherited-allowed`: the worker runs the caller's runtime, so
120
+ its score equals the caller's and the downward rule holds by construction, with
121
+ or without a recorded effort. No target configuration is resolved
122
+ (`target_config_id: null`, `target: {"inherit": true, ...}`), the transport
123
+ overlay is not consulted, and there is no CLI fallback: a host that cannot take
124
+ it gets `native-inherit-unavailable` with `failed_gate: "native-capability"` and
125
+ the `omnilane native-context` command. The plan carries `inherit: true`,
126
+ `effort: "inherited"`, `worker_contract.model_override: false`,
127
+ `inherit_caller_runtime: true` and `satisfies_lane_target: false`. The capability
128
+ file must set `inherits_caller_runtime: true`, name the caller's vendor and
129
+ model, and hold a new-agent row for that model matching mode, workdir, tools,
130
+ `shared-inherited` and `single-shot`; effort is deliberately not matched.
131
+
132
+ When no caller identity reaches the gate, the file's `vendor` and `current_model`
133
+ stand as the host's statement and the decision is
134
+ `native-inherited-unverified-caller` (`caller_kind: "model-unverified"`,
135
+ `caller_identity_verified: false`, `caller_identity_source: "host-asserted"`). It
136
+ has no `effective_ceiling`, publishes neither `aa-authorizer.json` nor
137
+ `aa-child-context.json`, and `jobs status` shows `caller_identity_verified: false`
138
+ and `satisfies_lane_target: false`. A file with no `current_model` is refused as
139
+ `missing-caller-context`. `omnilane native-context --vendor V --model M` writes
140
+ such a file only when the identity cannot be read or resolved, marks it
141
+ `caller_identity_verified: false`, and exits 2 if the statement contradicts an
142
+ identity it can read. This exists for a caller that cannot be read, for example a
143
+ codex command wrapped in `;`, `&&` or a pipe, where the shell codex started stays
144
+ between codex and the command and carries no `CODEX_THREAD_ID` of its own; run
145
+ alone, the same command is read. Lane dispatch from an unread caller is refused.
146
+ Completion checks vendor, model and harness; `runtime.effort` is whatever the
147
+ host observed. A human operator, an unidentified caller, `--vendor`/`--model`/
148
+ `--effort`/`--target-config`, and every CLI-only lifecycle are refused.
149
+
91
150
  Native supports only a caller-supervised single task. `--background`, explicit
92
151
  `--live` / `--single-shot`, `--thread`, `sysops`, explicit/environment whole-job
93
152
  or idle watchdogs, vote/multi-round and `exec` arbitration paths stay CLI or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnilane",
3
- "version": "0.42.9",
3
+ "version": "0.44.0",
4
4
  "description": "One routing table, every harness — classify subtasks into lanes and delegate each lane through a compatible caller-owned native agent or vendor CLI.",
5
5
  "bin": {
6
6
  "omnilane": "bin/omnilane"