ai-consultants 3.3.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 CHANGED
@@ -122,11 +122,19 @@ GEMINI_TIMEOUT=180
122
122
  GEMINI_CMD=agy
123
123
  # API-mode model ID (only used when GEMINI_USE_API=true)
124
124
  GEMINI_API_MODEL=gemini-3.1-pro-preview
125
+ # Reasoning effort for the agy CLI. Passed as --effort <value>. Accepts only
126
+ # low|medium|high; anything else is rejected by the provider and surfaces as a
127
+ # diagnosed error (provider owns the enum — same stance as QWEN3_REASONING_EFFORT).
128
+ # GEMINI_REASONING_EFFORT=
125
129
 
126
130
  # Codex - The Pragmatist
127
- CODEX_MODEL=gpt-5.5
131
+ CODEX_MODEL=gpt-5.6-sol
128
132
  CODEX_TIMEOUT=180
129
133
  CODEX_CMD=codex
134
+ # Reasoning effort for the codex CLI (and API mode). Passed as
135
+ # -c model_reasoning_effort=<value>. The provider owns the accepted enum;
136
+ # an unsupported value is rejected rather than silently ignored.
137
+ # CODEX_REASONING_EFFORT=
130
138
 
131
139
  # Mistral Vibe - The Devil's Advocate
132
140
  MISTRAL_MODEL=mistral-large-3
@@ -148,6 +156,10 @@ CLAUDE_MODEL=claude-opus-5 # ai-consultants:default
148
156
  CLAUDE_TIMEOUT=240
149
157
  CLAUDE_CMD=claude
150
158
  CLAUDE_API_MAX_TOKENS=16384
159
+ # Reasoning effort for the claude CLI (and API mode). Passed as --effort <value>.
160
+ # The provider owns the accepted enum; an unsupported value is rejected rather
161
+ # than silently ignored.
162
+ # CLAUDE_REASONING_EFFORT=
151
163
 
152
164
  # Qwen3 - The Analyst (CLI/API switchable v2.7)
153
165
  QWEN3_MODEL=qwen3.7-max
@@ -157,7 +169,7 @@ QWEN3_CMD=qwen
157
169
  # mode this is ignored (with a warning); set model.reasoningEffort in
158
170
  # ~/.qwen/settings.json instead. Accepted: none|minimal|low|medium|high|xhigh|max
159
171
  # (the provider's own enum). The valid subset depends on the model:
160
- # qwen3.8-max-preview takes all of them EXCEPT 'none' - thinking cannot be
172
+ # qwen3.8-max takes all of them EXCEPT 'none' - thinking cannot be
161
173
  # disabled on it - and defaults to xhigh. An unsupported combination is
162
174
  # rejected by the provider with a 400, not silently ignored.
163
175
  # QWEN3_REASONING_EFFORT=
package/CLAUDE.md CHANGED
@@ -6,7 +6,7 @@ AI Consultants is a multi-model coverage system that queries up to 11 AI consult
6
6
 
7
7
  **Self-Exclusion**: The invoking agent is automatically excluded from the panel to prevent self-consultation. Claude Code won't query Claude, Codex CLI won't query Codex, etc.
8
8
 
9
- **Version**: 3.3.0
9
+ **Version**: 3.4.0
10
10
 
11
11
  ## Distribution
12
12
 
