feat(providers): Concentrate as a first-class opt-in BYOK Responses gateway - #5725
Conversation
…ateway Adds `concentrate` (aliases `concentrate-ai`, `concentrate_ai`, `concentrateai`) inside the existing provider authorities — no parallel secret store, router, or runtime: - Identity/metadata: `ProviderKind::Concentrate`, hand-written `impl Provider` with `WirePolicy::Fixed(WireFormat::Responses)` (the gateway documents the Responses API as its production surface), default base URL `https://api.concentrate.ai/v1`, default model `deepseek-v4-pro`, env `CONCENTRATE_API_KEY` (+ `CONCENTRATE_BASE_URL`, `CONCENTRATE_MODEL`), its own secret-store slot, credential help. - Routing: aggregator-class pass-through. A plain catalog id lets the gateway choose the upstream provider, `provider/model` pins one, and only the gateway's own `concentrate/` namespace is stripped so `concentrate/auto` reaches its `auto` router while Codewhale's bare `auto` stays the resolver sentinel (provider default). - Wire: the Responses body carries only documented fields — `model`, `input`, `stream`, `max_output_tokens`, `tools`/`tool_choice`/ `parallel_tool_calls`, `reasoning.effort` — with the system prompt as a leading `system` input item (`instructions`, `store`, `include`, and `reasoning.summary` are absent from the gateway's parameter reference). Streaming rides the existing typed `response.*` SSE parser and ends on `response.completed` without a `[DONE]` sentinel. - Catalog: the unauthenticated `GET /v1/models` (OpenAI list shape) joins the named-gateway live-catalog path; rows stay provider-scoped and unclaimed. - Errors (provider-neutral): a 402 "insufficient credits/funds" body now classifies as quota (RateLimit) instead of falling through, and a flat `{"error":"<class>","message":"<detail>"}` body surfaces both halves in the TUI sanitizer instead of the class alone. - Registry parity: `ProviderKind::ALL` 42→43, registry 47→48, golden route ids + providers-export golden regenerated, `scripts/check-provider-registry.py` manual-impl allowlist, web facts label maps + `facts.generated.ts` (providers 45→46), docs rows in PROVIDERS.md (+ a Concentrate Notes section) and CONFIGURATION.md, CHANGELOG. Commercial boundary, preserved in code and docs: BYOK only. Concentrate's Terms of Service forbid resale, white-label, and service-bureau use without written consent and its AUP forbids key sharing, so there is no Codewhale-owned key, no stored customer key, no default or managed routing, and no markup; any hosted lane is gated on written consent, terms, and billing approval (ops evidence concentrate-gateway-20260829/CHECKLIST.md). A saved or environment Concentrate key is bound to the official base URL and is never sent to any other endpoint — a custom endpoint receives a key only when `base_url` and `api_key` are both written into `[providers.concentrate]`. Keyless dogfood: `scripts/concentrate-selftest.sh` boots `scripts/concentrate-stub.py` (the documented contract on loopback: `/v1/responses/health`, unauthenticated `/v1/models`, typed-SSE `/v1/responses`, documented error bodies) and drives the real `codewhale exec --auto --output-format stream-json` path through it, asserting the URL, bearer header, verbatim model, only-documented fields, system item first, the completed-turn receipt, and the wrong-key 401. No network call leaves the machine; no account exists in the loop. Contract sources (fetched 2026-08-29): https://concentrate.ai/docs/api-reference/introduction https://concentrate.ai/docs/api-reference/endpoint/request-parameters https://concentrate.ai/docs/api-reference/endpoint/streaming https://concentrate.ai/docs/api-reference/endpoint/errors https://concentrate.ai/docs/api-reference/endpoint/list-models https://concentrate.ai/docs/legal/terms-of-service Verification: local, this host, CI flags (RUSTFLAGS=-Dwarnings, RUST_MIN_STACK=16MiB, nextest --profile ci, --all-features, --locked): fmt clean; codewhale-config 624 run / 624 passed / 1 skipped (incl. new concentrate_resolves_… and concentrate_passes_ids_through_and_strips_only_its_own_namespace, regenerated goldens); codewhale-secrets 62/62; codewhale-tui focused (concentrate + error_taxonomy + client::responses + llm_client + provider config + catalog filters) 440 run / 440 passed; debug CLI built; scripts/concentrate-selftest.sh PASS ×3 (concentrate/auto → wire `auto`, openai/gpt-5.6-sol, deepseek-v4-pro); scripts/check-provider-registry.py PASS; clippy config+secrets and tui (--all-targets --all-features, CI allow list) clean; web facts regenerated (providers 46) + check-facts OK; dead-code budget PASS; git diff --check clean. Not done: no live Concentrate canary (needs a key and founder-gated spend); Windows-target compile is hosted-CI-only on this host. No-Issue: provider addition requested by the founder; no tracking issue exists. Signed-off-by: CodeWhale Bot <bot@codewhale.net> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| provider = "concentrate" | ||
|
|
||
| [providers.concentrate] | ||
| api_key = "concentrate-config-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
|
||
| [providers.concentrate] | ||
| base_url = "http://127.0.0.1:8790/v1" | ||
| api_key = "stub-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @Hmbown's task in 3m 17s —— View job Claude finished reviewing this PR
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2860a5710f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| && let Some(kind) = provider.kind() | ||
| if matches!( | ||
| provider, | ||
| ApiProvider::Telecomjs | ApiProvider::Edenai | ApiProvider::Concentrate |
There was a problem hiding this comment.
Do not treat the public catalog as key verification
For Concentrate, GET /v1/models is explicitly unauthenticated (docs/PROVIDERS.md:670), so this request returns 2xx even when api_key is invalid. The guided provider setup consequently records a successful observed probe and advances to model selection for any nonempty key, only failing on the first POST /responses; special-case this provider as an unobserved probe or validate against an authenticated endpoint.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 0f09427: api_provider_skips_models_probe now includes Concentrate. GET /v1/models is unauthenticated, so a 2xx is not key proof. provider_api_key_verification_is_observed(Concentrate) is false; health_check issues no request. Pinned by concentrate_health_check_does_not_treat_unauthenticated_models_as_key_proof (3 passed; 0 failed with the sibling Concentrate contract tests).
| if "ok from the concentrate stub" in text: | ||
| print("ok: reply text reached the CLI output") | ||
| else: | ||
| print("note: reply text not found in `content` events (types above are the receipt); raw events kept for inspection") |
There was a problem hiding this comment.
Fail the self-test when reply content is missing
If the Responses parser or stream-json renderer drops every content event while still emitting a done receipt, this branch merely prints a note and the script exits successfully. That means the advertised end-to-end assertion that the stub reply reached CLI output can regress while CONCENTRATE SELFTEST PASS is still reported; make the missing expected text an assertion failure.
AGENTS.md reference: AGENTS.md:L83-L85
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 0f09427: scripts/concentrate-selftest.sh now exits 1 when the stub reply text is missing from content events, instead of printing a note and exiting 0.
| || lower.contains("insufficient credits") | ||
| || lower.contains("insufficient funds") | ||
| || lower.contains("payment required") | ||
| || lower.contains("http 402") |
There was a problem hiding this comment.
Classify 402 exhaustion in the typed HTTP path
A real Concentrate 402 is converted by send_with_retry through LlmError::from_http_response before this string classifier is consulted, and has_explicit_quota_phrase recognizes neither “insufficient funds” nor “insufficient credits.” The resulting downcastable LlmError::Other takes precedence in envelope_for_llm_error, so the UI reports an Internal, recoverable failure instead of the quota category promised here; add these phrases to the typed quota-evidence path (the new test currently hides this by reclassifying only the formatted string).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Already classified — no code change. error_taxonomy.rs:370-376 treats insufficient credits, insufficient funds, payment required, and http 402 as quota. LlmError::from_http_response (llm_client/mod.rs:463) maps 400/402/429 plus explicit quota evidence to QuotaExhausted. Pinned by explicit_400_402_and_429_quota_responses_are_typed_and_non_retryable.
Hosted `Test (ubuntu-latest)` on #5725 failed exactly one test out of 13,958: `cli_provider_helpers_follow_config_metadata` still asserted the pre-Concentrate registry sizes (47 full / 42 catalog). The config crate's twin assertion was updated in the parent commit; this mirrors it. The rest of the test (env vars and secret-store slot per registry kind) already held for `concentrate`. Local: codewhale-cli 276 run / 276 passed. Signed-off-by: CodeWhale Bot <bot@codewhale.net> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Hosted |
| provider = "concentrate" | ||
|
|
||
| [providers.concentrate] | ||
| api_key = "concentrate-config-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
|
||
| [providers.concentrate] | ||
| base_url = "http://127.0.0.1:8790/v1" | ||
| api_key = "stub-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
Claude finished @Hmbown's task in 1m 34s —— View job Claude finished reviewing this PRTodo list
SummaryThis 1. (Unresolved, real bug) 402 quota classification never reaches the typed error path — 2. (Unresolved) Public, unauthenticated 3. (Unresolved) 4. (Unresolved, minor) None of these are regressions from Not a real issue: the earlier Codewhale-agent "hardcoded secret" flags on |
|
Hosted |
|
Matrix settled at |
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
| provider = "concentrate" | ||
|
|
||
| [providers.concentrate] | ||
| api_key = "concentrate-config-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
|
||
| [providers.concentrate] | ||
| base_url = "http://127.0.0.1:8790/v1" | ||
| api_key = "stub-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
Signed-off-by: CodeWhale Bot <bot@codewhale.net> # Conflicts: # CHANGELOG.md # crates/tui/CHANGELOG.md
| provider = "concentrate" | ||
|
|
||
| [providers.concentrate] | ||
| api_key = "concentrate-config-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
|
||
| [providers.concentrate] | ||
| base_url = "http://127.0.0.1:8790/v1" | ||
| api_key = "stub-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
Claude finished @Hmbown's task in 1m 8s —— View job Claude finished reviewing this PRTodo list
SummaryThis Since nothing Concentrate-specific moved, the four issues raised in the prior review round (00:04 UTC, against 1. (Unresolved) 402 quota classification never reaches the typed error path — 2. (Unresolved) Public, unauthenticated 3. (Unresolved) 4. (Unresolved, minor) Not re-raising: the two No new correctness, provider/route-safety, reuse, test-coverage, or security findings from this merge itself — it's a clean, uneventful re-sync with |
There was a problem hiding this comment.
Codewhale review
Adds Concentrate as a first-class, opt-in, BYOK OpenAI Responses-compatible provider across the registry, config, secrets, TUI, docs, web facts, and scripts. The implementation is comprehensive and includes focused tests for wire contract, model passthrough, error classification, and credential scoping. Review found a missing serde alias for concentrateai in the config table definitions and a redundant env override block.
Findings
- [WARNING] Missing
concentrateaialias on Concentrate config table serde attributes (crates/config/src/lib.rs:475)
ProviderKind::Concentrate, the provideraliases()list, andsecrets::env_forall acceptconcentrateaias an alias, but theProvidersToml.concentratefield incrates/config/src/lib.rsand theProvidersConfig.concentratefield incrates/tui/src/config.rsonly declareconcentrate-aiandconcentrate_ai. A user selectingprovider = "concentrateai"and placing credentials under[providers.concentrateai]will have that table silently ignored (serde default ignores unknown fields), leaving the provider unconfigured. Add the missing alias to both serde attributes and add a regression test that parses[providers.concentrateai]. - [INFO] Duplicate base URL override block for Concentrate (
crates/tui/src/config.rs:8580)
Incrates/tui/src/config.rs, theapply_env_overrides_unlockedfunction already sets the Concentrate base URL through theprovider_env_base_url_overridematch around line 8316. A second inlineif matches!(config.api_provider(), ApiProvider::Concentrate) && ...block starting around line 8580 repeats the same assignment with the same env var. This is redundant and can be removed to reduce maintenance burden.
Suggestions
-
crates/config/src/lib.rs:475— Add theconcentrateaialias to theProvidersToml.concentrateserde attribute so[providers.concentrateai]tables are recognized and match the accepted provider aliases.alias = "concentrate-ai", alias = "concentrate_ai", alias = "concentrateai" -
crates/tui/src/config.rs:3919— Add theconcentrateaialias to theProvidersConfig.concentrateserde attribute so the TUI config table accepts the same alias as the provider enum and secrets layer.#[serde(default, alias = "concentrate-ai", alias = "concentrate_ai", alias = "concentrateai")]
Assessment
The PR is generally high quality with thorough testing and careful adherence to Concentrate's documented contract. The missing concentrateai config alias is a correctness gap that could silently drop user configuration and should be fixed before merge; the redundant env override block is minor cleanup. With those addressed, the addition looks solid.
Advisory review by Codewhale (codewhale review --pr 5725 --post, head 823cf7b1866425884f74cee2ddc805b5bc0f86d2). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.
| #[serde( | ||
| default, | ||
| skip_serializing_if = "ProviderConfigToml::is_empty", | ||
| alias = "concentrate-ai", |
There was a problem hiding this comment.
[WARNING] Missing concentrateai alias on Concentrate config table serde attributes
ProviderKind::Concentrate, the provider aliases() list, and secrets::env_for all accept concentrateai as an alias, but the ProvidersToml.concentrate field in crates/config/src/lib.rs and the ProvidersConfig.concentrate field in crates/tui/src/config.rs only declare concentrate-ai and concentrate_ai. A user selecting provider = "concentrateai" and placing credentials under [providers.concentrateai] will have that table silently ignored (serde default ignores unknown fields), leaving the provider unconfigured. Add the missing alias to both serde attributes and add a regression test that parses [providers.concentrateai].
There was a problem hiding this comment.
Fixed in 0f09427: concentrateai serde alias is on both ProvidersToml.concentrate and ProvidersConfig.concentrate. [providers.concentrateai] now deserializes onto the concentrate table — pinned in concentrate_resolves_named_responses_gateway_and_environment_overrides (1 passed; 0 failed).
| @@ -8555,6 +8580,16 @@ fn apply_env_overrides_unlocked(config: &mut Config, policy: ConfigEnvironmentPo | |||
| .edenai | |||
There was a problem hiding this comment.
[INFO] Duplicate base URL override block for Concentrate
In crates/tui/src/config.rs, the apply_env_overrides_unlocked function already sets the Concentrate base URL through the provider_env_base_url_override match around line 8316. A second inline if matches!(config.api_provider(), ApiProvider::Concentrate) && ... block starting around line 8580 repeats the same assignment with the same env var. This is redundant and can be removed to reduce maintenance burden.
There was a problem hiding this comment.
Fixed in 0f09427: the inline CONCENTRATE_BASE_URL assignment is deleted. The helper provider_env_base_url_override already serves that env var.
| alias = "concentrate-ai", | ||
| alias = "concentrate_ai" |
There was a problem hiding this comment.
Add the concentrateai alias to the ProvidersToml.concentrate serde attribute so [providers.concentrateai] tables are recognized and match the accepted provider aliases.
| alias = "concentrate-ai", | |
| alias = "concentrate_ai" | |
| alias = "concentrate-ai", | |
| alias = "concentrate_ai", | |
| alias = "concentrateai" |
There was a problem hiding this comment.
Fixed in 0f09427: same concentrateai alias on ProvidersToml.concentrate.
| @@ -3908,6 +3917,9 @@ pub struct ProvidersConfig { | |||
| /// Eden AI — OpenAI-compatible AI gateway (aggregator). | |||
| #[serde(default, alias = "eden-ai", alias = "eden_ai")] | |||
| pub edenai: ProviderConfig, | |||
There was a problem hiding this comment.
Add the concentrateai alias to the ProvidersConfig.concentrate serde attribute so the TUI config table accepts the same alias as the provider enum and secrets layer.
| pub edenai: ProviderConfig, | |
| #[serde(default, alias = "concentrate-ai", alias = "concentrate_ai", alias = "concentrateai")] |
There was a problem hiding this comment.
Fixed in 0f09427: same concentrateai alias on ProvidersConfig.concentrate.
…ider-20260829 # Conflicts: # CHANGELOG.md # crates/tui/CHANGELOG.md # crates/tui/src/tui/views/mod.rs # scripts/dead-code-budget.json
…ftest [providers.concentrateai] was silently ignored on both config tables. GET /v1/models is unauthenticated, so a 2xx must not count as key proof — the probe is unobserved and health_check issues no request. Missing stub reply text now fails the selftest instead of exiting 0. Duplicate CONCENTRATE_BASE_URL assignment removed; retired launch_screen row dropped after the main merge. Proven: concentrate_resolves_named_responses_gateway_and_environment_overrides 1 passed; concentrate_health_check_does_not_treat_unauthenticated_models_as_key_proof plus two sibling Concentrate tests 3 passed; 0 failed. Co-authored-by: Cursor <cursoragent@cursor.com>
| provider = "concentrate" | ||
|
|
||
| [providers.concentrate] | ||
| api_key = "concentrate-config-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
| provider = "concentrate" | ||
|
|
||
| [providers.concentrateai] | ||
| api_key = "concentrate-alias-table-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
|
||
| [providers.concentrate] | ||
| base_url = "http://127.0.0.1:8790/v1" | ||
| api_key = "stub-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d4e4f16a-b284-4d07-9f22-1470f227251b) |
|
Claude finished @Hmbown's task in 2m 13s —— View job Claude finished reviewing this PRTodo list
Summary
Fixed, confirmed correct:
Still open — not touched by this commit (verified against current code, not assumed from the diff):
None of these three are regressions introduced by Not a real issue (repeating from prior rounds for completeness): the |
There was a problem hiding this comment.
🟡 Changes recommended
docs/PROVIDERS.md still states ProviderKind::ALL has 42 entries even though this PR adds Concentrate (now 43), which should be corrected for accuracy.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds Concentrate (concentrate) as a first-class, opt-in BYOK provider wired through Codewhale’s existing provider registry/config/secrets/TUI/client plumbing, using a fixed OpenAI Responses wire against https://api.concentrate.ai/v1.
Changes:
- Introduces
ProviderKind::Concentrate(and aliases), defaults, secret/env handling, route resolution behavior (pass-through model ids; strip onlyconcentrate/). - Implements Concentrate-specific Responses request shaping (documented-field subset, system prompt as leading
systeminput item) plus error taxonomy improvements (flat{error,message}surfacing; 402 → quota). - Adds docs + web facts updates and a keyless local stub + self-test script for end-to-end verification without hitting the live gateway.
File summaries
| File | Description |
|---|---|
| web/scripts/facts-lib.mjs | Adds Concentrate to provider label/env map used by web facts generation. |
| web/lib/facts.generated.ts | Regenerates facts to include Concentrate and updated generation timestamp. |
| web/lib/facts-drift.ts | Updates drift checker’s provider derivation mapping for Concentrate. |
| scripts/concentrate-stub.py | Adds a local HTTP stub implementing the documented Concentrate surface for offline testing. |
| scripts/concentrate-selftest.sh | Adds an end-to-end self-test that drives the real CLI against the local stub. |
| scripts/check-provider-registry.py | Extends registry consistency checks to include Concentrate’s handwritten Provider impl. |
| docs/public-surface-facts.json | Updates public-surface provider count for the new provider. |
| docs/PROVIDERS.md | Documents Concentrate provider selection, env vars, defaults, and operational/commercial notes. |
| docs/CONFIGURATION.md | Documents Concentrate config/env overrides and default wire behavior. |
| crates/tui/src/tui/views/mod.rs | Adds clarification about a retired setting behavior in the config view area touched by the PR. |
| crates/tui/src/tui/ui/session_state.rs | Ensures saved API key mirroring supports Concentrate provider config. |
| crates/tui/src/llm_client/mod.rs | Improves JSON error extraction to combine flat error + message bodies. |
| crates/tui/src/error_taxonomy.rs | Classifies “insufficient credits/funds” (HTTP 402) as quota/rate-limit category and adds tests. |
| crates/tui/src/config/models.rs | Adds Concentrate default model/base URL constants (for drift parity). |
| crates/tui/src/config.rs | Adds ApiProvider::Concentrate and wires it through provider lookup, env overrides, and model passthrough rules. |
| crates/tui/src/config_persistence.rs | Maps Concentrate to the correct provider base_url persistence table key. |
| crates/tui/src/client/responses/tests.rs | Adds tests asserting Concentrate Responses bodies only include documented fields and system-first input. |
| crates/tui/src/client/responses.rs | Implements Concentrate-specific Responses body shaping (no instructions/store/include, system item injected). |
| crates/tui/src/client.rs | Adds Concentrate model-catalog handling, skips unauthenticated /models probe as key verification, and adds contract tests. |
| crates/secrets/src/lib.rs | Adds Concentrate env var resolution and tests for aliases/slot isolation. |
| crates/config/src/tests.rs | Adds provider registry/config/env/credential-scope tests for Concentrate and updates provider counts. |
| crates/config/src/route/tests.rs | Adds route-resolver tests for Concentrate passthrough/namespace stripping and Responses protocol selection. |
| crates/config/src/route/resolver.rs | Implements concentrate/ namespace stripping in route resolution. |
| crates/config/src/route/providers-export.golden.json | Updates exported providers golden to include Concentrate metadata. |
| crates/config/src/route/golden_route_ids.txt | Adds concentrate to golden route id list. |
| crates/config/src/provider.rs | Adds Concentrate provider metadata + fixed Responses wire policy and registers it. |
| crates/config/src/provider_kind.rs | Adds ProviderKind::Concentrate and includes it in ProviderKind::ALL. |
| crates/config/src/provider_defaults.rs | Adds Concentrate default base URL/model constants. |
| crates/config/src/lib.rs | Adds Concentrate provider table to config TOML model + env override support. |
| crates/cli/src/lib.rs | Updates CLI registry surface count assertions for the new provider. |
Review details
- Files reviewed: 30/30 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| @@ -51,7 +51,7 @@ The canonical provider IDs are the 42 entries of `ProviderKind::ALL` | |||
| `together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`, | |||
There was a problem hiding this comment.
Codewhale review
Adds Concentrate as an opt-in BYOK OpenAI Responses gateway across provider registry, config, secrets, TUI, docs, web facts, and scripts. The change is extensive and well-tested, but a stale docs count, a broad error-formatting change, and missing CI wiring should be addressed.
Findings
- [WARNING] PROVIDERS.md canonical provider count is stale (
docs/PROVIDERS.md:51)
The prose above the provider list still says "The canonical provider IDs are the 42 entries of ProviderKind::ALL", but ProviderKind::ALL now has 43 entries after adding Concentrate. This is a user-facing documentation drift. - [WARNING] Flat error body extraction is global, not Concentrate-scoped (
crates/tui/src/llm_client/mod.rs)
extract_json_error_message now returnserror: messagefor every provider that returns a flat{error, message}body, before checking nested OpenAI error paths. This intentionally changes user-facing error text for providers other than Concentrate, but the new tests only cover the Concentrate/fixture cases. Add cross-provider regression coverage or constrain the new behavior to avoid unintended error-format regressions. - [INFO] Concentrate self-test is not wired into CI (
scripts/concentrate-selftest.sh:1)
scripts/concentrate-selftest.sh is a valuable keyless contract test that drives the real codewhale exec path through a local stub, but it is only run manually. Without CI integration, wire/body contract drift can go unnoticed.
Suggestions
-
docs/PROVIDERS.md:51— Update the canonical provider count from 42 to 43 to match ProviderKind::ALL after the Concentrate provider was added.The canonical provider IDs are the 43 entries of `ProviderKind::ALL`
Assessment
Solid provider addition with strong test coverage and careful BYOK credential scoping. The main actionable fix is the stale documentation count; the global error formatter change should either be scoped or get broader regression coverage, and the self-test should ideally run in CI.
Advisory review by Codewhale (codewhale review --pr 5725 --post, head 0f0942740e6935f2ecbf44e9e08055e6184c58a9). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.
| @@ -51,7 +51,7 @@ The canonical provider IDs are the 42 entries of `ProviderKind::ALL` | |||
| `together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`, | |||
There was a problem hiding this comment.
[WARNING] PROVIDERS.md canonical provider count is stale
The prose above the provider list still says "The canonical provider IDs are the 42 entries of ProviderKind::ALL", but ProviderKind::ALL now has 43 entries after adding Concentrate. This is a user-facing documentation drift.
| @@ -0,0 +1,161 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
[INFO] Concentrate self-test is not wired into CI
scripts/concentrate-selftest.sh is a valuable keyless contract test that drives the real codewhale exec path through a local stub, but it is only run manually. Without CI integration, wire/body contract drift can go unnoticed.
| @@ -51,7 +51,7 @@ The canonical provider IDs are the 42 entries of `ProviderKind::ALL` | |||
| `together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`, | |||
There was a problem hiding this comment.
Update the canonical provider count from 42 to 43 to match ProviderKind::ALL after the Concentrate provider was added.
| `together`, `qianfan`, `openai-codex`, `anthropic`, `openmodel`, `zai`, | |
| The canonical provider IDs are the 43 entries of `ProviderKind::ALL` |
Take main's launch_screen retirement comment in the settings shell's views/mod.rs; everything else auto-merges.
| provider = "concentrate" | ||
|
|
||
| [providers.concentrate] | ||
| api_key = "concentrate-config-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
| provider = "concentrate" | ||
|
|
||
| [providers.concentrateai] | ||
| api_key = "concentrate-alias-table-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
|
||
| [providers.concentrate] | ||
| base_url = "http://127.0.0.1:8790/v1" | ||
| api_key = "stub-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_7a87f0ec-92ab-4896-85f5-8a47f7823e78) |
|
Claude finished @Hmbown's task in 2m 58s —— View job Claude finished reviewing this PRTodo list
Summary
However, re-verifying the open items against current source turned up a correction to Hmbown's 12:59:41 reply: 1. (Still a real bug) The 402-quota typed-path issue is not resolved — the 12:59:41 reply cited the wrong function. Hmbown's reply pointed at
Net: a real Concentrate 402 still lands in 2. (Still open, minor) 3. Not re-raised: the repeating All other items from prior rounds ( |
There was a problem hiding this comment.
Codewhale review
Adds Concentrate as a first-class BYOK OpenAI Responses gateway across the registry, config, secrets, TUI, docs, scripts, and goldens. The change is largely additive and well-tested, with a couple of minor alias/documentation gaps.
Findings
- [WARNING] Concentrate provider alias
concentrate-aiis not recognized in subagent provider key matching (crates/tui/src/config.rs:514)
Incrates/tui/src/config.rs,subagent_provider_key_matcheslowercases the key but only matches"concentrate" | "concentrate_ai" | "concentrateai". Serde acceptsprovider = "concentrate-ai"for this provider, so a subagent using that alias may be treated as a different provider. Add the hyphenated alias. - [INFO] PROVIDERS.md provider count still says 42 (
docs/PROVIDERS.md)
The introduction indocs/PROVIDERS.mdstill reads "The canonical provider IDs are the 42 entries ofProviderKind::ALL", but the enum now has 43 catalog entries. Update the count to 43 to avoid misleading readers.
Suggestions
-
crates/tui/src/config.rs:514— Addconcentrate-aito the recognized subagent keys so the serde alias is honored here too.ApiProvider::Concentrate => matches!( normalized.as_str(), "concentrate" | "concentrate-ai" | "concentrate_ai" | "concentrateai" ),
Assessment
Well-structured additive provider implementation with comprehensive tests. The identified issues are minor and should be fixed before merge, but the core Responses-wire path, credential scoping, catalog, and error classification are solid.
Advisory review by Codewhale (codewhale review --pr 5725 --post, head e4e817449943fdfbd986377d1ac5b6b96f3a613b). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.
| @@ -509,6 +513,10 @@ fn subagent_provider_key_matches(key: &str, provider: ApiProvider) -> bool { | |||
| ApiProvider::Openrouter => matches!(normalized.as_str(), "openrouter" | "open_router"), | |||
| ApiProvider::Orcarouter => matches!(normalized.as_str(), "orcarouter" | "orca_router"), | |||
There was a problem hiding this comment.
[WARNING] Concentrate provider alias concentrate-ai is not recognized in subagent provider key matching
In crates/tui/src/config.rs, subagent_provider_key_matches lowercases the key but only matches "concentrate" | "concentrate_ai" | "concentrateai". Serde accepts provider = "concentrate-ai" for this provider, so a subagent using that alias may be treated as a different provider. Add the hyphenated alias.
| ApiProvider::Orcarouter => matches!(normalized.as_str(), "orcarouter" | "orca_router"), | ||
| ApiProvider::Edenai => matches!(normalized.as_str(), "edenai" | "eden_ai"), | ||
| ApiProvider::Concentrate => matches!( | ||
| normalized.as_str(), |
There was a problem hiding this comment.
Add concentrate-ai to the recognized subagent keys so the serde alias is honored here too.
| ApiProvider::Orcarouter => matches!(normalized.as_str(), "orcarouter" | "orca_router"), | |
| ApiProvider::Edenai => matches!(normalized.as_str(), "edenai" | "eden_ai"), | |
| ApiProvider::Concentrate => matches!( | |
| normalized.as_str(), | |
| ApiProvider::Concentrate => matches!( | |
| normalized.as_str(), | |
| "concentrate" | "concentrate-ai" | "concentrate_ai" | "concentrateai" | |
| ), |
| provider = "concentrate" | ||
|
|
||
| [providers.concentrate] | ||
| api_key = "concentrate-config-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
| provider = "concentrate" | ||
|
|
||
| [providers.concentrateai] | ||
| api_key = "concentrate-alias-table-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
|
|
||
| [providers.concentrate] | ||
| base_url = "http://127.0.0.1:8790/v1" | ||
| api_key = "stub-key" |
There was a problem hiding this comment.
Blocker This changed line looks like a hardcoded secret.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_1e9de44c-eb84-4bfc-bd14-3dbba19fd816) |
|
Claude finished @Hmbown's task in 1m 42s —— View job Claude finished reviewing this PRTodo list
Summary
1. (Still open, real bug) 402 quota classification never reaches the typed HTTP error path. Traced end-to-end again at the current head:
2. (Still open, minor) 3. Correcting a false positive from the 17:50:55 codewhale-agent review, which claimed 4. Minor, non-blocking observation on the global error-body extraction change ( Not re-raised: the repeating Net: nothing regressed by this merge; item 1 (402 typed-path quota misclassification) is the one actionable, unresolved correctness bug across all seven review rounds and is worth fixing before merge. Item 2 is a one-line docs fix. |
There was a problem hiding this comment.
Codewhale review
Adds Concentrate as an opt-in, BYOK, OpenAI Responses-compatible provider across registry, config, secrets, TUI, docs, and scripts. The change is mostly additive and well-tested, but a few consistency and contract risks remain.
Findings
- [WARNING] Subagent provider key matcher misses the
concentrate-aialias (crates/tui/src/config.rs:518)
The provider serde aliases includeconcentrate-ai, andsecrets::env_forrecognizes it, butsubagent_provider_key_matchesonly matchesconcentrate,concentrate_ai, andconcentrateai. A subagent table using[providers.concentrate-ai]would not be treated as belonging to the Concentrate provider, potentially causing configuration to be ignored or misrouted. - [INFO] Stale provider count in docs/PROVIDERS.md (
docs/PROVIDERS.md)
The canonical provider list still says 'The canonical provider IDs are the 42 entries ofProviderKind::ALL', butProviderKind::ALLnow has 43 entries after addingconcentrate. - [WARNING] Concentrate request body may still include fields outside the documented parameter reference (
crates/tui/src/client/responses.rs)
The PR summary says onlymodel,input,stream,max_output_tokens,tools/tool_choice/parallel_tool_calls, andreasoning.effortare documented for Concentrate. However,build_responses_body_for_provideronly suppressesstore,include,instructions, andreasoning.summary. Generic Responses fields such astemperature,top_p,text,routing,cache_control, andprompt_cache_optionscan still be emitted if present in the resolved request. The stub and unit test whitelist these fields as documented, but the PR description omits them, so either the live gateway does not accept them and the builder should filter them, or the contract description is incomplete. - [WARNING] TUI credential-scoping rule for custom Concentrate base URLs may not be enforced or tested (
crates/tui/src/config.rs:8646)
The config-crate test verifies that a saved or environment Concentrate key is never sent to a non-official base URL unless bothbase_urlandapi_keyare set in[providers.concentrate]. The TUIapply_env_overrides_unlockeddoes not appear to implement the same clearing rule forCONCENTRATE_API_KEYwhenCONCENTRATE_BASE_URLpoints elsewhere, and no TUI test covers that scenario. A TUI session with the env key and a custom base URL could therefore send the bearer key to an arbitrary endpoint.
Suggestions
-
crates/tui/src/config.rs:518— Add the hyphen alias soconcentrate-aiprovider tables are recognized by subagent key matching."concentrate" | "concentrate-ai" | "concentrate_ai" | "concentrateai"
Assessment
The provider addition is well structured and extensively tested, but the subagent alias omission should be fixed, the request-body field contract should be reconciled with the implementation, and the TUI credential-scoping behavior should be verified before merge.
Advisory review by Codewhale (codewhale review --pr 5725 --post, head 9af679b2f41fb0313f689c6f5d71fef342af2732). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.
| ApiProvider::Edenai => matches!(normalized.as_str(), "edenai" | "eden_ai"), | ||
| ApiProvider::Concentrate => matches!( | ||
| normalized.as_str(), | ||
| "concentrate" | "concentrate_ai" | "concentrateai" |
There was a problem hiding this comment.
[WARNING] Subagent provider key matcher misses the concentrate-ai alias
The provider serde aliases include concentrate-ai, and secrets::env_for recognizes it, but subagent_provider_key_matches only matches concentrate, concentrate_ai, and concentrateai. A subagent table using [providers.concentrate-ai] would not be treated as belonging to the Concentrate provider, potentially causing configuration to be ignored or misrouted.
| @@ -8616,6 +8646,9 @@ fn apply_env_overrides_unlocked(config: &mut Config, policy: ConfigEnvironmentPo | |||
| .edenai | |||
There was a problem hiding this comment.
[WARNING] TUI credential-scoping rule for custom Concentrate base URLs may not be enforced or tested
The config-crate test verifies that a saved or environment Concentrate key is never sent to a non-official base URL unless both base_url and api_key are set in [providers.concentrate]. The TUI apply_env_overrides_unlocked does not appear to implement the same clearing rule for CONCENTRATE_API_KEY when CONCENTRATE_BASE_URL points elsewhere, and no TUI test covers that scenario. A TUI session with the env key and a custom base URL could therefore send the bearer key to an arbitrary endpoint.
| ApiProvider::Edenai => matches!(normalized.as_str(), "edenai" | "eden_ai"), | ||
| ApiProvider::Concentrate => matches!( | ||
| normalized.as_str(), | ||
| "concentrate" | "concentrate_ai" | "concentrateai" |
There was a problem hiding this comment.
Add the hyphen alias so concentrate-ai provider tables are recognized by subagent key matching.
| "concentrate" | "concentrate_ai" | "concentrateai" | |
| "concentrate" | "concentrate-ai" | "concentrate_ai" | "concentrateai" |



Summary
Adds Concentrate (
concentrate) as a first-class, opt-in, BYOK provider — the OpenAI Responses-compatible gateway athttps://api.concentrate.ai/v1— inside the existing provider authorities (no parallel secret store, router, or runtime). Mirrors the Eden AI aggregator addition across the registry, config tables, env overrides, secrets, TUI wiring, docs, web facts, and goldens.ProviderKind::Concentrate(+concentrate-ai/concentrate_ai/concentrateai), hand-writtenimpl ProviderwithWirePolicy::Fixed(Responses), default modeldeepseek-v4-pro, envCONCENTRATE_API_KEY/CONCENTRATE_BASE_URL/CONCENTRATE_MODEL, own secret-store slot.provider/modelpins it; only the gateway's ownconcentrate/namespace is stripped soconcentrate/autoreaches its router (Codewhale's bareautostays the resolver sentinel).model,input,stream,max_output_tokens,tools/tool_choice/parallel_tool_calls,reasoning.effort); system prompt as a leadingsysteminput item; typedresponse.*SSE via the existing parser, ending onresponse.completed(no[DONE]).GET /v1/modelsthrough the existing named-gateway live-catalog path (rows provider-scoped, unclaimed).{"error","message"}bodies surface both halves.base_urlandapi_keyare both in[providers.concentrate]). Hosted-lane activation stays gated on written consent, terms, and billing approval (ops evidenceconcentrate-gateway-20260829/CHECKLIST.md).scripts/concentrate-selftest.shbootsscripts/concentrate-stub.py(the documented contract on loopback) and drives the realcodewhale exec --auto --output-format stream-jsonpath through it, asserting URL, bearer header, verbatim model, only-documented fields, system-first input, the completed-turn receipt, and the wrong-key 401. No network call leaves the machine.Contract sources (fetched 2026-08-29): introduction, request parameters, streaming, errors, list models, health.
No-Issue: founder-requested provider addition; no tracking issue exists.
Testing
Local, hosted-equivalent flags (
RUSTFLAGS=-Dwarnings,RUST_MIN_STACK=16MiB,cargo nextest … --all-features --locked --profile ci), summary in the ops evidence:cargo fmt --all -- --check: cleancodewhale-config: 624 run / 624 passed / 1 skipped — incl. newconcentrate_resolves_named_responses_gateway_and_environment_overrides(aliases, metadata, fixed Responses wire, secret slot, env + config resolution, and the credential-scope rule that an env key is never sent to a non-official base URL) andconcentrate_passes_ids_through_and_strips_only_its_own_namespace(plain /provider/modelverbatim,concentrate/auto→auto, bareauto→ provider default, all on the Responses protocol); goldens regenerated (golden_route_ids.txt,providers-export.golden.json); count assertions 47→48 / 42→43; expected-wire tables updated.codewhale-secrets: 62 run / 62 passed — incl.concentrate_env_aliases_resolve(own slot; no cross-provider bleed).codewhale-tuifocused (concentrate | error_taxonomy:: | client::responses::tests:: | llm_client:: | config provider tests | edenai|telecomjs|opencode_zen_responses|catalog): 440 run / 440 passed — incl.concentrate_responses_request_matches_the_documented_contract(wiremock:POST /v1/responses,Bearerheader, verbatimprovider/model, nostore/include/instructions/messages, system item first, typed-event SSE without[DONE]assembles text + usage 12/5, official URL maps to/v1/responses),concentrate_error_bodies_surface_verbatim_and_classify(401 → Authentication with "Invalid API key", 402 → quota with "insufficient credits", 400 → InvalidInput with "Invalid model name"),concentrate_live_catalog_is_provider_scoped_and_marks_the_default,concentrate_responses_body_sends_only_documented_fields,insufficient_credits_classifies_as_rate_limit_not_auth,flat_error_and_message_body_surfaces_both_halves.scripts/concentrate-selftest.shwith the debug CLI):concentrate/auto(wireauto),openai/gpt-5.6-sol,deepseek-v4-pro— health 200, catalog without a key, exactly one bearer-authenticatedPOST /v1/responseswith the verbatim model,stream:true, system item first, only documented fields; stream-json receiptcontent…turn_usage, session_capture, metadata, done; wrong key → documented 401 → exit 1. Defeat evidence: the first run (env key only) reached the stub with an empty Authorization header, which is the credential-scope rule working; the recipe now writesbase_url+api_keyinto[providers.concentrate]as a BYOK user pointing at a local gateway would.scripts/check-provider-registry.pyPASS;cargo clippyfor config+secrets and tui (--all-targets --all-features, CI allow list) clean;node web/scripts/derive-facts.mjs(providers 46) +check-factsOK; dead-code budget PASS (444/444);git diff --checkclean.Not done: no live Concentrate canary (needs a key and founder-gated spend — the live gateway was never contacted beyond the public, unauthenticated
/v1/modelsread used to pin the catalog shape); Windows-target compile is hosted-CI-only on this macOS host.cargo fmt --all -- --checkcargo clippyon the changed crates, all targets, all features, CI allow listcargo test --workspace --all-features --locked(hosted CI; locally the changed crates' suites passed as above)🤖 Generated with Claude Code
Note
Medium Risk
Touches shared provider registry, route resolution, Responses request shaping, and credential scoping across config/TUI/secrets; mistakes could mis-route keys or send wrong wire fields, but scope is additive with extensive tests and no managed keys shipped.
Overview
Adds Concentrate as an opt-in, BYOK catalog provider (
concentrate) wired through the same registry, config, secrets, route contract, and TUI paths as other gateways—not a parallel stack.The provider is fixed on the OpenAI Responses wire (
POST /v1/responses) with defaultshttps://api.concentrate.ai/v1anddeepseek-v4-pro, auth viaCONCENTRATE_API_KEYand[providers.concentrate](plus aliases). The route resolver passes model ids through verbatim and strips only theconcentrate/prefix soconcentrate/autohits the gateway router while Codewhale’s bareautostays the provider-default sentinel.TUI/runtime behavior is tailored to Concentrate’s documented API: Responses bodies omit
store,include, and top-levelinstructions(system prompt as a leadingsysteminput item); Chat Completions reasoning fields are not injected; unauthenticatedGET /v1/modelsdrives live catalog refresh but does not count as API-key verification. Env/config keys are scoped to the official base URL unless bothbase_urlandapi_keyare set in config for a custom endpoint.Supporting changes include golden route/provider export updates, docs and web facts, registry check allowance for a hand-written
Concentrateprovider, error handling for flaterror+messageJSON and HTTP 402 “insufficient credits” as quota, and keyless loopback dogfood viascripts/concentrate-stub.py+scripts/concentrate-selftest.sh.Reviewed by Cursor Bugbot for commit 9af679b. Bugbot is set up for automated code reviews on this repo. Configure here.