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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-consultants",
3
- "version": "3.2.0",
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.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.5" (default), "gpt-5.4", "gpt-5.4-nano", "gpt-5.3-codex", etc.
148
- CODEX_MODEL="${CODEX_MODEL:-gpt-5.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.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-4-6" ;;
602
- gemini) echo "Gemini 3.5 Flash (High)" ;;
603
- codex) echo "gpt-5.4" ;;
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.5 Flash (Low)" ;;
619
- codex) echo "gpt-5.4-nano" ;;
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.2.0"
782
+ AI_CONSULTANTS_VERSION="3.4.0"
@@ -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
- version=$("$cmd" --version 2>/dev/null | head -1 || echo "")
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"
@@ -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-preview all reported — 'minimal', 'medium' and 'max' are accepted
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-preview returns "The
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-4-6}"
251
+ local model="${2:-claude-sonnet-5}"
252
252
  local max_tokens="${3:-16384}"
253
253
 
254
254
  jq -n \
@@ -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 - --model "Gemini 3.1 Pro (High)"
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 - no consultant CLI exposes a reasoning-effort flag. Set it in the CLI's own configuration, or switch this consultant to API 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
- SYNTHESIS_ARGS=("${GEMINI_CMD:-agy}" "-p" "$prompt")
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
  ;;
@@ -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",
@@ -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
- # JSON output exposes provider-measured usage and cost. In particular,
74
- # output_tokens includes adaptive thinking that is absent from .result.
75
- echo "$FULL_QUERY" | run_query \
76
- "Claude" \
77
- "$TEMP_OUTPUT" \
78
- "$CLAUDE_TIMEOUT_SECONDS" \
79
- "$CLAUDE_CMD" --print --model "$MODEL_USED" --output-format json
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
- exit_code=$?
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 ---
@@ -1,13 +1,14 @@
1
1
  #!/bin/bash
2
- # query_codex.sh - Query OpenAI Codex CLI or API (v2.6 with API mode support)
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.3-codex)
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
- # Build command
71
- CMD_ARGS=("$CODEX_CMD" "exec" "--skip-git-repo-check")
72
- if [[ -n "$CODEX_MODEL" ]]; then
73
- CMD_ARGS+=("-m" "$CODEX_MODEL")
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
- # Codex uses query as the last argument, not from stdin
77
- run_query \
78
- "Codex" \
79
- "$TEMP_OUTPUT" \
80
- "$CODEX_TIMEOUT_SECONDS" \
81
- "${CMD_ARGS[@]}" "$FULL_QUERY" < /dev/null
82
-
83
- exit_code=$?
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.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
@@ -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
- run_query \
89
- "Gemini" \
90
- "$TEMP_OUTPUT" \
91
- "$GEMINI_TIMEOUT_SECONDS" \
92
- "$GEMINI_CMD" -p "$FULL_QUERY" --model "$GEMINI_MODEL" </dev/null
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
- exit_code=$?
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 ---