omnilane 0.42.8 → 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.md CHANGED
@@ -44,42 +44,170 @@ CLI or seven — dispatch picks the first candidate you actually have, and a lan
44
44
  with nothing available simply turns off. The default table works on a single
45
45
  subscription.
46
46
 
47
- **[⬇ Jump to the 60-second start](#-60-second-start)** · **[❓ Read the FAQ](#-faq)**
47
+ **[⬇ 60-second start](#-60-second-start)** · **[🤖 Let your AI assistant drive it](#-let-your-ai-assistant-drive-omnilane)** · **[❓ FAQ](#-faq)**
48
48
 
49
49
  ## ⚡ 60-second start
50
50
 
51
- **The quick way — install from npm:**
51
+ You, a person at a terminal, can dispatch right away.
52
+
53
+ **1. Install.**
52
54
 
53
55
  ```bash
54
- npm i -g omnilane # install the CLI
55
- export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1 # you are the operator, not a model
56
- omnilane route hardest-coding "fix the flaky auth token refresh"
57
- omnilane doctor # see which AI CLIs / keys you have
58
- omnilane ui start # optional: watch jobs live in your browser
56
+ npm i -g omnilane
59
57
  ```
60
58
 
61
- **Or clone the repo** (gets you the routing table and skill to customise):
59
+ Or clone it, which also gives you the routing table and the skill to customise:
62
60
 
63
61
  ```bash
64
62
  git clone https://github.com/Seraphim0916/omnilane && cd omnilane
65
63
  ./install.sh # finds your CLIs, links the skill, speaks your language
66
- export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1 # you are the operator, not a model
64
+ ```
65
+
66
+ **2. See what you have.** `doctor` lists which model CLIs and API keys omnilane
67
+ can reach, so you know what will actually run. It changes nothing.
68
+
69
+ ```bash
70
+ omnilane doctor
71
+ omnilane list # the routing table this machine resolves
72
+ ```
73
+
74
+ **3. Say you are the operator, then dispatch.**
75
+
76
+ ```bash
77
+ export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1
67
78
  omnilane route hardest-coding "fix the flaky auth token refresh"
79
+ omnilane ui start # optional: watch jobs live in your browser
80
+ ```
81
+
82
+ > **Why the export?** omnilane checks every delegation against the capability
83
+ > score of whoever is asking, so a dispatch has to say who that is. A human says
84
+ > it once with `OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1` (or `--operator-asserted-human`
85
+ > per call). A model cannot say it for itself: its identity is read from the CLI
86
+ > that launched it. With neither, the dispatch is refused with
87
+ > `missing-caller-context` before any job exists.
88
+
89
+ That is all a human needs. The rest of this section is for the more useful
90
+ setup: your AI assistant dispatching on its own.
91
+
92
+ ## 🤖 Let your AI assistant drive omnilane
93
+
94
+ The assistant (Claude Code, Codex, Grok Build or Antigravity) reads a skill file
95
+ that tells it how to pick a lane and dispatch. Four steps, once per machine.
96
+
97
+ ### Step 1 — Give the assistant the skill
98
+
99
+ `./install.sh` links it for every CLI it finds. By hand:
100
+
101
+ | Assistant | How |
102
+ |---|---|
103
+ | Claude Code | `claude plugin marketplace add <this repo>` then `claude plugin install omnilane@omnilane` (also gives `/route`, `/route-jobs` and the completion inbox), or link `skills/omnilane` into `~/.claude/skills/` |
104
+ | Codex | link `skills/omnilane` into `~/.codex/skills/` |
105
+ | Grok Build | `grok plugin install <this repo> --trust` |
106
+ | Antigravity | `agy plugin install <this repo>` (check first with `agy plugin validate <this repo>`) |
107
+
108
+ ### Step 2 — Prove, once, that each CLI selects the model it is asked for
109
+
110
+ A model caller is only allowed to dispatch to a target this machine has *proven*:
111
+ that `codex -m gpt-5.6-sol` really runs Sol, and so on. The proof is a local file,
112
+ the **transport overlay**. Nothing ships with one. Without it every lane refuses
113
+ a model caller with `runtime-mapping-unverified`, and `omnilane doctor` warns
114
+ `no overlay configured`.
115
+
116
+ Build it from a normal desktop terminal. (An ssh login cannot read the keychain
117
+ the CLIs log in with, so it would report them all as not logged in.)
118
+
119
+ ```bash
120
+ cd "$(npm root -g)/omnilane" # or your clone
121
+ ROOT=~/.omnilane/transport-evidence/first-sweep
122
+ python3 scripts/lib/probe_sweep.py --root "$ROOT" # one tiny prompt per selector, about 55 calls
123
+ python3 scripts/lib/build_overlay.py --root "$ROOT"
124
+ cp "$ROOT/transport-contracts.local.json" ~/.omnilane/transport-contracts.local.json
125
+ echo 'export OMNILANE_AA_TRANSPORT_OVERLAY="$HOME/.omnilane/transport-contracts.local.json"' >> ~/.omnilane/local.sh
126
+ omnilane doctor | grep transport-overlay # PASS, with a count per vendor
127
+ ```
128
+
129
+ A vendor you are not logged in to is reported `unprobeable` and simply stays
130
+ unverified; the others work.
131
+
132
+ ### Step 3 — Keep the proof current without doing it by hand
133
+
134
+ The overlay pins each CLI executable by hash, and **the CLIs update themselves**,
135
+ often weekly. After an update that vendor's lanes are refused until the overlay
136
+ is re-signed. `omnilane resign` does the whole job: finds what changed, re-probes
137
+ only that vendor, checks the result, swaps it in, sends one real dispatch to
138
+ confirm, and restores the old file if that fails.
139
+
140
+ It will not re-sign just anything. A changed CLI is re-signed **unattended** only
141
+ when it carries the same code-signing team as the one on record and sits in the
142
+ same install location. So tell it once which signers you accept:
143
+
144
+ ```bash
145
+ omnilane resign --record-signers # once, right after Step 2
146
+ ```
147
+
148
+ If you patch a vendor CLI yourself after every update and re-sign it adhoc,
149
+ tell omnilane that too, once per vendor; an adhoc update in the same install
150
+ directory is then re-signed unattended as well:
151
+
152
+ ```bash
153
+ omnilane resign --trust-adhoc claude # only if you re-sign claude adhoc yourself
154
+ ```
155
+
156
+ Then let it run every day. Any scheduler works as long as it runs **inside your
157
+ desktop login session** (the CLIs need the keychain). On macOS, a LaunchAgent:
158
+
159
+ ```bash
160
+ cat > ~/Library/LaunchAgents/dev.omnilane.resign.plist <<'EOF'
161
+ <?xml version="1.0" encoding="UTF-8"?>
162
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
163
+ <plist version="1.0"><dict>
164
+ <key>Label</key><string>dev.omnilane.resign</string>
165
+ <key>ProgramArguments</key><array><string>/bin/zsh</string><string>-lc</string><string>omnilane resign</string></array>
166
+ <key>StartCalendarInterval</key><dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
167
+ <key>StandardOutPath</key><string>/tmp/omnilane-resign.log</string>
168
+ <key>StandardErrorPath</key><string>/tmp/omnilane-resign.log</string>
169
+ </dict></plist>
170
+ EOF
171
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.omnilane.resign.plist
68
172
  ```
69
173
 
70
- > **Why that export?** Omnilane gates every delegation against the caller's own
71
- > capability score, so a dispatch has to say who is asking. A human at a terminal
72
- > asserts that once with `OMNILANE_AA_OPERATOR_ASSERTED_HUMAN=1`, or per call with
73
- > `--operator-asserted-human`. A model driving omnilane cannot assert it for
74
- > itself. Its identity is read from the nearest launching CLI: explicit model and
75
- > effort flags, or for Codex app-server/no-model launches, a bound current-turn
76
- > rollout (never app-server startup defaults). An ordinary session passes nothing,
77
- > and `omnilane whoami` prints that identity as a `--caller-context FILE`. With
78
- > neither an assertion nor a readable identity, the dispatch is refused with
79
- > `missing-caller-context` before any job is created.
174
+ This release verified `omnilane resign` from a desktop terminal session, including
175
+ a real unattended re-sign of a Codex self-update; the LaunchAgent wrapper above is
176
+ an example and was not itself exercised. Check it on your machine with
177
+ `launchctl kickstart gui/$(id -u)/dev.omnilane.resign`.
80
178
 
81
- > New to this? Run `omnilane doctor` first — it tells you which model CLIs and
82
- > API keys omnilane can already reach, so you know what will actually run.
179
+ What `omnilane resign` exits with:
180
+
181
+ | Exit | Meaning | You do |
182
+ |---|---|---|
183
+ | 0 | nothing had changed, or everything that changed was re-signed | nothing |
184
+ | 10 | `--check` only: something changed | run `omnilane resign` |
185
+ | 20 | a vendor needs you: new or missing signer, unsigned or locally patched binary, new install directory, or the provider refused probes that passed last time | read the message. It prints either "retry later" or the exact `omnilane resign --vendor V --approve V` to run after you have looked |
186
+ | 30 | the re-signed overlay failed its real dispatch and the previous one was restored | nothing is broken; read the log |
187
+ | 2 | no overlay is configured | do Step 2 |
188
+
189
+ Two limits to know. Signer checks use macOS code signatures, so on Linux every
190
+ changed CLI stops at exit 20 for your `--approve`. And a binary with no real
191
+ signature (a locally patched CLI, for example) always stops for approval: nothing
192
+ ties it to its vendor, which is the point of the check.
193
+
194
+ ### Step 4 — Try it from inside the assistant
195
+
196
+ Ask your assistant to run `omnilane whoami`. It should answer with the model and
197
+ effort it is running as, and a score. Then ask it to delegate something small:
198
+ "use omnilane to have the triage lane count the TODO comments in this repo".
199
+
200
+ If it is refused, the refusal says which check failed and what to do:
201
+
202
+ | `failed_gate` | In plain words | Fix |
203
+ |---|---|---|
204
+ | `caller-identity` | omnilane could not tell which model is asking | have it run `omnilane whoami` **as the only command** in that tool call. Codex in particular is unreadable behind `; echo $?`, `&&` or a pipe |
205
+ | `target-transport` | this machine has not proven that target, or a CLI updated since | `omnilane resign` (Step 3) |
206
+ | `downward-ceiling` | the target model scores higher than the model asking; a model may only delegate sideways or down | pick one of the `eligible_lanes` the refusal lists, or start the assistant at a higher effort |
207
+
208
+ A Codex automation that wakes an existing thread records no effort. omnilane
209
+ then holds that caller to its model's lowest score instead of refusing it: cheap
210
+ lanes keep working, expensive ones say which effort would reach them.
83
211
 
84
212
  ## 🧭 How it works
85
213
 
@@ -160,52 +288,59 @@ request; this is not a free-form shell parser in `dispatch.sh`.
160
288
  table. If an explicit target is absent or unavailable, the command fails
161
289
  clearly instead of falling back to another vendor or family.
162
290
 
163
- ## Native-first delegation, terminal-compatible
291
+ ## Using the assistant's own sub-agents
164
292
 
165
- Model routing and execution are separate. `--executor auto` (default) selects
166
- a caller-owned native tool only from explicit structured capabilities. Without
167
- that context a standalone terminal uses legacy CLI. `--executor cli` forces
168
- the old behavior; `--executor native` rejects missing/incompatible capability.
169
- Same vendor is not same model; explicit model/vendor/effort are preserved.
170
- On native rejection, auto reports a CLI reason and keeps the exact resolved
171
- target rather than substituting another vendor/model.
293
+ By default omnilane hands work to a vendor's command-line tool. When the model
294
+ that should do the work belongs to the assistant's **own** vendor, going out
295
+ through a second CLI is a detour: another login, another process, another thing
296
+ that breaks when that CLI updates. Most assistants can start a sub-agent
297
+ themselves, and omnilane can plan the work for that instead. Two ways.
172
298
 
173
- ```sh
174
- # Standalone terminal: CLI dry run, no jobs or provider calls.
175
- omnilane route --executor auto --dry-run hardest-coding "Review this change"
299
+ **A worker that runs exactly what the assistant runs (`--inherit`).** The
300
+ assistant starts its sub-agent *without choosing a model or an effort*, so the
301
+ worker is a copy of the caller. A copy cannot be stronger than the original,
302
+ which is the only thing omnilane's score check is there to prevent, so this path
303
+ needs no vendor CLI and no transport overlay, and it keeps working when the
304
+ caller's effort is unknown or an overlay has gone stale.
176
305
 
177
- # Host supplies honest shared/inherited capability JSON; inspect the linked schema.
178
- omnilane route --executor native --native-context /absolute/capability.json --workdir /absolute/repo hardest-coding "Review this change"
179
- # The host now spawns its native agent tool, waits and writes actual evidence.
306
+ ```sh
307
+ omnilane native-context --workdir /absolute/repo --inherits-caller-runtime # prints a capability file
308
+ omnilane route --inherit --native-context /path/printed/above --workdir /absolute/repo triage "Count the TODO comments"
309
+ # -> a PENDING handoff (JSON). The assistant now starts its own sub-agent with no
310
+ # model argument, checks the result, and records it:
180
311
  omnilane jobs --json complete-native JOB_ID /absolute/completion.json
181
312
  omnilane jobs --json status JOB_ID
182
- omnilane jobs --json result JOB_ID
183
- omnilane jobs --json list --status pending
184
313
  ```
185
314
 
186
- Native route emits **pending handoff JSON**, not a shell-native invocation or
187
- completed job. Codex `collaboration.spawn_agent` has no sandbox/tool/workdir
188
- restriction parameters and inherits parent tools/filesystem. Its honest request
189
- and matching capability row explicitly use `shared-inherited` with empty tool
190
- arrays; `advise`/`work` and workdir are task intent, not an OS boundary. Hard
191
- isolation remains same-model CLI in auto and rejects forced native.
192
-
193
- The caller spawns the real agent with the exact resolved model/effort, then
194
- ingests the actual agent ID, runtime model/effort/vendor/harness/backend,
195
- outcome, public result, and evidence. An explicit model override uses
196
- `fork_turns: "none"` or bounded positive history, never `fork_turns: "all"`.
197
- Unknown caller current model may be omitted when the route explicitly selects
198
- an exact model declared by the matching capability row. Duplicate completion is
199
- rejected. Native cancellation never signals PIDs; the caller separately stops
200
- any spawned agent.
201
-
202
- Background/durable/live/named CLI sessions, sysops, unsupported isolation,
203
- vote/arbitration and multi-round paths remain CLI-only. Native integration is
204
- limited to list/status/result/cancel/completion, not CLI wait/retry/mailbox or
205
- goal-loop. Protocol handling needs Python 3.9+; legacy terminal CLI remains
206
- compatible. Tests are fixtures, not live native acceptance. The parent alone
207
- syncs the host AGENTS managed block after review.
208
- See [schemas, complete examples and limitations](docs/native-executor.md).
315
+ The honest part: the handoff is marked `satisfies_lane_target: false`. The lane
316
+ is only a label for the kind of work. A result produced this way was made by
317
+ "the assistant's own sub-agent", never by "the hardest-coding model", and a lane
318
+ that needs a stronger model than the caller is still refused.
319
+ `--inherits-caller-runtime` is the assistant's own statement that its sub-agent
320
+ tool behaves this way; omnilane cannot observe it. What is known per assistant:
321
+
322
+ | Assistant | Sub-agent without a model argument |
323
+ |---|---|
324
+ | Claude Code | documented to use the main conversation's model, and the session's effort unless the agent definition sets one. True for the built-in general-purpose agent with `CLAUDE_CODE_SUBAGENT_MODEL` unset. Run end to end in this release |
325
+ | Codex | `collaboration.spawn_agent` with no model and no effort. Run end to end in this release |
326
+ | Grok Build | documented to inherit the parent's model (the bundled `general-purpose` agent is `model: inherit`); effort not documented. Not run in this release |
327
+ | Antigravity | no sub-agent tool found in `agy` 1.2.7. Not available |
328
+
329
+ **A specific model the assistant's tool can select.** Describe what the tool
330
+ really accepts in a capability file (start from `omnilane native-context`, add a
331
+ row per exact model and effort) and pass `--native-context FILE` to an ordinary
332
+ `omnilane route`. omnilane uses the sub-agent only when a row matches exactly:
333
+ model, effort, mode, workdir, tools, isolation and lifecycle. Same vendor is not
334
+ same model, and nothing is guessed from the CLIs you have installed.
335
+ `--executor native` fails instead of falling back; `--executor cli` forces the
336
+ external CLI. When a same-vendor target goes out through the CLI only because no
337
+ file was given, dispatch now says so.
338
+
339
+ Either way the sub-agent shares the assistant's tools and filesystem: there is no
340
+ operating-system sandbox, and `advise`/`work` are intent, not enforcement.
341
+ Background, live, named-thread, multi-round, vote and `sysops` work stays on the
342
+ CLI path. Protocol handling needs Python 3.9+. Schemas, the completion file,
343
+ agent reuse and cancellation: [docs/native-executor.md](docs/native-executor.md).
209
344
 
210
345
  <details>
211
346
  <summary><b>Model-role guidance (delegation still required)</b></summary>
@@ -637,6 +772,9 @@ When that fails, run `omnilane whoami` — it either prints a
637
772
  `--effort`, a model alias, no scored row). A model must not assert the human
638
773
  exemption for itself.
639
774
 
775
+ Every refusal is one JSON line on stderr. Read `failed_gate`, `reason` and
776
+ `next_command` first; `eligible_lanes` lists what you can still dispatch.
777
+
640
778
  `runtime-mapping-unverified` — your identity is fine, but the *target* has no
641
779
  proven host-local request selector. Either it was never probed, or its probe
642
780
  failed; `omnilane doctor` reports the count of such configurations and the
@@ -650,7 +788,13 @@ and runner-script hash, and Codex and Claude evidence paths embed version
650
788
  directories, so an upgrade removes the file rather than changing its digest.
651
789
  Tagged evidence degrades only its own vendor; untagged evidence, such as the
652
790
  probe manifest, still closes the whole gate. Doctor names the file and the
653
- vendor; the dispatch skill carries the re-signing runbook.
791
+ vendor; `omnilane resign` re-probes and re-signs it, and the dispatch skill
792
+ carries the runbook behind that command.
793
+
794
+ `target-above-effective-ceiling` — nothing is broken. The target scores above the
795
+ caller. `required_caller_effort` names the effort the calling session would need;
796
+ a caller marked `caller_degraded` was launched by a harness that recorded no
797
+ effort (a Codex heartbeat automation does this) and is held to its model's floor.
654
798
 
655
799
  </details>
656
800
 
@@ -706,6 +850,83 @@ working notes, including per-benchmark caveats, live in
706
850
 
707
851
  ## 📜 Release history
708
852
 
853
+ ## What's new in v0.44.0
854
+
855
+ - **A CLI you patch yourself is re-signed unattended too.** If a local step
856
+ re-signs a vendor CLI adhoc after every update (a post-update patch, for
857
+ instance), the signer check used to hold every such update for `--approve`.
858
+ Run `omnilane resign --trust-adhoc VENDOR` once per vendor and an adhoc update
859
+ in the same install directory now goes through the daily `omnilane resign`
860
+ like a same-signer one. An unsigned executable, an adhoc one in another
861
+ directory, and every other vendor still stop for you. The trust is recorded
862
+ on the overlay, survives later re-signs of any vendor, and is an operator
863
+ action a model never runs. Works for all four vendors.
864
+ - **An expired login says "log in", not "retry later".** `Failed to
865
+ authenticate`, `OAuth session expired`, `Invalid API key`, `Unauthorized` and
866
+ `401` now mark the vendor unprobeable, and the held-vendor message tells you
867
+ to log in first.
868
+ - Upgrade: `npm i -g omnilane@0.44.0`. If you are coming from 0.42.x, run
869
+ `omnilane resign --record-signers` once as well (see the 0.43.0 notes).
870
+
871
+ ## What's new in v0.43.1
872
+
873
+ Install this rather than 0.43.0. In 0.43.0, `build_overlay.py` and `probe.py`
874
+ failed to import on Python 3.9, which broke the first-install overlay build and
875
+ `omnilane resign` on that version. Nothing else changed; everything in the 0.43.0
876
+ notes below applies. Upgrade: `npm i -g omnilane@0.43.1`, then once:
877
+ `omnilane resign --record-signers`.
878
+
879
+ ## What's new in v0.43.0
880
+
881
+ In ten days 0.42.x refused every model caller four times, each time over a fact
882
+ omnilane does not control: a renamed launcher, a runner script changed without a
883
+ re-sign, four vendor CLIs updating themselves in one week, and a Codex automation
884
+ that records no effort. Each became "nothing can be dispatched". This release
885
+ turns each into a narrower, explained outcome, and repairs the common one itself.
886
+
887
+ - **Vendor CLI updated? `omnilane resign`.** It finds what changed, re-probes only
888
+ that vendor, checks the result, swaps it in, confirms with one real dispatch,
889
+ and restores the old overlay if that fails. It re-signs **unattended** only when
890
+ the new executable carries the same code-signing team and sits in the same
891
+ place; anything else stops with the exact `--approve` command for you. Run
892
+ `omnilane resign --record-signers` once, schedule `omnilane resign` daily, and
893
+ updates stop being your problem. Verified on a real Codex self-update
894
+ (0.155.0 → 0.155.1): no approval, every mapping kept, exit 0.
895
+ - **A refusal tells the model what to do.** Every refused dispatch now carries
896
+ `failed_gate`, `reason`, `next_command`, `required_caller_effort`, and
897
+ `eligible_lanes` — the lanes that caller *can* reach right now.
898
+ - **No recorded effort narrows instead of refusing.** A Codex heartbeat
899
+ automation is held to its model's lowest score rather than being refused on
900
+ every lane. Cheap lanes keep working; expensive ones say which effort is needed.
901
+ - **The assistant's own sub-agents.** `omnilane native-context` writes the
902
+ capability file that used to be hand-made, and `omnilane route --inherit` plans
903
+ a worker that is a copy of the caller: no external CLI, no overlay, works even
904
+ when the caller cannot be identified, and is honestly marked as *not* the
905
+ lane's target model. Run end to end in Claude Code and Codex desktop.
906
+ - **Codex: one omnilane command per tool call.** `omnilane whoami; echo $?`
907
+ cannot be identified, `omnilane whoami` alone can. The refusal now says so.
908
+ - **Doctor sees a CLI that moved** beside its old file, and warns, with the
909
+ steps, when no overlay exists at all.
910
+ - **Rewritten skill and tutorial.** The skill is now a five-step procedure a
911
+ model follows; this README walks through letting an assistant drive omnilane.
912
+ - **Limits.** Unattended re-signing relies on macOS code signatures; on Linux, and
913
+ for any unsigned or locally patched CLI, every update asks for `--approve`.
914
+ `--inherit` has not been run inside Grok Build, and Antigravity exposes no
915
+ sub-agent tool. Full detail: [CHANGELOG](CHANGELOG.md).
916
+ - **Upgrade.** `npm i -g omnilane@0.43.0`, then once: `omnilane resign --record-signers`.
917
+
918
+ ## What's new in v0.42.9
919
+
920
+ - **Codex desktop behind a launcher.** When ChatGPT.app starts its app-server
921
+ through codex-profile-switch, the process is named `codex-modified`, and 0.42.8
922
+ walked past it looking for one named `codex`, so every dispatch from such a
923
+ thread was refused with `missing-caller-context`. The name is now recognised;
924
+ the launcher's sibling `codex-code-mode-host` is still not treated as a CLI.
925
+ - **Accepted on a real desktop thread.** On 2026-09-13 `whoami` exited 0 from a
926
+ Codex desktop thread running through the launcher, reporting
927
+ `codex/gpt-5-6-sol-medium (score 46)` read from the `codex-modified` process.
928
+ - **Upgrade.** Run `npm i -g omnilane@0.42.9`.
929
+
709
930
  ## What's new in v0.42.8
710
931
 
711
932
  - **Codex current-turn identity.** `app-server` always ignores startup model and
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` 的退出码:
149
+
150
+ | 退出码 | 含义 | 你要做的事 |
151
+ |---|---|---|
152
+ | 0 | 没有东西变动,或变动的都已重签 | 不用做 |
153
+ | 10 | 只有 `--check` 会出现:有东西变了 | 执行 `omnilane resign` |
154
+ | 20 | 有一家需要你:签署者是新的或没有记录、可执行文件没有签名或在本地被改过、换了安装目录,或者供应商这次拒绝了上次通过的探测 | 读消息。它会打印“稍后重试”,或打印出你看过之后应执行的那一行 `omnilane resign --vendor V --approve V` |
155
+ | 30 | 重签后的覆盖文件没通过真实派工,已恢复为前一份 | 没有东西损坏,读日志即可 |
156
+ | 2 | 没有配置覆盖文件 | 做第 2 步 |
72
157
 
73
- > 第一次用?先跑 `omnilane doctor`——它会告诉你 omnilane 现在能接到哪些模型 CLI 与
74
- > API 金钥,你就知道实际会跑什么。
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,39 @@ 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
+
709
+ ## v0.42.9 新功能
710
+
711
+ - **经启动器带起的 Codex 桌面版。** ChatGPT.app 若通过 codex-profile-switch 启动 app-server,
712
+ 进程名是 `codex-modified`;0.42.8 只找名为 `codex` 的进程,会直接跳过它,该会话的每一次派发
713
+ 都被拒为 `missing-caller-context`。现在能识别这个名称;同目录的 `codex-code-mode-host` 仍不视为 CLI。
714
+ - **实机验收。** 2026-09-13 在经启动器带起的 Codex 桌面版会话执行 `whoami`,退出 0,
715
+ 读出 `codex/gpt-5-6-sol-medium (score 46)`,来源是 `codex-modified` 进程。
716
+ 升级:`npm i -g omnilane@0.42.9`。
717
+
559
718
  ## v0.42.8 新功能
560
719
 
561
720
  - **Codex 当前轮次身份。** `app-server` 始终忽略启动参数中的模型和强度默认值。