ai-consultants 3.2.0 → 3.4.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/.env.example +14 -2
- package/CLAUDE.md +29 -6
- package/README.md +9 -6
- package/SKILL.md +7 -2
- package/docs/COST_RATES.md +13 -13
- package/docs/JSON_SCHEMA.md +4 -0
- package/docs/RECIPES.md +17 -7
- package/docs/cost_rates.json +52 -23
- package/docs/releases/v3.3.0.md +80 -0
- package/docs/releases/v3.4.0.md +102 -0
- package/package.json +1 -1
- package/references/configuration.md +8 -3
- package/scripts/config.sh +9 -9
- package/scripts/configure.sh +9 -0
- package/scripts/doctor.sh +8 -2
- package/scripts/lib/api.sh +3 -3
- package/scripts/lib/common.sh +28 -3
- package/scripts/lib/schema.json +9 -0
- package/scripts/query_claude.sh +30 -9
- package/scripts/query_codex.sh +116 -18
- package/scripts/query_gemini.sh +32 -7
- package/scripts/query_grok.sh +161 -46
- package/scripts/query_kimi.sh +46 -0
- package/scripts/update_clis.sh +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-consultants",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "A harness for every question - a panel of up to 11 AI models that picks its own deliberation strategy per question (convergence, adversarial verification, tournament) and checks its work before answering",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ai-consultants": "bin/ai-consultants"
|
|
@@ -239,7 +239,7 @@ ENABLE_DEEPSEEK=false
|
|
|
239
239
|
|
|
240
240
|
```bash
|
|
241
241
|
GEMINI_MODEL=Gemini 3.1 Pro (High) # agy CLI display name; API mode uses GEMINI_API_MODEL
|
|
242
|
-
CODEX_MODEL=gpt-5.
|
|
242
|
+
CODEX_MODEL=gpt-5.6-sol
|
|
243
243
|
CLAUDE_MODEL=claude-opus-5
|
|
244
244
|
CLAUDE_API_MAX_TOKENS=16384 # API only: adaptive thinking + visible output
|
|
245
245
|
MISTRAL_MODEL=mistral-large-3
|
|
@@ -263,10 +263,15 @@ tool-free strict sandbox. If the CLI is missing, cannot execute, or has no
|
|
|
263
263
|
usable authentication, the same query and model fall back to `GROK_API_URL`
|
|
264
264
|
when `GROK_API_KEY` is set. Failures after a request launches (including
|
|
265
265
|
timeouts, model errors, and empty output) are returned without API fallback.
|
|
266
|
-
Set `GROK_USE_API=true` only to force the API path.
|
|
266
|
+
Set `GROK_USE_API=true` only to force the API path. The adapter accepts any CLI
|
|
267
|
+
version that exposes the complete required headless interface and requested
|
|
268
|
+
model; `metadata.cli_version` is provenance, not a version pin.
|
|
267
269
|
|
|
268
270
|
`KIMI_MODEL` is passed directly to `kimi --model`, so `kimi-code/k3` overrides
|
|
269
|
-
any older default stored in the user's Kimi CLI configuration.
|
|
271
|
+
any older default stored in the user's Kimi CLI configuration. Kimi CLI
|
|
272
|
+
compatibility is also capability-probed: prompt mode, `stream-json`, provider
|
|
273
|
+
inventory, and the requested model must be present. No numeric CLI version is
|
|
274
|
+
used as a gate.
|
|
270
275
|
|
|
271
276
|
## Model Quality Tiers (v2.5)
|
|
272
277
|
|
package/scripts/config.sh
CHANGED
|
@@ -144,8 +144,8 @@ export GEMINI_USE_API
|
|
|
144
144
|
# CODEX CONFIGURATION - The Pragmatist
|
|
145
145
|
# =============================================================================
|
|
146
146
|
|
|
147
|
-
# Model: "gpt-5.
|
|
148
|
-
CODEX_MODEL="${CODEX_MODEL:-gpt-5.
|
|
147
|
+
# Model: "gpt-5.6-sol" (default), "gpt-5.6-terra", "gpt-5.6-luna", etc.
|
|
148
|
+
CODEX_MODEL="${CODEX_MODEL:-gpt-5.6-sol}"
|
|
149
149
|
CODEX_TIMEOUT_SECONDS="${CODEX_TIMEOUT:-180}"
|
|
150
150
|
CODEX_CMD="${CODEX_CMD:-codex}"
|
|
151
151
|
|
|
@@ -584,7 +584,7 @@ get_model_for_tier() {
|
|
|
584
584
|
case "$consultant" in
|
|
585
585
|
claude) echo "claude-opus-5" ;;
|
|
586
586
|
gemini) echo "Gemini 3.1 Pro (High)" ;;
|
|
587
|
-
codex) echo "gpt-5.
|
|
587
|
+
codex) echo "gpt-5.6-sol" ;;
|
|
588
588
|
mistral) echo "mistral-large-3" ;;
|
|
589
589
|
cursor) echo "composer-2.5" ;;
|
|
590
590
|
deepseek) echo "deepseek-v4-pro" ;;
|
|
@@ -598,9 +598,9 @@ get_model_for_tier() {
|
|
|
598
598
|
;;
|
|
599
599
|
standard|medium|balanced)
|
|
600
600
|
case "$consultant" in
|
|
601
|
-
claude) echo "claude-sonnet-
|
|
602
|
-
gemini) echo "Gemini 3.
|
|
603
|
-
codex) echo "gpt-5.
|
|
601
|
+
claude) echo "claude-sonnet-5" ;;
|
|
602
|
+
gemini) echo "Gemini 3.6 Flash (High)" ;;
|
|
603
|
+
codex) echo "gpt-5.6-terra" ;;
|
|
604
604
|
mistral) echo "mistral-medium-latest" ;;
|
|
605
605
|
cursor) echo "composer-2" ;;
|
|
606
606
|
deepseek) echo "deepseek-v4-flash" ;;
|
|
@@ -615,8 +615,8 @@ get_model_for_tier() {
|
|
|
615
615
|
economy|fast|quick)
|
|
616
616
|
case "$consultant" in
|
|
617
617
|
claude) echo "claude-haiku-4-5" ;;
|
|
618
|
-
gemini) echo "Gemini 3.
|
|
619
|
-
codex) echo "gpt-5.
|
|
618
|
+
gemini) echo "Gemini 3.6 Flash (Low)" ;;
|
|
619
|
+
codex) echo "gpt-5.6-luna" ;;
|
|
620
620
|
mistral) echo "devstral-small-2" ;;
|
|
621
621
|
cursor) echo "gemini-3-flash" ;;
|
|
622
622
|
deepseek) echo "deepseek-v4-flash" ;;
|
|
@@ -779,4 +779,4 @@ EOF
|
|
|
779
779
|
# VERSION
|
|
780
780
|
# =============================================================================
|
|
781
781
|
|
|
782
|
-
AI_CONSULTANTS_VERSION="3.
|
|
782
|
+
AI_CONSULTANTS_VERSION="3.4.0"
|
package/scripts/configure.sh
CHANGED
|
@@ -285,6 +285,15 @@ if [[ "$(read_value "$WORK_FILE" CLAUDE_MODEL 2>/dev/null || true)" == "claude-o
|
|
|
285
285
|
echo "Migrated managed Claude default: claude-opus-4-8 -> claude-opus-5" >&2
|
|
286
286
|
fi
|
|
287
287
|
|
|
288
|
+
# Same shape for Codex: `init` once wrote gpt-5.5 as an unmarked value, so
|
|
289
|
+
# configure treated it as a deliberate pin and never reached gpt-5.6-sol. A
|
|
290
|
+
# real pin is preserved by `--set CODEX_MODEL=gpt-5.5`.
|
|
291
|
+
if [[ "$(read_value "$WORK_FILE" CODEX_MODEL 2>/dev/null || true)" == "gpt-5.5" ]] \
|
|
292
|
+
&& ! has_value_marker "$WORK_FILE" CODEX_MODEL "$PIN_MARKER"; then
|
|
293
|
+
set_value CODEX_MODEL "gpt-5.6-sol $DEFAULT_MARKER"
|
|
294
|
+
echo "Migrated managed Codex default: gpt-5.5 -> gpt-5.6-sol" >&2
|
|
295
|
+
fi
|
|
296
|
+
|
|
288
297
|
configure_cli_only() {
|
|
289
298
|
local enable_key="$1" cmd_key="$2" cmd enabled=false
|
|
290
299
|
cmd=$(effective_input_value "$cmd_key")
|
package/scripts/doctor.sh
CHANGED
|
@@ -242,9 +242,15 @@ check_cli_consultant() {
|
|
|
242
242
|
return 0
|
|
243
243
|
fi
|
|
244
244
|
|
|
245
|
-
# Get version (--version is standard and fast, no timeout needed)
|
|
245
|
+
# Get version (--version is standard and fast, no timeout needed).
|
|
246
|
+
# Gemini/agy must run under agy_env so an SSH session still reaches the
|
|
247
|
+
# Keychain credential store (see agy_env in lib/common.sh).
|
|
246
248
|
local version
|
|
247
|
-
|
|
249
|
+
if [[ "$name" == "Gemini" ]]; then
|
|
250
|
+
version=$(agy_env "$cmd" --version 2>/dev/null | head -1 || echo "")
|
|
251
|
+
else
|
|
252
|
+
version=$("$cmd" --version 2>/dev/null | head -1 || echo "")
|
|
253
|
+
fi
|
|
248
254
|
|
|
249
255
|
if [[ -n "$version" ]]; then
|
|
250
256
|
_print " ✓ $name: $version"
|
package/scripts/lib/api.sh
CHANGED
|
@@ -188,7 +188,7 @@ build_qwen_request() {
|
|
|
188
188
|
# 'reasoning_effort' must be one of: 'none', 'minimal', 'low', 'medium',
|
|
189
189
|
# 'high', 'xhigh', 'max'
|
|
190
190
|
# Note this is WIDER than the low|high|xhigh that the public write-ups for
|
|
191
|
-
# qwen3.8-max
|
|
191
|
+
# qwen3.8-max all reported — 'minimal', 'medium' and 'max' are accepted
|
|
192
192
|
# by the model too. Taken from the API rather than from documentation for
|
|
193
193
|
# exactly that reason.
|
|
194
194
|
#
|
|
@@ -196,7 +196,7 @@ build_qwen_request() {
|
|
|
196
196
|
# facts in shell is what produced the stale-model bugs this release also fixes,
|
|
197
197
|
# and the same builder serves GLM/Grok/DeepSeek/MiniMax. An unsupported
|
|
198
198
|
# combination is rejected by the provider with a 400 that run_api_query
|
|
199
|
-
# surfaces without retrying — e.g. 'none' on qwen3.8-max
|
|
199
|
+
# surfaces without retrying — e.g. 'none' on qwen3.8-max returns "The
|
|
200
200
|
# value of the enable_thinking parameter is restricted to True", which is a
|
|
201
201
|
# model fact, not a syntax error.
|
|
202
202
|
validate_reasoning_effort() {
|
|
@@ -248,7 +248,7 @@ build_openai_request() {
|
|
|
248
248
|
# Usage: build_anthropic_request <prompt> <model> [max_tokens]
|
|
249
249
|
build_anthropic_request() {
|
|
250
250
|
local prompt="$1"
|
|
251
|
-
local model="${2:-claude-sonnet-
|
|
251
|
+
local model="${2:-claude-sonnet-5}"
|
|
252
252
|
local max_tokens="${3:-16384}"
|
|
253
253
|
|
|
254
254
|
jq -n \
|
package/scripts/lib/common.sh
CHANGED
|
@@ -140,6 +140,29 @@ check_command() {
|
|
|
140
140
|
return 0
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
# Run a command with SSH session markers stripped from the environment.
|
|
144
|
+
#
|
|
145
|
+
# WHY (do not "simplify" this away): the Antigravity CLI (`agy`, our Gemini
|
|
146
|
+
# transport) picks its credential store from the environment. When it sees any
|
|
147
|
+
# of SSH_CLIENT, SSH_CONNECTION, or SSH_TTY it switches to a file-based token
|
|
148
|
+
# store and never consults the macOS Keychain. A user who signed in locally has
|
|
149
|
+
# credentials only in the Keychain, so every `agy` call from an SSH session
|
|
150
|
+
# fails auth — and re-signing-in does not help, because the new login also lands
|
|
151
|
+
# in the Keychain the CLI has already decided to ignore. Clearing the three
|
|
152
|
+
# markers for the `agy` process restores Keychain lookup. On a local (non-SSH)
|
|
153
|
+
# session they are already unset, so this is a pure no-op there. It grants no
|
|
154
|
+
# extra tools or filesystem access; it only changes which credential store the
|
|
155
|
+
# CLI consults.
|
|
156
|
+
#
|
|
157
|
+
# AGY_ENV_PREFIX is the exec-safe form (external `env` binary) for use under
|
|
158
|
+
# run_with_timeout / GNU timeout, which cannot invoke shell functions. agy_env
|
|
159
|
+
# is the same-shell convenience wrapper. Keep both in lockstep via this array.
|
|
160
|
+
#
|
|
161
|
+
# Usage (same shell): agy_env <command> [args...]
|
|
162
|
+
# Usage (under timeout / command arrays): "${AGY_ENV_PREFIX[@]}" <command> [args...]
|
|
163
|
+
AGY_ENV_PREFIX=(env -u SSH_CLIENT -u SSH_CONNECTION -u SSH_TTY)
|
|
164
|
+
agy_env() { "${AGY_ENV_PREFIX[@]}" "$@"; }
|
|
165
|
+
|
|
143
166
|
# =============================================================================
|
|
144
167
|
# MAIN QUERY EXECUTION FUNCTION
|
|
145
168
|
# =============================================================================
|
|
@@ -149,8 +172,9 @@ check_command() {
|
|
|
149
172
|
# Usage: run_query <consultant_name> <output_file> <timeout_sec> <command...>
|
|
150
173
|
#
|
|
151
174
|
# The query is passed via stdin to the command.
|
|
175
|
+
# Use the exec-safe array form under run_query (agy_env is same-shell only).
|
|
152
176
|
# Example:
|
|
153
|
-
# echo "$QUERY" | run_query "Gemini" "/tmp/out.json" 120 agy -p
|
|
177
|
+
# echo "$QUERY" | run_query "Gemini" "/tmp/out.json" 120 "${AGY_ENV_PREFIX[@]}" agy -p "..." --model "Gemini 3.1 Pro (High)"
|
|
154
178
|
#
|
|
155
179
|
run_query() {
|
|
156
180
|
local consultant_name="$1"
|
|
@@ -485,7 +509,7 @@ warn_effort_ignored_in_cli() {
|
|
|
485
509
|
if is_api_mode "$agent"; then
|
|
486
510
|
return 0
|
|
487
511
|
fi
|
|
488
|
-
log_warn "[$agent] $var is ignored in CLI mode -
|
|
512
|
+
log_warn "[$agent] $var is ignored in CLI mode - this consultant's CLI does not expose a reasoning-effort flag. Set it in the CLI's own configuration, or switch this consultant to API mode."
|
|
489
513
|
}
|
|
490
514
|
|
|
491
515
|
# Log API mode status for debugging
|
|
@@ -1311,7 +1335,8 @@ build_synthesis_args() {
|
|
|
1311
1335
|
# stdin and "-" is not a stdin sentinel. A prior `-p -` made agy answer
|
|
1312
1336
|
# a literal "-" with a generic greeting -> unparseable synthesis ->
|
|
1313
1337
|
# manual_review. Pass the prompt directly, mirroring query_gemini.sh.
|
|
1314
|
-
|
|
1338
|
+
# AGY_ENV_PREFIX is exec-safe under timeout (see agy_env above).
|
|
1339
|
+
SYNTHESIS_ARGS=("${AGY_ENV_PREFIX[@]}" "${GEMINI_CMD:-agy}" "-p" "$prompt")
|
|
1315
1340
|
local model="${SYNTHESIS_MODEL:-${GEMINI_MODEL:-}}"
|
|
1316
1341
|
[[ -n "$model" ]] && SYNTHESIS_ARGS+=("--model" "$model")
|
|
1317
1342
|
;;
|
package/scripts/lib/schema.json
CHANGED
|
@@ -167,6 +167,15 @@
|
|
|
167
167
|
"type": "string",
|
|
168
168
|
"description": "Exact model version string"
|
|
169
169
|
},
|
|
170
|
+
"cli_version": {
|
|
171
|
+
"type": "string",
|
|
172
|
+
"description": "Observed consultant CLI version for provenance only; compatibility is not decided from this string."
|
|
173
|
+
},
|
|
174
|
+
"cli_compatibility": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"enum": ["capability-probed", "incompatible"],
|
|
177
|
+
"description": "Result of checking the CLI's required command, model, and structured-output capabilities before dispatch."
|
|
178
|
+
},
|
|
170
179
|
"timestamp": {
|
|
171
180
|
"type": "string",
|
|
172
181
|
"format": "date-time",
|
package/scripts/query_claude.sh
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
# CLAUDE_USE_API - Use API mode instead of CLI (default: false)
|
|
10
10
|
# CLAUDE_API_MAX_TOKENS - API thinking + visible-output budget (default: 16384)
|
|
11
11
|
# ANTHROPIC_API_KEY - API key for API mode
|
|
12
|
+
# CLAUDE_REASONING_EFFORT - Optional CLI/API reasoning effort
|
|
12
13
|
# ENABLE_PERSONA - Enable "The Synthesizer" persona (default: true)
|
|
13
14
|
|
|
14
15
|
set -euo pipefail
|
|
@@ -64,21 +65,41 @@ if is_api_mode "claude"; then
|
|
|
64
65
|
else
|
|
65
66
|
exit_code=$?
|
|
66
67
|
fi
|
|
67
|
-
warn_effort_ignored_in_cli "Claude"
|
|
68
68
|
else
|
|
69
69
|
# --- CLI Mode ---
|
|
70
70
|
log_api_mode_status "claude"
|
|
71
71
|
check_command "$CLAUDE_CMD" "Claude CLI" "Visit https://docs.anthropic.com/en/docs/claude-code" || exit 1
|
|
72
72
|
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
# Optional CLI effort control. Unset leaves the CLI's own default alone;
|
|
74
|
+
# when set, pass --effort through after the shared validation gate. Do not
|
|
75
|
+
# invent a local allowlist — the provider rejects what it does not accept.
|
|
76
|
+
CLAUDE_ARGS=("$CLAUDE_CMD" --print --model "$MODEL_USED" --output-format json)
|
|
77
|
+
effort_ok=true
|
|
78
|
+
if [[ -n "${CLAUDE_REASONING_EFFORT:-}" ]]; then
|
|
79
|
+
source "$SCRIPT_DIR/lib/api.sh"
|
|
80
|
+
if ! cli_effort=$(validate_reasoning_effort "$CLAUDE_REASONING_EFFORT" "$CONSULTANT_NAME"); then
|
|
81
|
+
effort_ok=false
|
|
82
|
+
exit_code=1
|
|
83
|
+
else
|
|
84
|
+
CLAUDE_ARGS+=(--effort "$cli_effort")
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
if [[ "$effort_ok" == "true" ]]; then
|
|
89
|
+
# JSON output exposes provider-measured usage and cost. In particular,
|
|
90
|
+
# output_tokens includes adaptive thinking that is absent from .result.
|
|
91
|
+
# Keep failures inside an explicit conditional so `set -e` cannot skip
|
|
92
|
+
# exit_code assignment (same guard as the API branch).
|
|
93
|
+
if echo "$FULL_QUERY" | run_query \
|
|
94
|
+
"Claude" \
|
|
95
|
+
"$TEMP_OUTPUT" \
|
|
96
|
+
"$CLAUDE_TIMEOUT_SECONDS" \
|
|
97
|
+
"${CLAUDE_ARGS[@]}"; then
|
|
98
|
+
exit_code=0
|
|
99
|
+
else
|
|
100
|
+
exit_code=$?
|
|
101
|
+
fi
|
|
102
|
+
fi
|
|
82
103
|
fi
|
|
83
104
|
|
|
84
105
|
# --- Calculate latency ---
|
package/scripts/query_codex.sh
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# query_codex.sh - Query OpenAI Codex CLI or API (
|
|
2
|
+
# query_codex.sh - Query OpenAI Codex CLI or API (isolated CLI runtime)
|
|
3
3
|
#
|
|
4
4
|
# Usage: ./query_codex.sh "question" [context_file] [output_file]
|
|
5
5
|
#
|
|
6
6
|
# Environment variables:
|
|
7
|
-
# CODEX_MODEL - Model to use (default: gpt-5.
|
|
7
|
+
# CODEX_MODEL - Model to use (default: gpt-5.6-sol)
|
|
8
8
|
# CODEX_TIMEOUT - Timeout in seconds (default: 180)
|
|
9
9
|
# CODEX_USE_API - Use API mode instead of CLI (default: false)
|
|
10
10
|
# OPENAI_API_KEY - API key for API mode
|
|
11
|
+
# CODEX_REASONING_EFFORT - Optional CLI/API reasoning effort
|
|
11
12
|
# ENABLE_PERSONA - Enable "The Pragmatist" persona (default: true)
|
|
12
13
|
|
|
13
14
|
set -euo pipefail
|
|
@@ -39,6 +40,22 @@ START_TIME=$(get_timestamp_ms)
|
|
|
39
40
|
|
|
40
41
|
# --- Execution (CLI or API mode) ---
|
|
41
42
|
TEMP_OUTPUT=$(mktemp)
|
|
43
|
+
CODEX_RUNTIME_DIR=""
|
|
44
|
+
exit_code=1
|
|
45
|
+
|
|
46
|
+
cleanup() {
|
|
47
|
+
rm -f "$TEMP_OUTPUT" "${TEMP_OUTPUT}.err"
|
|
48
|
+
|
|
49
|
+
# CODEX_RUNTIME_DIR is always created by this script under the system temp
|
|
50
|
+
# directory. Validate the prefix before recursive cleanup so an unexpected
|
|
51
|
+
# empty or overridden value can never widen the deletion target.
|
|
52
|
+
local temp_prefix="${TMPDIR:-/tmp}"
|
|
53
|
+
temp_prefix="${temp_prefix%/}/ai-consultants-codex."
|
|
54
|
+
if [[ -n "$CODEX_RUNTIME_DIR" && "$CODEX_RUNTIME_DIR" == "$temp_prefix"* ]]; then
|
|
55
|
+
rm -rf -- "$CODEX_RUNTIME_DIR"
|
|
56
|
+
fi
|
|
57
|
+
}
|
|
58
|
+
trap cleanup EXIT
|
|
42
59
|
|
|
43
60
|
if is_api_mode "codex"; then
|
|
44
61
|
# --- API Mode ---
|
|
@@ -61,26 +78,104 @@ if is_api_mode "codex"; then
|
|
|
61
78
|
else
|
|
62
79
|
exit_code=$?
|
|
63
80
|
fi
|
|
64
|
-
warn_effort_ignored_in_cli "Codex"
|
|
65
81
|
else
|
|
66
|
-
# --- CLI Mode ---
|
|
82
|
+
# --- CLI Mode (isolated HOME/CWD; auth via real CODEX_HOME) ---
|
|
67
83
|
log_api_mode_status "codex"
|
|
68
84
|
check_command "$CODEX_CMD" "Codex CLI" "npm install -g @openai/codex" || exit 1
|
|
69
85
|
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
86
|
+
# Resolve the real Codex home BEFORE overriding HOME. --ignore-user-config
|
|
87
|
+
# skips config.toml but auth still resolves through CODEX_HOME; repointing
|
|
88
|
+
# it at the isolated home would break authentication.
|
|
89
|
+
real_codex_home="${CODEX_HOME:-${HOME}/.codex}"
|
|
90
|
+
|
|
91
|
+
runtime_base="${TMPDIR:-/tmp}"
|
|
92
|
+
runtime_base="${runtime_base%/}"
|
|
93
|
+
CODEX_RUNTIME_DIR=$(mktemp -d "$runtime_base/ai-consultants-codex.XXXXXX")
|
|
94
|
+
chmod 700 "$CODEX_RUNTIME_DIR"
|
|
95
|
+
|
|
96
|
+
isolated_home="$CODEX_RUNTIME_DIR/home"
|
|
97
|
+
isolated_cwd="$CODEX_RUNTIME_DIR/workspace"
|
|
98
|
+
prompt_file="$CODEX_RUNTIME_DIR/prompt.txt"
|
|
99
|
+
payload_file="$CODEX_RUNTIME_DIR/payload.txt"
|
|
100
|
+
mkdir -p "$isolated_home" "$isolated_cwd"
|
|
101
|
+
chmod 700 "$isolated_home" "$isolated_cwd"
|
|
102
|
+
printf '%s' "$FULL_QUERY" > "$prompt_file"
|
|
103
|
+
: > "$payload_file"
|
|
104
|
+
chmod 600 "$prompt_file" "$payload_file"
|
|
105
|
+
|
|
106
|
+
cli_effort=""
|
|
107
|
+
effort_ok=true
|
|
108
|
+
if [[ -n "${CODEX_REASONING_EFFORT:-}" ]]; then
|
|
109
|
+
# Keep the existing validate_reasoning_effort gate; pass the value
|
|
110
|
+
# through and let the CLI reject what it does not accept.
|
|
111
|
+
source "$SCRIPT_DIR/lib/api.sh"
|
|
112
|
+
if ! cli_effort=$(validate_reasoning_effort "$CODEX_REASONING_EFFORT" "$CONSULTANT_NAME"); then
|
|
113
|
+
effort_ok=false
|
|
114
|
+
exit_code=1
|
|
115
|
+
fi
|
|
74
116
|
fi
|
|
75
117
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
118
|
+
if [[ "$effort_ok" == "true" ]]; then
|
|
119
|
+
# Isolated Codex headless contract (verified against codex-cli 0.146.0):
|
|
120
|
+
# HOME is ephemeral so ambient ~/.codex config, hooks, MCP, and
|
|
121
|
+
# project instructions stay outside the consultation.
|
|
122
|
+
# CODEX_HOME stays on the real auth store.
|
|
123
|
+
# --ignore-user-config is safe here only because we pin the model with
|
|
124
|
+
# -m. It also makes Codex ignore any -p <profile>, which is why we
|
|
125
|
+
# must not add -p.
|
|
126
|
+
# -s read-only: the consultant answers; it never edits.
|
|
127
|
+
# -C points at an empty temp dir so the host repo's AGENTS.md stays out
|
|
128
|
+
# (build_context.sh already inlined the relevant files into FULL_QUERY).
|
|
129
|
+
# trailing - reads the prompt from stdin (no ARG_MAX / process-list).
|
|
130
|
+
# -o writes the final agent message to a private payload file.
|
|
131
|
+
CMD_ARGS=(
|
|
132
|
+
env
|
|
133
|
+
HOME="$isolated_home"
|
|
134
|
+
CODEX_HOME="$real_codex_home"
|
|
135
|
+
"$CODEX_CMD"
|
|
136
|
+
exec
|
|
137
|
+
--ephemeral
|
|
138
|
+
--ignore-user-config
|
|
139
|
+
--ignore-rules
|
|
140
|
+
--skip-git-repo-check
|
|
141
|
+
-m "$CODEX_MODEL"
|
|
142
|
+
-s read-only
|
|
143
|
+
-C "$isolated_cwd"
|
|
144
|
+
-o "$payload_file"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if [[ -n "$cli_effort" ]]; then
|
|
148
|
+
CMD_ARGS+=("-c" "model_reasoning_effort=${cli_effort}")
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
CMD_ARGS+=("-")
|
|
152
|
+
|
|
153
|
+
# run_query buffers stdin once and replays it on each retry.
|
|
154
|
+
if run_query \
|
|
155
|
+
"Codex" \
|
|
156
|
+
"$TEMP_OUTPUT" \
|
|
157
|
+
"$CODEX_TIMEOUT_SECONDS" \
|
|
158
|
+
"${CMD_ARGS[@]}" < "$prompt_file"; then
|
|
159
|
+
exit_code=0
|
|
160
|
+
else
|
|
161
|
+
exit_code=$?
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Prefer the -o payload over stdout only when the run succeeded.
|
|
165
|
+
# A non-empty payload must never rewrite a timeout, auth error, or
|
|
166
|
+
# exhausted-retry into success — a partial answer would enter synthesis
|
|
167
|
+
# as a healthy consultation. Exit 0 with an empty payload remains a
|
|
168
|
+
# diagnosed failure.
|
|
169
|
+
if [[ $exit_code -eq 0 ]]; then
|
|
170
|
+
if [[ -s "$payload_file" ]]; then
|
|
171
|
+
cp "$payload_file" "$TEMP_OUTPUT"
|
|
172
|
+
else
|
|
173
|
+
exit_code=1
|
|
174
|
+
log_warn "[$CONSULTANT_NAME] Codex wrote an empty last-message payload"
|
|
175
|
+
printf 'Codex wrote an empty last-message payload\n' > "${TEMP_OUTPUT}.err"
|
|
176
|
+
fi
|
|
177
|
+
fi
|
|
178
|
+
fi
|
|
84
179
|
fi
|
|
85
180
|
|
|
86
181
|
# --- Calculate latency ---
|
|
@@ -88,12 +183,15 @@ END_TIME=$(get_timestamp_ms)
|
|
|
88
183
|
LATENCY_MS=$((END_TIME - START_TIME))
|
|
89
184
|
|
|
90
185
|
# --- Configuration for response building ---
|
|
91
|
-
MODEL_USED="${CODEX_MODEL:-gpt-5.
|
|
186
|
+
MODEL_USED="${CODEX_MODEL:-gpt-5.6-sol}"
|
|
92
187
|
PERSONA_NAME=$(get_persona_name "$CONSULTANT_NAME")
|
|
93
188
|
|
|
94
189
|
# --- Post-processing: use shared helper ---
|
|
190
|
+
# process_consultant_response returns the consultant exit code. Guard the call
|
|
191
|
+
# so a non-zero return does not abort under set -e before we cat the output
|
|
192
|
+
# file and exit with that code.
|
|
95
193
|
process_consultant_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" \
|
|
96
|
-
"$TEMP_OUTPUT" "$OUTPUT_FILE" "$exit_code" "$LATENCY_MS" "" "$FULL_QUERY"
|
|
194
|
+
"$TEMP_OUTPUT" "$OUTPUT_FILE" "$exit_code" "$LATENCY_MS" "" "$FULL_QUERY" || true
|
|
97
195
|
|
|
98
196
|
cat "$OUTPUT_FILE"
|
|
99
197
|
exit $exit_code
|
package/scripts/query_gemini.sh
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# GEMINI_USE_API - Use Google AI API mode instead of the agy CLI (default: false)
|
|
14
14
|
# GEMINI_API_MODEL - API model ID for API mode (default: gemini-3.1-pro-preview)
|
|
15
15
|
# GEMINI_API_KEY - API key for API mode
|
|
16
|
+
# GEMINI_REASONING_EFFORT - Optional CLI reasoning effort
|
|
16
17
|
# ENABLE_PERSONA - Enable "The Architect" persona (default: true)
|
|
17
18
|
|
|
18
19
|
set -euo pipefail
|
|
@@ -68,7 +69,6 @@ if is_api_mode "gemini"; then
|
|
|
68
69
|
else
|
|
69
70
|
exit_code=$?
|
|
70
71
|
fi
|
|
71
|
-
warn_effort_ignored_in_cli "Gemini"
|
|
72
72
|
else
|
|
73
73
|
# --- CLI Mode (Antigravity CLI: agy) ---
|
|
74
74
|
log_api_mode_status "gemini"
|
|
@@ -85,13 +85,38 @@ else
|
|
|
85
85
|
# -p argument. agy has --model (no -m alias) and no read-from-file flag, so a
|
|
86
86
|
# very large FULL_QUERY goes through argv (ARG_MAX-bounded; fine for normal
|
|
87
87
|
# contexts). stdin is /dev/null (agy ignores it; run_query's cat needs an EOF).
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
# Strip SSH_* markers so agy consults the macOS Keychain (see agy_env /
|
|
89
|
+
# AGY_ENV_PREFIX in lib/common.sh). Use the exec-safe prefix array — not the
|
|
90
|
+
# agy_env function — because run_query dispatches through GNU timeout.
|
|
91
|
+
#
|
|
92
|
+
# Optional CLI effort: when GEMINI_REASONING_EFFORT is set, pass --effort.
|
|
93
|
+
# agy accepts only a subset (e.g. low|medium|high); unsupported values fail
|
|
94
|
+
# at the provider and surface as diagnosed errors — no local allowlist.
|
|
95
|
+
GEMINI_ARGS=("${AGY_ENV_PREFIX[@]}" "$GEMINI_CMD" -p "$FULL_QUERY" --model "$GEMINI_MODEL")
|
|
96
|
+
effort_ok=true
|
|
97
|
+
if [[ -n "${GEMINI_REASONING_EFFORT:-}" ]]; then
|
|
98
|
+
source "$SCRIPT_DIR/lib/api.sh"
|
|
99
|
+
if ! cli_effort=$(validate_reasoning_effort "$GEMINI_REASONING_EFFORT" "$CONSULTANT_NAME"); then
|
|
100
|
+
effort_ok=false
|
|
101
|
+
exit_code=1
|
|
102
|
+
else
|
|
103
|
+
GEMINI_ARGS+=(--effort "$cli_effort")
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
93
106
|
|
|
94
|
-
|
|
107
|
+
if [[ "$effort_ok" == "true" ]]; then
|
|
108
|
+
# Keep failures inside an explicit conditional so `set -e` cannot skip
|
|
109
|
+
# exit_code assignment (same guard as the API branch).
|
|
110
|
+
if run_query \
|
|
111
|
+
"Gemini" \
|
|
112
|
+
"$TEMP_OUTPUT" \
|
|
113
|
+
"$GEMINI_TIMEOUT_SECONDS" \
|
|
114
|
+
"${GEMINI_ARGS[@]}" </dev/null; then
|
|
115
|
+
exit_code=0
|
|
116
|
+
else
|
|
117
|
+
exit_code=$?
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
95
120
|
fi
|
|
96
121
|
|
|
97
122
|
# --- Calculate latency ---
|