omnilane 0.42.9 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- child = None
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, child
296
- child = pid
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, value in latest.items():
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
- return ("codex", latest["model"], latest["effort"]), f"thread {thread}, turn {latest['turn_id']}"
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
- raise ValueError("codex direct child CODEX_THREAD_ID is missing or not a UUID")
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
- path = directory / (row["id"].replace("/", "--") + ".json")
471
- text = json.dumps({
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
- }, indent=2, sort_keys=True) + "\n"
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
- print(f"omnilane: caller is {row['id']} (score {row['score']}), read from pid {pid}{provenance}",
517
- file=sys.stderr)
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"
@@ -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", "vendor", "model", "effort", "workdir", "task"):
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
- emit("PASS", "no overlay configured; every runtime mapping stays unverified")
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
 
@@ -6,6 +6,8 @@ descriptor with the original command/stream fields plus a vendor-specific
6
6
  verdict, its reason, the observed model, and the tier of evidence that model
7
7
  rests on.
8
8
  """
9
+ from __future__ import annotations
10
+
9
11
  import argparse
10
12
  import glob
11
13
  import json