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
package/scripts/dispatch.sh
CHANGED
|
@@ -35,6 +35,13 @@ MODE="advise"; WORKDIR="$PWD"; BACKGROUND=0; DRY_RUN=0
|
|
|
35
35
|
OVERRIDE_VENDOR=""; OVERRIDE_MODEL=""; OVERRIDE_EFFORT=""; OVERRIDE_TIMEOUT=""
|
|
36
36
|
OVERRIDE_JOB_TIMEOUT=""; OVERRIDE_IDLE_TIMEOUT=""; SESSION_REQUEST="auto"
|
|
37
37
|
THREAD_NAME=""; THREAD_MODE=""; THREAD_ID=""; THREAD_TURN=""; THREAD_CREATED=""
|
|
38
|
+
EXECUTOR="auto"; NATIVE_CONTEXT=""; RESOLVE_WITH_CONTEXT=0
|
|
39
|
+
SELECTED_EXECUTOR="cli"; EXECUTOR_REASON="no-native-context"
|
|
40
|
+
AA_POLICY_FILE="${OMNILANE_AA_POLICY_FILE:-$OMNILANE_REPO/config/aa-model-policy.json}"
|
|
41
|
+
AA_CALLER_CONTEXT="${OMNILANE_AA_CALLER_CONTEXT:-}"
|
|
42
|
+
AA_OPERATOR_ASSERTED_HUMAN="${OMNILANE_AA_OPERATOR_ASSERTED_HUMAN:-0}"
|
|
43
|
+
AA_TARGET_CONFIG=""; AA_POLICY_ACTIVE=0; AA_EXPLICIT_TARGET=0
|
|
44
|
+
AA_LAST_DECISION=""; AA_SELECTED_DECISION=""; AA_VOTE_PREFLIGHT=0
|
|
38
45
|
|
|
39
46
|
usage_error() {
|
|
40
47
|
echo 'usage: dispatch.sh [--background] [--dry-run] [--thread NAME] [flags] LANE "TASK" | [--json] --list|--validate [--json] | [--json] --explain LANE [--json] | --help' >&2
|
|
@@ -58,11 +65,18 @@ flags:
|
|
|
58
65
|
--background run in the background and print the JOB_ID
|
|
59
66
|
--dry-run print the fully resolved dispatch plan and stop
|
|
60
67
|
before any provider call or job state
|
|
68
|
+
--executor auto|native|cli caller-owned native handoff or legacy CLI
|
|
69
|
+
--native-context FILE explicit JSON capabilities; native is not a binary
|
|
70
|
+
--caller-context FILE exact model caller identity plus inherited ceiling
|
|
71
|
+
--operator-asserted-human explicit AA model-ceiling exemption; assertion only
|
|
72
|
+
--aa-policy FILE frozen AA policy registry (default: repo config)
|
|
73
|
+
--transport-overlay FILE host-local hashed request-selector contracts
|
|
74
|
+
--target-config ID pin one runtime-verified registry configuration
|
|
61
75
|
--mode advise|work|sysops
|
|
62
76
|
advise (read-only, default), work (may edit files),
|
|
63
|
-
or sysops (
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
or sysops (vendor sandbox disabled, for service
|
|
78
|
+
operations like launchctl; supported by
|
|
79
|
+
codex, claude, grok, and gemini)
|
|
66
80
|
--workdir DIR working directory handed to the vendor CLI
|
|
67
81
|
--vendor V pin one configured vendor (codex|claude|grok|gemini|kimi|qwen|opencode|openrouter|deepseek|zai|mistral|groq|cerebras)
|
|
68
82
|
--model M override the routed model
|
|
@@ -282,6 +296,9 @@ print_dry_run_plan() {
|
|
|
282
296
|
print_dry_run_value vendor "$VENDOR"
|
|
283
297
|
print_dry_run_value model "$MODEL"
|
|
284
298
|
print_dry_run_value effort "$EFFORT"
|
|
299
|
+
print_dry_run_value executor "$SELECTED_EXECUTOR"
|
|
300
|
+
print_dry_run_value executor_reason "$EXECUTOR_REASON"
|
|
301
|
+
printf 'aa_policy=%s\n' "$AA_SELECTED_DECISION"
|
|
285
302
|
print_dry_run_value mode "$MODE"
|
|
286
303
|
print_dry_run_value workdir "$WORKDIR"
|
|
287
304
|
printf 'timeout=%s\n' "$TIMEOUT"
|
|
@@ -351,10 +368,59 @@ routing_candidate_available() {
|
|
|
351
368
|
fi
|
|
352
369
|
}
|
|
353
370
|
|
|
371
|
+
aa_policy_decide() {
|
|
372
|
+
# vendor model effort [target-config] -> structured JSON in AA_LAST_DECISION
|
|
373
|
+
local vendor="$1" model="$2" effort="$3" target_config="${4:-}" rc=0
|
|
374
|
+
local args=(--registry "$AA_POLICY_FILE" --vendor "$vendor" --model "$model" --effort "$effort")
|
|
375
|
+
[[ -z "$target_config" ]] || args+=(--target-config "$target_config")
|
|
376
|
+
if [[ "$AA_OPERATOR_ASSERTED_HUMAN" == "1" ]]; then
|
|
377
|
+
args+=(--operator-asserted-human)
|
|
378
|
+
elif [[ -n "$AA_CALLER_CONTEXT" ]]; then
|
|
379
|
+
args+=(--caller-context "$AA_CALLER_CONTEXT")
|
|
380
|
+
fi
|
|
381
|
+
AA_LAST_DECISION="$(python3 "$OMNILANE_REPO/scripts/lib/aa_policy.py" "${args[@]}")" || rc=$?
|
|
382
|
+
return "$rc"
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
aa_vote_child_spec() {
|
|
386
|
+
case "$1" in
|
|
387
|
+
codex) printf 'gpt-6-astra\txhigh\n' ;;
|
|
388
|
+
claude) printf 'claude-fable-5-1\txhigh\n' ;;
|
|
389
|
+
gemini) printf 'gemini-3.8-flash-medium\t-\n' ;;
|
|
390
|
+
grok) printf 'grok-4.6\t-\n' ;;
|
|
391
|
+
*) return 1 ;;
|
|
392
|
+
esac
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
aa_policy_candidate_allowed() {
|
|
396
|
+
local vendor="$1" model="$2" effort="$3" voter spec child_model child_effort
|
|
397
|
+
local voters=()
|
|
398
|
+
AA_VOTE_PREFLIGHT=0
|
|
399
|
+
if [[ "$vendor" != "vote" ]]; then
|
|
400
|
+
aa_policy_decide "$vendor" "$model" "$effort" "$AA_TARGET_CONFIG"
|
|
401
|
+
return $?
|
|
402
|
+
fi
|
|
403
|
+
[[ -z "$AA_TARGET_CONFIG" ]] || {
|
|
404
|
+
AA_LAST_DECISION='{"schema_version":1,"allowed":false,"code":"vote-target-config-invalid","message":"--target-config cannot identify an entire vote panel"}'
|
|
405
|
+
return 3
|
|
406
|
+
}
|
|
407
|
+
IFS=',' read -ra voters <<< "$model"
|
|
408
|
+
for voter in ${voters[@]+"${voters[@]}"}; do
|
|
409
|
+
spec="$(aa_vote_child_spec "$voter")" || {
|
|
410
|
+
AA_LAST_DECISION='{"schema_version":1,"allowed":false,"code":"unknown-vote-child","message":"vote panel contains an unknown child"}'
|
|
411
|
+
return 3
|
|
412
|
+
}
|
|
413
|
+
child_model="${spec%%$'\t'*}"; child_effort="${spec##*$'\t'}"
|
|
414
|
+
aa_policy_decide "$voter" "$child_model" "$child_effort" || return $?
|
|
415
|
+
done
|
|
416
|
+
AA_VOTE_PREFLIGHT=1
|
|
417
|
+
return 0
|
|
418
|
+
}
|
|
419
|
+
|
|
354
420
|
# Pick the first candidate whose vendor CLI is present ("off" always matches).
|
|
355
421
|
# Sets RESOLVED_SPEC / RESOLVED_FIELDS / RESOLVED_IDX / RESOLVED_TOTAL.
|
|
356
422
|
resolve_chain() {
|
|
357
|
-
local chain="$1" requested_vendor="${2:-}" seg i=0 vendor
|
|
423
|
+
local chain="$1" requested_vendor="${2:-}" seg i=0 vendor model effort considered=0
|
|
358
424
|
RESOLVED_SPEC=""; RESOLVED_IDX=0; RESOLVED_TOTAL=0; RESOLVED_FIELDS=()
|
|
359
425
|
local SEGS=() F=()
|
|
360
426
|
IFS='|' read -ra SEGS <<< "$chain"
|
|
@@ -371,16 +437,35 @@ resolve_chain() {
|
|
|
371
437
|
}
|
|
372
438
|
F=("${PARSED_FIELDS[@]}")
|
|
373
439
|
vendor="${F[0]:-}"
|
|
440
|
+
model="${OVERRIDE_MODEL:-${F[1]:-}}"
|
|
441
|
+
effort="${OVERRIDE_EFFORT:-${F[2]:-}}"
|
|
374
442
|
if [[ -n "$requested_vendor" ]]; then
|
|
375
443
|
[[ "$vendor" == "$requested_vendor" ]] || continue
|
|
444
|
+
considered=1
|
|
445
|
+
if [[ "$AA_POLICY_ACTIVE" -eq 1 ]] &&
|
|
446
|
+
! aa_policy_candidate_allowed "$vendor" "$model" "$effort"; then
|
|
447
|
+
return 6
|
|
448
|
+
fi
|
|
376
449
|
RESOLVED_SPEC="$seg"; RESOLVED_FIELDS=("${F[@]}"); RESOLVED_IDX="$i"
|
|
377
|
-
|
|
450
|
+
AA_SELECTED_DECISION="$AA_LAST_DECISION"
|
|
451
|
+
if [[ "$RESOLVE_WITH_CONTEXT" -eq 1 ]] || routing_candidate_available "$vendor" "$model"; then return 0; fi
|
|
378
452
|
return 4
|
|
379
453
|
fi
|
|
380
|
-
if [[ "$
|
|
454
|
+
if [[ "$AA_EXPLICIT_TARGET" -eq 1 && "$considered" -eq 1 ]]; then
|
|
455
|
+
return 6
|
|
456
|
+
fi
|
|
457
|
+
considered=1
|
|
458
|
+
if [[ "$AA_POLICY_ACTIVE" -eq 1 ]] &&
|
|
459
|
+
! aa_policy_candidate_allowed "$vendor" "$model" "$effort"; then
|
|
460
|
+
[[ "$AA_EXPLICIT_TARGET" -eq 1 ]] && return 6
|
|
461
|
+
continue
|
|
462
|
+
fi
|
|
463
|
+
if [[ "$RESOLVE_WITH_CONTEXT" -eq 1 || "$vendor" == "off" ]] || routing_candidate_available "$vendor" "$model"; then
|
|
381
464
|
RESOLVED_SPEC="$seg"; RESOLVED_FIELDS=("${F[@]}")
|
|
465
|
+
AA_SELECTED_DECISION="$AA_LAST_DECISION"
|
|
382
466
|
RESOLVED_IDX="$i"; return 0
|
|
383
467
|
fi
|
|
468
|
+
[[ "$AA_EXPLICIT_TARGET" -eq 1 ]] && return 4
|
|
384
469
|
done
|
|
385
470
|
[[ -n "$requested_vendor" ]] && return 5
|
|
386
471
|
return 1
|
|
@@ -627,7 +712,9 @@ while [[ $# -gt 0 ]]; do
|
|
|
627
712
|
}
|
|
628
713
|
SESSION_REQUEST="single-shot"; shift ;;
|
|
629
714
|
--dry-run) DRY_RUN=1; shift ;;
|
|
630
|
-
--
|
|
715
|
+
--operator-asserted-human)
|
|
716
|
+
AA_OPERATOR_ASSERTED_HUMAN=1; shift ;;
|
|
717
|
+
--mode|--workdir|--vendor|--model|--effort|--timeout|--job-timeout|--idle-timeout|--thread|--executor|--native-context|--caller-context|--aa-policy|--target-config|--transport-overlay)
|
|
631
718
|
# Value-taking flags: a missing value must be a clean usage error (exit 2),
|
|
632
719
|
# not a `set -u` "unbound variable" crash on $2.
|
|
633
720
|
[[ $# -ge 2 ]] || { echo "omnilane: $1 needs a value" >&2; exit 2; }
|
|
@@ -641,6 +728,14 @@ while [[ $# -gt 0 ]]; do
|
|
|
641
728
|
--job-timeout) OVERRIDE_JOB_TIMEOUT="$2" ;;
|
|
642
729
|
--idle-timeout) OVERRIDE_IDLE_TIMEOUT="$2" ;;
|
|
643
730
|
--thread) THREAD_NAME="$2" ;;
|
|
731
|
+
--executor) EXECUTOR="$2" ;;
|
|
732
|
+
--native-context)
|
|
733
|
+
[[ -n "$2" ]] || { echo "omnilane: native context path is empty" >&2; exit 2; }
|
|
734
|
+
NATIVE_CONTEXT="$2" ;;
|
|
735
|
+
--caller-context) AA_CALLER_CONTEXT="$2" ;;
|
|
736
|
+
--aa-policy) AA_POLICY_FILE="$2" ;;
|
|
737
|
+
--target-config) AA_TARGET_CONFIG="$2" ;;
|
|
738
|
+
--transport-overlay) export OMNILANE_AA_TRANSPORT_OVERLAY="$2" ;;
|
|
644
739
|
esac
|
|
645
740
|
shift 2 ;;
|
|
646
741
|
-*) echo "omnilane: unknown flag" >&2; exit 2 ;;
|
|
@@ -686,6 +781,35 @@ if [[ -n "$OVERRIDE_VENDOR" ]] &&
|
|
|
686
781
|
fi
|
|
687
782
|
|
|
688
783
|
depth_guard
|
|
784
|
+
command -v python3 >/dev/null 2>&1 || {
|
|
785
|
+
echo "omnilane: exact-AA policy requires Python 3" >&2; exit 2
|
|
786
|
+
}
|
|
787
|
+
[[ "$AA_OPERATOR_ASSERTED_HUMAN" == "0" || "$AA_OPERATOR_ASSERTED_HUMAN" == "1" ]] || {
|
|
788
|
+
echo "omnilane: OMNILANE_AA_OPERATOR_ASSERTED_HUMAN must be 0 or 1" >&2; exit 2
|
|
789
|
+
}
|
|
790
|
+
if [[ "$AA_OPERATOR_ASSERTED_HUMAN" == "1" && -n "$AA_CALLER_CONTEXT" ]]; then
|
|
791
|
+
echo "omnilane: caller context and operator assertion are mutually exclusive" >&2
|
|
792
|
+
exit 2
|
|
793
|
+
fi
|
|
794
|
+
[[ -n "$AA_POLICY_FILE" ]] || { echo "omnilane: AA policy path is empty" >&2; exit 2; }
|
|
795
|
+
if [[ -n "${OMNILANE_AA_TRANSPORT_OVERLAY:-}" ]]; then
|
|
796
|
+
OMNILANE_AA_OVERLAY_SHA256="$(file_sha256 "$OMNILANE_AA_TRANSPORT_OVERLAY")"
|
|
797
|
+
export OMNILANE_AA_OVERLAY_SHA256
|
|
798
|
+
fi
|
|
799
|
+
AA_POLICY_ACTIVE=1
|
|
800
|
+
if [[ -n "$OVERRIDE_VENDOR" || -n "$OVERRIDE_MODEL" || -n "$OVERRIDE_EFFORT" || -n "$AA_TARGET_CONFIG" ]]; then
|
|
801
|
+
AA_EXPLICIT_TARGET=1
|
|
802
|
+
fi
|
|
803
|
+
case "$EXECUTOR" in
|
|
804
|
+
cli) EXECUTOR_REASON="forced-cli" ;;
|
|
805
|
+
auto|native)
|
|
806
|
+
if [[ "$EXECUTOR" == "native" || -n "$NATIVE_CONTEXT" ]]; then
|
|
807
|
+
# Resolve independently of installed executables. Native rejection may
|
|
808
|
+
# fall back only to this exact CLI target, not to the next routing row.
|
|
809
|
+
RESOLVE_WITH_CONTEXT=1
|
|
810
|
+
fi ;;
|
|
811
|
+
*) echo "omnilane: invalid executor (auto|native|cli)" >&2; exit 2 ;;
|
|
812
|
+
esac
|
|
689
813
|
|
|
690
814
|
CHAIN="$(raw_lane_line "$LANE")" || { echo "omnilane: unknown lane '$LANE' (try --list)" >&2; exit 2; }
|
|
691
815
|
if [[ -n "$OVERRIDE_VENDOR" ]]; then
|
|
@@ -699,7 +823,11 @@ if [[ -n "$OVERRIDE_VENDOR" ]]; then
|
|
|
699
823
|
echo "omnilane: requested vendor '$OVERRIDE_VENDOR' is configured for lane '$LANE' but its CLI is unavailable" >&2
|
|
700
824
|
exit 4
|
|
701
825
|
;;
|
|
702
|
-
|
|
826
|
+
6)
|
|
827
|
+
printf '%s\n' "$AA_LAST_DECISION" >&2
|
|
828
|
+
exit 3
|
|
829
|
+
;;
|
|
830
|
+
5)
|
|
703
831
|
echo "omnilane: requested vendor '$OVERRIDE_VENDOR' is not configured for lane '$LANE'" >&2
|
|
704
832
|
exit 2
|
|
705
833
|
;;
|
|
@@ -710,11 +838,16 @@ if [[ -n "$OVERRIDE_VENDOR" ]]; then
|
|
|
710
838
|
esac
|
|
711
839
|
fi
|
|
712
840
|
else
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
841
|
+
resolve_rc=0
|
|
842
|
+
resolve_chain "$CHAIN" || resolve_rc=$?
|
|
843
|
+
if [[ "$resolve_rc" -eq 6 ]]; then
|
|
844
|
+
printf '%s\n' "$AA_LAST_DECISION" >&2
|
|
845
|
+
exit 3
|
|
846
|
+
elif [[ "$resolve_rc" -ne 0 ]]; then
|
|
847
|
+
echo "omnilane: no eligible available target for lane '$LANE' (chain:$CHAIN)." >&2
|
|
848
|
+
[[ -z "$AA_LAST_DECISION" ]] || printf '%s\n' "$AA_LAST_DECISION" >&2
|
|
716
849
|
exit 4
|
|
717
|
-
|
|
850
|
+
fi
|
|
718
851
|
fi
|
|
719
852
|
|
|
720
853
|
FIELDS=("${RESOLVED_FIELDS[@]}")
|
|
@@ -722,6 +855,61 @@ VENDOR="${FIELDS[0]}"; MODEL="${FIELDS[1]:-}"; EFFORT="${FIELDS[2]:-}"
|
|
|
722
855
|
[[ -n "$OVERRIDE_MODEL" ]] && MODEL="$OVERRIDE_MODEL"
|
|
723
856
|
[[ -n "$OVERRIDE_EFFORT" ]] && EFFORT="$OVERRIDE_EFFORT"
|
|
724
857
|
|
|
858
|
+
AA_REGISTRY_SHA256="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["registry_sha256"])' "$AA_SELECTED_DECISION")"
|
|
859
|
+
AA_CALLER_SHA256="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1]).get("caller_context_sha256", ""))' "$AA_SELECTED_DECISION")"
|
|
860
|
+
export OMNILANE_AA_POLICY_FILE="$AA_POLICY_FILE"
|
|
861
|
+
export OMNILANE_AA_REGISTRY_SHA256="$AA_REGISTRY_SHA256"
|
|
862
|
+
export OMNILANE_AA_CALLER_CONTEXT="$AA_CALLER_CONTEXT"
|
|
863
|
+
export OMNILANE_AA_CALLER_SHA256="$AA_CALLER_SHA256"
|
|
864
|
+
export OMNILANE_AA_OPERATOR_ASSERTED_HUMAN="$AA_OPERATOR_ASSERTED_HUMAN"
|
|
865
|
+
export OMNILANE_AA_VOTE_PREFLIGHT="$AA_VOTE_PREFLIGHT"
|
|
866
|
+
|
|
867
|
+
# Shared routing fields are fixed before executor selection. Native never
|
|
868
|
+
# sources runners or launches a shell agent.
|
|
869
|
+
TIMEOUT="$OVERRIDE_TIMEOUT"
|
|
870
|
+
if [[ -z "$TIMEOUT" ]]; then
|
|
871
|
+
LANE_TIMEOUT_VAR="OMNILANE_TIMEOUT_$(printf '%s' "${LANE//-/_}" | tr '[:lower:]' '[:upper:]')"
|
|
872
|
+
TIMEOUT="${!LANE_TIMEOUT_VAR:-}"
|
|
873
|
+
fi
|
|
874
|
+
[[ -n "$TIMEOUT" ]] || TIMEOUT="${OMNILANE_TIMEOUT:-600}"
|
|
875
|
+
[[ "$TIMEOUT" =~ ^[1-9][0-9]*$ ]] || {
|
|
876
|
+
echo "omnilane: invalid timeout (want a positive integer of seconds)" >&2; exit 2
|
|
877
|
+
}
|
|
878
|
+
if [[ "$RESOLVE_WITH_CONTEXT" -eq 1 ]]; then
|
|
879
|
+
command -v python3 >/dev/null 2>&1 || { echo "omnilane: native protocol requires Python 3" >&2; exit 2; }
|
|
880
|
+
NATIVE_JOB_VAR="OMNILANE_JOB_TIMEOUT_$(printf '%s' "${LANE//-/_}" | tr '[:lower:]' '[:upper:]')"
|
|
881
|
+
NATIVE_ARGS=(route --home "$OMNILANE_HOME" --executor "$EXECUTOR" --lane "$LANE"
|
|
882
|
+
--vendor "$VENDOR" --model="$MODEL" --effort="$EFFORT" --workdir "$WORKDIR"
|
|
883
|
+
--mode "$MODE" --task="$TASK" --session "$SESSION_REQUEST" --thread "$THREAD_NAME"
|
|
884
|
+
--policy "$AA_POLICY_FILE" --expected-registry-sha256 "$AA_REGISTRY_SHA256"
|
|
885
|
+
--timeout "$TIMEOUT" --job-timeout "${OVERRIDE_JOB_TIMEOUT:-${!NATIVE_JOB_VAR:-${OMNILANE_JOB_TIMEOUT:-}}}"
|
|
886
|
+
--idle-timeout "${OVERRIDE_IDLE_TIMEOUT:-${OMNILANE_IDLE_TIMEOUT:-}}")
|
|
887
|
+
[[ -z "$NATIVE_CONTEXT" ]] || NATIVE_ARGS+=(--context "$NATIVE_CONTEXT")
|
|
888
|
+
[[ -z "$AA_TARGET_CONFIG" ]] || NATIVE_ARGS+=(--target-config "$AA_TARGET_CONFIG")
|
|
889
|
+
if [[ "$AA_OPERATOR_ASSERTED_HUMAN" == "1" ]]; then
|
|
890
|
+
NATIVE_ARGS+=(--operator-asserted-human)
|
|
891
|
+
elif [[ -n "$AA_CALLER_CONTEXT" ]]; then
|
|
892
|
+
NATIVE_ARGS+=(--caller-context "$AA_CALLER_CONTEXT")
|
|
893
|
+
[[ -z "$AA_CALLER_SHA256" ]] || NATIVE_ARGS+=(--expected-caller-sha256 "$AA_CALLER_SHA256")
|
|
894
|
+
fi
|
|
895
|
+
[[ "$BACKGROUND" -eq 0 ]] || NATIVE_ARGS+=(--background)
|
|
896
|
+
[[ "$DRY_RUN" -eq 0 ]] || NATIVE_ARGS+=(--dry-run)
|
|
897
|
+
NATIVE_RC=0
|
|
898
|
+
NATIVE_OUTPUT="$(python3 "$OMNILANE_REPO/scripts/lib/native.py" "${NATIVE_ARGS[@]}")" || NATIVE_RC=$?
|
|
899
|
+
if [[ "$NATIVE_RC" -eq 0 ]]; then
|
|
900
|
+
printf '%s\n' "$NATIVE_OUTPUT"
|
|
901
|
+
exit 0
|
|
902
|
+
elif [[ "$NATIVE_RC" -ne 10 ]]; then
|
|
903
|
+
exit "$NATIVE_RC"
|
|
904
|
+
fi
|
|
905
|
+
EXECUTOR_REASON="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["reason"])' "$NATIVE_OUTPUT")"
|
|
906
|
+
MODEL="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["model"])' "$NATIVE_OUTPUT")"
|
|
907
|
+
[[ "$VENDOR" == "off" ]] || routing_candidate_available "$VENDOR" "$MODEL" || {
|
|
908
|
+
echo "omnilane: native rejected ($EXECUTOR_REASON); exact CLI target '$VENDOR' unavailable; no vendor/model substitution" >&2
|
|
909
|
+
exit 4
|
|
910
|
+
}
|
|
911
|
+
fi
|
|
912
|
+
|
|
725
913
|
if [[ -n "$THREAD_NAME" ]]; then
|
|
726
914
|
case "$VENDOR" in
|
|
727
915
|
claude|codex|grok|gemini) ;;
|
|
@@ -785,13 +973,32 @@ fi
|
|
|
785
973
|
RUNNER="$OMNILANE_REPO/scripts/runners/run-$VENDOR.sh"
|
|
786
974
|
[[ -x "$RUNNER" ]] || { echo "omnilane: no runner for vendor '$VENDOR'" >&2; exit 2; }
|
|
787
975
|
|
|
976
|
+
# Fail closed before creating job state or starting a provider when the selected
|
|
977
|
+
# runtime cannot enforce the requested mode. Dry-run reports the same gap.
|
|
978
|
+
if [[ "$VENDOR" == "grok" && "$MODE" == "work" && "$(uname -s)" == "Darwin" ]]; then
|
|
979
|
+
echo "omnilane: Grok work requires disabled agent-tool network; xAI CLI child-network restrictions are not enforced on macOS" >&2
|
|
980
|
+
exit 2
|
|
981
|
+
fi
|
|
982
|
+
|
|
788
983
|
SESSION_MODE="single-shot"
|
|
789
|
-
if [[ -z "$THREAD_NAME" && "$SESSION_REQUEST" != "single-shot" ]] && live_vendor_capable "$VENDOR"
|
|
790
|
-
|
|
984
|
+
if [[ -z "$THREAD_NAME" && "$SESSION_REQUEST" != "single-shot" ]] && live_vendor_capable "$VENDOR" &&
|
|
985
|
+
[[ "$SESSION_REQUEST" == "live" || "$VENDOR" == "claude" || "$VENDOR" == "gemini" ]]; then
|
|
986
|
+
# Preserve established auto behavior: Codex/Grok require an explicit --live.
|
|
987
|
+
# Claude/Gemini retain their existing auto-selected resident sessions.
|
|
988
|
+
# Grok ACP currently exposes no enforceable restricted policy.
|
|
989
|
+
# Only explicit sysops dispatch may use its resident ACP session.
|
|
990
|
+
if [[ "$VENDOR" != "grok" || "$MODE" == "sysops" ]]; then
|
|
991
|
+
SESSION_MODE="live"
|
|
992
|
+
fi
|
|
791
993
|
fi
|
|
792
|
-
if [[ "$SESSION_REQUEST" == "live" ]]
|
|
793
|
-
|
|
794
|
-
|
|
994
|
+
if [[ "$SESSION_REQUEST" == "live" ]]; then
|
|
995
|
+
if [[ "$VENDOR" == "grok" && "$MODE" != "sysops" ]]; then
|
|
996
|
+
echo "omnilane: Grok --live supports only explicit --mode sysops; restricted ACP policy is unavailable" >&2
|
|
997
|
+
exit 2
|
|
998
|
+
elif ! live_vendor_capable "$VENDOR"; then
|
|
999
|
+
echo "omnilane: vendor '$VENDOR' cannot run live; live-capable vendors: $(live_capable_vendors)" >&2
|
|
1000
|
+
exit 2
|
|
1001
|
+
fi
|
|
795
1002
|
fi
|
|
796
1003
|
export OMNILANE_SESSION_MODE="$SESSION_MODE"
|
|
797
1004
|
if [[ "$SESSION_REQUEST" == "live" ]]; then
|
|
@@ -804,15 +1011,6 @@ fi
|
|
|
804
1011
|
# Resolve here and export so every runner (and vote's sub-runners) inherits the
|
|
805
1012
|
# same value without a per-runner code change; they already read OMNILANE_TIMEOUT.
|
|
806
1013
|
# This bounds each runner CLI call, not the whole dispatch (see header note).
|
|
807
|
-
TIMEOUT="$OVERRIDE_TIMEOUT"
|
|
808
|
-
if [[ -z "$TIMEOUT" ]]; then
|
|
809
|
-
LANE_TIMEOUT_VAR="OMNILANE_TIMEOUT_$(printf '%s' "${LANE//-/_}" | tr '[:lower:]' '[:upper:]')"
|
|
810
|
-
TIMEOUT="${!LANE_TIMEOUT_VAR:-}"
|
|
811
|
-
fi
|
|
812
|
-
[[ -n "$TIMEOUT" ]] || TIMEOUT="${OMNILANE_TIMEOUT:-600}"
|
|
813
|
-
[[ "$TIMEOUT" =~ ^[1-9][0-9]*$ ]] || {
|
|
814
|
-
echo "omnilane: invalid timeout (want a positive integer of seconds)" >&2; exit 2
|
|
815
|
-
}
|
|
816
1014
|
export OMNILANE_TIMEOUT="$TIMEOUT"
|
|
817
1015
|
|
|
818
1016
|
IDLE_TIMEOUT="$OVERRIDE_IDLE_TIMEOUT"
|
|
@@ -822,9 +1020,20 @@ IDLE_TIMEOUT="$OVERRIDE_IDLE_TIMEOUT"
|
|
|
822
1020
|
exit 2
|
|
823
1021
|
}
|
|
824
1022
|
export OMNILANE_IDLE_TIMEOUT="$IDLE_TIMEOUT"
|
|
1023
|
+
# The idle cap only exists inside a live mailbox, so a single-shot dispatch
|
|
1024
|
+
# would be told about a limit that never applies to it.
|
|
1025
|
+
if [[ "$SESSION_MODE" == "live" && "$IDLE_TIMEOUT" -gt 0 && "$TIMEOUT" -gt "$IDLE_TIMEOUT" ]]; then
|
|
1026
|
+
echo "omnilane: --timeout is ${TIMEOUT}s, but the independent live mailbox idle cap remains ${IDLE_TIMEOUT}s; adjust it with --idle-timeout SECONDS" >&2
|
|
1027
|
+
fi
|
|
825
1028
|
|
|
826
1029
|
JOB_SUPERVISOR="$OMNILANE_REPO/scripts/lib/job-timeout.pl"
|
|
827
|
-
|
|
1030
|
+
JOB_WORKER_SOURCE="$OMNILANE_REPO/scripts/lib/job-worker.sh"
|
|
1031
|
+
# Pinned deliberately: /bin/bash is 3.2 on macOS while a PATH bash is typically
|
|
1032
|
+
# 5.x, so the worker and every library it sources must stay bash-3.2 compatible.
|
|
1033
|
+
# No declare -A, mapfile, ${v,,}, [[ -v ]], ;;&, EPOCHSECONDS or coproc.
|
|
1034
|
+
JOB_WORKER_BASH="/bin/bash"
|
|
1035
|
+
[[ -x "$JOB_WORKER_BASH" ]] || { echo "omnilane: fixed worker interpreter is unavailable: $JOB_WORKER_BASH" >&2; exit 2; }
|
|
1036
|
+
JOB_WORKER_BASH_VERSION="$($JOB_WORKER_BASH -c 'printf %s "$BASH_VERSION"')"
|
|
828
1037
|
|
|
829
1038
|
# Optional whole-job seconds: flag > per-lane env > global env > automatic
|
|
830
1039
|
# non-Git Codex work guard > disabled.
|
|
@@ -870,7 +1079,7 @@ if [[ -n "$JOB_TIMEOUT" && ! "$JOB_TIMEOUT" =~ ^[1-9][0-9]{0,8}$ ]]; then
|
|
|
870
1079
|
fi
|
|
871
1080
|
JOB_TIMEOUT_JSON="${JOB_TIMEOUT:-null}"
|
|
872
1081
|
unset OMNILANE_JOB_SUPERVISED
|
|
873
|
-
[[ -x "$
|
|
1082
|
+
[[ -x "$JOB_WORKER_SOURCE" ]] || { echo "omnilane: internal job worker is unavailable" >&2; exit 2; }
|
|
874
1083
|
if [[ -n "$JOB_TIMEOUT" ]]; then
|
|
875
1084
|
[[ -f "$JOB_SUPERVISOR" ]] || { echo "omnilane: whole-job timeout supervisor is unavailable" >&2; exit 2; }
|
|
876
1085
|
command -v perl &>/dev/null || { echo "omnilane: --job-timeout requires perl" >&2; exit 2; }
|
|
@@ -889,6 +1098,7 @@ if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
|
889
1098
|
exit 0
|
|
890
1099
|
fi
|
|
891
1100
|
|
|
1101
|
+
printf 'omnilane: executor=cli reason=%s\n' "$EXECUTOR_REASON" >&2
|
|
892
1102
|
mkdir -p "$OMNILANE_HOME"
|
|
893
1103
|
if [[ ! -d "$JOBS_ROOT" ]]; then
|
|
894
1104
|
mkdir -m 700 "$JOBS_ROOT"
|
|
@@ -904,6 +1114,58 @@ chmod 700 "$JOBS_ROOT"
|
|
|
904
1114
|
JOB_ID="$(date +%Y%m%d-%H%M%S)-$$-$RANDOM"
|
|
905
1115
|
JOB_DIR="$JOBS_ROOT/$JOB_ID"
|
|
906
1116
|
mkdir -m 700 "$JOB_DIR"
|
|
1117
|
+
# Snapshot authorizer separately from the worker identity. Never hand a worker
|
|
1118
|
+
# its parent's identity as its own caller context.
|
|
1119
|
+
python3 - "$OMNILANE_REPO" "$JOB_DIR" "$AA_POLICY_FILE" "$AA_REGISTRY_SHA256" "$AA_CALLER_CONTEXT" "$AA_CALLER_SHA256" "$AA_SELECTED_DECISION" "$VENDOR" <<'AA_PUBLISH'
|
|
1120
|
+
import json, sys
|
|
1121
|
+
from pathlib import Path
|
|
1122
|
+
sys.path.insert(0, str(Path(sys.argv[1]) / "scripts/lib"))
|
|
1123
|
+
import aa_policy
|
|
1124
|
+
_, repo, directory, policy, policy_sha, context, context_sha, raw, vendor = sys.argv
|
|
1125
|
+
registry, _ = aa_policy.load_registry(policy, policy_sha)
|
|
1126
|
+
caller = aa_policy.load_caller(context, registry, context_sha)[0] if context else None
|
|
1127
|
+
decision = json.loads(raw)
|
|
1128
|
+
if vendor == "vote":
|
|
1129
|
+
decision["target_config_id"] = None
|
|
1130
|
+
decision["child_context"] = None
|
|
1131
|
+
decision["target_request"] = {"vendor": "vote", "model": None, "effort": None}
|
|
1132
|
+
aa_policy.publish_context(directory, registry, caller, decision, policy)
|
|
1133
|
+
AA_PUBLISH
|
|
1134
|
+
AA_POLICY_FILE="$JOB_DIR/aa-registry.json"
|
|
1135
|
+
AA_REGISTRY_SHA256="$(file_sha256 "$AA_POLICY_FILE")"
|
|
1136
|
+
AA_CALLER_CONTEXT=""
|
|
1137
|
+
AA_CALLER_SHA256=""
|
|
1138
|
+
if [[ -f "$JOB_DIR/aa-authorizer.json" ]]; then
|
|
1139
|
+
AA_CALLER_CONTEXT="$JOB_DIR/aa-authorizer.json"
|
|
1140
|
+
AA_CALLER_SHA256="$(file_sha256 "$AA_CALLER_CONTEXT")"
|
|
1141
|
+
fi
|
|
1142
|
+
export OMNILANE_AA_POLICY_FILE="$AA_POLICY_FILE"
|
|
1143
|
+
export OMNILANE_AA_REGISTRY_SHA256="$AA_REGISTRY_SHA256"
|
|
1144
|
+
export OMNILANE_AA_AUTHORIZER_CONTEXT="$AA_CALLER_CONTEXT"
|
|
1145
|
+
export OMNILANE_AA_AUTHORIZER_SHA256="$AA_CALLER_SHA256"
|
|
1146
|
+
export OMNILANE_AA_AUTHORIZER_HUMAN="$AA_OPERATOR_ASSERTED_HUMAN"
|
|
1147
|
+
export OMNILANE_AA_CONTEXT_DIR="$JOB_DIR"
|
|
1148
|
+
export OMNILANE_AA_TARGET_CONFIG="$AA_TARGET_CONFIG"
|
|
1149
|
+
# Human exemptions belong only to this operator launch, never to a model child.
|
|
1150
|
+
unset OMNILANE_AA_OPERATOR_ASSERTED_HUMAN
|
|
1151
|
+
export OMNILANE_AA_CALLER_CONTEXT=""
|
|
1152
|
+
export OMNILANE_AA_CALLER_SHA256=""
|
|
1153
|
+
if [[ -f "$JOB_DIR/aa-child-context.json" ]]; then
|
|
1154
|
+
export OMNILANE_AA_CALLER_CONTEXT="$JOB_DIR/aa-child-context.json"
|
|
1155
|
+
OMNILANE_AA_CALLER_SHA256="$(file_sha256 "$JOB_DIR/aa-child-context.json")"
|
|
1156
|
+
export OMNILANE_AA_CALLER_SHA256
|
|
1157
|
+
fi
|
|
1158
|
+
JOB_WORKER="$JOB_DIR/job-worker.sh"
|
|
1159
|
+
JOB_WORKER_TMP="$JOB_DIR/.job-worker.tmp.$$-$RANDOM"
|
|
1160
|
+
(umask 077; cat "$JOB_WORKER_SOURCE" > "$JOB_WORKER_TMP")
|
|
1161
|
+
chmod 400 "$JOB_WORKER_TMP"
|
|
1162
|
+
mv "$JOB_WORKER_TMP" "$JOB_WORKER"
|
|
1163
|
+
JOB_WORKER_SOURCE_SHA256="$(file_sha256 "$JOB_WORKER_SOURCE")" || exit 2
|
|
1164
|
+
JOB_WORKER_SHA256="$(file_sha256 "$JOB_WORKER")" || exit 2
|
|
1165
|
+
if [[ "$JOB_WORKER_SHA256" != "$JOB_WORKER_SOURCE_SHA256" ]]; then
|
|
1166
|
+
echo "omnilane: job worker source changed while creating the per-job snapshot" >&2
|
|
1167
|
+
exit 1
|
|
1168
|
+
fi
|
|
907
1169
|
FOREMAN_SESSION=""
|
|
908
1170
|
if [[ -n "${CLAUDE_CODE_SESSION_ID+x}" ]]; then
|
|
909
1171
|
if [[ "$CLAUDE_CODE_SESSION_ID" =~ ^[A-Za-z0-9._:-]+$ &&
|
|
@@ -941,6 +1203,25 @@ else
|
|
|
941
1203
|
"$(date -u +%FT%TZ)" > "$JOB_DIR/meta.json")
|
|
942
1204
|
fi
|
|
943
1205
|
|
|
1206
|
+
META_TMP="$JOB_DIR/.meta.tmp.$$-$RANDOM"
|
|
1207
|
+
META_BASE="$(cat "$JOB_DIR/meta.json")"
|
|
1208
|
+
if [[ "$META_BASE" != *'}' ]]; then
|
|
1209
|
+
rm "$META_TMP" 2>/dev/null || true
|
|
1210
|
+
echo "omnilane: cannot add worker provenance to malformed meta.json" >&2
|
|
1211
|
+
exit 1
|
|
1212
|
+
fi
|
|
1213
|
+
META_BASE="${META_BASE%\}}"
|
|
1214
|
+
META_BASE="$META_BASE,\"executor\":\"cli\",\"executor_reason\":\"$(json_escape "$EXECUTOR_REASON")\""
|
|
1215
|
+
printf '%s,"worker_interpreter_path":"%s","worker_interpreter_version":"%s","job_worker_source_path":"%s","job_worker_source_sha256":"%s","job_worker_path":"%s","job_worker_sha256":"%s"}\n' \
|
|
1216
|
+
"$META_BASE" "$(json_escape "$JOB_WORKER_BASH")" \
|
|
1217
|
+
"$(json_escape "$JOB_WORKER_BASH_VERSION")" "$(json_escape "$JOB_WORKER_SOURCE")" \
|
|
1218
|
+
"$(json_escape "$JOB_WORKER_SOURCE_SHA256")" "$(json_escape "$JOB_WORKER")" \
|
|
1219
|
+
"$(json_escape "$JOB_WORKER_SHA256")" \
|
|
1220
|
+
> "$META_TMP"
|
|
1221
|
+
json_file_round_trip_valid "$META_TMP" || exit 1
|
|
1222
|
+
chmod 600 "$META_TMP"
|
|
1223
|
+
mv "$META_TMP" "$JOB_DIR/meta.json"
|
|
1224
|
+
|
|
944
1225
|
if [[ -n "$THREAD_NAME" ]]; then
|
|
945
1226
|
printf 'omnilane: thread %s turn %s (%s session %s, %s)\n' \
|
|
946
1227
|
"$THREAD_NAME" "$THREAD_TURN" "$VENDOR" "$THREAD_ID" "$THREAD_MODE"
|
|
@@ -948,6 +1229,9 @@ fi
|
|
|
948
1229
|
|
|
949
1230
|
secure_job_files() {
|
|
950
1231
|
find "$JOB_DIR" -type f -exec chmod 600 {} +
|
|
1232
|
+
if [[ -f "$JOB_WORKER" && ! -L "$JOB_WORKER" ]]; then
|
|
1233
|
+
chmod 400 "$JOB_WORKER"
|
|
1234
|
+
fi
|
|
951
1235
|
}
|
|
952
1236
|
|
|
953
1237
|
sanitize_utf8() {
|
|
@@ -1023,6 +1307,9 @@ write_completion_record() {
|
|
|
1023
1307
|
"$rc" "$(json_escape "$finished")" "$(json_escape "$tail_value")" \
|
|
1024
1308
|
> "$tmp" || write_rc=$?
|
|
1025
1309
|
fi
|
|
1310
|
+
if [[ "$write_rc" -eq 0 ]] && ! json_file_round_trip_valid "$tmp"; then
|
|
1311
|
+
write_rc=1
|
|
1312
|
+
fi
|
|
1026
1313
|
if [[ "$write_rc" -eq 0 ]]; then
|
|
1027
1314
|
chmod 600 "$tmp" || write_rc=$?
|
|
1028
1315
|
fi
|
|
@@ -1047,7 +1334,7 @@ finish_job() {
|
|
|
1047
1334
|
secure_job_files
|
|
1048
1335
|
(umask 077; printf '%s\n' "$rc" > "$JOB_DIR/exit")
|
|
1049
1336
|
if [[ "${OMNILANE_INBOX:-1}" != "0" ]]; then
|
|
1050
|
-
write_completion_record "$rc" >/dev/null
|
|
1337
|
+
write_completion_record "$rc" >/dev/null || true
|
|
1051
1338
|
fi
|
|
1052
1339
|
FINISHED_RC="$rc"
|
|
1053
1340
|
}
|
|
@@ -1057,11 +1344,15 @@ run_job() {
|
|
|
1057
1344
|
write_current_pid_file "$JOB_DIR/pid"
|
|
1058
1345
|
set +e
|
|
1059
1346
|
if [[ -n "$JOB_TIMEOUT" ]]; then
|
|
1060
|
-
|
|
1061
|
-
"$
|
|
1347
|
+
OMNILANE_JOB_WORKER_REPO="$OMNILANE_REPO" \
|
|
1348
|
+
OMNILANE_JOB_WORKER_EXPECTED_SHA256="$JOB_WORKER_SHA256" \
|
|
1349
|
+
OMNILANE_JOB_SUPERVISED=1 perl "$JOB_SUPERVISOR" "$JOB_TIMEOUT" \
|
|
1350
|
+
"$JOB_WORKER_BASH" "$JOB_WORKER" "$VENDOR" "$MODE" "$WORKDIR" "$MODEL" "$EFFORT" \
|
|
1062
1351
|
"$JOB_DIR/task.txt" "$JOB_DIR/out.txt"
|
|
1063
1352
|
else
|
|
1064
|
-
"$
|
|
1353
|
+
OMNILANE_JOB_WORKER_REPO="$OMNILANE_REPO" \
|
|
1354
|
+
OMNILANE_JOB_WORKER_EXPECTED_SHA256="$JOB_WORKER_SHA256" \
|
|
1355
|
+
"$JOB_WORKER_BASH" "$JOB_WORKER" "$VENDOR" "$MODE" "$WORKDIR" "$MODEL" "$EFFORT" \
|
|
1065
1356
|
"$JOB_DIR/task.txt" "$JOB_DIR/out.txt"
|
|
1066
1357
|
fi
|
|
1067
1358
|
rc=$?
|
package/scripts/doctor.sh
CHANGED
|
@@ -28,10 +28,13 @@ done
|
|
|
28
28
|
echo "omnilane: --probe-timeout requires --probe V" >&2
|
|
29
29
|
exit 2
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
32
|
+
REPO="${OMNILANE_DOCTOR_REPO:-$SCRIPT_ROOT}"
|
|
32
33
|
OMNILANE_HOME="${OMNILANE_HOME:-$HOME/.omnilane}"
|
|
33
34
|
PROBE_SCRIPT="${OMNILANE_PROVIDER_PROBE_SCRIPT:-$REPO/scripts/provider-probe.sh}"
|
|
34
35
|
GOAL_LOOP="${OMNILANE_DOCTOR_GOAL_LOOP:-$REPO/scripts/lib/goal-loop.sh}"
|
|
36
|
+
# shellcheck disable=SC1091
|
|
37
|
+
source "$SCRIPT_ROOT/scripts/lib/live-protocol.sh"
|
|
35
38
|
PASS_COUNT=0
|
|
36
39
|
WARN_COUNT=0
|
|
37
40
|
FAIL_COUNT=0
|
|
@@ -355,6 +358,57 @@ fi
|
|
|
355
358
|
|
|
356
359
|
# A real inference call is explicit and single-vendor only. Default doctor
|
|
357
360
|
# remains local/offline. Never relay provider output or runner stderr.
|
|
361
|
+
live_capable_present=""
|
|
362
|
+
live_unavailable_present=""
|
|
363
|
+
resolved_codex_bin="$(
|
|
364
|
+
set +u
|
|
365
|
+
[[ -f "$OMNILANE_HOME/local.sh" ]] && . "$OMNILANE_HOME/local.sh" 2>/dev/null
|
|
366
|
+
printf '%s' "${CODEX_BIN:-codex}"
|
|
367
|
+
)"
|
|
368
|
+
resolved_grok_bin="$(
|
|
369
|
+
set +u
|
|
370
|
+
[[ -f "$OMNILANE_HOME/local.sh" ]] && . "$OMNILANE_HOME/local.sh" 2>/dev/null
|
|
371
|
+
printf '%s' "${GROK_BIN:-grok}"
|
|
372
|
+
)"
|
|
373
|
+
for name in $vendor_present; do
|
|
374
|
+
case "$name" in
|
|
375
|
+
claude|gemini)
|
|
376
|
+
live_capable_present="$live_capable_present $name"
|
|
377
|
+
;;
|
|
378
|
+
codex)
|
|
379
|
+
if codex_live_surface_available "$resolved_codex_bin"; then
|
|
380
|
+
live_capable_present="$live_capable_present codex"
|
|
381
|
+
report PASS codex-live "app-server initialize handshake succeeded"
|
|
382
|
+
else
|
|
383
|
+
live_unavailable_present="$live_unavailable_present codex"
|
|
384
|
+
report PASS codex-live "unavailable: app-server initialize handshake failed; upgrade codex"
|
|
385
|
+
fi
|
|
386
|
+
;;
|
|
387
|
+
grok)
|
|
388
|
+
if grok_live_surface_available "$resolved_grok_bin"; then
|
|
389
|
+
live_capable_present="$live_capable_present grok"
|
|
390
|
+
report PASS grok-live "agent stdio initialize handshake succeeded"
|
|
391
|
+
else
|
|
392
|
+
live_unavailable_present="$live_unavailable_present grok"
|
|
393
|
+
report PASS grok-live "unavailable: agent stdio initialize handshake failed"
|
|
394
|
+
fi
|
|
395
|
+
;;
|
|
396
|
+
*)
|
|
397
|
+
live_unavailable_present="$live_unavailable_present $name"
|
|
398
|
+
;;
|
|
399
|
+
esac
|
|
400
|
+
done
|
|
401
|
+
if [[ -n "$live_capable_present" ]]; then
|
|
402
|
+
report PASS live-capable "${live_capable_present# }"
|
|
403
|
+
else
|
|
404
|
+
report WARN live-capable "none"
|
|
405
|
+
fi
|
|
406
|
+
if [[ -n "$live_unavailable_present" ]]; then
|
|
407
|
+
report PASS live-unavailable "${live_unavailable_present# }"
|
|
408
|
+
else
|
|
409
|
+
report PASS live-unavailable "none"
|
|
410
|
+
fi
|
|
411
|
+
|
|
358
412
|
if [[ -n "$PROBE_VENDOR" ]]; then
|
|
359
413
|
if [[ ! -x "$PROBE_SCRIPT" ]]; then
|
|
360
414
|
report FAIL provider-probe "probe runner is unavailable"
|