omnilane 0.42.9 → 0.45.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +260 -1
- package/README.ja.md +190 -32
- package/README.ko.md +190 -32
- package/README.md +319 -73
- package/README.zh-CN.md +190 -31
- package/README.zh-TW.md +180 -53
- package/VERSION +1 -1
- package/bin/omnilane +29 -0
- package/completions/_omnilane +1 -1
- package/completions/omnilane.bash +1 -1
- package/completions/omnilane.fish +2 -0
- package/config/aa-model-policy.json +1339 -1170
- package/docs/aa-model-coverage-2026-09-05.json +43 -1
- package/docs/model-capabilities-2026-09.md +195 -4
- package/docs/native-executor.md +59 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/routing.yaml +25 -23
- package/scripts/aa_rebaseline.py +409 -0
- package/scripts/configure.sh +1 -1
- package/scripts/dispatch.sh +54 -4
- package/scripts/doctor.sh +7 -1
- package/scripts/lib/aa_lanes.py +135 -0
- package/scripts/lib/aa_policy.py +165 -8
- package/scripts/lib/build_overlay.py +54 -18
- package/scripts/lib/caller_identity.py +75 -13
- package/scripts/lib/cli_provenance.py +82 -0
- package/scripts/lib/native.py +107 -2
- package/scripts/lib/native_context.py +145 -0
- package/scripts/lib/overlay_health.py +24 -2
- package/scripts/lib/probe.py +2 -0
- package/scripts/lib/probe_sweep.py +189 -0
- package/scripts/lib/resign.py +493 -0
- package/scripts/release-audit.sh +31 -0
- package/skills/omnilane/SKILL.md +393 -477
|
@@ -40,6 +40,8 @@ Selector = tuple[str, Optional[str], Optional[str]]
|
|
|
40
40
|
Lookup = Callable[[int], Optional[tuple[int, list[str]]]]
|
|
41
41
|
EnvironmentLookup = Callable[[int], dict[str, str]]
|
|
42
42
|
UUID_PATTERN = re.compile(r"[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}\Z")
|
|
43
|
+
CODEX_TOOL_HOSTS = frozenset(("codex-code-mode-host",))
|
|
44
|
+
SHELLS = frozenset(("zsh", "bash", "sh", "dash", "fish"))
|
|
43
45
|
TURN_END_EVENTS = frozenset(("task_complete", "turn_complete", "turn_aborted"))
|
|
44
46
|
CODEX_SANDBOX_REFUSAL = (
|
|
45
47
|
"Codex sandbox (CODEX_SANDBOX=seatbelt) blocked process inspection; omnilane also "
|
|
@@ -186,6 +188,17 @@ def resolve(registry: dict, vendor: str, model: str | None,
|
|
|
186
188
|
return None, f"no scored configuration for {vendor} {model} at effort {effort}"
|
|
187
189
|
|
|
188
190
|
|
|
191
|
+
def floor_row(registry: dict, vendor: str, model: str | None) -> dict | None:
|
|
192
|
+
"""The lowest-scored row of a known model, for a caller whose effort is unrecorded.
|
|
193
|
+
|
|
194
|
+
Whatever effort actually ran scores at least this much, so the floor can only
|
|
195
|
+
narrow what the caller may dispatch; it never widens it.
|
|
196
|
+
"""
|
|
197
|
+
rows = [row for row in registry["scored_configs"]
|
|
198
|
+
if row["vendor"] == vendor and row["model"] == model]
|
|
199
|
+
return min(rows, key=lambda row: (row["score"], row["id"])) if rows else None
|
|
200
|
+
|
|
201
|
+
|
|
189
202
|
def _thread_environment(entries: list[bytes]) -> dict[str, str]:
|
|
190
203
|
values = [entry.partition(b"=")[2] for entry in entries
|
|
191
204
|
if entry.partition(b"=")[0] == b"CODEX_THREAD_ID"]
|
|
@@ -281,7 +294,7 @@ def _launcher(pid: int, lookup: Lookup) -> tuple[int, Selector, int | None] | No
|
|
|
281
294
|
Nearest wins: a codex worker started by a Claude session is a codex caller.
|
|
282
295
|
"""
|
|
283
296
|
seen: set[int] = set()
|
|
284
|
-
|
|
297
|
+
below: list[tuple[int, list[str]]] = []
|
|
285
298
|
for _ in range(MAX_DEPTH):
|
|
286
299
|
if pid <= 0 or pid in seen:
|
|
287
300
|
return None
|
|
@@ -292,12 +305,25 @@ def _launcher(pid: int, lookup: Lookup) -> tuple[int, Selector, int | None] | No
|
|
|
292
305
|
ppid, argv = entry
|
|
293
306
|
selector = read_selector(argv)
|
|
294
307
|
if selector is not None:
|
|
295
|
-
return pid, selector,
|
|
296
|
-
|
|
308
|
+
return pid, selector, _thread_anchor(below)
|
|
309
|
+
below.append((pid, argv))
|
|
297
310
|
pid = ppid
|
|
298
311
|
return None
|
|
299
312
|
|
|
300
313
|
|
|
314
|
+
def _thread_anchor(below: list[tuple[int, list[str]]]) -> int | None:
|
|
315
|
+
"""The process whose initial environment codex wrote for this conversation.
|
|
316
|
+
|
|
317
|
+
That is codex's direct child, unless codex runs commands through its own tool
|
|
318
|
+
host: one host serves every conversation of an app-server, so it carries no
|
|
319
|
+
thread id and the process it starts for the command does.
|
|
320
|
+
"""
|
|
321
|
+
for pid, argv in reversed(below):
|
|
322
|
+
if not (argv and Path(argv[0]).name in CODEX_TOOL_HOSTS):
|
|
323
|
+
return pid
|
|
324
|
+
return None
|
|
325
|
+
|
|
326
|
+
|
|
301
327
|
def find_launcher(pid: int, lookup: Lookup = _process) -> tuple[int, Selector] | None:
|
|
302
328
|
try:
|
|
303
329
|
found = _launcher(pid, lookup)
|
|
@@ -402,11 +428,20 @@ def _rollout_selector(thread: str,
|
|
|
402
428
|
event, context_turn, event_turn = ended
|
|
403
429
|
raise ValueError(f"latest turn_context turn {context_turn} has already ended "
|
|
404
430
|
f"({event}, turn {event_turn})" + _rollout_age_hint(last_timestamp, now))
|
|
405
|
-
for key
|
|
431
|
+
for key in ("turn_id", "model"):
|
|
432
|
+
value = latest[key]
|
|
406
433
|
if not isinstance(value, str) or not value.strip():
|
|
407
434
|
detail = " (turn unknown)" if key == "turn_id" else ""
|
|
408
435
|
raise ValueError(f"latest turn_context {key} must be a non-empty string{detail}")
|
|
409
|
-
|
|
436
|
+
effort = latest["effort"]
|
|
437
|
+
source = f"thread {thread}, turn {latest['turn_id']}"
|
|
438
|
+
if effort is None:
|
|
439
|
+
# Heartbeat automations wake a thread without recording an effort. The
|
|
440
|
+
# model is still known, so the caller degrades instead of going blind.
|
|
441
|
+
return ("codex", latest["model"], None), source + ", turn_context records no effort"
|
|
442
|
+
if not isinstance(effort, str) or not effort.strip():
|
|
443
|
+
raise ValueError("latest turn_context effort must be a non-empty string")
|
|
444
|
+
return ("codex", latest["model"], effort), source
|
|
410
445
|
|
|
411
446
|
|
|
412
447
|
def read_caller(pid: int, lookup: Lookup = _process,
|
|
@@ -444,7 +479,17 @@ def read_caller(pid: int, lookup: Lookup = _process,
|
|
|
444
479
|
detail = f"errno {error.errno}" if isinstance(error, OSError) else "invalid environment block"
|
|
445
480
|
raise ValueError(f"cannot read codex direct child initial environment ({detail})") from None
|
|
446
481
|
if not inherited or not UUID_PATTERN.fullmatch(inherited):
|
|
447
|
-
|
|
482
|
+
entry = lookup(child)
|
|
483
|
+
name = Path(entry[1][0]).name if entry and entry[1] else "unknown"
|
|
484
|
+
hint = ""
|
|
485
|
+
if name.lstrip("-") in SHELLS:
|
|
486
|
+
# codex starts `zsh -lc '<command>'`; the shell sets the thread id and, for one
|
|
487
|
+
# simple command, execs into it, so that command is the direct child. With
|
|
488
|
+
# `;`, `&&`, `|` or a subshell the shell stays, and its own start had no id.
|
|
489
|
+
hint = ("; run the omnilane command as the only command of the tool call, "
|
|
490
|
+
"with no `;`, `&&`, `|` or subshell around it")
|
|
491
|
+
raise ValueError(f"codex direct child CODEX_THREAD_ID is missing or not a UUID "
|
|
492
|
+
f"(read from pid {child}, {name}){hint}")
|
|
448
493
|
if inherited != thread:
|
|
449
494
|
raise ValueError("CODEX_THREAD_ID mismatch between current process and codex direct child")
|
|
450
495
|
selector, source = _rollout_selector(thread, current_environment, now)
|
|
@@ -463,18 +508,25 @@ def load_registry(path: str | Path) -> tuple[dict, str]:
|
|
|
463
508
|
os.environ.update(saved)
|
|
464
509
|
|
|
465
510
|
|
|
466
|
-
def write_context(row: dict, registry: dict, home: Path) -> Path:
|
|
511
|
+
def write_context(row: dict, registry: dict, home: Path, effort_unverified: bool = False) -> Path:
|
|
467
512
|
"""One file per identity: every session launched the same way is the same caller."""
|
|
468
513
|
directory = Path(home) / "caller-context"
|
|
469
514
|
directory.mkdir(parents=True, exist_ok=True)
|
|
470
|
-
|
|
471
|
-
|
|
515
|
+
stem = row["id"].replace("/", "--")
|
|
516
|
+
if effort_unverified:
|
|
517
|
+
# Not the same caller as one that really launched at the floor effort.
|
|
518
|
+
stem = f"{row['vendor']}--{row['model'].replace('.', '-')}--effort-unverified"
|
|
519
|
+
path = directory / (stem + ".json")
|
|
520
|
+
value = {
|
|
472
521
|
"schema_version": 1,
|
|
473
522
|
"snapshot_id": registry["snapshot"]["id"],
|
|
474
523
|
"kind": "model",
|
|
475
524
|
"caller": {key: row[key] for key in aa_policy.IDENTITY_FIELDS},
|
|
476
525
|
"inherited_ceiling": row["score"],
|
|
477
|
-
}
|
|
526
|
+
}
|
|
527
|
+
if effort_unverified:
|
|
528
|
+
value["effort_unverified"] = True
|
|
529
|
+
text = json.dumps(value, indent=2, sort_keys=True) + "\n"
|
|
478
530
|
try:
|
|
479
531
|
if path.read_text() == text:
|
|
480
532
|
return path
|
|
@@ -507,14 +559,24 @@ def main(argv: list[str] | None = None, environment: Mapping[str, str] = os.envi
|
|
|
507
559
|
print(f"omnilane: cannot read the caller identity: {error}", file=sys.stderr)
|
|
508
560
|
return 3
|
|
509
561
|
row, reason = resolve(registry, vendor, model, effort)
|
|
562
|
+
degraded = False
|
|
563
|
+
if row is None and vendor == "codex" and model and effort is None:
|
|
564
|
+
row = floor_row(registry, vendor, model)
|
|
565
|
+
degraded = row is not None
|
|
510
566
|
if row is None:
|
|
511
567
|
print(f"omnilane: cannot read the caller identity from pid {pid}: {reason}", file=sys.stderr)
|
|
512
568
|
return 3
|
|
513
569
|
home = Path(os.environ.get("OMNILANE_HOME") or Path.home() / ".omnilane")
|
|
514
|
-
path = write_context(row, registry, home)
|
|
570
|
+
path = write_context(row, registry, home, effort_unverified=degraded)
|
|
515
571
|
provenance = f", {source}" if source else ""
|
|
516
|
-
|
|
517
|
-
|
|
572
|
+
if degraded:
|
|
573
|
+
print(f"omnilane: caller is {vendor}/{model} at an unrecorded effort; degraded to its "
|
|
574
|
+
f"lowest-scored row {row['id']} (ceiling {row['score']}), read from pid {pid}"
|
|
575
|
+
f"{provenance}. Lanes above that ceiling need a session launched with an "
|
|
576
|
+
f"explicit effort.", file=sys.stderr)
|
|
577
|
+
else:
|
|
578
|
+
print(f"omnilane: caller is {row['id']} (score {row['score']}), read from pid {pid}"
|
|
579
|
+
f"{provenance}", file=sys.stderr)
|
|
518
580
|
print(path)
|
|
519
581
|
return 0
|
|
520
582
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Who signed a vendor CLI, and whether a changed one may be re-signed unattended.
|
|
3
|
+
|
|
4
|
+
The transport overlay pins each vendor's executable by hash. Vendor CLIs update
|
|
5
|
+
themselves, so that pin goes stale as routine traffic. Re-pinning on sight would
|
|
6
|
+
turn the pin into a rubber stamp; this module is what stands in between. A
|
|
7
|
+
changed executable may be re-probed without an operator only when it still
|
|
8
|
+
carries the signer recorded when the overlay was last signed and still lives in
|
|
9
|
+
the same install location. Anything else is a notification, not a re-sign.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
import subprocess
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
ADHOC = "adhoc"
|
|
18
|
+
UNSIGNED = "unsigned"
|
|
19
|
+
# The operator's statement that this vendor's executable is expected to be adhoc
|
|
20
|
+
# here (a local patch step re-signs it), so an adhoc update in the same install
|
|
21
|
+
# location may be re-probed unattended. Nothing else is waived.
|
|
22
|
+
TRUST_ADHOC = "adhoc-in-install-location"
|
|
23
|
+
# 1.0.25 -> 1.0.30 inside a path is an update; a different directory is not.
|
|
24
|
+
VERSION_SEGMENT = re.compile(r"\d+(?:\.\d+)+(?:[-+][0-9A-Za-z.]+)*")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def facts(path: Path | str, runner=subprocess.run) -> dict:
|
|
28
|
+
"""codesign's view of one executable: the signing team, or why there is none."""
|
|
29
|
+
target = str(path)
|
|
30
|
+
try:
|
|
31
|
+
verified = runner(["codesign", "--verify", "--strict", target],
|
|
32
|
+
capture_output=True, text=True, timeout=60)
|
|
33
|
+
described = runner(["codesign", "-dv", "--verbose=2", target],
|
|
34
|
+
capture_output=True, text=True, timeout=60)
|
|
35
|
+
except (OSError, subprocess.SubprocessError) as error:
|
|
36
|
+
return {"signer": UNSIGNED, "identifier": None, "valid": False,
|
|
37
|
+
"detail": f"codesign unavailable: {error.__class__.__name__}"}
|
|
38
|
+
fields = {}
|
|
39
|
+
for line in described.stderr.splitlines():
|
|
40
|
+
key, separator, value = line.partition("=")
|
|
41
|
+
if separator and key not in fields:
|
|
42
|
+
fields[key] = value
|
|
43
|
+
valid = verified.returncode == 0
|
|
44
|
+
team = fields.get("TeamIdentifier")
|
|
45
|
+
if not valid:
|
|
46
|
+
signer = UNSIGNED
|
|
47
|
+
elif fields.get("Signature") == "adhoc" or team in (None, "not set"):
|
|
48
|
+
signer = ADHOC
|
|
49
|
+
else:
|
|
50
|
+
signer = team
|
|
51
|
+
return {"signer": signer, "identifier": fields.get("Identifier"), "valid": valid}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def family(path: Path | str) -> str:
|
|
55
|
+
"""An install location with its version numbers blanked out."""
|
|
56
|
+
return VERSION_SEGMENT.sub("<version>", str(path))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def verdict(recorded: dict | None, recorded_path: str | None, current: dict,
|
|
60
|
+
current_path: Path | str) -> tuple[bool, str]:
|
|
61
|
+
"""May this changed executable be re-probed unattended? And the reason either way."""
|
|
62
|
+
if current["signer"] == UNSIGNED:
|
|
63
|
+
return False, "the executable is unsigned, so nothing ties it to the vendor; an operator has to approve it"
|
|
64
|
+
if current["signer"] == ADHOC:
|
|
65
|
+
if (recorded or {}).get("operator_trust") == TRUST_ADHOC and recorded_path \
|
|
66
|
+
and family(recorded_path) == family(current_path):
|
|
67
|
+
return True, "adhoc, which the operator trusts in this install location"
|
|
68
|
+
if (recorded or {}).get("operator_trust") == TRUST_ADHOC:
|
|
69
|
+
return False, (f"adhoc and installed at {current_path}, outside the location the operator "
|
|
70
|
+
f"trusts, {family(recorded_path)}")
|
|
71
|
+
return False, ("the executable is adhoc, so nothing ties it to the vendor; an operator has to "
|
|
72
|
+
"approve it (or, for a local patch step, trust it here: omnilane resign --trust-adhoc VENDOR)")
|
|
73
|
+
if not recorded or not recorded.get("signer"):
|
|
74
|
+
return False, ("the live overlay recorded no signer for this vendor, so there is nothing "
|
|
75
|
+
"to compare against; an operator approves the first signer")
|
|
76
|
+
if recorded["signer"] != current["signer"]:
|
|
77
|
+
return False, (f"signed by {current['signer']}, but the overlay was signed against "
|
|
78
|
+
f"{recorded['signer']}")
|
|
79
|
+
if recorded_path and family(recorded_path) != family(current_path):
|
|
80
|
+
return False, (f"installed at {current_path}, outside the recorded location "
|
|
81
|
+
f"{family(recorded_path)}")
|
|
82
|
+
return True, f"still signed by {current['signer']} in the recorded install location"
|
package/scripts/lib/native.py
CHANGED
|
@@ -91,7 +91,11 @@ def capability_context(path):
|
|
|
91
91
|
ctx = read_json(path)
|
|
92
92
|
fields(ctx, ("schema_version", "harness", "vendor", "capabilities", "requirements"),
|
|
93
93
|
("current_model", "current_effort", "agent_strategy", "existing_agent",
|
|
94
|
-
"preserve_existing_context", "new_agent_capacity"
|
|
94
|
+
"preserve_existing_context", "new_agent_capacity", "inherits_caller_runtime",
|
|
95
|
+
"caller_identity_verified"))
|
|
96
|
+
for flag in ("inherits_caller_runtime", "caller_identity_verified"):
|
|
97
|
+
if flag in ctx:
|
|
98
|
+
check(type(ctx[flag]) is bool, "invalid " + flag)
|
|
95
99
|
check(type(ctx["schema_version"]) is int and ctx["schema_version"] == 1, "unsupported context version")
|
|
96
100
|
identifier(ctx["harness"], "harness")
|
|
97
101
|
identifier(ctx["vendor"], "vendor")
|
|
@@ -210,6 +214,89 @@ def choose(args, ctx):
|
|
|
210
214
|
return "native", "exact-idle-reuse-match" if strategy == "reuse" else "exact-capability-match", model
|
|
211
215
|
|
|
212
216
|
|
|
217
|
+
def choose_inherited(args, ctx, identity):
|
|
218
|
+
"""A worker spawned with no model override. There is no CLI to fall back to."""
|
|
219
|
+
if ctx is None:
|
|
220
|
+
return None, "no-native-context"
|
|
221
|
+
if ctx.get("inherits_caller_runtime") is not True:
|
|
222
|
+
return None, "host-does-not-assert-runtime-inheritance"
|
|
223
|
+
if args.background or args.session != "auto" or args.thread:
|
|
224
|
+
return None, "cli-session-lifecycle"
|
|
225
|
+
if args.job_timeout or args.idle_timeout:
|
|
226
|
+
return None, "cli-watchdog-required"
|
|
227
|
+
if args.mode == "sysops":
|
|
228
|
+
return None, "sysops-requires-cli"
|
|
229
|
+
if ctx["vendor"] != identity["vendor"]:
|
|
230
|
+
return None, "vendor-mismatch"
|
|
231
|
+
if ctx.get("current_model", identity["model"]) != identity["model"]:
|
|
232
|
+
return None, "current-model-mismatch"
|
|
233
|
+
if ctx.get("agent_strategy", "new") != "new" or "existing_agent" in ctx:
|
|
234
|
+
return None, "inherit-spawns-a-new-agent"
|
|
235
|
+
if ctx.get("new_agent_capacity") == "exhausted":
|
|
236
|
+
return None, "new-agent-capacity-exhausted"
|
|
237
|
+
req = ctx["requirements"]
|
|
238
|
+
if req["isolation"] != "shared-inherited" or req["lifecycle"] != "single-shot":
|
|
239
|
+
return None, "unsupported-isolation-or-lifecycle"
|
|
240
|
+
# Effort is deliberately not matched: inheriting it is the whole point.
|
|
241
|
+
caps = [cap for cap in ctx["capabilities"]
|
|
242
|
+
if cap["model"] == identity["model"] and cap.get("agent_strategy", "new") == "new"
|
|
243
|
+
and args.mode in cap["modes"] and args.workdir in cap["workdirs"]
|
|
244
|
+
and set(req["tools"]) <= set(cap["tools"])
|
|
245
|
+
and "shared-inherited" in cap["isolations"] and "single-shot" in cap["lifecycles"]]
|
|
246
|
+
if not caps:
|
|
247
|
+
return None, "no-capability-row-for-caller-model-mode-and-workdir"
|
|
248
|
+
return "native", "inherited-caller-runtime"
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def route_inherited(args, ctx, registry, registry_sha):
|
|
252
|
+
caller = caller_sha = None
|
|
253
|
+
if args.caller_context:
|
|
254
|
+
caller, caller_sha = aa_policy.load_caller(
|
|
255
|
+
args.caller_context, registry, args.expected_caller_sha256)
|
|
256
|
+
host = None
|
|
257
|
+
if caller is None and ctx is not None and "current_model" in ctx:
|
|
258
|
+
host = {"vendor": ctx["vendor"], "model": ctx["current_model"]}
|
|
259
|
+
decision = aa_policy.decide_inherited(
|
|
260
|
+
registry, registry_sha, caller=caller, caller_sha256=caller_sha,
|
|
261
|
+
operator_asserted_human=args.operator_asserted_human, host_asserted=host)
|
|
262
|
+
if not decision["allowed"]:
|
|
263
|
+
print(aa_policy._json_line(decision), end="", file=sys.stderr)
|
|
264
|
+
return 3
|
|
265
|
+
identity = caller["caller"] if caller is not None else host
|
|
266
|
+
executor, reason = choose_inherited(args, ctx, identity)
|
|
267
|
+
if executor is None:
|
|
268
|
+
# An inherited worker exists only inside the harness; the CLI is not a fallback for it.
|
|
269
|
+
decision.update(allowed=False, code="native-inherit-unavailable",
|
|
270
|
+
message="this harness cannot take an inherited native worker: " + reason,
|
|
271
|
+
failed_gate="native-capability", reason=reason, child_context=None,
|
|
272
|
+
next_command="omnilane native-context --workdir " + args.workdir)
|
|
273
|
+
print(aa_policy._json_line(decision), end="", file=sys.stderr)
|
|
274
|
+
return 3
|
|
275
|
+
plan = {"schema_version": 1, "executor": executor, "executor_reason": reason, "inherit": True,
|
|
276
|
+
"caller_identity_verified": decision["caller_identity_verified"],
|
|
277
|
+
"caller_identity_source": decision["caller_identity_source"],
|
|
278
|
+
"vendor": identity["vendor"], "model": identity["model"], "effort": "inherited",
|
|
279
|
+
"harness": ctx["harness"], "lane": args.lane, "mode": args.mode,
|
|
280
|
+
"workdir": args.workdir, "task": args.task,
|
|
281
|
+
"requirements": ctx["requirements"], "timeout": args.timeout,
|
|
282
|
+
"worker_contract": {
|
|
283
|
+
"no_nested_dispatch": True,
|
|
284
|
+
"isolation": ctx["requirements"]["isolation"],
|
|
285
|
+
"mode_is_task_intent": True,
|
|
286
|
+
"caller_enforces_deadline": True,
|
|
287
|
+
"model_override": False,
|
|
288
|
+
"inherit_caller_runtime": True,
|
|
289
|
+
"satisfies_lane_target": False,
|
|
290
|
+
},
|
|
291
|
+
"aa_policy": decision,
|
|
292
|
+
"state": "planned" if args.dry_run else "pending",
|
|
293
|
+
"job_id": None, "task_id": None, "agent_id": None,
|
|
294
|
+
"provider_invoked": False, "job_state_created": False,
|
|
295
|
+
"agent_strategy": "new", "existing_agent_id": None, "preserve_existing_context": False,
|
|
296
|
+
"new_agent_capacity": ctx.get("new_agent_capacity", "unknown")}
|
|
297
|
+
return publish_plan(args, plan, decision, registry, caller)
|
|
298
|
+
|
|
299
|
+
|
|
213
300
|
def stamp():
|
|
214
301
|
return datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
215
302
|
|
|
@@ -302,6 +389,8 @@ def route(args):
|
|
|
302
389
|
registry, registry_sha = aa_policy.load_registry(
|
|
303
390
|
args.policy, args.expected_registry_sha256
|
|
304
391
|
)
|
|
392
|
+
if getattr(args, "inherit", False):
|
|
393
|
+
return route_inherited(args, ctx, registry, registry_sha)
|
|
305
394
|
caller = None
|
|
306
395
|
caller_sha = None
|
|
307
396
|
if args.caller_context:
|
|
@@ -355,6 +444,10 @@ def route(args):
|
|
|
355
444
|
plan["worker_contract"].update(backend="collaboration.followup_task",
|
|
356
445
|
preserve_existing_context=True,
|
|
357
446
|
caller_rechecks_idle_before_followup=True)
|
|
447
|
+
return publish_plan(args, plan, policy_decision, registry, caller)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def publish_plan(args, plan, policy_decision, registry, caller):
|
|
358
451
|
if args.dry_run:
|
|
359
452
|
print(json_text(plan), end="")
|
|
360
453
|
return 0
|
|
@@ -393,6 +486,8 @@ def validate_completion(value, state):
|
|
|
393
486
|
for key in ("vendor", "model", "effort", "harness", "backend"):
|
|
394
487
|
text_value(runtime[key], "runtime " + key, 256)
|
|
395
488
|
for key in ("vendor", "model", "effort", "harness"):
|
|
489
|
+
if key == "effort" and state.get("inherit") is True:
|
|
490
|
+
continue # the host reports what it observed; nothing was requested
|
|
396
491
|
check(runtime[key] == state[key], "runtime " + key + " mismatch")
|
|
397
492
|
strategy = state.get("agent_strategy", "new")
|
|
398
493
|
if strategy == "reuse":
|
|
@@ -448,6 +543,10 @@ def job_command(args):
|
|
|
448
543
|
summary.update(agent_strategy=state.get("agent_strategy", "new"),
|
|
449
544
|
existing_agent_id=state.get("existing_agent_id"),
|
|
450
545
|
preserve_existing_context=state.get("preserve_existing_context", False))
|
|
546
|
+
if state.get("inherit"):
|
|
547
|
+
summary.update(inherit=True,
|
|
548
|
+
caller_identity_verified=state.get("caller_identity_verified", True),
|
|
549
|
+
satisfies_lane_target=False)
|
|
451
550
|
if args.action == "result":
|
|
452
551
|
summary["completion"] = state.get("completion")
|
|
453
552
|
if args.json:
|
|
@@ -463,8 +562,12 @@ def main():
|
|
|
463
562
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
464
563
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
465
564
|
p = sub.add_parser("route")
|
|
466
|
-
for key in ("home", "lane", "
|
|
565
|
+
for key in ("home", "lane", "workdir", "task"):
|
|
467
566
|
p.add_argument("--" + key, required=True)
|
|
567
|
+
for key in ("vendor", "model", "effort"):
|
|
568
|
+
p.add_argument("--" + key, default="")
|
|
569
|
+
p.add_argument("--inherit", action="store_true",
|
|
570
|
+
help="spawn a worker that inherits the caller's own model and effort")
|
|
468
571
|
p.add_argument("--executor", choices=("auto", "native", "cli"), required=True)
|
|
469
572
|
p.add_argument("--mode", choices=("advise", "work", "sysops"), required=True)
|
|
470
573
|
p.add_argument("--context")
|
|
@@ -488,6 +591,8 @@ def main():
|
|
|
488
591
|
p.add_argument("job_id")
|
|
489
592
|
p.add_argument("input", nargs="?")
|
|
490
593
|
args = parser.parse_args()
|
|
594
|
+
if args.command == "route" and not args.inherit and not args.vendor:
|
|
595
|
+
parser.error("--vendor is required unless --inherit is given")
|
|
491
596
|
try:
|
|
492
597
|
return route(args) if args.command == "route" else job_command(args)
|
|
493
598
|
except (ValueError, OSError, RecursionError, TypeError, KeyError) as error:
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Write the native capability file for the harness this runs under.
|
|
3
|
+
|
|
4
|
+
Dispatch only hands work to a harness's own sub-agent tool when the harness says
|
|
5
|
+
what that tool can do; without such a file every same-harness target goes out
|
|
6
|
+
through an external CLI. This writes the file from what is observable: the
|
|
7
|
+
caller's vendor, model and effort are read exactly as `omnilane whoami` reads
|
|
8
|
+
them. What cannot be observed from a shell is stated by the host that runs this,
|
|
9
|
+
with flags: `--inherits-caller-runtime` says its sub-agent tool, given no model
|
|
10
|
+
override, runs the caller's own model and effort. Where the identity cannot be
|
|
11
|
+
read at all, `--vendor` and `--model` are the host's statement of what it is; the
|
|
12
|
+
file then says so, and serves an inherited worker only. Prints the file's path.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
25
|
+
import caller_identity # noqa: E402
|
|
26
|
+
|
|
27
|
+
HARNESSES = {"codex": "codex", "claude": "claude-code", "grok": "grok-build", "gemini": "antigravity"}
|
|
28
|
+
UNVERIFIED_EFFORT = "unverified"
|
|
29
|
+
HOST_STATEMENT_HINT = ("omnilane: if this harness cannot be read, state what it is: omnilane native-context "
|
|
30
|
+
"--vendor VENDOR --model MODEL --inherits-caller-runtime (serves --inherit only)")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build(vendor: str, model: str, effort: str | None, *, harness: str, workdirs: list[str],
|
|
34
|
+
modes: list[str], inherits: bool, verified: bool = True) -> dict:
|
|
35
|
+
value = {
|
|
36
|
+
"schema_version": 1,
|
|
37
|
+
"harness": harness,
|
|
38
|
+
"vendor": vendor,
|
|
39
|
+
"current_model": model,
|
|
40
|
+
"requirements": {"tools": [], "isolation": "shared-inherited", "lifecycle": "single-shot"},
|
|
41
|
+
"capabilities": [{
|
|
42
|
+
"model": model,
|
|
43
|
+
# An unrecorded effort matches no lane target; only --inherit can use this row.
|
|
44
|
+
"efforts": [effort or UNVERIFIED_EFFORT],
|
|
45
|
+
"modes": modes,
|
|
46
|
+
"workdirs": workdirs,
|
|
47
|
+
"tools": [],
|
|
48
|
+
"isolations": ["shared-inherited"],
|
|
49
|
+
"lifecycles": ["single-shot"],
|
|
50
|
+
}],
|
|
51
|
+
}
|
|
52
|
+
if effort:
|
|
53
|
+
value["current_effort"] = effort
|
|
54
|
+
if inherits:
|
|
55
|
+
value["inherits_caller_runtime"] = True
|
|
56
|
+
if not verified:
|
|
57
|
+
value["caller_identity_verified"] = False
|
|
58
|
+
return value
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main(argv: list[str] | None = None, read_caller=caller_identity.read_caller) -> int:
|
|
62
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
63
|
+
parser.add_argument("--workdir", action="append", type=Path,
|
|
64
|
+
help="directory a native worker may be given; may repeat (default: cwd)")
|
|
65
|
+
parser.add_argument("--mode", action="append", choices=("advise", "work"),
|
|
66
|
+
help="task intent the host accepts; may repeat (default: advise)")
|
|
67
|
+
parser.add_argument("--harness", help="harness name (default: derived from the caller's vendor)")
|
|
68
|
+
parser.add_argument("--inherits-caller-runtime", action="store_true",
|
|
69
|
+
help="the host states that a sub-agent spawned with no model override "
|
|
70
|
+
"runs the caller's own model and effort")
|
|
71
|
+
parser.add_argument("--registry",
|
|
72
|
+
default=os.environ.get("OMNILANE_AA_POLICY_FILE")
|
|
73
|
+
or str(caller_identity.REPO / "config" / "aa-model-policy.json"))
|
|
74
|
+
parser.add_argument("--vendor", help="host-asserted vendor, used only when the identity cannot be read")
|
|
75
|
+
parser.add_argument("--model", help="host-asserted current model, used only when the identity cannot be read")
|
|
76
|
+
parser.add_argument("--out", type=Path, help="file to write (default: under ~/.omnilane)")
|
|
77
|
+
args = parser.parse_args(argv)
|
|
78
|
+
if bool(args.vendor) != bool(args.model):
|
|
79
|
+
print("omnilane: --vendor and --model go together", file=sys.stderr)
|
|
80
|
+
return 2
|
|
81
|
+
for name in (args.vendor, args.model):
|
|
82
|
+
if name and not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]{0,127}", name):
|
|
83
|
+
print(f"omnilane: invalid name: {name}", file=sys.stderr)
|
|
84
|
+
return 2
|
|
85
|
+
verified, recorded = True, None
|
|
86
|
+
try:
|
|
87
|
+
registry, _ = caller_identity.load_registry(args.registry)
|
|
88
|
+
_, (vendor, model, effort), _ = read_caller(os.getpid())
|
|
89
|
+
except (ValueError, OSError) as error:
|
|
90
|
+
if not args.vendor:
|
|
91
|
+
print(f"omnilane: cannot read the caller identity: {error}\n{HOST_STATEMENT_HINT}", file=sys.stderr)
|
|
92
|
+
return 3
|
|
93
|
+
print(f"omnilane: caller identity not verified ({error}); using the host's statement",
|
|
94
|
+
file=sys.stderr)
|
|
95
|
+
verified, vendor, model = False, args.vendor, args.model
|
|
96
|
+
else:
|
|
97
|
+
if args.vendor and (args.vendor != vendor or (model is not None and args.model != model)):
|
|
98
|
+
print(f"omnilane: this harness reads as {vendor}/{model}, not {args.vendor}/{args.model}",
|
|
99
|
+
file=sys.stderr)
|
|
100
|
+
return 2
|
|
101
|
+
row, reason = caller_identity.resolve(registry, vendor, model, effort)
|
|
102
|
+
if row is not None:
|
|
103
|
+
# Codex spells its reasoning-off effort "none"; the registry stores it as null.
|
|
104
|
+
recorded = row["effort"] or ("none" if vendor == "codex" else None)
|
|
105
|
+
elif not (vendor == "codex" and model and effort is None
|
|
106
|
+
and caller_identity.floor_row(registry, vendor, model)):
|
|
107
|
+
if not args.vendor:
|
|
108
|
+
print(f"omnilane: cannot describe this harness: {reason}\n{HOST_STATEMENT_HINT}",
|
|
109
|
+
file=sys.stderr)
|
|
110
|
+
return 3
|
|
111
|
+
print(f"omnilane: caller identity not verified ({reason}); using the host's statement",
|
|
112
|
+
file=sys.stderr)
|
|
113
|
+
verified, model = False, args.model
|
|
114
|
+
workdirs = []
|
|
115
|
+
for path in args.workdir or [Path.cwd()]:
|
|
116
|
+
resolved = path.expanduser().resolve()
|
|
117
|
+
if not resolved.is_dir():
|
|
118
|
+
print(f"omnilane: not a directory: {path}", file=sys.stderr)
|
|
119
|
+
return 2
|
|
120
|
+
workdirs.append(str(resolved))
|
|
121
|
+
value = build(vendor, model, recorded,
|
|
122
|
+
harness=args.harness or HARNESSES.get(vendor, vendor),
|
|
123
|
+
workdirs=sorted(set(workdirs)), modes=sorted(set(args.mode or ["advise"])),
|
|
124
|
+
inherits=args.inherits_caller_runtime, verified=verified)
|
|
125
|
+
text = json.dumps(value, indent=2, sort_keys=True) + "\n"
|
|
126
|
+
out = args.out
|
|
127
|
+
if out is None:
|
|
128
|
+
home = Path(os.environ.get("OMNILANE_HOME") or Path.home() / ".omnilane")
|
|
129
|
+
digest = hashlib.sha256(text.encode()).hexdigest()[:12]
|
|
130
|
+
out = home / "native-context" / f"{value['harness']}--{model.replace('.', '-')}--{digest}.json"
|
|
131
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
132
|
+
staging = out.with_name(f".{out.name}.{os.getpid()}.tmp")
|
|
133
|
+
staging.write_text(text)
|
|
134
|
+
os.replace(staging, out)
|
|
135
|
+
note = "" if args.inherits_caller_runtime else (
|
|
136
|
+
"; pass --inherits-caller-runtime if this harness's sub-agent tool inherits your runtime")
|
|
137
|
+
print(f"omnilane: native context for {value['harness']} {model} "
|
|
138
|
+
f"(effort {recorded or 'unrecorded'}{'' if verified else ', identity host-asserted'}){note}",
|
|
139
|
+
file=sys.stderr)
|
|
140
|
+
print(out)
|
|
141
|
+
return 0
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
raise SystemExit(main())
|
|
@@ -53,7 +53,10 @@ def offenders(overlay_path: Path) -> list[str]:
|
|
|
53
53
|
def main() -> None:
|
|
54
54
|
overlay_path = os.environ.get("OMNILANE_AA_TRANSPORT_OVERLAY", "")
|
|
55
55
|
if not overlay_path:
|
|
56
|
-
|
|
56
|
+
if os.environ.get("OMNILANE_AA_OPERATOR_ASSERTED_HUMAN") == "1":
|
|
57
|
+
emit("PASS", "no overlay configured; fine for a human operator, but a model caller "
|
|
58
|
+
"would be refused on every lane (README, 'Let your AI assistant drive omnilane')")
|
|
59
|
+
emit("WARN", "no overlay configured, so a model caller is refused on every lane (runtime-mapping-unverified). First install: probe_sweep.py --root ROOT, build_overlay.py --root ROOT, then export OMNILANE_AA_TRANSPORT_OVERLAY in local.sh; see the README, 'Let your AI assistant drive omnilane'")
|
|
57
60
|
if not Path(overlay_path).exists():
|
|
58
61
|
emit("FAIL", f"configured overlay is missing: {overlay_path}")
|
|
59
62
|
|
|
@@ -87,10 +90,29 @@ def main() -> None:
|
|
|
87
90
|
"record who answered")
|
|
88
91
|
|
|
89
92
|
stale = registry.get("_stale_transport_vendors", [])
|
|
93
|
+
# The gate hashes the path the overlay recorded. An update that installs beside
|
|
94
|
+
# the old executable leaves that path intact, so only this comparison sees it.
|
|
95
|
+
moved = []
|
|
96
|
+
try:
|
|
97
|
+
import resign
|
|
98
|
+
report = resign.detect(overlay, resign.current_anchors())
|
|
99
|
+
for vendor, entry in report.items():
|
|
100
|
+
# Only what the overlay actually pinned can have moved.
|
|
101
|
+
reasons = [reason for reason in entry["reasons"]
|
|
102
|
+
if reason.startswith("runs ") or reason.endswith(" changed")]
|
|
103
|
+
if reasons and entry["recorded_cli_path"] and vendor not in stale:
|
|
104
|
+
moved.append(f"{vendor}: {'; '.join(reasons)}")
|
|
105
|
+
except Exception: # noqa: BLE001 - a health line must never raise
|
|
106
|
+
moved = []
|
|
107
|
+
if moved and not stale:
|
|
108
|
+
emit("WARN", f"the runners no longer execute what the overlay pinned ({' | '.join(moved)}); "
|
|
109
|
+
f"run `omnilane resign`; still loading: {summary}{extra}")
|
|
90
110
|
if stale:
|
|
91
111
|
detail = "; ".join(o for o in offenders(Path(overlay_path))) or "unknown cause"
|
|
112
|
+
if moved:
|
|
113
|
+
detail += "; also moved without going stale: " + " | ".join(moved)
|
|
92
114
|
emit("WARN", f"stale vendor(s) {', '.join(stale)} degraded to unverified "
|
|
93
|
-
f"({detail}); still verified: {summary}{extra}")
|
|
115
|
+
f"({detail}); run `omnilane resign`; still verified: {summary}{extra}")
|
|
94
116
|
emit("PASS", f"verified mappings: {summary}{extra}")
|
|
95
117
|
|
|
96
118
|
|
package/scripts/lib/probe.py
CHANGED