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.
- package/.claude-plugin/marketplace.json +4 -4
- package/.claude-plugin/plugin.json +2 -2
- package/CHANGELOG.md +71 -1
- package/README.ja.md +63 -33
- package/README.ko.md +63 -32
- package/README.md +147 -86
- package/README.zh-CN.md +61 -30
- package/README.zh-TW.md +124 -75
- package/VERSION +1 -1
- package/config/aa-model-policy.json +3046 -0
- package/docs/aa-model-coverage-2026-09-05.json +29204 -0
- package/docs/completion-wakeup.md +126 -0
- package/docs/model-capabilities-2026-09.md +380 -0
- package/docs/native-executor.md +264 -0
- package/docs/release-notes-0.42.1.md +32 -0
- package/hooks/routing-instruction.md +101 -40
- package/package.json +8 -2
- package/plugin.json +2 -2
- package/routing.local.yaml.example +8 -3
- package/routing.yaml +16 -16
- package/scripts/completion-wakeup.py +390 -0
- package/scripts/configure.sh +4 -4
- package/scripts/dispatch.sh +323 -32
- package/scripts/doctor.sh +55 -1
- package/scripts/jobs.sh +64 -17
- package/scripts/lib/aa_policy.py +473 -0
- package/scripts/lib/aa_retry.py +77 -0
- package/scripts/lib/common.sh +106 -1
- package/scripts/lib/job-worker.sh +314 -20
- package/scripts/lib/live-protocol.sh +147 -2
- package/scripts/lib/native.py +507 -0
- package/scripts/lib/normalize-claude-stream.py +72 -0
- package/scripts/lib/prepare-agy-mode.py +374 -0
- package/scripts/release-audit.sh +103 -0
- package/scripts/runners/run-claude.sh +81 -47
- package/scripts/runners/run-codex-live.py +462 -0
- package/scripts/runners/run-codex.sh +62 -3
- package/scripts/runners/run-gemini.sh +85 -10
- package/scripts/runners/run-grok-live.py +426 -0
- package/scripts/runners/run-grok.sh +117 -6
- package/scripts/runners/run-vote.sh +6 -3
- 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
|
}
|