ai-consultants 3.1.1 → 3.3.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 +2 -1
- package/CLAUDE.md +19 -5
- package/README.md +15 -5
- package/SKILL.md +7 -2
- package/docs/COST_RATES.md +3 -3
- package/docs/JSON_SCHEMA.md +4 -0
- package/docs/cost_rates.json +11 -7
- package/docs/releases/v3.2.0.md +96 -0
- package/docs/releases/v3.3.0.md +80 -0
- package/package.json +1 -1
- package/references/configuration.md +19 -3
- package/scripts/config.sh +4 -3
- package/scripts/configure.sh +51 -4
- package/scripts/lib/api.sh +6 -2
- package/scripts/lib/api_query.sh +18 -1
- package/scripts/lib/common.sh +8 -4
- package/scripts/lib/costs.sh +17 -7
- package/scripts/lib/schema.json +9 -0
- package/scripts/query_claude.sh +39 -8
- package/scripts/query_grok.sh +161 -46
- package/scripts/query_kimi.sh +46 -0
package/.env.example
CHANGED
|
@@ -144,9 +144,10 @@ KIMI_TIMEOUT=180
|
|
|
144
144
|
KIMI_CMD=kimi
|
|
145
145
|
|
|
146
146
|
# Claude - The Synthesizer
|
|
147
|
-
CLAUDE_MODEL=claude-opus-
|
|
147
|
+
CLAUDE_MODEL=claude-opus-5 # ai-consultants:default
|
|
148
148
|
CLAUDE_TIMEOUT=240
|
|
149
149
|
CLAUDE_CMD=claude
|
|
150
|
+
CLAUDE_API_MAX_TOKENS=16384
|
|
150
151
|
|
|
151
152
|
# Qwen3 - The Analyst (CLI/API switchable v2.7)
|
|
152
153
|
QWEN3_MODEL=qwen3.7-max
|
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.
|
|
9
|
+
**Version**: 3.3.0
|
|
10
10
|
|
|
11
11
|
## Distribution
|
|
12
12
|
|
|
@@ -368,7 +368,7 @@ 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-
|
|
371
|
+
| **premium** | Latest flagship models | claude-opus-5, Gemini 3.1 Pro (High), gpt-5.5 |
|
|
372
372
|
| **standard** | Good quality at reasonable cost | claude-sonnet-4-6, Gemini 3.5 Flash (High), gpt-5.4 |
|
|
373
373
|
| **economy** | Optimized for speed and low cost | claude-haiku-4-5, Gemini 3.5 Flash (Low), gpt-5.4-nano |
|
|
374
374
|
|
|
@@ -400,12 +400,12 @@ Three new presets leverage the model tiers:
|
|
|
400
400
|
./scripts/consult_all.sh --preset fast "quick syntax question"
|
|
401
401
|
```
|
|
402
402
|
|
|
403
|
-
### Premium Model Defaults (
|
|
403
|
+
### Premium Model Defaults (July 2026)
|
|
404
404
|
All consultants now use premium models by default:
|
|
405
405
|
|
|
406
406
|
| Consultant | Default Model |
|
|
407
407
|
|------------|---------------|
|
|
408
|
-
| Claude | claude-opus-
|
|
408
|
+
| Claude | claude-opus-5 |
|
|
409
409
|
| Gemini | Gemini 3.1 Pro (High) (via agy CLI) |
|
|
410
410
|
| Codex | gpt-5.5 |
|
|
411
411
|
| Mistral | mistral-large-3 |
|
|
@@ -613,7 +613,8 @@ for f in scripts/*.sh scripts/lib/*.sh; do bash -n "$f" && echo "OK: $f"; done
|
|
|
613
613
|
| `ENABLE_SMART_ROUTING` | false | Intelligent routing by category |
|
|
614
614
|
| `ENABLE_COST_TRACKING` | true | Track costs |
|
|
615
615
|
| `MAX_SESSION_COST` | 1.00 | Max budget ($) |
|
|
616
|
-
| `CLAUDE_MODEL` | claude-opus-
|
|
616
|
+
| `CLAUDE_MODEL` | claude-opus-5 | Claude model (v2.5) |
|
|
617
|
+
| `CLAUDE_API_MAX_TOKENS` | 16384 | Claude API thinking + visible-output budget |
|
|
617
618
|
| `GEMINI_MODEL` | Gemini 3.1 Pro (High) | Gemini agy CLI model (v2.15) |
|
|
618
619
|
| `GEMINI_API_MODEL` | gemini-3.1-pro-preview | Gemini API-mode model ID (v2.15) |
|
|
619
620
|
| `CODEX_MODEL` | gpt-5.5 | Codex model (v2.5) |
|
|
@@ -786,6 +787,19 @@ curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/s
|
|
|
786
787
|
|
|
787
788
|
## Changelog
|
|
788
789
|
|
|
790
|
+
### v3.3.0
|
|
791
|
+
- **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
|
+
- **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.
|
|
793
|
+
- **Optional capabilities remain optional.** Grok's `--no-auto-update` is used when the help surface advertises it, but its absence does not reject an otherwise compatible CLI. The capability probe itself uses `--help` and model/provider inventory commands; it does not send a consultation. Grok preserves its existing narrowly classified API fallback when the required CLI route is unavailable, while Kimi continues to fail closed because it has no API transport.
|
|
794
|
+
- **Regression coverage pins the compatibility contract rather than today's binaries.** Grok's suite accepts an alternate compatible version, rejects an incomplete interface before dispatch, checks requested-model availability, and preserves the post-launch/API-fallback boundary (32 checks). Kimi's suite covers an alternate compatible version, an incomplete interface, and a missing requested model (8 checks). Full release gate: 17/17 suites; core suite: 299/299; ShellCheck green. Installed Grok 0.2.114 and Kimi 0.27.0 also passed no-model-call probes.
|
|
795
|
+
|
|
796
|
+
### v3.2.0
|
|
797
|
+
- **Claude premium upgraded from Opus 4.8 to Opus 5.** The default `CLAUDE_MODEL`, premium/max/best tier, standalone adapter fallback, configuration examples, model table, and cost catalog now resolve to the canonical `claude-opus-5` ID (`scripts/config.sh:274`, `scripts/query_claude.sh:29`). Pricing remains $5/$25 per million tokens; `claude-opus-4-8` stays in the legacy catalog for historical responses and intentional pins.
|
|
798
|
+
- **Opus 5 adaptive thinking is accounted for end to end.** Claude CLI mode now requests the JSON result envelope, extracts visible `.result`, aggregates provider usage across `modelUsage`, and persists the provider's exact `costUSD` (`scripts/query_claude.sh:72-126`, `scripts/lib/common.sh:904-980`, `scripts/lib/costs.sh:307-354`). This fixes the review finding that pricing only the visible answer undercounted hidden thinking and cache charges. The implementation deliberately prefers provider cost over reconstructing cache pricing from token totals.
|
|
799
|
+
- **Anthropic API responses no longer disappear when thinking precedes text.** `parse_anthropic_response` selects every `type=text` block rather than assuming `.content[0].text` (`scripts/lib/api.sh:325-343`). The API token budget is now the configurable `CLAUDE_API_MAX_TOKENS=16384`, shared by adaptive thinking and visible output; `stop_reason=max_tokens` fails closed instead of admitting a partial answer into synthesis (`scripts/lib/api_query.sh:94-153`).
|
|
800
|
+
- **Historical generated defaults migrate with provenance.** Before this release, `init` copied `CLAUDE_MODEL=claude-opus-4-8` without distinguishing a generated default from a user pin, so the new runtime fallback was unreachable for existing installations. `configure` now upgrades that exact historical value to Opus 5 and writes `# ai-consultants:default`; model values supplied by environment, interactive input, or `--set` receive `# ai-consultants:pin` and survive later rewrites (`scripts/configure.sh:183-291`, `scripts/configure.sh:357-420`). An intentionally retained 4.8 value should be reasserted once with `--set CLAUDE_MODEL=claude-opus-4-8`.
|
|
801
|
+
- **Verification expanded around all four review findings.** `test_query_claude.sh` exercises CLI JSON extraction, measured usage, provider cost persistence, and session billing. `test_api_transport.sh` covers thinking-before-text, the 16,384-token request default, invalid budgets, and truncation rejection; `test_configure.sh` covers managed migration and persistent pins. Full gate: 17/17 suites; core suite: 299/299; shellcheck green. The real maintainer config was not rewritten during implementation and remained on 4.8 until the release installation step.
|
|
802
|
+
|
|
789
803
|
### v3.1.1
|
|
790
804
|
- **`doctor` no longer aborts at the first missing enabled consultant.** The v3.1.0 tag workflow exposed the pre-existing `set -e` interaction when Grok became CLI-first and the clean Ubuntu runner had no `grok` binary: the main JSON diagnostic exited before emitting output. Missing CLI/API configuration checks now add their issue and continue to the final summary, which still exits unhealthy. `test_doctor.sh` stubs the complete CLI roster, rejects empty JSON output, and includes a dedicated missing-Grok regression. Full gate: 16/16 suites; shellcheck green.
|
|
791
805
|
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# AI Consultants v3.
|
|
1
|
+
# AI Consultants v3.3.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
|
-
[](https://github.com/matteoscurati/ai-consultants)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://docs.anthropic.com/en/docs/claude-code/skills)
|
|
8
8
|
[](https://github.com/matteoscurati/ai-consultants)
|
|
@@ -388,7 +388,10 @@ to the xAI API only when the CLI is missing, cannot launch, or has no usable
|
|
|
388
388
|
authentication and `GROK_API_KEY` is configured; post-launch request failures
|
|
389
389
|
are surfaced without a silent API charge. Qwen3 and MiniMax can also switch
|
|
390
390
|
from their CLI to API transport. Gemini, Codex, Claude, and Mistral are
|
|
391
|
-
CLI/API switchable.
|
|
391
|
+
CLI/API switchable. Grok and Kimi compatibility is capability-probed before
|
|
392
|
+
dispatch: the requested model, headless arguments, and structured-output
|
|
393
|
+
surface must be available. Any CLI version is accepted when those checks pass;
|
|
394
|
+
the observed version is response provenance only.
|
|
392
395
|
|
|
393
396
|
### API-Only Consultants
|
|
394
397
|
|
|
@@ -444,7 +447,7 @@ parallel and returns the coverage union.
|
|
|
444
447
|
|
|
445
448
|
| Consultant | Premium | Standard | Economy |
|
|
446
449
|
|------------|---------|----------|---------|
|
|
447
|
-
| Claude | claude-opus-
|
|
450
|
+
| Claude | claude-opus-5 | claude-sonnet-4-6 | claude-haiku-4-5 |
|
|
448
451
|
| Gemini | Gemini 3.1 Pro (High) | Gemini 3.5 Flash (High) | Gemini 3.5 Flash (Low) |
|
|
449
452
|
| Codex | gpt-5.5 | gpt-5.4 | gpt-5.4-nano |
|
|
450
453
|
| Mistral | mistral-large-3 | mistral-medium-latest | devstral-small-2 |
|
|
@@ -546,7 +549,13 @@ with `--set`). Rewrites create a private timestamped backup. Use `--advanced` to
|
|
|
546
549
|
review every parameter or `--dry-run` to preview a redacted result.
|
|
547
550
|
Auto-selected `*_USE_API` values are marked `# ai-consultants:auto`, allowing a
|
|
548
551
|
later run to adapt when a CLI or credential changes. Environment variables,
|
|
549
|
-
`--set`, and unmarked values remain explicit user choices
|
|
552
|
+
`--set`, and unmarked values remain explicit user choices, except for the exact
|
|
553
|
+
historical generated Claude default described below.
|
|
554
|
+
Managed model defaults use `# ai-consultants:default`; `configure` upgrades the
|
|
555
|
+
historical unmarked `CLAUDE_MODEL=claude-opus-4-8` default to Opus 5. To keep
|
|
556
|
+
4.8 intentionally, run
|
|
557
|
+
`ai-consultants configure --set CLAUDE_MODEL=claude-opus-4-8`; explicit model
|
|
558
|
+
overrides are stored with `# ai-consultants:pin`.
|
|
550
559
|
|
|
551
560
|
Enter credentials through `--interactive`/`--advanced` or export them before the
|
|
552
561
|
run; avoid passing API keys through `--set`, where the shell may retain them in
|
|
@@ -563,6 +572,7 @@ DEFAULT_STRATEGY=coverage # coverage | compare_only | majority | risk_averse
|
|
|
563
572
|
ENABLE_SMART_ROUTING=true # Auto-select consultants by question category
|
|
564
573
|
MAX_SESSION_COST=1.00 # USD budget cap (paired with ENABLE_BUDGET_LIMIT=true to enforce)
|
|
565
574
|
KIMI_MODEL=kimi-code/k3 # Pin the Kimi consultant to K3
|
|
575
|
+
CLAUDE_API_MAX_TOKENS=16384 # Shared thinking + visible-output budget in API mode
|
|
566
576
|
```
|
|
567
577
|
|
|
568
578
|
Full reference: [`references/configuration.md`](references/configuration.md). Copy-paste workflows: [`docs/RECIPES.md`](docs/RECIPES.md). For category-aware preset suggestions: `ai-consultants doctor --suggest-preset --question "..."`.
|
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.
|
|
8
|
+
version: 3.3.0
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
# AI Consultants v3.
|
|
11
|
+
# AI Consultants v3.3.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
|
|
|
@@ -45,6 +45,11 @@ Configuration (presets, strategies, features, personas, API keys) can be managed
|
|
|
45
45
|
|
|
46
46
|
**CLI/API Mode**: Gemini, Codex, Claude, Mistral, Qwen, Grok, and MiniMax can switch between CLI and API mode via `*_USE_API` environment variables. Grok is CLI-first on Grok Build with `grok-4.5` and falls back to the xAI API when the CLI cannot run. Gemini auto-selects API mode when `GEMINI_API_KEY` is set (no `agy` install needed) and the CLI otherwise.
|
|
47
47
|
|
|
48
|
+
Grok and Kimi are compatible by capability rather than by CLI version. Before
|
|
49
|
+
dispatch, their adapters verify the required headless arguments, requested
|
|
50
|
+
model, and structured-output surface. Compatible version changes are accepted;
|
|
51
|
+
the observed version is recorded only as response provenance.
|
|
52
|
+
|
|
48
53
|
**Self-Exclusion**: The invoking agent is automatically excluded from the panel. When invoked from Claude Code, Claude is excluded; when invoked from Codex CLI, Codex is excluded, etc.
|
|
49
54
|
|
|
50
55
|
## Requirements
|
package/docs/COST_RATES.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Cost Rates - AI Consultants v3.
|
|
1
|
+
# Cost Rates - AI Consultants v3.3.0
|
|
2
2
|
|
|
3
3
|
This page documents the per-token rates used by the cost tracking system.
|
|
4
4
|
|
|
@@ -26,7 +26,7 @@ Since v2.5, models are organized into three tiers. Use `apply_model_tier()` or `
|
|
|
26
26
|
| gpt-5.5 | Premium | $0.005 | Codex |
|
|
27
27
|
| gpt-5.4 | Standard | $0.0025 | Codex |
|
|
28
28
|
| gpt-5.4-nano | Economy | $0.0002 | Codex |
|
|
29
|
-
| claude-opus-
|
|
29
|
+
| claude-opus-5 | Premium | $0.005 | Claude |
|
|
30
30
|
| claude-sonnet-4-6 | Standard | $0.003 | Claude |
|
|
31
31
|
| claude-haiku-4-5 | Economy | $0.001 | Claude |
|
|
32
32
|
| mistral-large-3 | Premium | $0.002 | Mistral |
|
|
@@ -59,7 +59,7 @@ Since v2.5, models are organized into three tiers. Use `apply_model_tier()` or `
|
|
|
59
59
|
| gpt-5.5 | Premium | $0.030 | Codex |
|
|
60
60
|
| gpt-5.4 | Standard | $0.015 | Codex |
|
|
61
61
|
| gpt-5.4-nano | Economy | $0.00125 | Codex |
|
|
62
|
-
| claude-opus-
|
|
62
|
+
| claude-opus-5 | Premium | $0.025 | Claude |
|
|
63
63
|
| claude-sonnet-4-6 | Standard | $0.015 | Claude |
|
|
64
64
|
| claude-haiku-4-5 | Economy | $0.005 | Claude |
|
|
65
65
|
| mistral-large-3 | Premium | $0.006 | Mistral |
|
package/docs/JSON_SCHEMA.md
CHANGED
|
@@ -37,6 +37,8 @@ The schema is defined in `scripts/lib/schema.json` following JSON Schema Draft-0
|
|
|
37
37
|
"tokens_output": 300,
|
|
38
38
|
"latency_ms": 2345,
|
|
39
39
|
"model_version": "gemini-3.1-pro-preview",
|
|
40
|
+
"cli_version": "0.27.0",
|
|
41
|
+
"cli_compatibility": "capability-probed",
|
|
40
42
|
"timestamp": "2024-01-14T12:34:56Z"
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -209,6 +211,8 @@ Present only if Multi-Agent Debate is enabled (round >= 2):
|
|
|
209
211
|
| `tokens_output` | integer | Completion tokens, when the provider reported them (API mode only) |
|
|
210
212
|
| `latency_ms` | integer | Response time in ms |
|
|
211
213
|
| `model_version` | string | Exact model version |
|
|
214
|
+
| `cli_version` | string | Observed CLI version for provenance; never a compatibility gate |
|
|
215
|
+
| `cli_compatibility` | string | Capability-probe result: `capability-probed` or `incompatible` |
|
|
212
216
|
| `timestamp` | string | ISO 8601 timestamp |
|
|
213
217
|
|
|
214
218
|
## Enum Values
|
package/docs/cost_rates.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.
|
|
3
|
-
"updated": "2026-07-
|
|
2
|
+
"version": "3.3.0",
|
|
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.",
|
|
6
|
-
"_comment_premium": "Premium tier models -
|
|
7
|
-
"claude-opus-
|
|
6
|
+
"_comment_premium": "Premium tier models - Jul 2026",
|
|
7
|
+
"claude-opus-5": {
|
|
8
8
|
"input": 0.005,
|
|
9
9
|
"output": 0.025
|
|
10
10
|
},
|
|
@@ -137,6 +137,10 @@
|
|
|
137
137
|
"output": 0.001
|
|
138
138
|
},
|
|
139
139
|
"_comment_legacy": "Legacy models for backwards compatibility",
|
|
140
|
+
"claude-opus-4-8": {
|
|
141
|
+
"input": 0.005,
|
|
142
|
+
"output": 0.025
|
|
143
|
+
},
|
|
140
144
|
"claude-opus-4-7": {
|
|
141
145
|
"input": 0.005,
|
|
142
146
|
"output": 0.025
|
|
@@ -331,8 +335,8 @@
|
|
|
331
335
|
"qwen3.8-max-preview"
|
|
332
336
|
],
|
|
333
337
|
"consultant_fallbacks": {
|
|
334
|
-
"_comment": "Premium defaults - Updated
|
|
335
|
-
"claude": "claude-opus-
|
|
338
|
+
"_comment": "Premium defaults - Updated Jul 2026",
|
|
339
|
+
"claude": "claude-opus-5",
|
|
336
340
|
"gemini": "Gemini 3.1 Pro (High)",
|
|
337
341
|
"codex": "gpt-5.5",
|
|
338
342
|
"mistral": "mistral-large-3",
|
|
@@ -347,7 +351,7 @@
|
|
|
347
351
|
"model_tiers": {
|
|
348
352
|
"_comment": "Model tiers for apply_model_tier() function",
|
|
349
353
|
"premium": {
|
|
350
|
-
"claude": "claude-opus-
|
|
354
|
+
"claude": "claude-opus-5",
|
|
351
355
|
"gemini": "Gemini 3.1 Pro (High)",
|
|
352
356
|
"codex": "gpt-5.5",
|
|
353
357
|
"mistral": "mistral-large-3",
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Release v3.2.0
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-07-25
|
|
4
|
+
**Type:** Minor — Claude moves to Opus 5 with adaptive-thinking-safe transports and billing
|
|
5
|
+
**Previous:** v3.1.1
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- **Claude now uses `claude-opus-5` as its premium default** across direct configuration, model tiers, documentation, and cost tracking.
|
|
10
|
+
- **Claude CLI costs now come directly from the provider**, including adaptive thinking and cache charges that are absent from the visible answer.
|
|
11
|
+
- **Anthropic API thinking blocks are handled correctly**, and responses truncated by the shared thinking/output budget are rejected instead of synthesized as complete.
|
|
12
|
+
- **Existing generated Opus 4.8 defaults migrate to Opus 5**, while explicit model pins are recorded and preserved.
|
|
13
|
+
- **Opus 4.8 remains supported** for historical responses and intentional overrides.
|
|
14
|
+
|
|
15
|
+
## What's New
|
|
16
|
+
|
|
17
|
+
### Opus 5 premium default
|
|
18
|
+
|
|
19
|
+
The Claude consultant, premium/max/best quality tier, standalone adapter fallback, examples, and cost catalog now use the canonical `claude-opus-5` model ID. Standard and economy tiers remain on Claude Sonnet 4.6 and Haiku 4.5.
|
|
20
|
+
|
|
21
|
+
Opus 5 retains the previous $5 per million input tokens and $25 per million output tokens. The legacy Opus 4.8 rates remain available so historical sessions and pinned installations continue to be priced correctly.
|
|
22
|
+
|
|
23
|
+
### Exact Claude CLI usage and cost
|
|
24
|
+
|
|
25
|
+
Claude CLI mode now requests its JSON result envelope. AI Consultants extracts:
|
|
26
|
+
|
|
27
|
+
- the visible answer from `.result`;
|
|
28
|
+
- measured input, cache, and output usage from `modelUsage`;
|
|
29
|
+
- the provider-reported `costUSD`.
|
|
30
|
+
|
|
31
|
+
The exact provider cost takes precedence over local token-rate reconstruction. This includes adaptive-thinking and cache charges even though those tokens do not appear in the visible answer.
|
|
32
|
+
|
|
33
|
+
### Adaptive-thinking-safe API mode
|
|
34
|
+
|
|
35
|
+
Anthropic responses may place a thinking block before one or more visible text blocks. The parser now collects every visible `type=text` block instead of assuming the first content block contains the answer.
|
|
36
|
+
|
|
37
|
+
The new `CLAUDE_API_MAX_TOKENS` setting defaults to 16,384 tokens. Anthropic counts adaptive thinking and visible output against this shared budget. If the provider returns `stop_reason=max_tokens`, the consultation fails as truncated rather than entering synthesis as a complete response.
|
|
38
|
+
|
|
39
|
+
### Managed configuration migration
|
|
40
|
+
|
|
41
|
+
Older `ai-consultants init` templates wrote the then-current Opus 4.8 default as an ordinary value. Running `ai-consultants configure` now recognizes that exact historical generated default, upgrades it to Opus 5, and records managed provenance.
|
|
42
|
+
|
|
43
|
+
Explicit model choices are written with an `# ai-consultants:pin` marker and survive subsequent configuration refreshes.
|
|
44
|
+
|
|
45
|
+
## Breaking Changes
|
|
46
|
+
|
|
47
|
+
None — this release is backwards-compatible. Opus 4.8 remains selectable and priced.
|
|
48
|
+
|
|
49
|
+
If `CLAUDE_MODEL=claude-opus-4-8` was an intentional unmarked override rather than the generated default, reassert it once after upgrading:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ai-consultants configure --set CLAUDE_MODEL=claude-opus-4-8
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Upgrade Guide
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# If installed via git clone
|
|
59
|
+
cd ~/.claude/skills/ai-consultants && git pull
|
|
60
|
+
|
|
61
|
+
# If installed via curl | bash
|
|
62
|
+
curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/scripts/install.sh | bash
|
|
63
|
+
|
|
64
|
+
# If used through npx
|
|
65
|
+
npx ai-consultants@latest "your question"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Refresh persistent configuration so a historical generated Opus 4.8 value migrates:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
ai-consultants configure
|
|
72
|
+
ai-consultants doctor
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
API users may override the shared thinking/output budget if needed:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
ai-consultants configure --set CLAUDE_API_MAX_TOKENS=32768
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Verification
|
|
82
|
+
|
|
83
|
+
- Full repository gate: 17/17 suites
|
|
84
|
+
- Core suite: 299/299 checks
|
|
85
|
+
- Claude CLI JSON/cost regression suite: 6/6 checks
|
|
86
|
+
- Anthropic transport regression suite: 48/48 checks
|
|
87
|
+
- Shellcheck and release-surface validation
|
|
88
|
+
- Live local Claude CLI JSON envelope verified with `claude-opus-5`
|
|
89
|
+
|
|
90
|
+
## Commits
|
|
91
|
+
|
|
92
|
+
- `3963c3c` feat: upgrade Claude consultant to Opus 5
|
|
93
|
+
|
|
94
|
+
## Contributors
|
|
95
|
+
|
|
96
|
+
- Matteo Scurati
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Release v3.3.0
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-07-29
|
|
4
|
+
**Type:** Minor — capability-based Grok and Kimi CLI compatibility
|
|
5
|
+
**Previous:** v3.2.0
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- Grok and Kimi now prove CLI compatibility from the interface they actually need, not from a version string.
|
|
10
|
+
- Compatible future and vendor-custom CLI versions are accepted without a release-specific allowlist.
|
|
11
|
+
- Incomplete interfaces and missing requested models are rejected before a model request begins.
|
|
12
|
+
- Responses expose the observed CLI version as provenance alongside the capability-probe result.
|
|
13
|
+
|
|
14
|
+
## What's New
|
|
15
|
+
|
|
16
|
+
### Capability-based CLI compatibility
|
|
17
|
+
|
|
18
|
+
Before dispatch, the Grok adapter validates its complete headless argument
|
|
19
|
+
surface, strict sandbox controls, structured output, authentication state, and
|
|
20
|
+
the requested model. The probe runs inside an isolated environment and does not
|
|
21
|
+
send a consultation.
|
|
22
|
+
|
|
23
|
+
Kimi now validates prompt mode, `stream-json`, provider inventory, and the exact
|
|
24
|
+
configured model before dispatch. A CLI with a different version number remains
|
|
25
|
+
compatible when these capabilities are present; a CLI that cannot satisfy the
|
|
26
|
+
contract fails before a model call.
|
|
27
|
+
|
|
28
|
+
Grok's `--no-auto-update` flag remains optional. It is enabled when the installed
|
|
29
|
+
CLI advertises it and omitted otherwise, so a useful automation control does not
|
|
30
|
+
become an accidental version constraint.
|
|
31
|
+
|
|
32
|
+
### Diagnostic provenance
|
|
33
|
+
|
|
34
|
+
Successful Grok and Kimi CLI responses now include:
|
|
35
|
+
|
|
36
|
+
| Field | Meaning |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `metadata.cli_version` | The version reported by the CLI, retained for diagnosis and provenance |
|
|
39
|
+
| `metadata.cli_compatibility` | `capability-probed` when the required interface passed |
|
|
40
|
+
|
|
41
|
+
These fields are additive. Existing response consumers and non-CLI transports
|
|
42
|
+
do not need to provide them.
|
|
43
|
+
|
|
44
|
+
### Verification
|
|
45
|
+
|
|
46
|
+
- Full repository gate: 17/17 suites
|
|
47
|
+
- Core suite: 299/299 checks
|
|
48
|
+
- Grok adapter suite: 32/32 checks
|
|
49
|
+
- Kimi adapter suite: 8/8 checks
|
|
50
|
+
- ShellCheck: green
|
|
51
|
+
- Installed Grok 0.2.114 and Kimi 0.27.0: no-model-call capability probes passed
|
|
52
|
+
|
|
53
|
+
## Breaking Changes
|
|
54
|
+
|
|
55
|
+
None — this release is backwards-compatible. No configuration changes are
|
|
56
|
+
required.
|
|
57
|
+
|
|
58
|
+
An installed Grok or Kimi CLI that lacks the adapter's required command,
|
|
59
|
+
structured-output, or requested-model surface will now be rejected before
|
|
60
|
+
dispatch instead of failing later during a consultation.
|
|
61
|
+
|
|
62
|
+
## Upgrade Guide
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# npx (no install)
|
|
66
|
+
npx ai-consultants@latest "your question"
|
|
67
|
+
|
|
68
|
+
# git clone install
|
|
69
|
+
cd ~/.claude/skills/ai-consultants && git pull
|
|
70
|
+
|
|
71
|
+
# curl | bash install
|
|
72
|
+
curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/scripts/install.sh | bash
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
No configuration changes are required. If an adapter reports an incompatible
|
|
76
|
+
CLI, update that CLI and run `ai-consultants doctor` before retrying.
|
|
77
|
+
|
|
78
|
+
## Commits
|
|
79
|
+
|
|
80
|
+
- `c7bad95` Harden CLI capability checks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-consultants",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.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"
|
|
@@ -54,6 +54,11 @@ ai-consultants configure --dry-run
|
|
|
54
54
|
Use the hidden interactive credential prompts (or exported environment variables)
|
|
55
55
|
for API keys; command-line arguments may be visible in shell history and process
|
|
56
56
|
inspection.
|
|
57
|
+
Model defaults carry an `# ai-consultants:default` marker. On rewrite,
|
|
58
|
+
`configure` upgrades the historical unmarked
|
|
59
|
+
`CLAUDE_MODEL=claude-opus-4-8` generated default to Opus 5. An explicit model
|
|
60
|
+
override supplied with `--set` is stored with `# ai-consultants:pin`, so
|
|
61
|
+
`--set CLAUDE_MODEL=claude-opus-4-8` keeps the legacy model intentionally.
|
|
57
62
|
The exhaustive parameter contract is [`.env.example`](../.env.example); the
|
|
58
63
|
configurator derives its accepted keys from that template, while
|
|
59
64
|
[`scripts/config.sh`](../scripts/config.sh) remains the runtime source of truth.
|
|
@@ -235,7 +240,8 @@ ENABLE_DEEPSEEK=false
|
|
|
235
240
|
```bash
|
|
236
241
|
GEMINI_MODEL=Gemini 3.1 Pro (High) # agy CLI display name; API mode uses GEMINI_API_MODEL
|
|
237
242
|
CODEX_MODEL=gpt-5.5
|
|
238
|
-
CLAUDE_MODEL=claude-opus-
|
|
243
|
+
CLAUDE_MODEL=claude-opus-5
|
|
244
|
+
CLAUDE_API_MAX_TOKENS=16384 # API only: adaptive thinking + visible output
|
|
239
245
|
MISTRAL_MODEL=mistral-large-3
|
|
240
246
|
CURSOR_MODEL=composer-2.5
|
|
241
247
|
KIMI_MODEL=kimi-code/k3
|
|
@@ -246,16 +252,26 @@ DEEPSEEK_MODEL=deepseek-v4-pro
|
|
|
246
252
|
MINIMAX_MODEL=MiniMax-M2.7
|
|
247
253
|
```
|
|
248
254
|
|
|
255
|
+
Anthropic counts adaptive thinking and visible response tokens against the same
|
|
256
|
+
`CLAUDE_API_MAX_TOKENS` limit. The default is 16,384; a response that reaches
|
|
257
|
+
that limit is rejected as truncated instead of entering synthesis as a complete
|
|
258
|
+
answer.
|
|
259
|
+
|
|
249
260
|
`GROK_MODEL` is passed explicitly to `grok -m`. The default transport is the
|
|
250
261
|
headless Grok Build CLI using a private `--prompt-file` inside an isolated,
|
|
251
262
|
tool-free strict sandbox. If the CLI is missing, cannot execute, or has no
|
|
252
263
|
usable authentication, the same query and model fall back to `GROK_API_URL`
|
|
253
264
|
when `GROK_API_KEY` is set. Failures after a request launches (including
|
|
254
265
|
timeouts, model errors, and empty output) are returned without API fallback.
|
|
255
|
-
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.
|
|
256
269
|
|
|
257
270
|
`KIMI_MODEL` is passed directly to `kimi --model`, so `kimi-code/k3` overrides
|
|
258
|
-
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.
|
|
259
275
|
|
|
260
276
|
## Model Quality Tiers (v2.5)
|
|
261
277
|
|
package/scripts/config.sh
CHANGED
|
@@ -271,7 +271,8 @@ KIMI_CMD="${KIMI_CMD:-kimi}"
|
|
|
271
271
|
# CLAUDE CONFIGURATION - The Synthesizer (v2.2)
|
|
272
272
|
# =============================================================================
|
|
273
273
|
|
|
274
|
-
CLAUDE_MODEL="${CLAUDE_MODEL:-claude-opus-
|
|
274
|
+
CLAUDE_MODEL="${CLAUDE_MODEL:-claude-opus-5}"
|
|
275
|
+
CLAUDE_API_MAX_TOKENS="${CLAUDE_API_MAX_TOKENS:-16384}"
|
|
275
276
|
CLAUDE_TIMEOUT_SECONDS="${CLAUDE_TIMEOUT:-240}"
|
|
276
277
|
CLAUDE_CMD="${CLAUDE_CMD:-claude}"
|
|
277
278
|
|
|
@@ -581,7 +582,7 @@ get_model_for_tier() {
|
|
|
581
582
|
case "$tier" in
|
|
582
583
|
premium|max|best)
|
|
583
584
|
case "$consultant" in
|
|
584
|
-
claude) echo "claude-opus-
|
|
585
|
+
claude) echo "claude-opus-5" ;;
|
|
585
586
|
gemini) echo "Gemini 3.1 Pro (High)" ;;
|
|
586
587
|
codex) echo "gpt-5.5" ;;
|
|
587
588
|
mistral) echo "mistral-large-3" ;;
|
|
@@ -778,4 +779,4 @@ EOF
|
|
|
778
779
|
# VERSION
|
|
779
780
|
# =============================================================================
|
|
780
781
|
|
|
781
|
-
AI_CONSULTANTS_VERSION="3.
|
|
782
|
+
AI_CONSULTANTS_VERSION="3.3.0"
|
package/scripts/configure.sh
CHANGED
|
@@ -181,6 +181,25 @@ set_value() {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
AUTO_MARKER="# ai-consultants:auto"
|
|
184
|
+
DEFAULT_MARKER="# ai-consultants:default"
|
|
185
|
+
PIN_MARKER="# ai-consultants:pin"
|
|
186
|
+
|
|
187
|
+
has_value_marker() {
|
|
188
|
+
local file="$1" key="$2" marker="$3" line normalized
|
|
189
|
+
[[ -r "$file" ]] || return 1
|
|
190
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
191
|
+
line="${line%$'\r'}"
|
|
192
|
+
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
193
|
+
normalized="${line#"${line%%[![:space:]]*}"}"
|
|
194
|
+
if [[ "$normalized" =~ ^export[[:space:]]+ ]]; then
|
|
195
|
+
normalized="${normalized#export}"
|
|
196
|
+
normalized="${normalized#"${normalized%%[![:space:]]*}"}"
|
|
197
|
+
fi
|
|
198
|
+
[[ "$normalized" == "$key="* ]] || continue
|
|
199
|
+
[[ "$normalized" == *"$marker"* ]] && return 0
|
|
200
|
+
done < "$file"
|
|
201
|
+
return 1
|
|
202
|
+
}
|
|
184
203
|
|
|
185
204
|
is_auto_managed_value() {
|
|
186
205
|
local file="$1" key="$2" line normalized raw="" seen=false
|
|
@@ -239,13 +258,33 @@ set_auto_value() {
|
|
|
239
258
|
# Merge existing persistent values first, then current environment overrides.
|
|
240
259
|
while IFS= read -r key; do
|
|
241
260
|
if [[ -f "$OUTPUT_FILE" ]] && value=$(read_value "$OUTPUT_FILE" "$key"); then
|
|
242
|
-
|
|
261
|
+
if has_value_marker "$OUTPUT_FILE" "$key" "$DEFAULT_MARKER"; then
|
|
262
|
+
set_value "$key" "$value $DEFAULT_MARKER"
|
|
263
|
+
elif has_value_marker "$OUTPUT_FILE" "$key" "$PIN_MARKER"; then
|
|
264
|
+
set_value "$key" "$value $PIN_MARKER"
|
|
265
|
+
else
|
|
266
|
+
set_value "$key" "$value"
|
|
267
|
+
fi
|
|
243
268
|
fi
|
|
244
269
|
if [[ -n "${!key+x}" ]]; then
|
|
245
|
-
|
|
270
|
+
if [[ "$key" == *_MODEL ]]; then
|
|
271
|
+
set_value "$key" "${!key} $PIN_MARKER"
|
|
272
|
+
else
|
|
273
|
+
set_value "$key" "${!key}"
|
|
274
|
+
fi
|
|
246
275
|
fi
|
|
247
276
|
done < <(list_parameters)
|
|
248
277
|
|
|
278
|
+
# Before model defaults had provenance markers, `init` copied the then-current
|
|
279
|
+
# Claude default as an ordinary-looking value. Refresh that exact historical
|
|
280
|
+
# default when configure rewrites the file. A real legacy pin is preserved by
|
|
281
|
+
# `--set CLAUDE_MODEL=claude-opus-4-8`, which records the pin marker.
|
|
282
|
+
if [[ "$(read_value "$WORK_FILE" CLAUDE_MODEL 2>/dev/null || true)" == "claude-opus-4-8" ]] \
|
|
283
|
+
&& ! has_value_marker "$WORK_FILE" CLAUDE_MODEL "$PIN_MARKER"; then
|
|
284
|
+
set_value CLAUDE_MODEL "claude-opus-5 $DEFAULT_MARKER"
|
|
285
|
+
echo "Migrated managed Claude default: claude-opus-4-8 -> claude-opus-5" >&2
|
|
286
|
+
fi
|
|
287
|
+
|
|
249
288
|
configure_cli_only() {
|
|
250
289
|
local enable_key="$1" cmd_key="$2" cmd enabled=false
|
|
251
290
|
cmd=$(effective_input_value "$cmd_key")
|
|
@@ -327,7 +366,11 @@ prompt_value() {
|
|
|
327
366
|
else
|
|
328
367
|
read -r -p "$key [$current]: " reply
|
|
329
368
|
if [[ -n "$reply" ]]; then
|
|
330
|
-
|
|
369
|
+
if [[ "$key" == *_MODEL ]]; then
|
|
370
|
+
set_value "$key" "$reply $PIN_MARKER"
|
|
371
|
+
else
|
|
372
|
+
set_value "$key" "$reply"
|
|
373
|
+
fi
|
|
331
374
|
fi
|
|
332
375
|
fi
|
|
333
376
|
}
|
|
@@ -368,7 +411,11 @@ fi
|
|
|
368
411
|
|
|
369
412
|
# Explicit command-line overrides have final precedence.
|
|
370
413
|
for ((i=0; i<${#SET_KEYS[@]}; i++)); do
|
|
371
|
-
|
|
414
|
+
if [[ "${SET_KEYS[$i]}" == *_MODEL ]]; then
|
|
415
|
+
set_value "${SET_KEYS[$i]}" "${SET_VALUES[$i]} $PIN_MARKER"
|
|
416
|
+
else
|
|
417
|
+
set_value "${SET_KEYS[$i]}" "${SET_VALUES[$i]}"
|
|
418
|
+
fi
|
|
372
419
|
done
|
|
373
420
|
|
|
374
421
|
enabled_count=0
|
package/scripts/lib/api.sh
CHANGED
|
@@ -249,7 +249,7 @@ build_openai_request() {
|
|
|
249
249
|
build_anthropic_request() {
|
|
250
250
|
local prompt="$1"
|
|
251
251
|
local model="${2:-claude-sonnet-4-6}"
|
|
252
|
-
local max_tokens="${3:-
|
|
252
|
+
local max_tokens="${3:-16384}"
|
|
253
253
|
|
|
254
254
|
jq -n \
|
|
255
255
|
--arg model "$model" \
|
|
@@ -327,7 +327,11 @@ parse_anthropic_response() {
|
|
|
327
327
|
local response="$1"
|
|
328
328
|
|
|
329
329
|
local content
|
|
330
|
-
|
|
330
|
+
# Opus 5 may put a thinking block before the visible text block. Select
|
|
331
|
+
# every text block instead of assuming content[0] is user-visible output.
|
|
332
|
+
content=$(echo "$response" | jq -r \
|
|
333
|
+
'[.content[]? | select(.type == "text") | (.text // "")] | join("\n")' \
|
|
334
|
+
2>/dev/null)
|
|
331
335
|
|
|
332
336
|
if [[ -n "$content" && "$content" != "null" ]]; then
|
|
333
337
|
echo "$content"
|
package/scripts/lib/api_query.sh
CHANGED
|
@@ -94,7 +94,14 @@ run_api_mode_query() {
|
|
|
94
94
|
if [[ -n "$effort" ]]; then
|
|
95
95
|
log_warn "[$consultant_name] $effort_var is not supported on the 'anthropic' wire format and is ignored."
|
|
96
96
|
fi
|
|
97
|
-
|
|
97
|
+
local max_tokens_var api_max_tokens
|
|
98
|
+
max_tokens_var="$(to_upper "$consultant_name")_API_MAX_TOKENS"
|
|
99
|
+
api_max_tokens="${!max_tokens_var:-16384}"
|
|
100
|
+
if ! [[ "$api_max_tokens" =~ ^[1-9][0-9]*$ ]]; then
|
|
101
|
+
log_error "[$consultant_name] $max_tokens_var must be a positive integer (got: $api_max_tokens)"
|
|
102
|
+
return 1
|
|
103
|
+
fi
|
|
104
|
+
request_body=$(build_anthropic_request "$query" "$model" "$api_max_tokens")
|
|
98
105
|
auth_style="anthropic"
|
|
99
106
|
;;
|
|
100
107
|
qwen)
|
|
@@ -134,6 +141,16 @@ run_api_mode_query() {
|
|
|
134
141
|
local raw_response
|
|
135
142
|
raw_response=$(cat "$temp_response")
|
|
136
143
|
|
|
144
|
+
# Anthropic counts adaptive thinking and visible output against the same
|
|
145
|
+
# max_tokens budget. A max_tokens stop is therefore an incomplete answer,
|
|
146
|
+
# not a successful partial response suitable for synthesis.
|
|
147
|
+
if [[ "$api_format" == "anthropic" ]] \
|
|
148
|
+
&& [[ "$(jq -r '.stop_reason // empty' <<<"$raw_response" 2>/dev/null)" == "max_tokens" ]]; then
|
|
149
|
+
log_error "[$consultant_name] API response was truncated at ${api_max_tokens:-the configured} max tokens"
|
|
150
|
+
rm -f "$temp_response"
|
|
151
|
+
return 1
|
|
152
|
+
fi
|
|
153
|
+
|
|
137
154
|
local parsed_content
|
|
138
155
|
case "$api_format" in
|
|
139
156
|
google_ai)
|
package/scripts/lib/common.sh
CHANGED
|
@@ -902,6 +902,7 @@ resolve_response_tokens() {
|
|
|
902
902
|
|
|
903
903
|
# Build metadata JSON for responses
|
|
904
904
|
# Usage: build_response_metadata <latency_ms> <model> [error_msg] [tokens] [source]
|
|
905
|
+
# [tokens_input] [tokens_output] [provider_cost_usd]
|
|
905
906
|
#
|
|
906
907
|
# tokens_used was hardcoded to 0 until v2.25, which made every session's cost
|
|
907
908
|
# report read $0.00 regardless of what was actually spent (calculate_session_cost
|
|
@@ -915,6 +916,7 @@ build_response_metadata() {
|
|
|
915
916
|
local source="${5:-unknown}"
|
|
916
917
|
local tokens_in="${6:-}"
|
|
917
918
|
local tokens_out="${7:-}"
|
|
919
|
+
local provider_cost="${8:-}"
|
|
918
920
|
|
|
919
921
|
jq -n \
|
|
920
922
|
--argjson latency "$latency" \
|
|
@@ -925,9 +927,11 @@ build_response_metadata() {
|
|
|
925
927
|
--arg source "$source" \
|
|
926
928
|
--arg t_in "$tokens_in" \
|
|
927
929
|
--arg t_out "$tokens_out" \
|
|
930
|
+
--arg provider_cost "$provider_cost" \
|
|
928
931
|
'{tokens_used: $tokens, tokens_source: $source, latency_ms: $latency, model_version: $model, timestamp: $timestamp}
|
|
929
932
|
+ (if $t_in != "" then {tokens_input: ($t_in | tonumber)} else {} end)
|
|
930
933
|
+ (if $t_out != "" then {tokens_output: ($t_out | tonumber)} else {} end)
|
|
934
|
+
+ (if $provider_cost != "" then {provider_cost_usd: ($provider_cost | tonumber)} else {} end)
|
|
931
935
|
+ (if $error != "" then {error: $error} else {} end)'
|
|
932
936
|
}
|
|
933
937
|
|
|
@@ -943,15 +947,14 @@ build_structured_response() {
|
|
|
943
947
|
local tokens_source="${7:-unknown}"
|
|
944
948
|
local tokens_in="${8:-}"
|
|
945
949
|
local tokens_out="${9:-}"
|
|
946
|
-
local
|
|
947
|
-
local tokens_out="${9:-}"
|
|
950
|
+
local provider_cost="${10:-}"
|
|
948
951
|
|
|
949
952
|
jq -n \
|
|
950
953
|
--arg consultant "$consultant" \
|
|
951
954
|
--arg model "$model" \
|
|
952
955
|
--arg persona "$persona" \
|
|
953
956
|
--argjson inner "$inner_json" \
|
|
954
|
-
--argjson metadata "$(build_response_metadata "$latency" "$model" "" "$tokens" "$tokens_source" "$tokens_in" "$tokens_out")" \
|
|
957
|
+
--argjson metadata "$(build_response_metadata "$latency" "$model" "" "$tokens" "$tokens_source" "$tokens_in" "$tokens_out" "$provider_cost")" \
|
|
955
958
|
'{consultant: $consultant, model: $model, persona: $persona, response: $inner.response, confidence: $inner.confidence, metadata: $metadata}'
|
|
956
959
|
}
|
|
957
960
|
|
|
@@ -967,13 +970,14 @@ build_fallback_response() {
|
|
|
967
970
|
local tokens_source="${7:-unknown}"
|
|
968
971
|
local tokens_in="${8:-}"
|
|
969
972
|
local tokens_out="${9:-}"
|
|
973
|
+
local provider_cost="${10:-}"
|
|
970
974
|
|
|
971
975
|
jq -n \
|
|
972
976
|
--arg consultant "$consultant" \
|
|
973
977
|
--arg model "$model" \
|
|
974
978
|
--arg persona "$persona" \
|
|
975
979
|
--arg response "$response_text" \
|
|
976
|
-
--argjson metadata "$(build_response_metadata "$latency" "$model" "" "$tokens" "$tokens_source" "$tokens_in" "$tokens_out")" \
|
|
980
|
+
--argjson metadata "$(build_response_metadata "$latency" "$model" "" "$tokens" "$tokens_source" "$tokens_in" "$tokens_out" "$provider_cost")" \
|
|
977
981
|
'{consultant: $consultant, model: $model, persona: $persona,
|
|
978
982
|
response: {summary: "Unstructured response - see detailed", detailed: $response, approach: "unknown", pros: [], cons: [], caveats: ["Unstructured output from consultant"]},
|
|
979
983
|
confidence: {score: 5, reasoning: "Confidence not provided by consultant", uncertainty_factors: ["Non-standard response format"]},
|
package/scripts/lib/costs.sh
CHANGED
|
@@ -306,11 +306,8 @@ _billable_response_files() {
|
|
|
306
306
|
# Calculate total session cost from responses
|
|
307
307
|
# Usage: calculate_session_cost <responses_dir>
|
|
308
308
|
#
|
|
309
|
-
# Prefers
|
|
310
|
-
# (API mode)
|
|
311
|
-
# split to record: consultations are large-context/short-reply, so applying it
|
|
312
|
-
# to a measured total materially overstates cost when output rates exceed
|
|
313
|
-
# input rates.
|
|
309
|
+
# Prefers an exact provider cost when recorded (Claude CLI), then the provider's
|
|
310
|
+
# prompt/completion split (API mode), and only then the legacy 60/40 guess.
|
|
314
311
|
calculate_session_cost() {
|
|
315
312
|
local responses_dir="$1"
|
|
316
313
|
local total_cost=0
|
|
@@ -318,6 +315,13 @@ calculate_session_cost() {
|
|
|
318
315
|
|
|
319
316
|
while IFS= read -r f; do
|
|
320
317
|
[[ -n "$f" ]] || continue
|
|
318
|
+
local provider_cost
|
|
319
|
+
provider_cost=$(jq -r '.metadata.provider_cost_usd // empty' "$f" 2>/dev/null)
|
|
320
|
+
if [[ "$provider_cost" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
|
|
321
|
+
total_cost=$(echo "scale=6; $total_cost + $provider_cost" | bc)
|
|
322
|
+
continue
|
|
323
|
+
fi
|
|
324
|
+
|
|
321
325
|
local model input_tokens output_tokens
|
|
322
326
|
model=$(jq -r '.model // "default"' "$f" 2>/dev/null)
|
|
323
327
|
input_tokens=$(jq -r '.metadata.tokens_input // empty' "$f" 2>/dev/null)
|
|
@@ -338,6 +342,11 @@ calculate_session_cost() {
|
|
|
338
342
|
total_cost=$(echo "scale=6; $total_cost + $cost" | bc)
|
|
339
343
|
done < <(_billable_response_files "$responses_dir")
|
|
340
344
|
|
|
345
|
+
# Provider-reported costs may carry fewer decimal places than locally
|
|
346
|
+
# estimated values. Normalize the public result to the existing 6-place
|
|
347
|
+
# contract without changing its numeric value.
|
|
348
|
+
total_cost=$(echo "scale=6; $total_cost / 1" | bc)
|
|
349
|
+
|
|
341
350
|
# bc drops the leading zero on sub-1 values (".115000"); restore it as
|
|
342
351
|
# estimate_query_cost does, so callers can compare numerically.
|
|
343
352
|
[[ "$total_cost" == .* ]] && total_cost="0$total_cost"
|
|
@@ -351,8 +360,9 @@ calculate_session_cost() {
|
|
|
351
360
|
#
|
|
352
361
|
# Two things can make the number less than it looks:
|
|
353
362
|
# - credit-billed models contribute 0 because they have no per-token price;
|
|
354
|
-
# - CLI-mode consultants
|
|
355
|
-
#
|
|
363
|
+
# - CLI-mode consultants without provider usage have a 4-chars-per-token
|
|
364
|
+
# approximation (metadata.tokens_source = "estimated"). Claude CLI is the
|
|
365
|
+
# exception: its JSON envelope supplies measured usage and exact cost.
|
|
356
366
|
# Both are stated rather than folded silently into a confident-looking total.
|
|
357
367
|
#
|
|
358
368
|
# Rescans the directory rather than reading state left by
|
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
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
# Usage: ./query_claude.sh "question" [context_file] [output_file]
|
|
5
5
|
#
|
|
6
6
|
# Environment variables:
|
|
7
|
-
# CLAUDE_MODEL - Model to use (default: claude-opus-
|
|
7
|
+
# CLAUDE_MODEL - Model to use (default: claude-opus-5)
|
|
8
8
|
# CLAUDE_TIMEOUT - Timeout in seconds (default: 240)
|
|
9
9
|
# CLAUDE_USE_API - Use API mode instead of CLI (default: false)
|
|
10
|
+
# CLAUDE_API_MAX_TOKENS - API thinking + visible-output budget (default: 16384)
|
|
10
11
|
# ANTHROPIC_API_KEY - API key for API mode
|
|
11
12
|
# ENABLE_PERSONA - Enable "The Synthesizer" persona (default: true)
|
|
12
13
|
|
|
@@ -25,7 +26,7 @@ OUTPUT_FILE="${3:-/tmp/claude_response.json}"
|
|
|
25
26
|
ENABLE_PERSONA="${ENABLE_PERSONA:-true}"
|
|
26
27
|
CONSULTANT_NAME="Claude"
|
|
27
28
|
CLAUDE_CMD="${CLAUDE_CMD:-claude}"
|
|
28
|
-
MODEL_USED="${CLAUDE_MODEL:-claude-opus-
|
|
29
|
+
MODEL_USED="${CLAUDE_MODEL:-claude-opus-5}"
|
|
29
30
|
|
|
30
31
|
# --- Build query ---
|
|
31
32
|
FULL_QUERY=$(build_full_query "$QUERY" "$CONTEXT_FILE")
|
|
@@ -69,12 +70,13 @@ else
|
|
|
69
70
|
log_api_mode_status "claude"
|
|
70
71
|
check_command "$CLAUDE_CMD" "Claude CLI" "Visit https://docs.anthropic.com/en/docs/claude-code" || exit 1
|
|
71
72
|
|
|
72
|
-
#
|
|
73
|
+
# JSON output exposes provider-measured usage and cost. In particular,
|
|
74
|
+
# output_tokens includes adaptive thinking that is absent from .result.
|
|
73
75
|
echo "$FULL_QUERY" | run_query \
|
|
74
76
|
"Claude" \
|
|
75
77
|
"$TEMP_OUTPUT" \
|
|
76
78
|
"$CLAUDE_TIMEOUT_SECONDS" \
|
|
77
|
-
"$CLAUDE_CMD" --print --model "$MODEL_USED"
|
|
79
|
+
"$CLAUDE_CMD" --print --model "$MODEL_USED" --output-format json
|
|
78
80
|
|
|
79
81
|
exit_code=$?
|
|
80
82
|
fi
|
|
@@ -88,16 +90,45 @@ PERSONA_NAME=$(get_persona_name "$CONSULTANT_NAME")
|
|
|
88
90
|
|
|
89
91
|
# --- Post-processing: wrap in full schema using shared helpers ---
|
|
90
92
|
if [[ $exit_code -eq 0 && -f "$TEMP_OUTPUT" && -s "$TEMP_OUTPUT" ]]; then
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
PROVIDER_COST=""
|
|
94
|
+
if ! is_api_mode "claude"; then
|
|
95
|
+
CLI_ENVELOPE=$(cat "$TEMP_OUTPUT")
|
|
96
|
+
if ! echo "$CLI_ENVELOPE" | jq -e \
|
|
97
|
+
'.type == "result" and (.result | type == "string")' >/dev/null 2>&1; then
|
|
98
|
+
rm -f "$TEMP_OUTPUT"
|
|
99
|
+
build_error_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" \
|
|
100
|
+
"Claude CLI returned an invalid JSON result envelope" "$LATENCY_MS" > "$OUTPUT_FILE"
|
|
101
|
+
cat "$OUTPUT_FILE"
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
RAW_RESPONSE=$(echo "$CLI_ENVELOPE" | jq -r '.result')
|
|
105
|
+
_TOK_IN=$(echo "$CLI_ENVELOPE" | jq -r \
|
|
106
|
+
'([.modelUsage[]?
|
|
107
|
+
| (.inputTokens // 0)
|
|
108
|
+
+ (.cacheCreationInputTokens // 0)
|
|
109
|
+
+ (.cacheReadInputTokens // 0)] | add)
|
|
110
|
+
// ((.usage.input_tokens // 0)
|
|
111
|
+
+ (.usage.cache_creation_input_tokens // 0)
|
|
112
|
+
+ (.usage.cache_read_input_tokens // 0))')
|
|
113
|
+
_TOK_OUT=$(echo "$CLI_ENVELOPE" | jq -r \
|
|
114
|
+
'([.modelUsage[]? | (.outputTokens // 0)] | add)
|
|
115
|
+
// (.usage.output_tokens // 0)')
|
|
116
|
+
_TOK=$((_TOK_IN + _TOK_OUT))
|
|
117
|
+
_TOK_SRC="measured"
|
|
118
|
+
PROVIDER_COST=$(echo "$CLI_ENVELOPE" | jq -r \
|
|
119
|
+
'[.modelUsage[]?.costUSD | numbers] | add // empty')
|
|
120
|
+
else
|
|
121
|
+
RAW_RESPONSE=$(cat "$TEMP_OUTPUT")
|
|
122
|
+
read -r _TOK _TOK_SRC _TOK_IN _TOK_OUT <<< "$(resolve_response_tokens "$FULL_QUERY" "$RAW_RESPONSE")"
|
|
123
|
+
fi
|
|
93
124
|
clear_api_token_split
|
|
94
125
|
rm -f "$TEMP_OUTPUT"
|
|
95
126
|
|
|
96
127
|
# Try to parse as structured JSON
|
|
97
128
|
if echo "$RAW_RESPONSE" | jq -e '.response.summary' > /dev/null 2>&1; then
|
|
98
|
-
build_structured_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" "$RAW_RESPONSE" "$LATENCY_MS" "$_TOK" "$_TOK_SRC" "$_TOK_IN" "$_TOK_OUT" > "$OUTPUT_FILE"
|
|
129
|
+
build_structured_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" "$RAW_RESPONSE" "$LATENCY_MS" "$_TOK" "$_TOK_SRC" "$_TOK_IN" "$_TOK_OUT" "$PROVIDER_COST" > "$OUTPUT_FILE"
|
|
99
130
|
else
|
|
100
|
-
build_fallback_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" "$RAW_RESPONSE" "$LATENCY_MS" "$_TOK" "$_TOK_SRC" "$_TOK_IN" "$_TOK_OUT" > "$OUTPUT_FILE"
|
|
131
|
+
build_fallback_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" "$RAW_RESPONSE" "$LATENCY_MS" "$_TOK" "$_TOK_SRC" "$_TOK_IN" "$_TOK_OUT" "$PROVIDER_COST" > "$OUTPUT_FILE"
|
|
101
132
|
fi
|
|
102
133
|
else
|
|
103
134
|
rm -f "$TEMP_OUTPUT"
|
package/scripts/query_grok.sh
CHANGED
|
@@ -36,6 +36,9 @@ START_TIME=$(get_timestamp_ms)
|
|
|
36
36
|
TEMP_OUTPUT=$(mktemp)
|
|
37
37
|
GROK_RUNTIME_DIR=""
|
|
38
38
|
TRANSPORT="cli"
|
|
39
|
+
GROK_CLI_VERSION=""
|
|
40
|
+
GROK_CLI_COMPATIBILITY=""
|
|
41
|
+
GROK_SUPPORTS_NO_AUTO_UPDATE=false
|
|
39
42
|
exit_code=1
|
|
40
43
|
|
|
41
44
|
cleanup() {
|
|
@@ -67,6 +70,93 @@ run_grok_api() {
|
|
|
67
70
|
fi
|
|
68
71
|
}
|
|
69
72
|
|
|
73
|
+
observe_grok_cli_version() {
|
|
74
|
+
local version
|
|
75
|
+
version=$("$GROK_CMD" --version 2>/dev/null | head -1 || true)
|
|
76
|
+
if [[ "$version" =~ ^grok[[:space:]]+([^[:space:]]+) ]]; then
|
|
77
|
+
printf '%s\n' "${BASH_REMATCH[1]}"
|
|
78
|
+
elif [[ -n "$version" ]]; then
|
|
79
|
+
printf '%s\n' "$version"
|
|
80
|
+
else
|
|
81
|
+
printf 'unknown\n'
|
|
82
|
+
fi
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
grok_cli_supports_required_interface() {
|
|
86
|
+
local probe_home="$1"
|
|
87
|
+
local probe_grok_home="$2"
|
|
88
|
+
local probe_workspace="$3"
|
|
89
|
+
local help flag
|
|
90
|
+
|
|
91
|
+
help=$(env HOME="$probe_home" GROK_HOME="$probe_grok_home" \
|
|
92
|
+
"$GROK_CMD" --help 2>&1 || true)
|
|
93
|
+
for flag in \
|
|
94
|
+
--prompt-file --model --cwd --output-format --no-plan --no-subagents \
|
|
95
|
+
--no-memory --disable-web-search --max-turns --permission-mode \
|
|
96
|
+
--sandbox --tools --deny --verbatim; do
|
|
97
|
+
grep -q -- "$flag" <<< "$help" || return 1
|
|
98
|
+
done
|
|
99
|
+
grep -Eq '^[[:space:]]+models([[:space:]]|$)' <<< "$help" || return 1
|
|
100
|
+
|
|
101
|
+
# Exercise the complete headless argument surface under --help. This checks
|
|
102
|
+
# parser compatibility without starting a session or sending a prompt.
|
|
103
|
+
env HOME="$probe_home" GROK_HOME="$probe_grok_home" \
|
|
104
|
+
"$GROK_CMD" \
|
|
105
|
+
--prompt-file /dev/null \
|
|
106
|
+
-m "$GROK_MODEL" \
|
|
107
|
+
--cwd "$probe_workspace" \
|
|
108
|
+
--output-format plain \
|
|
109
|
+
--no-plan \
|
|
110
|
+
--no-subagents \
|
|
111
|
+
--no-memory \
|
|
112
|
+
--disable-web-search \
|
|
113
|
+
--max-turns 1 \
|
|
114
|
+
--permission-mode dontAsk \
|
|
115
|
+
--sandbox strict \
|
|
116
|
+
--tools "" \
|
|
117
|
+
--deny Bash \
|
|
118
|
+
--deny Edit \
|
|
119
|
+
--deny Read \
|
|
120
|
+
--deny Grep \
|
|
121
|
+
--deny MCPTool \
|
|
122
|
+
--deny WebFetch \
|
|
123
|
+
--deny WebSearch \
|
|
124
|
+
--verbatim \
|
|
125
|
+
--help >/dev/null 2>&1 || return 1
|
|
126
|
+
|
|
127
|
+
if grep -q -- '--no-auto-update' <<< "$help"; then
|
|
128
|
+
GROK_SUPPORTS_NO_AUTO_UPDATE=true
|
|
129
|
+
else
|
|
130
|
+
GROK_SUPPORTS_NO_AUTO_UPDATE=false
|
|
131
|
+
fi
|
|
132
|
+
GROK_CLI_COMPATIBILITY="capability-probed"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
grok_cli_exposes_requested_model() {
|
|
136
|
+
local isolated_home="$1"
|
|
137
|
+
local isolated_grok_home="$2"
|
|
138
|
+
local models
|
|
139
|
+
|
|
140
|
+
if ! models=$(env HOME="$isolated_home" GROK_HOME="$isolated_grok_home" \
|
|
141
|
+
"$GROK_CMD" models 2>&1); then
|
|
142
|
+
printf '%s\n' "$models"
|
|
143
|
+
return 1
|
|
144
|
+
fi
|
|
145
|
+
grep -Fq "You are logged in with grok.com." <<< "$models" || {
|
|
146
|
+
printf '%s\n' "$models"
|
|
147
|
+
return 1
|
|
148
|
+
}
|
|
149
|
+
awk -v requested="$GROK_MODEL" '
|
|
150
|
+
{
|
|
151
|
+
line = $0
|
|
152
|
+
sub(/^[[:space:]*]+/, "", line)
|
|
153
|
+
split(line, fields, /[[:space:]]+/)
|
|
154
|
+
if (fields[1] == requested) found = 1
|
|
155
|
+
}
|
|
156
|
+
END { exit(found ? 0 : 1) }
|
|
157
|
+
' <<< "$models"
|
|
158
|
+
}
|
|
159
|
+
|
|
70
160
|
grok_cli_is_unavailable() {
|
|
71
161
|
local cli_exit_code="$1"
|
|
72
162
|
local error_file="$2"
|
|
@@ -85,7 +175,7 @@ grok_cli_is_unavailable() {
|
|
|
85
175
|
# model errors, timeouts, empty output, or other post-launch failures here;
|
|
86
176
|
# those must surface instead of silently creating a billable API request.
|
|
87
177
|
grep -Eiq \
|
|
88
|
-
'not authenticated|authentication( is)? required|authentication failed|unauthorized|run .?grok login|please .*log ?in|no (valid )?(credentials|access token)|missing .*credential|token.*expired|(^|[^0-9])401([^0-9]|$)|permission denied|cannot execute|exec format error|no such file or directory|failed to (start|launch|spawn)|could not (start|launch|spawn)' \
|
|
178
|
+
'not authenticated|authentication( is)? required|authentication failed|unauthorized|run .?grok login|please .*log ?in|no (valid )?(credentials|access token)|missing .*credential|token.*expired|(^|[^0-9])401([^0-9]|$)|permission denied|cannot execute|exec format error|no such file or directory|failed to (start|launch|spawn)|could not (start|launch|spawn)|lacks the required capabilities|does not expose the requested model' \
|
|
89
179
|
"$error_file"
|
|
90
180
|
}
|
|
91
181
|
|
|
@@ -128,51 +218,66 @@ else
|
|
|
128
218
|
fi
|
|
129
219
|
fi
|
|
130
220
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
--no-plan
|
|
145
|
-
--no-subagents
|
|
146
|
-
--no-memory
|
|
147
|
-
--disable-web-search
|
|
148
|
-
--max-turns 1
|
|
149
|
-
--permission-mode dontAsk
|
|
150
|
-
--sandbox strict
|
|
151
|
-
--tools ""
|
|
152
|
-
--deny Bash
|
|
153
|
-
--deny Edit
|
|
154
|
-
--deny Read
|
|
155
|
-
--deny Grep
|
|
156
|
-
--deny MCPTool
|
|
157
|
-
--deny WebFetch
|
|
158
|
-
--deny WebSearch
|
|
159
|
-
--verbatim
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
# Newer Grok Build releases document --no-auto-update for automation;
|
|
163
|
-
# keep compatibility with older installed builds that do not expose it.
|
|
164
|
-
if "$GROK_CMD" --help 2>&1 | grep -q -- '--no-auto-update'; then
|
|
165
|
-
GROK_ARGS+=("--no-auto-update")
|
|
166
|
-
fi
|
|
167
|
-
|
|
168
|
-
if run_query \
|
|
169
|
-
"$CONSULTANT_NAME" \
|
|
170
|
-
"$TEMP_OUTPUT" \
|
|
171
|
-
"$GROK_TIMEOUT_SECONDS" \
|
|
172
|
-
"${GROK_ARGS[@]}" </dev/null; then
|
|
173
|
-
exit_code=0
|
|
221
|
+
GROK_CLI_VERSION=$(observe_grok_cli_version)
|
|
222
|
+
if ! grok_cli_supports_required_interface \
|
|
223
|
+
"$isolated_home" "$isolated_grok_home" "$isolated_workspace"; then
|
|
224
|
+
GROK_CLI_COMPATIBILITY="incompatible"
|
|
225
|
+
printf 'Grok Build CLI lacks the required capabilities\n' > "${TEMP_OUTPUT}.err"
|
|
226
|
+
log_warn "[$CONSULTANT_NAME] Grok Build CLI is capability-incompatible (reported version: $GROK_CLI_VERSION)"
|
|
227
|
+
exit_code=69
|
|
228
|
+
elif ! grok_cli_exposes_requested_model \
|
|
229
|
+
"$isolated_home" "$isolated_grok_home" > "${TEMP_OUTPUT}.err"; then
|
|
230
|
+
printf 'Grok Build CLI does not expose the requested model %s\n' \
|
|
231
|
+
"$GROK_MODEL" >> "${TEMP_OUTPUT}.err"
|
|
232
|
+
log_warn "[$CONSULTANT_NAME] Grok Build CLI does not expose $GROK_MODEL"
|
|
233
|
+
exit_code=69
|
|
174
234
|
else
|
|
175
|
-
|
|
235
|
+
# Official Grok Build headless contract:
|
|
236
|
+
# --prompt-file avoids argv size/process-list exposure, -m pins the
|
|
237
|
+
# model, and plain output contains the final assistant text.
|
|
238
|
+
# dontAsk + strict sandbox make every unapproved tool fail closed.
|
|
239
|
+
GROK_ARGS=(
|
|
240
|
+
env
|
|
241
|
+
HOME="$isolated_home"
|
|
242
|
+
GROK_HOME="$isolated_grok_home"
|
|
243
|
+
"$GROK_CMD"
|
|
244
|
+
--prompt-file "$prompt_file"
|
|
245
|
+
-m "$GROK_MODEL"
|
|
246
|
+
--cwd "$isolated_workspace"
|
|
247
|
+
--output-format plain
|
|
248
|
+
--no-plan
|
|
249
|
+
--no-subagents
|
|
250
|
+
--no-memory
|
|
251
|
+
--disable-web-search
|
|
252
|
+
--max-turns 1
|
|
253
|
+
--permission-mode dontAsk
|
|
254
|
+
--sandbox strict
|
|
255
|
+
--tools ""
|
|
256
|
+
--deny Bash
|
|
257
|
+
--deny Edit
|
|
258
|
+
--deny Read
|
|
259
|
+
--deny Grep
|
|
260
|
+
--deny MCPTool
|
|
261
|
+
--deny WebFetch
|
|
262
|
+
--deny WebSearch
|
|
263
|
+
--verbatim
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# --no-auto-update is a useful optional capability, not a version
|
|
267
|
+
# requirement. Compatible older or vendor-custom builds may omit it.
|
|
268
|
+
if [[ "$GROK_SUPPORTS_NO_AUTO_UPDATE" == "true" ]]; then
|
|
269
|
+
GROK_ARGS+=("--no-auto-update")
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
if run_query \
|
|
273
|
+
"$CONSULTANT_NAME" \
|
|
274
|
+
"$TEMP_OUTPUT" \
|
|
275
|
+
"$GROK_TIMEOUT_SECONDS" \
|
|
276
|
+
"${GROK_ARGS[@]}" </dev/null; then
|
|
277
|
+
exit_code=0
|
|
278
|
+
else
|
|
279
|
+
exit_code=$?
|
|
280
|
+
fi
|
|
176
281
|
fi
|
|
177
282
|
else
|
|
178
283
|
log_warn "[$CONSULTANT_NAME] Grok Build CLI not found: $GROK_CMD"
|
|
@@ -214,7 +319,17 @@ fi
|
|
|
214
319
|
# Preserve which route actually answered without changing the shared schema.
|
|
215
320
|
if [[ -s "$OUTPUT_FILE" ]]; then
|
|
216
321
|
response_tmp=$(mktemp)
|
|
217
|
-
if jq --arg transport "$TRANSPORT"
|
|
322
|
+
if jq --arg transport "$TRANSPORT" \
|
|
323
|
+
--arg cli_version "$GROK_CLI_VERSION" \
|
|
324
|
+
--arg cli_compatibility "$GROK_CLI_COMPATIBILITY" '
|
|
325
|
+
.metadata.transport = $transport |
|
|
326
|
+
if $transport == "cli" and $cli_version != "" then
|
|
327
|
+
.metadata.cli_version = $cli_version |
|
|
328
|
+
.metadata.cli_compatibility = $cli_compatibility
|
|
329
|
+
else
|
|
330
|
+
.
|
|
331
|
+
end
|
|
332
|
+
' \
|
|
218
333
|
"$OUTPUT_FILE" > "$response_tmp"; then
|
|
219
334
|
mv "$response_tmp" "$OUTPUT_FILE"
|
|
220
335
|
else
|
package/scripts/query_kimi.sh
CHANGED
|
@@ -40,9 +40,43 @@ START_TIME=$(get_timestamp_ms)
|
|
|
40
40
|
|
|
41
41
|
# --- Execution ---
|
|
42
42
|
TEMP_OUTPUT=$(mktemp)
|
|
43
|
+
KIMI_CLI_VERSION=""
|
|
44
|
+
|
|
45
|
+
observe_kimi_cli_version() {
|
|
46
|
+
local version
|
|
47
|
+
version=$("$KIMI_CMD" --version 2>/dev/null | head -1 || true)
|
|
48
|
+
printf '%s\n' "${version:-unknown}"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
kimi_cli_supports_required_interface() {
|
|
52
|
+
local help providers flag
|
|
53
|
+
|
|
54
|
+
help=$("$KIMI_CMD" --help 2>&1 || true)
|
|
55
|
+
for flag in --model --prompt --output-format; do
|
|
56
|
+
grep -q -- "$flag" <<< "$help" || return 1
|
|
57
|
+
done
|
|
58
|
+
grep -Eq '^[[:space:]]+provider([[:space:]]|$)' <<< "$help" || return 1
|
|
59
|
+
|
|
60
|
+
# Check the complete prompt-mode parser without running a model request.
|
|
61
|
+
"$KIMI_CMD" \
|
|
62
|
+
--model "$KIMI_MODEL" \
|
|
63
|
+
--prompt "" \
|
|
64
|
+
--output-format stream-json \
|
|
65
|
+
--help >/dev/null 2>&1 || return 1
|
|
66
|
+
|
|
67
|
+
providers=$("$KIMI_CMD" provider list --json 2>/dev/null || true)
|
|
68
|
+
jq -e --arg model "$KIMI_MODEL" '
|
|
69
|
+
.models[$model] != null
|
|
70
|
+
' <<< "$providers" >/dev/null 2>&1
|
|
71
|
+
}
|
|
43
72
|
|
|
44
73
|
# Check CLI prerequisite
|
|
45
74
|
check_command "$KIMI_CMD" "Kimi CLI" "curl -L code.kimi.com/install.sh | bash" || exit 1
|
|
75
|
+
KIMI_CLI_VERSION=$(observe_kimi_cli_version)
|
|
76
|
+
kimi_cli_supports_required_interface || {
|
|
77
|
+
log_error "[Kimi] Kimi CLI lacks the required capabilities for model $KIMI_MODEL (reported version: $KIMI_CLI_VERSION)"
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
46
80
|
|
|
47
81
|
# Kimi takes the prompt as the -p argument (it does NOT read stdin). The old
|
|
48
82
|
# `--quiet --input-format text` were NOT real flags on kimi 0.23.6 — commander
|
|
@@ -89,5 +123,17 @@ PERSONA_NAME=$(get_persona_name "$CONSULTANT_NAME")
|
|
|
89
123
|
process_consultant_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" \
|
|
90
124
|
"$TEMP_OUTPUT" "$OUTPUT_FILE" "$exit_code" "$LATENCY_MS" "" "$FULL_QUERY"
|
|
91
125
|
|
|
126
|
+
if [[ -s "$OUTPUT_FILE" ]]; then
|
|
127
|
+
response_tmp=$(mktemp)
|
|
128
|
+
if jq --arg cli_version "$KIMI_CLI_VERSION" '
|
|
129
|
+
.metadata.cli_version = $cli_version |
|
|
130
|
+
.metadata.cli_compatibility = "capability-probed"
|
|
131
|
+
' "$OUTPUT_FILE" > "$response_tmp"; then
|
|
132
|
+
mv "$response_tmp" "$OUTPUT_FILE"
|
|
133
|
+
else
|
|
134
|
+
rm -f "$response_tmp"
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
|
|
92
138
|
cat "$OUTPUT_FILE"
|
|
93
139
|
exit $exit_code
|