omnilane 0.34.0 → 0.42.1

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.
Files changed (42) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/CHANGELOG.md +71 -1
  4. package/README.ja.md +63 -33
  5. package/README.ko.md +63 -32
  6. package/README.md +147 -86
  7. package/README.zh-CN.md +61 -30
  8. package/README.zh-TW.md +124 -75
  9. package/VERSION +1 -1
  10. package/config/aa-model-policy.json +3046 -0
  11. package/docs/aa-model-coverage-2026-09-05.json +29204 -0
  12. package/docs/completion-wakeup.md +126 -0
  13. package/docs/model-capabilities-2026-09.md +380 -0
  14. package/docs/native-executor.md +264 -0
  15. package/docs/release-notes-0.42.1.md +32 -0
  16. package/hooks/routing-instruction.md +101 -40
  17. package/package.json +8 -2
  18. package/plugin.json +2 -2
  19. package/routing.local.yaml.example +8 -3
  20. package/routing.yaml +16 -16
  21. package/scripts/completion-wakeup.py +390 -0
  22. package/scripts/configure.sh +4 -4
  23. package/scripts/dispatch.sh +323 -32
  24. package/scripts/doctor.sh +55 -1
  25. package/scripts/jobs.sh +64 -17
  26. package/scripts/lib/aa_policy.py +473 -0
  27. package/scripts/lib/aa_retry.py +77 -0
  28. package/scripts/lib/common.sh +106 -1
  29. package/scripts/lib/job-worker.sh +314 -20
  30. package/scripts/lib/live-protocol.sh +147 -2
  31. package/scripts/lib/native.py +507 -0
  32. package/scripts/lib/normalize-claude-stream.py +72 -0
  33. package/scripts/lib/prepare-agy-mode.py +374 -0
  34. package/scripts/release-audit.sh +103 -0
  35. package/scripts/runners/run-claude.sh +81 -47
  36. package/scripts/runners/run-codex-live.py +462 -0
  37. package/scripts/runners/run-codex.sh +62 -3
  38. package/scripts/runners/run-gemini.sh +85 -10
  39. package/scripts/runners/run-grok-live.py +426 -0
  40. package/scripts/runners/run-grok.sh +117 -6
  41. package/scripts/runners/run-vote.sh +6 -3
  42. package/skills/omnilane/SKILL.md +217 -81
@@ -3,16 +3,128 @@
3
3
  # Shared live-mailbox protocol differences. Callers provide json_escape().
4
4
 
5
5
  live_capable_vendors() {
6
- printf 'claude, gemini'
6
+ printf 'claude, gemini, codex, grok'
7
7
  }
8
8
 
9
9
  live_vendor_capable() {
10
10
  case "$1" in
11
- claude|gemini) return 0 ;;
11
+ claude|gemini|codex|grok) return 0 ;;
12
12
  *) return 1 ;;
13
13
  esac
14
14
  }
15
15
 