@@ -368,9 +368,9 @@ Three tiers of models are available for each consultant, configurable via `apply
368
368
 
369
369
  | Tier | Description | Example Models |
370
370
  |------|-------------|----------------|
371
- | **premium** | Latest flagship models | claude-opus-5, Gemini 3.1 Pro (High), gpt-5.5 |
372
- | **standard** | Good quality at reasonable cost | claude-sonnet-4-6, Gemini 3.5 Flash (High), gpt-5.4 |
373
- | **economy** | Optimized for speed and low cost | claude-haiku-4-5, Gemini 3.5 Flash (Low), gpt-5.4-nano |
371
+ | **premium** | Latest flagship models | claude-opus-5, Gemini 3.1 Pro (High), gpt-5.6-sol |
372
+ | **standard** | Good quality at reasonable cost | claude-sonnet-5, Gemini 3.6 Flash (High), gpt-5.6-terra |
373
+ | **economy** | Optimized for speed and low cost | claude-haiku-4-5, Gemini 3.6 Flash (Low), gpt-5.6-luna |
374
374
 
375
375
  **Default models are now premium tier** for maximum quality.
376
376
 
@@ -407,7 +407,7 @@ All consultants now use premium models by default:
407
407
  |------------|---------------|
408
408
  | Claude | claude-opus-5 |
409
409
  | Gemini | Gemini 3.1 Pro (High) (via agy CLI) |
410
- | Codex | gpt-5.5 |
410
+ | Codex | gpt-5.6-sol |
411
411
  | Mistral | mistral-large-3 |
412
412
  | Cursor | composer-2.5 |
413
413
  | DeepSeek | deepseek-v4-pro |
@@ -617,7 +617,7 @@ for f in scripts/*.sh scripts/lib/*.sh; do bash -n "$f" && echo "OK: $f"; done
617
617
  | `CLAUDE_API_MAX_TOKENS` | 16384 | Claude API thinking + visible-output budget |
618
618
  | `GEMINI_MODEL` | Gemini 3.1 Pro (High) | Gemini agy CLI model (v2.15) |
619
619
  | `GEMINI_API_MODEL` | gemini-3.1-pro-preview | Gemini API-mode model ID (v2.15) |
620
- | `CODEX_MODEL` | gpt-5.5 | Codex model (v2.5) |
620
+ | `CODEX_MODEL` | gpt-5.6-sol | Codex model (v2.5) |
621
621
  | `MISTRAL_MODEL` | mistral-large-3 | Mistral model (v2.5) |
622
622
  | `SYNTHESIS_STRATEGY` | coverage | Synthesis strategy (coverage=union of distinct points) |
623
623
  | `ENABLE_SEMANTIC_CACHE` | true | Semantic response caching (v2.3) |
@@ -787,6 +787,23 @@ curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/s
787
787
 
788
788
  ## Changelog
789
789
 
790
+ ### v3.4.0
791
+ - **Gemini was unreachable from an SSH session, and no amount of re-authenticating helped.** `agy` picks its credential store from the environment: seeing `SSH_CLIENT`, `SSH_CONNECTION`, or `SSH_TTY` it switches to a file-based token store and never consults the macOS Keychain. A user who signed in locally therefore lost Gemini entirely over SSH, and signing in again landed the new credential in the Keychain the CLI had already decided to ignore. Reproduced live before fixing (`agy models` → "Please sign in"; the same command under `env -u SSH_CLIENT -u SSH_CONNECTION -u SSH_TTY` → full inventory), and confirmed again end to end after. Found by reading delegation-kit's 0.13.1 fix and checking whether the same failure applied here — it did, in the very session doing the work.
792
+ - **The fix ships in two shapes on purpose** (`lib/common.sh:163-164`). `AGY_ENV_PREFIX` is an array of `env -u …` for anything that reaches `agy` through `run_with_timeout`, because GNU `timeout` execs a binary and **cannot invoke a shell function**; `agy_env` is the same-shell wrapper, defined *from* that array so the two cannot drift. Applied at every executing call site — `query_gemini.sh`, `doctor.sh`, `update_clis.sh`, and `build_synthesis_args` (which is executed directly by `synthesize.sh:304`, not through timeout, so either shape would work there — it uses the array for consistency). The remaining `${GEMINI_CMD:-agy}` hits are `command -v` probes and config-dump strings, which execute nothing.
793
+ - **The Codex adapter was the least isolated in the panel, contradicting our own v3.1.0 policy.** That release isolated Grok specifically so "local plugins, hooks, skills, memories, and project instructions stay outside the consultation", then left Codex running `codex exec --skip-git-repo-check -m … "$FULL_QUERY"` in the ambient environment: the user's `~/.codex/config.toml`, the repo's `AGENTS.md`, their hooks, MCP servers, and execpolicy rules all reached an advisory consultation, the sandbox was unpinned, and the prompt rode in argv — the same `ARG_MAX` and process-list exposure v3.1.0 had already fixed once. CLI mode now uses an ephemeral mode-700 HOME/CWD with `--ephemeral --ignore-user-config --ignore-rules -s read-only`, prompt on stdin, answer from a private `-o` payload (`query_codex.sh:86-160`).
794
+ - **`CODEX_HOME` is resolved before HOME is overridden** (`query_codex.sh:89`) and passed through unchanged: `--ignore-user-config` skips `config.toml` but auth still resolves through `CODEX_HOME`, so repointing it at the isolated home would break authentication outright. Note the coupling recorded at `query_codex.sh:123`: `--ignore-user-config` is safe **only** because `-m` pins the model — it also makes Codex ignore any `-p <profile>`, so a profile must never be added here.
795
+ - **Review caught the `-o` payload rewriting real failures into successes.** The first implementation adopted the payload whenever it was non-empty, *regardless of how `run_query` finished* — so a timeout (124), an auth error, or exhausted retries that left a partial answer on disk all became `exit_code=0` and entered synthesis as a healthy consultation. That is strictly worse than the bug it replaced and defeats the v2.18/v2.19 diagnosed-failure machinery. Adoption is now gated on success; exit 0 with an empty payload remains a failure. Both directions are regression-tested, and both tests were confirmed non-vacuous by reintroducing the defect and watching them fail.
796
+ - **Correction, on the record: the justification for that override was measured and found false.** It was defended as avoiding "false empty-stdout failures", since `run_query` treats an empty stdout as a failure *and retries* — which would have meant every Codex consultation making `MAX_RETRIES` billable calls. Measured against the real CLI, stdout carries the answer (11 bytes), the `-o` file mirrors it, and only token telemetry goes to stderr. There was never a retry storm. `-o` is kept as defence in depth — and the isolation is what makes stdout clean in the first place, since `--ignore-user-config` plus an ephemeral HOME leaves no hooks to emit chatter.
797
+ - **`*_REASONING_EFFORT` had been a documented no-op on the default path.** `warn_effort_ignored_in_cli` announced the setting as API-mode only, but CLI-first transport has been mandatory since v2.21 — so in the shipped configuration the knob did nothing at all, while three CLIs did expose the control. Now wired: Codex `-c model_reasoning_effort=` (`query_codex.sh:148`), Claude `--effort` (`query_claude.sh:78-86`), Gemini `--effort` (`query_gemini.sh:97-105`). This is not cosmetic — cheap models do not degrade gracefully, so a consultant silently inheriting an unknown effort is not a controlled panel member.
798
+ - **Deliberately no per-CLI effort allowlist.** `agy` accepts only `low|medium|high`, but the accepted enum is a provider fact, not ours; an unsupported value comes back as a diagnosed 400 rather than being pre-empted by a table in shell. Same stance v2.25.0 took for Qwen, and for the same reason: hardcoding per-model facts is what produced the stale `MODEL_USED` bugs.
799
+ - **The parameter-contract test did its job on code it knew nothing about.** `test_configure.sh:91-121` scans every non-test `*.sh` for `${VAR:-}` and requires each name to be accepted by `configure --set`; the three new effort reads and then `CODEX_HOME` each failed it in turn. The effort variables are now declared in `.env.example` (commented out, matching the `QWEN3_REASONING_EFFORT` convention). `CODEX_HOME` went to the **exclusion list** instead (`test_configure.sh:105-107`), because it is owned by the Codex CLI: we read it to locate the user's auth store and must never persist it — declaring it would advertise `configure --set CODEX_HOME=…`, writing another tool's private auth path into our config. It joins `HOME`, `TMPDIR`, `XDG_CONFIG_HOME`, and `XAI_API_KEY`, and the distinction is now stated inline so nobody "fixes" it by moving it into `.env.example`.
800
+ - **The model catalog refresh would have been invisible to every existing installation — and only a live smoke test caught it.** The full gate was green (19/19, shellcheck clean, both new regression tests proven non-vacuous) and a real consultation still reported `model=gpt-5.5`. `init` copies `.env.example` verbatim, so the then-current Codex default was persisted as an ordinary unmarked value, and `configure` reads any unmarked value as a deliberate user pin. The proof sits side by side in an installed config: `CODEX_MODEL=gpt-5.5` beside `CLAUDE_MODEL=claude-opus-5 # ai-consultants:default`, where only the marked line can migrate. Fixed with the direct analogue of v3.2.0's Claude migration (`configure.sh:288-295`) — one exact historical value, one target, skipped on the pin marker, rewritten with the default marker. Verified against real config files: unmarked `gpt-5.5` migrates, a pin recorded via `--set` survives, an unrelated `gpt-5.4` is untouched.
801
+ - **Deliberately not generalised.** A table-driven "upgrade anything that looks old" rule would silently rewrite genuine user pins. Two adjacent narrow blocks is the intended shape; a third model needs a third block.
802
+ - **Model ids were verified by querying the installed binaries, never from documentation.** `agy models` (with the SSH markers cleared) returns slugs — `gemini-3.6-flash-high`, `gemini-3.1-pro-high` — but the display names we ship still resolve, checked by round-tripping both forms. Gemini premium stays `Gemini 3.1 Pro (High)` because the inventory has **no 3.6 Pro**; only standard/economy move to 3.6 Flash. Codex `gpt-5.6-sol|terra|luna` and `claude-sonnet-5` each answered a live probe.
803
+ - **Cost rates ship with the ids, not after them.** `calculate_session_cost` never calls `resolve_model_for_cost`, so a model absent from the catalog falls to the `*)` arm of `get_input_cost_per_1k` at 0.005/0.015 and is reported as a confident, wrong number — the trap documented at v2.25.0. Sonnet 5 is entered at its standard $3/$15 rate rather than the introductory $2/$10, which expires 2026-08-31; that slightly overstates for a few weeks and is exactly right afterwards.
804
+ - **Qwen: two ids, two deployments, not a rename.** delegation-kit's snapshot said `GET /models` lists both `qwen3.8-max` and `qwen3.8-max-preview`. Verified live against a real Token Plan account: both are served **and each completion reports its own requested id back in `.model`**, so `-preview` is not an alias and is not silently served by `qwen3.8-max`. Both stay catalogued and both stay in `unpriced_models` — removing the preview entry is precisely what would drop an existing pin onto `default_rate`. Recorded in `docs/RECIPES.md`; the shipped premium Qwen model remains `qwen3.7-max` and the negative assertion in `test_suite.sh` keeps that contract self-documenting.
805
+ - **Process note for the next stacked release.** `ci.yml` triggers on `pull_request: branches: [main]` with the default activity types, and a base-branch change fires `edited`, which is not among them — so a PR stacked on another branch never gets a server-side check until it is retargeted *and* reopened. Separately, `gh pr merge --delete-branch` on a stack base **closes** the child PR rather than retargeting it, and GitHub will neither retarget a closed PR nor reopen one whose base is gone; recovery meant pushing the base ref back from a commit still reachable in `main`. Adding `edited` to the workflow's activity types would close the first hole.
806
+
790
807
  ### v3.3.0
