omnilane 0.20.0 → 0.31.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 +37 -1
- package/README.ja.md +36 -3
- package/README.ko.md +36 -3
- package/README.md +38 -3
- package/README.zh-CN.md +36 -3
- package/README.zh-TW.md +36 -3
- package/VERSION +1 -1
- package/bin/omnilane +10 -1
- package/hooks/routing-instruction.md +16 -7
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/dispatch.sh +55 -7
- package/scripts/doctor.sh +26 -0
- package/scripts/jobs.sh +24 -12
- package/scripts/lib/goal-loop.sh +670 -0
- package/scripts/lib/job-worker.sh +98 -37
- package/scripts/lib/live-protocol.sh +70 -0
- package/scripts/runners/run-gemini.sh +109 -0
- package/skills/omnilane/SKILL.md +9 -4
|
@@ -7,6 +7,8 @@ set -euo pipefail
|
|
|
7
7
|
# Runtime-relative shared library.
|
|
8
8
|
# shellcheck disable=SC1091
|
|
9
9
|
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
|
10
|
+
# shellcheck disable=SC1091
|
|
11
|
+
source "$OMNILANE_REPO/scripts/lib/live-protocol.sh"
|
|
10
12
|
|
|
11
13
|
[[ $# -eq 7 ]] || { echo "omnilane: internal job worker received invalid arguments" >&2; exit 2; }
|
|
12
14
|
VENDOR="$1"; MODE="$2"; WORKDIR="$3"; MODEL="$4"; EFFORT="$5"
|
|
@@ -70,43 +72,72 @@ run_single_shot() {
|
|
|
70
72
|
return "$rc"
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
SESSION_MODE="${OMNILANE_SESSION_MODE:-auto}"
|
|
76
|
+
IDLE_TIMEOUT="${OMNILANE_IDLE_TIMEOUT:-900}"
|
|
77
|
+
LIVE_REQUIRED="${OMNILANE_LIVE_REQUIRED:-0}"
|
|
78
|
+
[[ "$SESSION_MODE" == "auto" || "$SESSION_MODE" == "live" || "$SESSION_MODE" == "single-shot" ]] || {
|
|
79
|
+
echo "omnilane: invalid worker session mode" >&2; exit 2
|
|
80
|
+
}
|
|
81
|
+
[[ "$IDLE_TIMEOUT" =~ ^(0|[1-9][0-9]*)$ ]] || {
|
|
82
|
+
echo "omnilane: invalid worker idle timeout" >&2; exit 2
|
|
83
|
+
}
|
|
84
|
+
[[ "$LIVE_REQUIRED" == "0" || "$LIVE_REQUIRED" == "1" ]] || {
|
|
85
|
+
echo "omnilane: invalid worker live requirement" >&2; exit 2
|
|
86
|
+
}
|
|
87
|
+
if [[ "$SESSION_MODE" == "auto" ]]; then
|
|
88
|
+
if live_vendor_capable "$VENDOR"; then SESSION_MODE="live"; else SESSION_MODE="single-shot"; fi
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if [[ "$SESSION_MODE" == "single-shot" ]]; then
|
|
74
92
|
set +e
|
|
75
|
-
|
|
93
|
+
if live_vendor_capable "$VENDOR"; then
|
|
94
|
+
run_single_shot "omnilane: vendor '$VENDOR' was resolved to single-shot mode"
|
|
95
|
+
else
|
|
96
|
+
run_single_shot "omnilane: vendor '$VENDOR' is not live-capable; ran in single-shot mode"
|
|
97
|
+
fi
|
|
76
98
|
rc=$?
|
|
77
99
|
set -e
|
|
78
100
|
exit "$rc"
|
|
79
101
|
fi
|
|
80
102
|
|
|
103
|
+
if ! live_vendor_capable "$VENDOR"; then
|
|
104
|
+
echo "omnilane: vendor '$VENDOR' cannot run required live session; live-capable vendors: $(live_capable_vendors)" >&2
|
|
105
|
+
exit 2
|
|
106
|
+
fi
|
|
107
|
+
|
|
81
108
|
JOB_DIR="${OUTPUT_FILE%/*}"
|
|
82
109
|
JOB_ID="${JOB_DIR##*/}"
|
|
83
110
|
INBOX_FIFO="$JOB_DIR/inbox.fifo"
|
|
111
|
+
RUNNER_INBOX_FIFO="$JOB_DIR/runner-inbox.fifo"
|
|
84
112
|
HOLDER_PID_FILE="$JOB_DIR/inbox.holder.pid"
|
|
85
113
|
READY_FILE="$JOB_DIR/inbox.ready"
|
|
86
114
|
EVENTS_FILE="${OUTPUT_FILE}.events.jsonl"
|
|
87
115
|
EVENTS_ALIAS="$JOB_DIR/events.jsonl"
|
|
88
116
|
close_requested=0
|
|
89
|
-
|
|
117
|
+
close_reason=""
|
|
118
|
+
runner_writer_open=0
|
|
119
|
+
inbox_open=0
|
|
120
|
+
events_reader_open=0
|
|
90
121
|
runner_pid=""
|
|
91
122
|
|
|
92
123
|
# Invoked by the EXIT trap below.
|
|
93
124
|
# shellcheck disable=SC2329
|
|
94
125
|
cleanup_live_mailbox() {
|
|
95
|
-
if [[ "$
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
rm "$READY_FILE" "$HOLDER_PID_FILE" "$INBOX_FIFO" 2>/dev/null || true
|
|
126
|
+
if [[ "$runner_writer_open" -eq 1 ]]; then exec 3>&-; runner_writer_open=0; fi
|
|
127
|
+
if [[ "$inbox_open" -eq 1 ]]; then exec 4>&-; inbox_open=0; fi
|
|
128
|
+
if [[ "$events_reader_open" -eq 1 ]]; then exec 5<&-; events_reader_open=0; fi
|
|
129
|
+
rm "$READY_FILE" "$HOLDER_PID_FILE" "$INBOX_FIFO" "$RUNNER_INBOX_FIFO" 2>/dev/null || true
|
|
100
130
|
}
|
|
101
131
|
|
|
102
132
|
prepare_live_mailbox() {
|
|
103
133
|
local path old_umask rc=0
|
|
104
|
-
for path in "$INBOX_FIFO" "$
|
|
134
|
+
for path in "$INBOX_FIFO" "$RUNNER_INBOX_FIFO" "$HOLDER_PID_FILE" \
|
|
135
|
+
"$READY_FILE" "$EVENTS_FILE" "$EVENTS_ALIAS"; do
|
|
105
136
|
[[ ! -e "$path" && ! -L "$path" ]] || return 1
|
|
106
137
|
done
|
|
107
138
|
old_umask="$(umask)"
|
|
108
139
|
umask 077
|
|
109
|
-
mkfifo "$INBOX_FIFO" || rc=$?
|
|
140
|
+
mkfifo "$INBOX_FIFO" "$RUNNER_INBOX_FIFO" || rc=$?
|
|
110
141
|
if [[ "$rc" -eq 0 ]]; then
|
|
111
142
|
: > "$EVENTS_FILE" || rc=$?
|
|
112
143
|
fi
|
|
@@ -114,31 +145,38 @@ prepare_live_mailbox() {
|
|
|
114
145
|
ln "$EVENTS_FILE" "$EVENTS_ALIAS" || rc=$?
|
|
115
146
|
fi
|
|
116
147
|
if [[ "$rc" -eq 0 ]]; then
|
|
117
|
-
chmod 600 "$INBOX_FIFO" "$EVENTS_FILE" "$EVENTS_ALIAS" || rc=$?
|
|
148
|
+
chmod 600 "$INBOX_FIFO" "$RUNNER_INBOX_FIFO" "$EVENTS_FILE" "$EVENTS_ALIAS" || rc=$?
|
|
118
149
|
fi
|
|
119
150
|
umask "$old_umask"
|
|
120
151
|
if [[ "$rc" -ne 0 ]]; then
|
|
121
|
-
rm "$EVENTS_ALIAS" "$EVENTS_FILE" "$INBOX_FIFO" 2>/dev/null || true
|
|
152
|
+
rm "$EVENTS_ALIAS" "$EVENTS_FILE" "$INBOX_FIFO" "$RUNNER_INBOX_FIFO" 2>/dev/null || true
|
|
122
153
|
fi
|
|
123
154
|
return "$rc"
|
|
124
155
|
}
|
|
125
156
|
|
|
126
157
|
last_result_status() {
|
|
127
|
-
local last_result
|
|
128
|
-
|
|
158
|
+
local event last_result=""
|
|
159
|
+
while IFS= read -r event || [[ -n "$event" ]]; do
|
|
160
|
+
if live_event_is_result "$VENDOR" "$event"; then last_result="$event"; fi
|
|
161
|
+
done < "$EVENTS_FILE"
|
|
129
162
|
[[ -n "$last_result" ]] || return 1
|
|
130
|
-
|
|
163
|
+
live_event_is_success "$VENDOR" "$last_result"
|
|
131
164
|
}
|
|
132
165
|
|
|
133
166
|
if ! prepare_live_mailbox; then
|
|
167
|
+
if [[ "$LIVE_REQUIRED" -eq 1 ]]; then
|
|
168
|
+
echo "omnilane: required $VENDOR live mailbox unavailable because FIFO setup failed" >&2
|
|
169
|
+
exit 1
|
|
170
|
+
fi
|
|
134
171
|
set +e
|
|
135
|
-
run_single_shot "omnilane:
|
|
172
|
+
run_single_shot "omnilane: $VENDOR live mailbox unavailable because FIFO setup failed; ran in single-shot mode"
|
|
136
173
|
rc=$?
|
|
137
174
|
set -e
|
|
138
175
|
exit "$rc"
|
|
139
176
|
fi
|
|
140
177
|
|
|
141
178
|
trap 'close_requested=1' USR1
|
|
179
|
+
trap 'close_requested=1' PIPE
|
|
142
180
|
trap cleanup_live_mailbox EXIT
|
|
143
181
|
truncate_payload "$PROMPT_FILE" 102400
|
|
144
182
|
INITIAL_TEXT="$(cat "$PROMPT_FILE")"
|
|
@@ -149,44 +187,67 @@ else
|
|
|
149
187
|
fi
|
|
150
188
|
|
|
151
189
|
write_current_pid_file "$HOLDER_PID_FILE"
|
|
152
|
-
export OMNILANE_INBOX="$
|
|
190
|
+
export OMNILANE_INBOX="$RUNNER_INBOX_FIFO"
|
|
153
191
|
"$RUNNER" "$MODE" "$WORKDIR" "$MODEL" "$EFFORT" "$PROMPT_FILE" "$OUTPUT_FILE" &
|
|
154
192
|
runner_pid=$!
|
|
155
193
|
|
|
156
|
-
# Open only after
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
fi
|
|
194
|
+
# Open the runner writer only after its FIFO reader starts.
|
|
195
|
+
exec 3> "$RUNNER_INBOX_FIFO"
|
|
196
|
+
runner_writer_open=1
|
|
197
|
+
exec 4<> "$INBOX_FIFO"
|
|
198
|
+
inbox_open=1
|
|
199
|
+
exec 5< "$EVENTS_FILE"
|
|
200
|
+
events_reader_open=1
|
|
201
|
+
initial_payload="$(live_encode_message "$VENDOR" "$JOB_ID" "$FOREMAN_SESSION_VALUE" "$INITIAL_TEXT")"
|
|
202
|
+
if ! printf '%s\n' "$initial_payload" >&3; then close_requested=1; fi
|
|
165
203
|
(umask 077; : > "$READY_FILE")
|
|
166
204
|
|
|
205
|
+
last_activity=$SECONDS
|
|
206
|
+
while kill -0 "$runner_pid" 2>/dev/null; do
|
|
207
|
+
[[ "$close_requested" -eq 0 ]] || break
|
|
208
|
+
incoming=""
|
|
209
|
+
if IFS= read -r -t 1 incoming <&4; then
|
|
210
|
+
if ! printf '%s\n' "$incoming" >&3; then close_requested=1; fi
|
|
211
|
+
last_activity=$SECONDS
|
|
212
|
+
fi
|
|
213
|
+
while IFS= read -r event <&5; do
|
|
214
|
+
if live_event_is_result "$VENDOR" "$event"; then
|
|
215
|
+
last_activity=$SECONDS
|
|
216
|
+
fi
|
|
217
|
+
done
|
|
218
|
+
if [[ "$IDLE_TIMEOUT" -gt 0 && $((SECONDS - last_activity)) -ge "$IDLE_TIMEOUT" ]]; then
|
|
219
|
+
close_reason="closed by idle cap after ${IDLE_TIMEOUT}s"
|
|
220
|
+
close_requested=1
|
|
221
|
+
fi
|
|
222
|
+
done
|
|
223
|
+
|
|
224
|
+
if [[ "$runner_writer_open" -eq 1 ]]; then exec 3>&-; runner_writer_open=0; fi
|
|
225
|
+
if [[ "$inbox_open" -eq 1 ]]; then exec 4>&-; inbox_open=0; fi
|
|
226
|
+
if [[ "$events_reader_open" -eq 1 ]]; then exec 5<&-; events_reader_open=0; fi
|
|
227
|
+
|
|
167
228
|
set +e
|
|
168
|
-
|
|
229
|
+
if [[ "$close_requested" -eq 1 ]]; then
|
|
230
|
+
kill -TERM "$runner_pid" 2>/dev/null || true
|
|
231
|
+
fi
|
|
232
|
+
wait "$runner_pid" 2>/dev/null
|
|
169
233
|
runner_rc=$?
|
|
170
234
|
set -e
|
|
171
235
|
|
|
236
|
+
if [[ -n "$close_reason" ]]; then
|
|
237
|
+
printf '\n%s\n' "$close_reason" >> "$OUTPUT_FILE"
|
|
238
|
+
emit_mode_notice "$close_reason" || true
|
|
239
|
+
fi
|
|
240
|
+
|
|
172
241
|
if [[ "$close_requested" -eq 1 ]]; then
|
|
173
|
-
exec 3>&-
|
|
174
|
-
inbox_holder_open=0
|
|
175
|
-
set +e
|
|
176
|
-
kill -TERM "$runner_pid" 2>/dev/null || true
|
|
177
|
-
wait "$runner_pid" 2>/dev/null
|
|
178
|
-
set -e
|
|
179
242
|
if last_result_status; then
|
|
180
243
|
rc=0
|
|
181
244
|
else
|
|
182
245
|
rc=1
|
|
183
|
-
emit_mode_notice "omnilane:
|
|
246
|
+
emit_mode_notice "omnilane: $VENDOR live mailbox closed without a successful result event" || true
|
|
184
247
|
fi
|
|
185
248
|
else
|
|
186
|
-
exec 3>&-
|
|
187
|
-
inbox_holder_open=0
|
|
188
249
|
rc="$runner_rc"
|
|
189
250
|
fi
|
|
190
251
|
|
|
191
|
-
trap - USR1
|
|
252
|
+
trap - USR1 PIPE
|
|
192
253
|
exit "$rc"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Shared live-mailbox protocol differences. Callers provide json_escape().
|
|
4
|
+
|
|
5
|
+
live_capable_vendors() {
|
|
6
|
+
printf 'claude, gemini'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
live_vendor_capable() {
|
|
10
|
+
case "$1" in
|
|
11
|
+
claude|gemini) return 0 ;;
|
|
12
|
+
*) return 1 ;;
|
|
13
|
+
esac
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
live_encode_message() {
|
|
17
|
+
local vendor="$1" job_id="$2" foreman_session="$3" text="$4"
|
|
18
|
+
case "$vendor" in
|
|
19
|
+
claude)
|
|
20
|
+
printf '{"type":"user","omnilane_job_id":"%s","foreman_session":"%s","message":{"role":"user","content":[{"type":"text","text":"%s"}]}}' \
|
|
21
|
+
"$(json_escape "$job_id")" "$(json_escape "$foreman_session")" \
|
|
22
|
+
"$(json_escape "$text")"
|
|
23
|
+
;;
|
|
24
|
+
gemini)
|
|
25
|
+
printf '{"event":"user","message":{"role":"user","content":[{"type":"text","text":"%s"}]}}' \
|
|
26
|
+
"$(json_escape "$text")"
|
|
27
|
+
;;
|
|
28
|
+
*) return 2 ;;
|
|
29
|
+
esac
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
live_event_is_result() {
|
|
33
|
+
local vendor="$1" event="$2" pattern
|
|
34
|
+
case "$vendor" in
|
|
35
|
+
claude) pattern='"type"[[:space:]]*:[[:space:]]*"result"' ;;
|
|
36
|
+
gemini) pattern='"event"[[:space:]]*:[[:space:]]*"result"' ;;
|
|
37
|
+
*) return 2 ;;
|
|
38
|
+
esac
|
|
39
|
+
[[ "$event" =~ $pattern ]]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
live_event_is_success() {
|
|
43
|
+
local vendor="$1" event="$2" pattern
|
|
44
|
+
live_event_is_result "$vendor" "$event" || return 1
|
|
45
|
+
case "$vendor" in
|
|
46
|
+
claude)
|
|
47
|
+
pattern='"is_error"[[:space:]]*:[[:space:]]*true'
|
|
48
|
+
[[ ! "$event" =~ $pattern ]]
|
|
49
|
+
;;
|
|
50
|
+
gemini)
|
|
51
|
+
pattern='"status"[[:space:]]*:[[:space:]]*"SUCCESS"'
|
|
52
|
+
[[ "$event" =~ $pattern ]]
|
|
53
|
+
;;
|
|
54
|
+
*) return 2 ;;
|
|
55
|
+
esac
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
live_count_result_events() {
|
|
59
|
+
local vendor="$1" path="$2" event count=0
|
|
60
|
+
[[ -f "$path" && ! -L "$path" ]] || {
|
|
61
|
+
printf '0'
|
|
62
|
+
return 0
|
|
63
|
+
}
|
|
64
|
+
while IFS= read -r event || [[ -n "$event" ]]; do
|
|
65
|
+
if live_event_is_result "$vendor" "$event"; then
|
|
66
|
+
count=$((count + 1))
|
|
67
|
+
fi
|
|
68
|
+
done < "$path"
|
|
69
|
+
printf '%s' "$count"
|
|
70
|
+
}
|
|
@@ -33,6 +33,115 @@ MODEL_ARGS=()
|
|
|
33
33
|
# plan = read-only tools (advise), accept-edits = file edits allowed (work).
|
|
34
34
|
if [[ "$MODE" == "advise" ]]; then MODE_ARGS=(--mode plan); else MODE_ARGS=(--mode accept-edits); fi
|
|
35
35
|
|
|
36
|
+
LIVE_INBOX="${OMNILANE_INBOX:-}"
|
|
37
|
+
if [[ -n "$LIVE_INBOX" && -p "$LIVE_INBOX" ]]; then
|
|
38
|
+
EVENTS_FILE="${OUTPUT_FILE}.events.jsonl"
|
|
39
|
+
STDERR_FILE="${OUTPUT_FILE}.stderr.log"
|
|
40
|
+
LIVE_CHILD_PID=""
|
|
41
|
+
|
|
42
|
+
if [[ -L "$EVENTS_FILE" || ( -e "$EVENTS_FILE" && ! -f "$EVENTS_FILE" ) ]]; then
|
|
43
|
+
echo "omnilane: unsafe Gemini live event path" >&2
|
|
44
|
+
exit 125
|
|
45
|
+
fi
|
|
46
|
+
(umask 077; : > "$EVENTS_FILE"; : > "$STDERR_FILE")
|
|
47
|
+
|
|
48
|
+
finalize_live_output() {
|
|
49
|
+
local tmp="${OUTPUT_FILE}.tmp"
|
|
50
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
51
|
+
echo "omnilane: cannot extract Gemini live result: python3 not found" >> "$STDERR_FILE"
|
|
52
|
+
return 1
|
|
53
|
+
fi
|
|
54
|
+
if ! python3 - "$EVENTS_FILE" "$tmp" <<'PY'
|
|
55
|
+
import json
|
|
56
|
+
import pathlib
|
|
57
|
+
import sys
|
|
58
|
+
|
|
59
|
+
events_path = pathlib.Path(sys.argv[1])
|
|
60
|
+
output_path = pathlib.Path(sys.argv[2])
|
|
61
|
+
last_response = None
|
|
62
|
+
|
|
63
|
+
with events_path.open(encoding="utf-8") as events:
|
|
64
|
+
for raw_line in events:
|
|
65
|
+
try:
|
|
66
|
+
event = json.loads(raw_line)
|
|
67
|
+
except json.JSONDecodeError:
|
|
68
|
+
continue
|
|
69
|
+
result = event.get("result")
|
|
70
|
+
if (
|
|
71
|
+
event.get("event") == "result"
|
|
72
|
+
and isinstance(result, dict)
|
|
73
|
+
and result.get("status") == "SUCCESS"
|
|
74
|
+
and isinstance(result.get("response"), str)
|
|
75
|
+
):
|
|
76
|
+
last_response = result["response"]
|
|
77
|
+
|
|
78
|
+
if last_response is None:
|
|
79
|
+
raise SystemExit(1)
|
|
80
|
+
|
|
81
|
+
output_path.write_text(last_response.rstrip("\n") + "\n", encoding="utf-8")
|
|
82
|
+
PY
|
|
83
|
+
then
|
|
84
|
+
echo "omnilane: Gemini live stream ended without a readable SUCCESS result" >> "$STDERR_FILE"
|
|
85
|
+
return 1
|
|
86
|
+
fi
|
|
87
|
+
mv "$tmp" "$OUTPUT_FILE"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Invoked by signal traps below.
|
|
91
|
+
# shellcheck disable=SC2329
|
|
92
|
+
stop_live_child() {
|
|
93
|
+
local signal_rc="$1" waited=0
|
|
94
|
+
trap - TERM HUP INT
|
|
95
|
+
set +e
|
|
96
|
+
if [[ -n "$LIVE_CHILD_PID" ]] && kill -0 "$LIVE_CHILD_PID" 2>/dev/null; then
|
|
97
|
+
kill -TERM "-$LIVE_CHILD_PID" 2>/dev/null || kill -TERM "$LIVE_CHILD_PID" 2>/dev/null || true
|
|
98
|
+
while kill -0 "$LIVE_CHILD_PID" 2>/dev/null && [[ "$waited" -lt 50 ]]; do
|
|
99
|
+
sleep 0.1
|
|
100
|
+
waited=$((waited + 1))
|
|
101
|
+
done
|
|
102
|
+
if kill -0 "$LIVE_CHILD_PID" 2>/dev/null; then
|
|
103
|
+
kill -KILL "-$LIVE_CHILD_PID" 2>/dev/null || kill -KILL "$LIVE_CHILD_PID" 2>/dev/null || true
|
|
104
|
+
fi
|
|
105
|
+
wait "$LIVE_CHILD_PID" 2>/dev/null
|
|
106
|
+
fi
|
|
107
|
+
finalize_live_output || true
|
|
108
|
+
[[ -s "$STDERR_FILE" ]] || rm "$STDERR_FILE" 2>/dev/null || true
|
|
109
|
+
exit "$signal_rc"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
set -m
|
|
113
|
+
(
|
|
114
|
+
cd "$RUN_DIR" || exit 127
|
|
115
|
+
run_with_timeout "$RUN_TIMEOUT" env \
|
|
116
|
+
-u GEMINI_API_KEY -u GOOGLE_API_KEY -u GOOGLE_AI_API_KEY \
|
|
117
|
+
NO_BROWSER=1 OMNILANE_DEPTH=1 \
|
|
118
|
+
"$AGY_BIN" --dangerously-skip-permissions --add-dir "$RUN_DIR" \
|
|
119
|
+
"${MODE_ARGS[@]}" ${MODEL_ARGS[@]+"${MODEL_ARGS[@]}"} \
|
|
120
|
+
--input-format stream-json --output-format stream-json -p "" \
|
|
121
|
+
< "$LIVE_INBOX" > "$EVENTS_FILE" 2> "$STDERR_FILE"
|
|
122
|
+
) &
|
|
123
|
+
LIVE_CHILD_PID=$!
|
|
124
|
+
set +m
|
|
125
|
+
trap 'stop_live_child 143' TERM
|
|
126
|
+
trap 'stop_live_child 129' HUP
|
|
127
|
+
trap 'stop_live_child 130' INT
|
|
128
|
+
|
|
129
|
+
set +e
|
|
130
|
+
wait "$LIVE_CHILD_PID"
|
|
131
|
+
RC=$?
|
|
132
|
+
set -e
|
|
133
|
+
trap - TERM HUP INT
|
|
134
|
+
if ! finalize_live_output && [[ "$RC" -eq 0 ]]; then
|
|
135
|
+
RC=1
|
|
136
|
+
fi
|
|
137
|
+
if grep -Eiq "$CAPACITY_PATTERN" "$EVENTS_FILE" "$STDERR_FILE" 2>/dev/null; then
|
|
138
|
+
echo "omnilane: gemini capacity exhausted" >> "$STDERR_FILE"
|
|
139
|
+
RC=126
|
|
140
|
+
fi
|
|
141
|
+
[[ -s "$STDERR_FILE" ]] || rm "$STDERR_FILE" 2>/dev/null || true
|
|
142
|
+
exit "$RC"
|
|
143
|
+
fi
|
|
144
|
+
|
|
36
145
|
set +e
|
|
37
146
|
(
|
|
38
147
|
cd "$RUN_DIR" || exit 127
|
package/skills/omnilane/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: omnilane
|
|
3
|
-
description: 'Universal model-routing table + cross-vendor dispatch for ANY harness (Claude Code, Codex, Grok Build, Antigravity). Use when delegating subtasks, choosing a model for work, planning multi-part tasks, or when asked about model routing, delegate, dispatch, which model, tier selection, escalate, 派工, 模型路由. One routing table;
|
|
3
|
+
description: 'Universal model-routing table + cross-vendor dispatch for ANY harness (Claude Code, Codex, Grok Build, Antigravity). Use when delegating subtasks, choosing a model for work, planning multi-part tasks, or when asked about model routing, delegate, dispatch, which model, tier selection, escalate, 派工, 模型路由. One routing table; implementation work dispatches by default via dispatch.sh — the main loop self-executes only reserved commander items.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# omnilane — one routing table, every harness
|
|
@@ -9,7 +9,11 @@ You (the main loop) may be Claude, GPT, Grok, or Gemini. The procedure is identi
|
|
|
9
9
|
|
|
10
10
|
1. **Identify your main model.** You know which model you are running as.
|
|
11
11
|
2. **Split the work into subtasks and classify each into a lane** (table below).
|
|
12
|
-
3. **
|
|
12
|
+
3. **Dispatch implementation work by default — even when the lane's model is
|
|
13
|
+
you.** Self-execute only reserved commander items: planning and
|
|
14
|
+
decomposition, writing task briefs, reviewing reports, acceptance checks,
|
|
15
|
+
replies to the operator, git commit/push, read-only verification, and
|
|
16
|
+
fixes of one line or less. Dispatch:
|
|
13
17
|
`<repo>/scripts/dispatch.sh [--vendor V] [--mode work] [--workdir DIR] <lane> "<task>"`
|
|
14
18
|
Add `--background` for long tasks; poll with `scripts/jobs.sh status|result <id>`.
|
|
15
19
|
Before changing lane order from anecdotal outcomes, run
|
|
@@ -155,8 +159,9 @@ dispatch stay in this skill and the CLI. Manage the local board with
|
|
|
155
159
|
|
|
156
160
|
## Per-model notes (apply the row matching YOUR main model)
|
|
157
161
|
|
|
158
|
-
- **Claude (Fable/Opus main)**: top judgment and taste are yours
|
|
159
|
-
|
|
162
|
+
- **Claude (Fable/Opus main)**: top judgment and taste are yours, but
|
|
163
|
+
implementation still dispatches by default — self-execute only reserved
|
|
164
|
+
commander items; push all coding volume out to the lanes.
|
|
160
165
|
- **Claude Sonnet main**: coordination/tools/mid-tier coding only; never
|
|
161
166
|
self-assign top judgment or hardest implementation.
|
|
162
167
|
- **GPT Sol main**: hardest coding + hard judgment are yours (use max for
|