16
+ codex_live_surface_available() {
17
+ local bin="${1:-${CODEX_BIN:-codex}}"
18
+ command -v "$bin" >/dev/null 2>&1 || return 1
19
+ command -v python3 >/dev/null 2>&1 || return 1
20
+ python3 - "$bin" 2>/dev/null <<'PY'
21
+ import json
22
+ import select
23
+ import subprocess
24
+ import sys
25
+
26
+ process = None
27
+ ok = False
28
+ try:
29
+ process = subprocess.Popen(
30
+ [sys.argv[1], "app-server"],
31
+ stdin=subprocess.PIPE,
32
+ stdout=subprocess.PIPE,
33
+ stderr=subprocess.DEVNULL,
34
+ text=True,
35
+ # Stay in the caller's supervised process group. A whole-job SIGKILL
36
+ # bypasses this probe's finally block, so a private session would leak.
37
+ start_new_session=False,
38
+ )
39
+ request = {
40
+ "jsonrpc": "2.0",
41
+ "id": 1,
42
+ "method": "initialize",
43
+ "params": {"clientInfo": {"name": "omnilane-probe", "version": "1"}},
44
+ }
45
+ process.stdin.write(json.dumps(request, separators=(",", ":")) + "\n")
46
+ process.stdin.flush()
47
+ # Keep stdin open until the reply arrives: codex 0.153.4 exits on EOF
48
+ # before answering, which made a healthy machine look live-unavailable.
49
+ if select.select([process.stdout], [], [], 3)[0]:
50
+ response = json.loads(process.stdout.readline())
51
+ ok = isinstance(response, dict) and isinstance(response.get("result"), dict)
52
+ except (OSError, ValueError, json.JSONDecodeError):
53
+ ok = False
54
+ finally:
55
+ if process is not None and process.poll() is None:
56
+ try:
57
+ process.terminate()
58
+ process.wait(timeout=1)
59
+ except (OSError, subprocess.TimeoutExpired):
60
+ try:
61
+ process.kill()
62
+ except OSError:
63
+ pass
64
+ process.wait()
65
+ sys.exit(0 if ok else 1)
66
+ PY
67
+ }
68
+
69
+ grok_live_surface_available() {
70
+ local bin="${1:-${GROK_BIN:-grok}}"
71
+ command -v "$bin" >/dev/null 2>&1 || return 1
72
+ command -v python3 >/dev/null 2>&1 || return 1
73
+
74
+ python3 - "$bin" 2>/dev/null <<'PY'
75
+ import json
76
+ import select
77
+ import subprocess
78
+ import sys
79
+
80
+ process = None
81
+ ok = False
82
+ try:
83
+ process = subprocess.Popen(
84
+ [sys.argv[1], "agent", "stdio"],
85
+ stdin=subprocess.PIPE,
86
+ stdout=subprocess.PIPE,
87
+ stderr=subprocess.DEVNULL,
88
+ text=True,
89
+ # Stay in the caller's supervised process group. A whole-job SIGKILL
90
+ # bypasses the probe's finally block, so a private session would leak.
91
+ start_new_session=False,
92
+ )
93
+ request = {
94
+ "jsonrpc": "2.0",
95
+ "id": 1,
96
+ "method": "initialize",
97
+ "params": {
98
+ "protocolVersion": 1,
99
+ "clientCapabilities": {
100
+ "fs": {"readTextFile": False, "writeTextFile": False}
101
+ },
102
+ },
103
+ }
104
+ process.stdin.write(json.dumps(request, separators=(",", ":")) + "\n")
105
+ process.stdin.flush()
106
+ # Same as the codex probe: keep stdin open until the reply arrives so an
107
+ # agent that exits on EOF before answering is not reported unavailable.
108
+ if select.select([process.stdout], [], [], 3)[0]:
109
+ response = json.loads(process.stdout.readline())
110
+ ok = isinstance(response, dict) and isinstance(response.get("result"), dict)
111
+ except (OSError, ValueError, json.JSONDecodeError):
112
+ ok = False
113
+ finally:
114
+ if process is not None and process.poll() is None:
115
+ try:
116
+ process.terminate()
117
+ process.wait(timeout=1)
118
+ except (OSError, subprocess.TimeoutExpired):
119
+ try:
120
+ process.kill()
121
+ except OSError:
122
+ pass
123
+ process.wait()
124
+ sys.exit(0 if ok else 1)
125
+ PY
126
+ }
127
+
16
128
  live_encode_message() {
17
129
  local vendor="$1" job_id="$2" foreman_session="$3" text="$4"
18
130
  case "$vendor" in
@@ -25,15 +137,40 @@ live_encode_message() {
25
137
  printf '{"event":"user","message":{"role":"user","content":[{"type":"text","text":"%s"}]}}' \
26
138
  "$(json_escape "$text")"
27
139
  ;;
140
+ codex)
141
+ printf '{"type":"codex-user","text":"%s"}' "$(json_escape "$text")"
142
+ ;;
143
+ grok)
144
+ printf '{"type":"grok-user","text":"%s"}' "$(json_escape "$text")"
145
+ ;;
28
146
  *) return 2 ;;
29
147
  esac
30
148
  }
31
149
 
150
+ # Cheap structural check, not a parse. Two properties matter more here than
151
+ # precision. It must not fork: the drain loop runs one call per stream event
152
+ # between inbox reads, so a per-event process would leave an operator's
153
+ # `jobs.sh send` waiting behind a busy stream. And it must not be able to fail
154
+ # because a parser is missing: a validator that cannot run would judge every
155
+ # event invalid, activity would never refresh, and the idle cap would kill
156
+ # healthy jobs. Strict parsing still guards the places where correctness
157
+ # depends on it (json_file_round_trip_valid).
158
+ live_event_is_valid() {
159
+ local event="$1" lead trail
160
+ lead="${event%%[![:space:]]*}"
161
+ event="${event#"$lead"}"
162
+ trail="${event##*[![:space:]]}"
163
+ event="${event%"$trail"}"
164
+ [[ "$event" == "{"*"}" ]]
165
+ }
166
+
32
167
  live_event_is_result() {
33
168
  local vendor="$1" event="$2" pattern
34
169
  case "$vendor" in
35
170
  claude) pattern='"type"[[:space:]]*:[[:space:]]*"result"' ;;
36
171
  gemini) pattern='"event"[[:space:]]*:[[:space:]]*"result"' ;;
172
+ codex) pattern='"method"[[:space:]]*:[[:space:]]*"turn/completed"' ;;
173
+ grok) pattern='"method"[[:space:]]*:[[:space:]]*"_x.ai/session/prompt_complete"' ;;
37
174
  *) return 2 ;;
38
175
  esac
39
176
  [[ "$event" =~ $pattern ]]
@@ -51,6 +188,14 @@ live_event_is_success() {
51
188
  pattern='"status"[[:space:]]*:[[:space:]]*"SUCCESS"'
52
189
  [[ "$event" =~ $pattern ]]
53
190
  ;;
191
+ codex)
192
+ pattern='"status"[[:space:]]*:[[:space:]]*"completed"'
193
+ [[ "$event" =~ $pattern ]]
194
+ ;;
195
+ grok)
196
+ pattern='"stopReason"[[:space:]]*:[[:space:]]*"end_turn"'
197
+ [[ "$event" =~ $pattern ]]
198
+ ;;
54
199
  *) return 2 ;;
55
200
  esac
56
201
  }