791
808
  - **Grok and Kimi compatibility is now established from capabilities, not inferred from an installed binary or its version string.** `grok_cli_supports_required_interface` validates the complete headless parser surface inside the same isolated HOME/CWD boundary used for dispatch, while `grok_cli_exposes_requested_model` verifies both usable authentication and the exact `GROK_MODEL` in the CLI inventory (`scripts/query_grok.sh:85-154`). Kimi similarly checks prompt mode, `stream-json`, the provider inventory, and `KIMI_MODEL` before any request (`scripts/query_kimi.sh:45-79`). Compatible future and vendor-custom versions therefore continue to work without a release-specific allowlist; an interface that cannot enforce the adapter's contract fails before a model call.
792
809
  - **The observed CLI version is provenance only.** Successful Grok and Kimi responses add `metadata.cli_version` and `metadata.cli_compatibility="capability-probed"`; the shared schema documents both fields without making either required for other transports (`scripts/query_grok.sh:319-336`, `scripts/query_kimi.sh:126-138`, `scripts/lib/schema.json:169-180`). This deliberately does not introduce a blessed-version range: the version is useful for diagnosis, while compatibility is decided by the exercised command, output, and model surfaces.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # AI Consultants v3.3.0
1
+ # AI Consultants v3.4.0
2
2
 
