omnilane 0.42.2 → 0.42.5
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +76 -1
- package/README.ja.md +62 -0
- package/README.ko.md +62 -0
- package/README.md +65 -0
- package/README.zh-CN.md +59 -0
- package/README.zh-TW.md +59 -0
- package/VERSION +1 -1
- package/docs/release-notes-0.42.5.md +99 -0
- package/package.json +2 -2
- package/plugin.json +1 -1
- package/scripts/doctor.sh +26 -0
- package/scripts/lib/aa_policy.py +29 -2
- package/scripts/lib/build_overlay.py +189 -0
- package/scripts/lib/overlay_health.py +91 -0
- package/scripts/lib/probe.py +184 -0
- package/skills/omnilane/SKILL.md +90 -1
- package/docs/release-notes-0.42.2.md +0 -31
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Request-selector probe harness for the omnilane AA transport overlay.
|
|
3
|
+
|
|
4
|
+
Runs one CLI invocation, captures raw stdout/stderr to files, and writes a
|
|
5
|
+
descriptor with the original command/stream fields plus a vendor-specific
|
|
6
|
+
verdict, its reason, and the observed model when the response proves it.
|
|
7
|
+
"""
|
|
8
|
+
import argparse
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import time
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
SWEEP_ID = os.environ.get("OMNILANE_TRANSPORT_SWEEP_ID", "overlay-reprobe-20260909")
|
|
17
|
+
DEFAULT_ROOT = Path.home() / ".omnilane" / "transport-evidence" / SWEEP_ID
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def verdict(
|
|
21
|
+
evidence_json: dict,
|
|
22
|
+
stdout_text: str,
|
|
23
|
+
stderr_text: str,
|
|
24
|
+
vendor: str,
|
|
25
|
+
expected_token: str | None,
|
|
26
|
+
) -> tuple[str, str, str | None]:
|
|
27
|
+
"""Judge raw evidence without reading files, running commands or mutating it.
|
|
28
|
+
|
|
29
|
+
Only Claude's billed modelUsage keys currently prove the responding model.
|
|
30
|
+
A requested selector (including the Codex banner) is not observed identity.
|
|
31
|
+
"""
|
|
32
|
+
if evidence_json.get("timed_out"):
|
|
33
|
+
return "fail", "timeout", None
|
|
34
|
+
if not expected_token:
|
|
35
|
+
return "fail", "missing-expected-token", None
|
|
36
|
+
|
|
37
|
+
exit_code = evidence_json.get("exit_code")
|
|
38
|
+
if vendor == "claude":
|
|
39
|
+
try:
|
|
40
|
+
response = json.loads(stdout_text)
|
|
41
|
+
except (json.JSONDecodeError, TypeError):
|
|
42
|
+
return "fail", "invalid-json", None
|
|
43
|
+
if not isinstance(response, dict):
|
|
44
|
+
return "fail", "invalid-json-result", None
|
|
45
|
+
usage = response.get("modelUsage")
|
|
46
|
+
models = sorted(usage) if isinstance(usage, dict) else []
|
|
47
|
+
observed_model = ", ".join(models) or None
|
|
48
|
+
result = response.get("result", "")
|
|
49
|
+
if not isinstance(result, str):
|
|
50
|
+
result = str(result)
|
|
51
|
+
if response.get("is_error"):
|
|
52
|
+
lower_result = result.lower()
|
|
53
|
+
if "limit" in lower_result or "quota" in lower_result:
|
|
54
|
+
reason = "quota-exhausted"
|
|
55
|
+
elif "api error" in lower_result:
|
|
56
|
+
reason = "api-error"
|
|
57
|
+
else:
|
|
58
|
+
reason = "result-error"
|
|
59
|
+
return "fail", f"{reason}: {result[:120]}", observed_model
|
|
60
|
+
if not models:
|
|
61
|
+
return "fail", "missing-model-usage", None
|
|
62
|
+
command = evidence_json.get("command", [])
|
|
63
|
+
requested_model = None
|
|
64
|
+
for index, argument in enumerate(command):
|
|
65
|
+
if argument == "--model" and index + 1 < len(command):
|
|
66
|
+
requested_model = command[index + 1]
|
|
67
|
+
elif isinstance(argument, str) and argument.startswith("--model="):
|
|
68
|
+
requested_model = argument.split("=", 1)[1]
|
|
69
|
+
if not requested_model:
|
|
70
|
+
return "fail", "missing-requested-model", observed_model
|
|
71
|
+
if models != [requested_model]:
|
|
72
|
+
return "fail", "model-mismatch", observed_model
|
|
73
|
+
if "unknown --effort" in stderr_text.lower():
|
|
74
|
+
return "fail", "effort-silently-defaulted", observed_model
|
|
75
|
+
if exit_code != 0:
|
|
76
|
+
return "fail", f"exit-code: {exit_code}", observed_model
|
|
77
|
+
if expected_token not in result:
|
|
78
|
+
return "fail", "missing-expected-token", observed_model
|
|
79
|
+
return "pass", "expected-token-and-model-matched", observed_model
|
|
80
|
+
|
|
81
|
+
if vendor in ("grok", "agy"):
|
|
82
|
+
if exit_code != 0:
|
|
83
|
+
return "fail", f"exit-code: {exit_code}: {stderr_text[:120]}", None
|
|
84
|
+
if stderr_text:
|
|
85
|
+
return "fail", f"unexpected-stderr: {stderr_text[:120]}", None
|
|
86
|
+
if expected_token not in stdout_text:
|
|
87
|
+
return "fail", "missing-expected-token", None
|
|
88
|
+
return "pass", "expected-token-and-clean-stderr", None
|
|
89
|
+
|
|
90
|
+
if vendor == "codex":
|
|
91
|
+
diagnostics = [line[:120] for line in stderr_text.splitlines()
|
|
92
|
+
if "error" in line.lower() or "warning" in line.lower()]
|
|
93
|
+
review = "; stderr-review: " + " | ".join(diagnostics) if diagnostics else ""
|
|
94
|
+
if exit_code != 0:
|
|
95
|
+
return "fail", f"exit-code: {exit_code}{review}", None
|
|
96
|
+
if expected_token not in stdout_text:
|
|
97
|
+
return "fail", f"missing-expected-token{review}", None
|
|
98
|
+
return "pass", f"expected-token-matched{review}", None
|
|
99
|
+
|
|
100
|
+
return "fail", f"unsupported-vendor: {vendor}", None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def probe(
|
|
104
|
+
name: str,
|
|
105
|
+
argv: list[str],
|
|
106
|
+
timeout: int = 180,
|
|
107
|
+
cwd: Path | None = None,
|
|
108
|
+
root: Path | None = None,
|
|
109
|
+
*,
|
|
110
|
+
vendor: str | None = None,
|
|
111
|
+
expected_token: str | None = None,
|
|
112
|
+
) -> dict:
|
|
113
|
+
if not expected_token:
|
|
114
|
+
raise ValueError("expected_token is required before running a probe")
|
|
115
|
+
if not argv:
|
|
116
|
+
raise ValueError("command is required")
|
|
117
|
+
vendor = vendor or Path(argv[0]).name
|
|
118
|
+
if vendor not in ("claude", "grok", "agy", "codex"):
|
|
119
|
+
raise ValueError(f"unsupported vendor: {vendor}; pass vendor explicitly")
|
|
120
|
+
root = (root or DEFAULT_ROOT).expanduser()
|
|
121
|
+
evidence = root / "evidence"
|
|
122
|
+
work = root / "work"
|
|
123
|
+
evidence.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
work.mkdir(parents=True, exist_ok=True)
|
|
125
|
+
out_path = evidence / f"{name}.stdout"
|
|
126
|
+
err_path = evidence / f"{name}.stderr"
|
|
127
|
+
started = time.time()
|
|
128
|
+
timed_out = False
|
|
129
|
+
env = dict(os.environ)
|
|
130
|
+
# The runners drop the API key so the subscription OAuth path is used.
|
|
131
|
+
env.pop("XAI_API_KEY", None)
|
|
132
|
+
with open(out_path, "wb") as out, open(err_path, "wb") as err:
|
|
133
|
+
proc = subprocess.Popen(argv, stdout=out, stderr=err, stdin=subprocess.DEVNULL,
|
|
134
|
+
cwd=str(cwd or work), env=env)
|
|
135
|
+
try:
|
|
136
|
+
rc = proc.wait(timeout=timeout)
|
|
137
|
+
except subprocess.TimeoutExpired:
|
|
138
|
+
timed_out = True
|
|
139
|
+
proc.kill()
|
|
140
|
+
rc = proc.wait()
|
|
141
|
+
record = {
|
|
142
|
+
"command": argv,
|
|
143
|
+
"cwd": str(cwd or work),
|
|
144
|
+
"exit_code": rc,
|
|
145
|
+
"timed_out": timed_out,
|
|
146
|
+
"elapsed_seconds": round(time.time() - started, 3),
|
|
147
|
+
"probed_at": datetime.fromtimestamp(started, timezone.utc).isoformat(),
|
|
148
|
+
"stdout": str(out_path),
|
|
149
|
+
"stderr": str(err_path),
|
|
150
|
+
}
|
|
151
|
+
record["verdict"], record["verdict_reason"], record["observed_model"] = verdict(
|
|
152
|
+
record, out_path.read_text(errors="replace"), err_path.read_text(errors="replace"),
|
|
153
|
+
vendor, expected_token,
|
|
154
|
+
)
|
|
155
|
+
(evidence / f"{name}.json").write_text(json.dumps(record, indent=2) + "\n")
|
|
156
|
+
return record
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def read(
|
|
160
|
+
name: str,
|
|
161
|
+
stream: str = "stdout",
|
|
162
|
+
limit: int = 4000,
|
|
163
|
+
root: Path | None = None,
|
|
164
|
+
) -> str:
|
|
165
|
+
path = (root or DEFAULT_ROOT).expanduser() / "evidence" / f"{name}.{stream}"
|
|
166
|
+
if not path.exists():
|
|
167
|
+
return ""
|
|
168
|
+
return path.read_text(errors="replace")[:limit]
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if __name__ == "__main__":
|
|
172
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
173
|
+
parser.add_argument("--root", type=Path, default=DEFAULT_ROOT)
|
|
174
|
+
parser.add_argument("--expect", required=True, help="expected response token")
|
|
175
|
+
parser.add_argument("--vendor", choices=("claude", "grok", "agy", "codex"),
|
|
176
|
+
help="defaults to the command executable's basename")
|
|
177
|
+
parser.add_argument("name")
|
|
178
|
+
parser.add_argument("command", nargs=argparse.REMAINDER)
|
|
179
|
+
args = parser.parse_args()
|
|
180
|
+
if not args.command:
|
|
181
|
+
parser.error("command is required")
|
|
182
|
+
rec = probe(args.name, args.command, root=args.root,
|
|
183
|
+
vendor=args.vendor, expected_token=args.expect)
|
|
184
|
+
print(json.dumps(rec, ensure_ascii=False))
|
package/skills/omnilane/SKILL.md
CHANGED
|
@@ -17,7 +17,12 @@ You (the main loop) may be Claude, GPT, Grok, or Gemini. The procedure is identi
|
|
|
17
17
|
reading public results, acceptance, operator replies, git commit/push and
|
|
18
18
|
governance edits. Workers execute the assigned task and never delegate again.
|
|
19
19
|
Read-only work uses advise; edits require `--mode work --workdir <repo>`.
|
|
20
|
-
`<repo>/scripts/dispatch.sh [--executor auto|native|cli] [--native-context FILE] [--vendor V] [--mode work] [--workdir DIR] <lane> "<task>"`
|
|
20
|
+
`<repo>/scripts/dispatch.sh --caller-context FILE [--executor auto|native|cli] [--native-context FILE] [--vendor V] [--mode work] [--workdir DIR] <lane> "<task>"`
|
|
21
|
+
|
|
22
|
+
A model caller MUST pass `--caller-context`; without it every dispatch is
|
|
23
|
+
refused with `missing-caller-context` before a job exists. Build that file
|
|
24
|
+
before the first dispatch — see **Frozen exact-AA downward gate** for the
|
|
25
|
+
schema, a worked example, and what to do when your own effort is unverifiable.
|
|
21
26
|
|
|
22
27
|
Add `--background` for long tasks; poll with `scripts/jobs.sh status|result <id>`.
|
|
23
28
|
Use `--thread NAME` when later claude, codex, grok or gemini dispatches
|
|
@@ -332,6 +337,90 @@ that exact frozen score and the inherited ceiling. Targets at or below it are al
|
|
|
332
337
|
unknown identities and unresolved request-selector mappings fail closed. No family,
|
|
333
338
|
displayed grade, highest-effort assumption, retry or fallback grants an uplift.
|
|
334
339
|
|
|
340
|
+
Build the file before the first dispatch, not after a refusal. Every field is an
|
|
341
|
+
exact identity: `caller` must reproduce one `scored_configs` row byte-for-byte,
|
|
342
|
+
and `snapshot_id` must equal the registry's own `snapshot.id`.
|
|
343
|
+
|
|
344
|
+
```json
|
|
345
|
+
{
|
|
346
|
+
"schema_version": 1,
|
|
347
|
+
"snapshot_id": "<registry snapshot.id>",
|
|
348
|
+
"kind": "model",
|
|
349
|
+
"caller": {"vendor": "claude", "model": "claude-opus-5", "effort": "high",
|
|
350
|
+
"reasoning": "adaptive", "fallback": null},
|
|
351
|
+
"inherited_ceiling": 52
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Set `inherited_ceiling` to your own row's score when you are the root caller, or
|
|
356
|
+
to the ceiling you were handed when you are a child.
|
|
357
|
+
|
|
358
|
+
**When your harness names a model but no effort, look before you guess.** The
|
|
359
|
+
launching process usually carries the exact flags. Walk your own ancestor chain
|
|
360
|
+
(`ps -o ppid=,comm= -p <pid>` upward, then `ps -o args= -p <ancestor>`) and read
|
|
361
|
+
its `--model` / `--effort`. Match the ancestor chain rather than the first
|
|
362
|
+
matching process on the host — a second session of the same CLI is common and
|
|
363
|
+
its flags are not yours. This is request-selector evidence, the same class the
|
|
364
|
+
transport overlay carries, and it does not certify upstream identity.
|
|
365
|
+
|
|
366
|
+
Only when that genuinely yields nothing: ask the operator, or declare the
|
|
367
|
+
lowest-scoring row of your model and say so in your report. Understating only
|
|
368
|
+
narrows what you may dispatch to, so it fails in the safe direction — but it is
|
|
369
|
+
the fallback, not the first move, and an unnecessarily low ceiling silently
|
|
370
|
+
closes lanes and pushes the question back onto the operator. Never raise the
|
|
371
|
+
declared effort to unblock a refused target, and never assert
|
|
372
|
+
`--operator-asserted-human` on your own behalf.
|
|
373
|
+
|
|
374
|
+
Three refusal codes mean different things and need different fixes.
|
|
375
|
+
`missing-caller-context` means you passed no file — write one.
|
|
376
|
+
`runtime-mapping-unverified` means the file is fine but the *target* has no proven
|
|
377
|
+
host-local request selector; that is fixed by a `--transport-overlay` entry backed
|
|
378
|
+
by real evidence, never by editing the frozen registry (its sha256 is pinned in
|
|
379
|
+
`scripts/lib/aa_policy.py`, so any edit fails the whole gate closed).
|
|
380
|
+
`invalid-policy-input` with "transport contract evidence changed" is neither: the
|
|
381
|
+
overlay itself will not load, so nothing about your caller or your target is wrong.
|
|
382
|
+
Run `omnilane doctor` first — its `transport-overlay` check names the offending
|
|
383
|
+
file and the vendor it belongs to. Do not go hunting by hand.
|
|
384
|
+
|
|
385
|
+
Upgrading a vendor CLI is the usual cause. The overlay pins the sha256 of each
|
|
386
|
+
vendor's executable and runner script, so a new release invalidates that vendor's
|
|
387
|
+
selector evidence. Evidence entries carry a `vendor` tag: a tagged entry that
|
|
388
|
+
drifts marks only its own vendor stale, and the other three keep dispatching.
|
|
389
|
+
Untagged evidence — `probe-manifest.json`, and any overlay built before the tags
|
|
390
|
+
existed — still fails the whole gate closed, which is what an unpatched host
|
|
391
|
+
looks like. Codex and Claude evidence paths embed version directories
|
|
392
|
+
(`releases/0.153.4-…`, `versions/2.1.263`), so their upgrades remove the file
|
|
393
|
+
rather than change its digest; both are treated as staleness, not corruption.
|
|
394
|
+
|
|
395
|
+
Re-signing is a probe, a rebuild, and an install, in that order. Back up
|
|
396
|
+
`~/.omnilane/transport-contracts.local.json` first; restoring it is the rollback.
|
|
397
|
+
`scripts/lib/probe.py --expect TOKEN [--vendor V] NAME COMMAND…` invokes the CLI
|
|
398
|
+
directly through `subprocess`, so it works while the gate is refusing everything —
|
|
399
|
+
this is what breaks the deadlock. `scripts/provider-probe.sh` goes through
|
|
400
|
+
`dispatch.sh` and therefore through the gate, so it is useless in this state.
|
|
401
|
+
Then `scripts/lib/build_overlay.py` rebuilds, and you copy the result over the
|
|
402
|
+
live overlay. Verify with a real dispatch on a lane belonging to the vendor you
|
|
403
|
+
re-probed; loading the registry in Python is not the runtime surface.
|
|
404
|
+
|
|
405
|
+
Keep the sweep where its default `--root` puts it,
|
|
406
|
+
`~/.omnilane/transport-evidence/<sweep-id>/`. The rebuilt overlay anchors
|
|
407
|
+
`probe-manifest.json` by absolute path as untagged evidence, so a sweep parked
|
|
408
|
+
inside a repository is one `git clean -fdx` away from taking every vendor down
|
|
409
|
+
at once — the same global refusal a re-signing session is usually trying to end.
|
|
410
|
+
|
|
411
|
+
Never sign a probe you did not read. `probe.py` records a `verdict` because exit
|
|
412
|
+
status alone is not evidence: the Claude CLI answers a quota refusal with a JSON
|
|
413
|
+
body carrying `is_error`, and it accepts an unknown `--effort` by silently using
|
|
414
|
+
the default, returning exit 0, the right `modelUsage`, and the expected token
|
|
415
|
+
with only a stderr warning to show for it. Effort is half of a scored identity,
|
|
416
|
+
so that path would certify a mapping at the wrong tier. Configurations whose
|
|
417
|
+
probes failed are recorded in the overlay's `unproven[]` and surfaced by doctor
|
|
418
|
+
instead of vanishing — six Fable rows sat unusable for two days in September
|
|
419
|
+
2026 because a 429 quota refusal left no trace anywhere. A refused probe is not
|
|
420
|
+
always transient: re-probing those six two days later returned the same 429, so
|
|
421
|
+
an `unproven[]` entry can mean the account, not the moment. Read the reason
|
|
422
|
+
before assuming a retry will clear it.
|
|
423
|
+
|
|
335
424
|
A `--transport-overlay /absolute/overlay.json` may prove a small set of host-local
|
|
336
425
|
request selectors using exact identities and hashed local contract evidence. It does
|
|
337
426
|
not change frozen AA scores or certify upstream provider identity. The explicit
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Omnilane 0.42.2
|
|
2
|
-
|
|
3
|
-
This patch repairs the Grok single-shot reasoning-effort transport. It does not raise caller scores or remove the exact-AA downward-delegation gate.
|
|
4
|
-
|
|
5
|
-
## Changes
|
|
6
|
-
|
|
7
|
-
- Explicit Grok effort is forwarded as `--reasoning-effort VALUE`. The supported selector spellings are `low`, `medium`, `high`, and `xhigh`; invalid values fail before provider startup.
|
|
8
|
-
- Grok 4.6 entries in the default routing table explicitly select `high`.
|
|
9
|
-
- A scored Grok target requires a verified `cli_reasoning_effort` mapping and the exact `--reasoning-effort` flag. The existing host-local transport overlay validates host, snapshot, exact identity, evidence hashes, vendor, flag, and effort.
|
|
10
|
-
- Explicit effort on the live ACP path is rejected rather than discarded. This release does not add Grok work-mode network isolation on macOS.
|
|
11
|
-
|
|
12
|
-
## Verification boundary
|
|
13
|
-
|
|
14
|
-
The frozen AA registry and its approved SHA remain unchanged. Transport evidence belongs to the local host; the package does not ship a blanket assertion that every Grok model/effort combination is verified.
|
|
15
|
-
|
|
16
|
-
`request-selector-contract` proves how Omnilane selects the model and effort. It does not independently authenticate the upstream model's internal identity: `upstream_identity_verified` remains false. A successful reply alone is not an identity attestation.
|
|
17
|
-
|
|
18
|
-
For an existing local overlay, use `selector_type: cli_reasoning_effort`, `cli_flag: --reasoning-effort`, and matching model/effort identity only after checking the installed CLI and runner. Retain absolute evidence paths and SHA-256 hashes. Changed evidence requires re-verification; do not merely relabel an old mapping as verified. Select it with `--transport-overlay /absolute/overlay.json` or `OMNILANE_AA_TRANSPORT_OVERLAY`.
|
|
19
|
-
|
|
20
|
-
## Upgrade
|
|
21
|
-
|
|
22
|
-
After npm publication:
|
|
23
|
-
|
|
24
|
-
```sh
|
|
25
|
-
npm i -g omnilane@0.42.2
|
|
26
|
-
omnilane --version
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Repo-symlink installations use the updated checkout. Do not rerun `install.sh` just to update the version. Local routing overrides take precedence; review any Grok entries still using `-`.
|
|
30
|
-
|
|
31
|
-
GitHub release, npm publication, local provider smoke, and Linux CI are separate verification surfaces. Release evidence must identify each result rather than equating one with the others.
|