3
3
  > **Coverage, not a single guess.** A panel of up to 11 frontier models from different vendors fans out on your question in parallel and hands you the *union* of what they collectively see — the risks, edge cases, and approaches a single model misses.
4
4
 
5
- [![Version](https://img.shields.io/badge/version-3.3.0-blue.svg)](https://github.com/matteoscurati/ai-consultants)
5
+ [![Version](https://img.shields.io/badge/version-3.4.0-blue.svg)](https://github.com/matteoscurati/ai-consultants)
6
6
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
7
  [![Claude Code Skill](https://img.shields.io/badge/Claude%20Code-Skill-orange.svg)](https://docs.anthropic.com/en/docs/claude-code/skills)
8
8
  [![GitHub stars](https://img.shields.io/github/stars/matteoscurati/ai-consultants?style=social)](https://github.com/matteoscurati/ai-consultants)
@@ -447,9 +447,9 @@ parallel and returns the coverage union.
447
447
 
448
448
  | Consultant | Premium | Standard | Economy |
449
449
  |------------|---------|----------|---------|
450
- | Claude | claude-opus-5 | claude-sonnet-4-6 | claude-haiku-4-5 |
451
- | Gemini | Gemini 3.1 Pro (High) | Gemini 3.5 Flash (High) | Gemini 3.5 Flash (Low) |
452
- | Codex | gpt-5.5 | gpt-5.4 | gpt-5.4-nano |
450
+ | Claude | claude-opus-5 | claude-sonnet-5 | claude-haiku-4-5 |
451
+ | Gemini | Gemini 3.1 Pro (High) | Gemini 3.6 Flash (High) | Gemini 3.6 Flash (Low) |
452
+ | Codex | gpt-5.6-sol | gpt-5.6-terra | gpt-5.6-luna |
453
453
  | Mistral | mistral-large-3 | mistral-medium-latest | devstral-small-2 |
454
454
  | Cursor | composer-2.5 | composer-2 | gemini-3-flash |
455
455
  | DeepSeek | deepseek-v4-pro | deepseek-v4-flash | deepseek-v4-flash |
package/SKILL.md CHANGED
@@ -5,10 +5,10 @@ license: MIT
5
5
  compatibility: Requires bash, jq, and at least 2 AI CLI tools (agy, codex, vibe, etc.). macOS and Linux.
6
6
  metadata:
7
7
  author: matteoscurati
8
- version: 3.3.0
8
+ version: 3.4.0
9
9
  ---
10
10
 
11
- # AI Consultants v3.3.0 - AI Expert Panel
11
+ # AI Consultants v3.4.0 - AI Expert Panel
12
12
 
13
13
  **Coverage, not a single guess.** Convene a panel of AI "consultants" from different vendors for coding questions: they fan out in parallel and you get the *union* of what they collectively see — the risks, edge cases, and approaches a single model misses. Each consultant has a **configurable persona** that decorrelates its analysis.
14
14
 
@@ -1,4 +1,4 @@
1
- # Cost Rates - AI Consultants v3.3.0
1
+ # Cost Rates - AI Consultants v3.4.0
2
2
 
3
3
  This page documents the per-token rates used by the cost tracking system.
4
4
 
@@ -21,13 +21,13 @@ Since v2.5, models are organized into three tiers. Use `apply_model_tier()` or `
21
21
  | Model | Tier | Cost/1K | Consultant |
22
22
  |-------|------|---------|------------|
23
23
  | Gemini 3.1 Pro (High) | Premium | $0.002 | Gemini (agy CLI) |
24
- | Gemini 3.5 Flash (High) | Standard | $0.0015 | Gemini (agy CLI) |
25
- | Gemini 3.5 Flash (Low) | Economy | $0.0015 | Gemini (agy CLI) |
26
- | gpt-5.5 | Premium | $0.005 | Codex |
27
- | gpt-5.4 | Standard | $0.0025 | Codex |
28
- | gpt-5.4-nano | Economy | $0.0002 | Codex |
24
+ | Gemini 3.6 Flash (High) | Standard | $0.0015 | Gemini (agy CLI) |
25
+ | Gemini 3.6 Flash (Low) | Economy | $0.0015 | Gemini (agy CLI) |
26
+ | gpt-5.6-sol | Premium | $0.005 | Codex |
27
+ | gpt-5.6-terra | Standard | $0.002 | Codex |
28
+ | gpt-5.6-luna | Economy | $0.0002 | Codex |
29
29
  | claude-opus-5 | Premium | $0.005 | Claude |
30
- | claude-sonnet-4-6 | Standard | $0.003 | Claude |
30
+ | claude-sonnet-5 | Standard | $0.003 | Claude |
31
31
  | claude-haiku-4-5 | Economy | $0.001 | Claude |
32
32
  | mistral-large-3 | Premium | $0.002 | Mistral |
33
33
  | mistral-medium-latest | Standard | $0.001 | Mistral |
@@ -54,13 +54,13 @@ Since v2.5, models are organized into three tiers. Use `apply_model_tier()` or `
54
54
  | Model | Tier | Cost/1K | Consultant |
55
55
  |-------|------|---------|------------|
56
56
  | Gemini 3.1 Pro (High) | Premium | $0.012 | Gemini (agy CLI) |
57
- | Gemini 3.5 Flash (High) | Standard | $0.009 | Gemini (agy CLI) |
58
- | Gemini 3.5 Flash (Low) | Economy | $0.009 | Gemini (agy CLI) |
59
- | gpt-5.5 | Premium | $0.030 | Codex |
60
- | gpt-5.4 | Standard | $0.015 | Codex |
61
- | gpt-5.4-nano | Economy | $0.00125 | Codex |
57
+ | Gemini 3.6 Flash (High) | Standard | $0.0075 | Gemini (agy CLI) |
58
+ | Gemini 3.6 Flash (Low) | Economy | $0.0075 | Gemini (agy CLI) |
59
+ | gpt-5.6-sol | Premium | $0.030 | Codex |
60
+ | gpt-5.6-terra | Standard | $0.012 | Codex |
61
+ | gpt-5.6-luna | Economy | $0.0012 | Codex |
62
62
  | claude-opus-5 | Premium | $0.025 | Claude |
63
- | claude-sonnet-4-6 | Standard | $0.015 | Claude |
63
+ | claude-sonnet-5 | Standard | $0.015 | Claude |
64
64
  | claude-haiku-4-5 | Economy | $0.005 | Claude |
65
65
  | mistral-large-3 | Premium | $0.006 | Mistral |
66
66
  | mistral-medium-latest | Standard | $0.003 | Mistral |
package/docs/RECIPES.md CHANGED
@@ -190,28 +190,38 @@ For every available variable and its default, see
190
190
  [`references/configuration.md`](../references/configuration.md). The executable
191
191
  source of truth is [`scripts/config.sh`](../scripts/config.sh).
192
192
 
193
- ## Qwen via Qwen Cloud Token Plan (opt-in, preview)
193
+ ## Qwen via Qwen Cloud Token Plan (opt-in)
194
194
 
195
- `qwen3.8-max-preview` is **not** served by DashScope/ModelStudio. It is reachable
195
+ `qwen3.8-max` is **not** served by DashScope/ModelStudio. It is reachable
196
196
  only through a Qwen Cloud **Token Plan** subscription, which has its own base URL
197
197
  and its own API key. This is opt-in: nothing below is a default, and the panel's
198
198
  premium Qwen model remains `qwen3.7-max`.
199
199
 
200
200
  Understand the tradeoffs before enabling it:
201
201
 
202
- - **Preview, not GA.** Alibaba describes it as continuously evolving. No open
203
- weights, no published benchmarks, no stable per-token price.
202
+ - **Token Plan only.** No open weights, no published benchmarks, no stable
203
+ per-token price.
204
204
  - **Billed in prepaid credits**, not per token — and credit consumption varies
205
205
  with reasoning depth. Cost reports therefore exclude it and say so explicitly
206
206
  rather than estimating a figure.
207
207
 
208
+ Both `qwen3.8-max` and `qwen3.8-max-preview` are served by Token Plan and are
209
+ **separate deployments**, not an alias or rename: each completion reports its
210
+ own requested id back in `.model`. Verified against the live endpoint on
211
+ 2026-08-05. Prefer `qwen3.8-max`; `qwen3.8-max-preview` is the older
212
+ deployment and the natural candidate for eventual retirement. An existing
213
+ `qwen3.8-max-preview` pin keeps working and stays correctly catalogued — both
214
+ ids are listed in `unpriced_models`, so the session report continues to
215
+ disclose them as credit-billed and excluded from the total rather than falling
216
+ through to `default_rate`.
217
+
208
218
  ### API mode (the effort knob works here)
209
219
 
210
220
  ```bash
211
221
  QWEN3_USE_API=true
212
222
  QWEN3_FORMAT=openai # Token Plan is OpenAI-compatible; DashScope is not
213
223
  QWEN3_API_URL=https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions
214
- QWEN3_MODEL=qwen3.8-max-preview
224
+ QWEN3_MODEL=qwen3.8-max
215
225
  QWEN3_API_KEY=<your Token Plan key>
216
226
  QWEN3_REASONING_EFFORT=high # none|minimal|low|medium|high|xhigh|max; default xhigh
217
227
  ```
@@ -240,8 +250,8 @@ ai-consultants cannot deliver it per call and warns if you set
240
250
  "modelProviders": {
241
251
  "openai": [
242
252
  {
243
- "id": "qwen3.8-max-preview",
244
- "name": "[Token Plan] qwen3.8-max-preview",
253
+ "id": "qwen3.8-max",
254
+ "name": "[Token Plan] qwen3.8-max",
245
255
  "baseUrl": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
246
256
  "envKey": "BAILIAN_TOKEN_PLAN_API_KEY",
247
257
  "generationConfig": { "reasoning": { "effort": "high" } }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.3.0",
2
+ "version": "3.4.0",
3
3
  "updated": "2026-07-25",
4
4
  "models": {
5
5
  "_comment_units": "All rates are USD per 1,000 tokens (per-1K). lib/costs.sh computes cost as (token_count / 1000) * rate, so every value here MUST be a per-1K rate = (provider $/1M price) / 1000. Do NOT paste $/MTok figures directly.",
@@ -17,18 +17,26 @@
17
17
  "input": 0.002,
18
18
  "output": 0.012
19
19
  },
20
- "Gemini 3.5 Flash (High)": {
20
+ "Gemini 3.6 Flash (High)": {
21
21
  "input": 0.0015,
22
- "output": 0.009
22
+ "output": 0.0075
23
23
  },
24
- "Gemini 3.5 Flash (Low)": {
24
+ "Gemini 3.6 Flash (Low)": {
25
25
  "input": 0.0015,
26
- "output": 0.009
26
+ "output": 0.0075
27
27
  },
28
- "gpt-5.5": {
28
+ "gpt-5.6-sol": {
29
29
  "input": 0.005,
30
30
  "output": 0.030
31
31
  },
32
+ "gpt-5.6-terra": {
33
+ "input": 0.002,
34
+ "output": 0.012
35
+ },
36
+ "gpt-5.6-luna": {
37
+ "input": 0.0002,
38
+ "output": 0.0012
39
+ },
32
40
  "mistral-large-3": {
33
41
  "input": 0.002,
34
42
  "output": 0.006
@@ -75,7 +83,7 @@
75
83
  "output": 0.00
76
84
  },
77
85
  "_comment_standard": "Standard tier models",
78
- "claude-sonnet-4-6": {
86
+ "claude-sonnet-5": {
79
87
  "input": 0.003,
80
88
  "output": 0.015
81
89
  },
@@ -137,6 +145,30 @@
137
145
  "output": 0.001
138
146
  },
139
147
  "_comment_legacy": "Legacy models for backwards compatibility",
148
+ "Gemini 3.5 Flash (High)": {
149
+ "input": 0.0015,
150
+ "output": 0.009
151
+ },
152
+ "Gemini 3.5 Flash (Low)": {
153
+ "input": 0.0015,
154
+ "output": 0.009
155
+ },
156
+ "gpt-5.5": {
157
+ "input": 0.005,
158
+ "output": 0.030
159
+ },
160
+ "gpt-5.4": {
161
+ "input": 0.0025,
162
+ "output": 0.015
163
+ },
164
+ "gpt-5.4-nano": {
165
+ "input": 0.0002,
166
+ "output": 0.00125
167
+ },
168
+ "claude-sonnet-4-6": {
169
+ "input": 0.003,
170
+ "output": 0.015
171
+ },
140
172
  "claude-opus-4-8": {
141
173
  "input": 0.005,
142
174
  "output": 0.025
@@ -289,14 +321,6 @@
289
321
  "input": 0.0003,
290
322
  "output": 0.0015
291
323
  },
292
- "gpt-5.4": {
293
- "input": 0.0025,
294
- "output": 0.015
295
- },
296
- "gpt-5.4-nano": {
297
- "input": 0.0002,
298
- "output": 0.00125
299
- },
300
324
  "composer-2.5": {
301
325
  "input": 0.0005,
302
326
  "output": 0.0025
@@ -326,19 +350,24 @@
326
350
  "output": 0.0025
327
351
  },
328
352
  "_comment_unpriced": "Credit-billed models: drawn from a prepaid subscription pool, with no published per-token price. Rated 0 so the estimator does not fabricate a figure - an absent model would otherwise fall through to default_rate (0.005/0.015) and be reported as a confident, wrong number. Every entry here must also be listed in unpriced_models, which is what makes the session report disclose the exclusion instead of implying the model was free.",
353
+ "qwen3.8-max": {
354
+ "input": 0,
355
+ "output": 0
356
+ },
329
357
  "qwen3.8-max-preview": {
330
358
  "input": 0,
331
359
  "output": 0
332
360
  }
333
361
  },
334
362
  "unpriced_models": [
363
+ "qwen3.8-max",
335
364
  "qwen3.8-max-preview"
336
365
  ],
337
366
  "consultant_fallbacks": {
338
- "_comment": "Premium defaults - Updated Jul 2026",
367
+ "_comment": "Premium defaults - Updated Aug 2026",
339
368
  "claude": "claude-opus-5",
340
369
  "gemini": "Gemini 3.1 Pro (High)",
341
- "codex": "gpt-5.5",
370
+ "codex": "gpt-5.6-sol",
342
371
  "mistral": "mistral-large-3",
343
372
  "deepseek": "deepseek-v4-pro",
344
373
  "glm": "glm-5.2",
@@ -353,7 +382,7 @@
353
382
  "premium": {
354
383
  "claude": "claude-opus-5",
355
384
  "gemini": "Gemini 3.1 Pro (High)",
356
- "codex": "gpt-5.5",
385
+ "codex": "gpt-5.6-sol",
357
386
  "mistral": "mistral-large-3",
358
387
  "deepseek": "deepseek-v4-pro",
359
388
  "glm": "glm-5.2",
@@ -364,9 +393,9 @@
364
393
  "minimax": "minimax-m2.7"
365
394
  },
366
395
  "standard": {
367
- "claude": "claude-sonnet-4-6",
368
- "gemini": "Gemini 3.5 Flash (High)",
369
- "codex": "gpt-5.4",
396
+ "claude": "claude-sonnet-5",
397
+ "gemini": "Gemini 3.6 Flash (High)",
398
+ "codex": "gpt-5.6-terra",
370
399
  "mistral": "mistral-medium-latest",
371
400
  "deepseek": "deepseek-v4-flash",
372
401
  "glm": "glm-5.2",
@@ -378,8 +407,8 @@
378
407
  },
379
408
  "economy": {
380
409
  "claude": "claude-haiku-4-5",
381
- "gemini": "Gemini 3.5 Flash (Low)",
382
- "codex": "gpt-5.4-nano",
410
+ "gemini": "Gemini 3.6 Flash (Low)",
411
+ "codex": "gpt-5.6-luna",
383
412
  "mistral": "devstral-small-2",
384
413
  "deepseek": "deepseek-v4-flash",
385
414
  "glm": "glm-4-flash",
@@ -0,0 +1,102 @@
1
+ # Release v3.4.0
2
+
3
+ **Date:** 2026-08-05
4
+ **Type:** Minor — Gemini works over SSH again, the Codex consultant is isolated, and the model catalog moves to the current generation
5
+ **Previous:** v3.3.0
6
+
7
+ ## Highlights
8
+
9
+ - **Gemini consultations from an SSH session were failing authentication, and re-signing-in could not fix it.** Now fixed at every `agy` call site.
10
+ - **The Codex consultant no longer inherits your local Codex environment** — no `config.toml`, no `AGENTS.md`, no hooks, no MCP servers, and a read-only sandbox.
11
+ - **Model catalog refreshed** to `gpt-5.6-sol/terra/luna`, `claude-sonnet-5`, and Gemini 3.6 Flash, every id verified against the installed CLIs rather than documentation.
12
+ - **`*_REASONING_EFFORT` actually works now.** It was honored in API mode only, which meant it did nothing in the default CLI-first configuration.
13
+ - **Existing installations receive the new Codex model.** Without the migration added here, they would have kept querying `gpt-5.5` indefinitely while every doc claimed otherwise.
14
+
15
+ ## What's New
16
+
17
+ ### Gemini authentication over SSH
18
+
19
+ The Antigravity CLI picks its credential store from the environment. When it sees any of `SSH_CLIENT`, `SSH_CONNECTION`, or `SSH_TTY`, it switches to a file-based token store and never consults the macOS Keychain. If you signed in locally — as almost everyone does — the credential lives in the Keychain, so every Gemini consultation over SSH failed to authenticate. Signing in again did not help: the new login also landed in the store the CLI had already decided to ignore.
20
+
21
+ Those three markers are now stripped for every `agy` invocation: consultations, `doctor`, `update-clis`, and synthesis. On a local session they are already unset, so this changes nothing there. It grants no additional tools and no filesystem access — only which credential store the CLI consults.
22
+
23
+ If you drive ai-consultants over SSH, this is the release that makes Gemini work.
24
+
25
+ ### The Codex consultant runs isolated
26
+
27
+ Previously the Codex adapter ran `codex exec` in your ambient environment. That meant your `~/.codex/config.toml`, the repository's `AGENTS.md`, your hooks, your MCP servers, and your execpolicy rules all shaped what is supposed to be an independent second opinion — and the sandbox was unpinned.
28
+
29
+ CLI mode now runs under an ephemeral HOME and working directory with `--ephemeral`, `--ignore-user-config`, `--ignore-rules`, and `-s read-only`. **Authentication is unaffected**: `CODEX_HOME` still resolves to your real Codex auth store.
30
+
31
+ Two side effects worth knowing:
32
+
33
+ - **Large contexts no longer break it.** The prompt is delivered on stdin rather than argv, which removes both the `ARG_MAX` ceiling and the exposure of your prompt in the process list.
34
+ - **Failures are reported as failures.** A timeout, authentication error, or exhausted retry is never rewritten into a success, even when a partial answer reached disk.
35
+
36
+ ### Reasoning effort works in CLI mode
37
+
38
+ `*_REASONING_EFFORT` was documented as API-mode only. Since CLI-first transport became mandatory in v2.21, that meant the setting did nothing at all in the default configuration — while three consultant CLIs did expose the control.
39
+
40
+ | Consultant | Variable | Passed as |
41
+ |---|---|---|
42
+ | Codex | `CODEX_REASONING_EFFORT` | `-c model_reasoning_effort=<value>` |
43
+ | Claude | `CLAUDE_REASONING_EFFORT` | `--effort <value>` |
44
+ | Gemini | `GEMINI_REASONING_EFFORT` | `--effort <value>` |
45
+
46
+ There is deliberately no local allowlist of accepted values. The enum belongs to the provider — `agy`, for instance, accepts only `low|medium|high` — and an unsupported value comes back as a diagnosed error rather than being silently pre-empted. Consultants whose CLI has no such flag still warn, as before.
47
+
48
+ All three are optional. Leave them unset and each CLI keeps its own default.
49
+
50
+ ### Model catalog
51
+
52
+ Every replacement id was verified live against the installed CLIs:
53
+
54
+ | Consultant | Premium | Standard | Economy |
55
+ |---|---|---|---|
56
+ | Codex | `gpt-5.6-sol` | `gpt-5.6-terra` | `gpt-5.6-luna` |
57
+ | Claude | `claude-opus-5` *(unchanged)* | `claude-sonnet-5` | `claude-haiku-4-5` *(unchanged)* |
58
+ | Gemini | `Gemini 3.1 Pro (High)` *(unchanged)* | `Gemini 3.6 Flash (High)` | `Gemini 3.6 Flash (Low)` |
59
+
60
+ Gemini's premium tier stays on 3.1 Pro because the live model inventory has no 3.6 Pro. Cost rates ship alongside the new ids — a model missing from the catalog would otherwise be billed at the fallback rate and reported as a confident, wrong figure. Superseded ids remain catalogued so pinned configurations and historical responses still resolve.
61
+
62
+ The opt-in Qwen Token Plan model is referenced as `qwen3.8-max`. Both it and `qwen3.8-max-preview` are still served by Token Plan, and they are separate deployments rather than aliases — each reports its own id back. Both stay catalogued, so an existing `-preview` pin keeps working and keeps its credit-billed cost disclosure. The shipped premium Qwen model remains `qwen3.7-max`.
63
+
64
+ ### Existing installations get the new Codex model
65
+
66
+ `init` copies `.env.example` verbatim, so the then-current Codex default was written into your config as an ordinary value — and `configure` reads any unmarked value as a deliberate pin. A catalog refresh alone would therefore never have reached you: your panel would have kept querying `gpt-5.5` while the docs, tier tables, and cost catalog all said `gpt-5.6-sol`.
67
+
68
+ `configure` now migrates that one exact historical value and marks it as managed, so future refreshes can reach it too. **An explicit pin is preserved** — if you deliberately want `gpt-5.5`, record it with `--set CODEX_MODEL=gpt-5.5` and it will survive.
69
+
70
+ ## Breaking Changes
71
+
72
+ None. All new parameters are additive, existing configurations migrate, and no consultant is added or removed.
73
+
74
+ Two behavior changes are worth noting even though nothing breaks:
75
+
76
+ - **Codex answers may differ**, because it no longer sees your local Codex configuration. That is the intent — an advisory consultation shaped by your own hooks and project instructions is not an independent opinion.
77
+ - **Codex and Gemini tier models changed.** Pin `CODEX_MODEL` / `GEMINI_MODEL` explicitly if you need the previous ones.
78
+
79
+ ## Upgrade Guide
80
+
81
+ ```bash
82
+ # If installed via npx, nothing to do — npx resolves the published version
83
+ npx ai-consultants "your question"
84
+
85
+ # If installed via git clone
86
+ cd ~/.claude/skills/ai-consultants && git pull
87
+
88
+ # If installed via curl | bash
89
+ curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/scripts/install.sh | bash
90
+ ```
91
+
92
+ Then, to pick up the new Codex default in an existing configuration:
93
+
94
+ ```bash
95
+ ai-consultants configure
96
+ ```
97
+
98
+ No other configuration changes are required. Run `ai-consultants doctor --live` afterwards to confirm each consultant actually answers — the static check only verifies that CLIs are installed and keys are present.
99
+
100
+ ## Contributors
101
+
102
+ - @matteoscurati
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-consultants",
3
- "version": "3.3.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
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.3.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
  ;;
@@ -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 ---
@@ -200,7 +200,13 @@ main() {
200
200
  continue
201
201
  fi
202
202
 
203
- ver="$(run_with_timeout 8 "$cmd" --version </dev/null 2>/dev/null | head -1 || true)"
203
+ # agy under SSH must not see SSH_* markers (Keychain credential store).
204
+ # AGY_ENV_PREFIX is exec-safe under GNU timeout (see lib/common.sh).
205
+ if [[ "$default_bin" == "agy" ]]; then
206
+ ver="$(run_with_timeout 8 "${AGY_ENV_PREFIX[@]}" "$cmd" --version </dev/null 2>/dev/null | head -1 || true)"
207
+ else
208
+ ver="$(run_with_timeout 8 "$cmd" --version </dev/null 2>/dev/null | head -1 || true)"
209
+ fi
204
210
  detect_method "$default_bin" "$cmd"
205
211
  log_info "$display ($cmd): installed [${ver:-version n/a}] -- install method: $DETECT_METHOD"
206
212