Commit graph

1126 commits

Author SHA1 Message Date
Gabriel Bogdanovsky
2709d4d31d
fix(openai): image generation fails with ChatGPT OAuth when the plan does not offer gpt-6-astra (#158895)
Fixes #158894.

OpenAI image generation over the Codex OAuth route no longer fails outright when the account cannot use the default Responses chat model. On a confirmed model-unavailable 400 (structured HTTP 400 with the exact evidenced error detail), it retries with the configured OpenAI chat models. Other 400s do not retry, and a working default is unchanged. The match lives in the OpenAI plugin; no public SDK surface is added.

Proof: fake OAuth profile, built CLI and a localhost Responses endpoint. main produces no image; this PR retries the configured fallback model and produces a PNG. Regression tests (including request-id-suffixed errors through the real HTTP normalizer) fail before and pass after.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-10-01 19:46:57 +08:00
Nicolas Rodriguez Sande
a338ea108c
fix(anthropic): recognize sdk-ts entrypoint in Claude session catalog (#143248)
* fix(anthropic): recognize sdk-ts entrypoint in Claude session catalog

Claude Code's bidirectional stream-json subprocess mode (used by the
anthropic plugin to serve adopted web sessions) writes transcript
lines tagged entrypoint: "sdk-ts". CLI_ENTRYPOINTS only recognized
"cli" and "sdk-cli", so discovery treated the first sdk-ts line as an
unrecognized entrypoint and stopped scanning the file, making the
session permanently invisible in the catalog even though its
transcript was otherwise valid.

Add "sdk-ts" to CLI_ENTRYPOINTS alongside the existing values so these
sessions are discovered, listed, and readable like any other Claude
CLI session.

* docs(anthropic): document sdk-ts as a recognized catalog entrypoint

Keep the Claude session catalog discovery docs in sync with the
CLI_ENTRYPOINTS fix: bidirectional stream-json sessions are now listed
alongside interactive and headless CLI sessions.

* docs(anthropic): scope sdk-ts discovery to Gateway reader

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-10-01 18:27:16 +08:00
Ayaan Zaidi
504d2905d6
fix(models): apply downloaded catalogs without a Gateway restart (#158000)
Related: #140086, #141885, #156535, #157838

## What Problem This Solves

A running Gateway doesn't see a newly downloaded hosted model catalog until it restarts. This PR publishes each accepted catalog through the existing prepared-runtime owner, without a restart. Model rows and their prices switch together as one generation, which keeps the invariant from #140086.

## User Impact

- Compatible downloads are adopted at the Gateway's background catalog check, or after an explicit `models.list` refresh. That refresh returns the currently accepted rows right away and runs adoption afterward.
- A turn admitted on catalog N keeps N's rows and prices until it finishes. New turns use N+1. Rows and prices are never mixed.
- A concurrent auth or config publication no longer postpones adoption to the next scheduled check (up to 6 h). Adoption waits for that publication to settle, then retries, up to 3 attempts.
- An owner whose build failed or timed out ends the adoption instead of waiting on unbounded work. Gateway shutdown cancels an adoption that is still preparing.
- Malformed, schema-invalid, too-new (`minVersion`) and older catalogs are rejected, and the previously accepted catalog stays in use.
- Changing `models.catalogRefresh.url` no longer needs a restart: the previous source's catalog stops applying, and the mirror's catalog is adopted at the next catalog check.

**Bad-catalog exposure:** with live apply, a *valid but wrong* published catalog reaches running Gateways at their next catalog check (at most every 6 h) or on the next explicit `models.list` refresh. It no longer waits for a restart. Recovery uses existing mechanisms only:
- Republish a corrected catalog with a newer `generatedAt`; Gateways adopt it the same way.
- Operators can set `models.catalogRefresh.enabled: false`, which withdraws remote rows and prices without a restart (covered by the Gateway integration test).

This PR adds no new kill switch, config option or env knob.

### Compatibility

No config keys, defaults, types, validation, stored rows, protocol or SDK contracts change. The only config-surface change is the `models.catalogRefresh.url` help text, which drops the stale "Changes apply after a Gateway restart" sentence, and its regenerated config-doc baseline hash. Existing configs validate unchanged and need no Doctor migration (maintainer confirmation: https://github.com/openclaw/openclaw/pull/158000#issuecomment-5913462783). Startup behavior is unchanged. Upgrade impact for existing installs: an accepted download activates at the next catalog check instead of the next restart.

## Why This Change Was Made

- `prepared-model-runtime.configured-refresh.ts` builds a complete candidate generation of the configured owners under the new catalog. One serialized commit then publishes rows, the accepted bundle, the pricing context and the reply-dispatch projection together.
- Adoption re-reads the stored catalog until the config it read under is still current, so a stale caller can't cancel a current adoption.
- Each preparation attempt has its own abort signal. A config advance restarts only the attempt; a newer catalog or shutdown ends the whole adoption, including pricing preparation.
- Between attempts, adoption waits only on publication gates: a pending replacement or an owner's pending publication.
- Adopted owners install the same plugin-retirement recovery as configured publication (#161267). After commit, a lost Gateway plugin loan republishes them through the normal recovery. Before commit, it restarts the adoption attempt, and the commit refuses any candidate whose plugin generation retired.

### Why downloads were restart-only, and what this keeps

Restart-only activation was a mechanism, not the goal. #140086 chose it to stop rows and prices from different catalog versions mixing, and #157838 was merged as "the prerequisite for applying new remote catalogs without a Gateway restart (rows and prices must switch together)". This PR is that follow-up. Every requirement those PRs set still holds:

| Original requirement | Source | How it holds here |
|---|---|---|
| Rows and prices from one catalog version; never mixed across reloads or new requests | #140086 | One serialized commit publishes owners, bundle, pricing context and dispatch; pricing contexts are keyed by the exact accepted catalog. Integration test: new rows appear only with new prices |
| Admitted work keeps its pair | #140086, #157838 | Runs carry their plugin generation's catalog; usage operations capture one pricing context. Integration test and live proof: the in-flight turn keeps the old price |
| Startup absence is a real state (no downloaded rows without prices) | #140086 | Absence → catalog goes through the same atomic commit; overlay absence tests unchanged |
| Worker replacement inherits the host's accepted pair, not a later download | #140086 | The commit updates the inherited pair; later workers and a worker-exit recovery keep it (overlay and integration tests) |
| Current enablement and source URL still gate eligibility | #140086, #156535 | Checked on every read and before adoption, including the default-install v1 fallback; disablement withdraws rows and prices together (integration test) |
| Bad or superseded downloads never replace the active pair | #140086, #141885 | Compatibility, `minVersion`, revision and `generatedAt` checks; stale reads can't cancel a current adoption (regression test) |
| Failed catalog checks retry at the remaining fresh interval, not a full TTL | #141885 | Unchanged scheduler behavior; the deleted notice test's retry case is restored for failed adoption (fails if the retry falls back to the full TTL) |
| Operators learn when a downloaded catalog is not yet active | #141885 | No longer needed: downloads activate at the next check. The restart notice and its tests are removed; `models refresh` says when a running Gateway applies the update |
| Billing-route prices switch with their rows | #156535 | `upstreamPricing` and `providerPricing` are part of the accepted catalog pair |

## Evidence

**Regressions.** Each fails with its fix reverted and passes with it:
- *Retries a scheduled adoption when its pending auth owner settles.* Runs through the real Gateway update scheduler. Reverted, it logs `remote model catalog check superseded; deferred to the next check`.
- *Does not let a read under a superseded config cancel the current adoption.* Reverted, both calls end `superseded`.
- *Ends adoption instead of joining a timed-out owner build.* Reverted, adoption never settles.
- *Does not hold Gateway shutdown on an adoption's pricing preparation.* Reverted, shutdown waits on the held preparation until the test times out.
- *Recovers adopted owners when their borrowed Gateway plugin retires after commit / before commit.* Without the recovery, both fail: `Prepared model runtime plugin generation retired` and `prepared reply dispatch runtime owner was not published`.
- *Uses the remaining stored TTL after a fresh startup check when adoption fails.* With the retry reverted to the full TTL, the second check doesn't run.

**Suites:**

| Suite | Result |
|---|---|
| `prepared-model-runtime.remote-publication.test.ts` | 10/10 |
| Gateway integration (`models-list.remote-catalog`) | v1 and v2 pass. Config and auth churn during preparation end `published` on the settled owners. Also covers retained admitted runs, rejected and stale bundles, worker replacement and disablement |
| `prepared-model-runtime*`, `server-plugin-reload*`, `update-startup`, and all PR-touched test files | pass |
| `tsgo:core`, all `tsgo:test:src` shards | pass |
| oxlint and oxfmt on changed files; `config:docs:check`, `config:schema:check`; max-lines, assertion-safety and test-timeout-race ratchets | pass |

Tests wait on owned completion signals (`withinTest`), not wall-clock deadlines.

**Live proof** on an isolated Gateway built from `cb8c9197fd` (no provider mocks). Later commits add plugin-retirement recovery for adopted owners, covered by the regression tests above, and rebases onto `main`. It used a real OpenAI key through `openai/gpt-4.1-mini`, and the build stamp was set before the real catalog's publication date. A client polled `models.list` back to back over one WebSocket for the whole run (1023 polls, no errors). One Gateway process (PID unchanged) and no restart:
1. The stored catalog was seeded with an older revision of the real `catalog.openclaw.ai` v2 catalog: generated 2 days earlier, `gpt-4.1-mini` priced ×10, plus one extra kimi row. `models.list` listed the extra row, and a turn priced **$4.00 / $16.00 per M** input/output.
2. `openclaw models refresh` downloaded the real catalog (`updated`, 1039 models). The listed rows didn't change for the next 7.1 s, and a turn in that window still priced **$4.00 / $16.00 per M**: a download stays inactive until the Gateway adopts it.
3. A long turn was admitted on the older catalog, then `models.list {refresh:true}` returned the older rows (extra kimi row still listed) and started adoption. The new catalog was visible 0.9 s later, while the long turn was still running: the extra kimi row was gone.
4. The in-flight turn finished at **$4.00 / $16.00 per M** (older rows and prices). The next turn priced **$0.40 / $1.60 per M** (real catalog).
5. `models.catalogRefresh.url` was moved to a local mirror of the real catalog through `config.patch`, and the mirror's catalog was adopted without a restart. The mirror then served malformed JSON: `models refresh` failed with `SyntaxError`, the model list was unchanged, and the next turn still priced $0.40 / $1.60 per M.

**Model picker during republication (also on `main`).** Right after the new generation commits, `models.list` shows the new generation's configured and static rows until its full catalog loads, then the full list. In the live run this lasted 109 ms. The same poller against a `main` build shows the same window after a `models.*` config reload (20 → 6 → 16 rows for about 350 ms), so this PR adds a new trigger for an existing behavior. It doesn't change it. The short list comes from the new generation, so rows and prices stay paired.

**Published-driver upgrade cells.** Candidate tarball built from a fresh clone at `6f682c7944` with the canonical Docker packaging script and no build-time overrides. sha256 `46d881a0…ef0ad`; embedded commit `6f682c7944`, version 2026.9.7. `6f682c7944` already includes the shutdown-cancellation and pricing-deadline commits. The current head the current head differs from it only by rebases onto `main`: `main` had moved the scheduled catalog check into `update-startup-catalog.ts`, and this PR's adoption call moved there unchanged (`git range-diff` shows no other production change; a `remoteCatalog: null` test-fixture field moved to main's relocated `cli-compaction.test-support.ts`).

| Driver → candidate | Scenario | Result |
|---|---|---|
| `openclaw@2026.9.6` | base | passed (930 s); updater outcome success, no recovery |
| `openclaw@2026.9.6` | plugin-deps-cleanup | passed (923 s); updater outcome success, no recovery |
| `openclaw@2026.9.7` (latest) | base | passed (813 s); updater outcome success, no recovery |

In every cell:
- Migration, post-Doctor config validation, survival, plugin-dependency cleanup and runtime-deps repair checks passed.
- The candidate Gateway logged ready, then its catalog check fetched and saved the hosted catalog about 0.2 s after starting. The hosted catalog is older than the candidate's build stamp, so adoption ends `unchanged`, which isn't logged. After a 300 s settlement window, `/readyz` (`ready:true`, nothing failing) and a Gateway `status` RPC passed, and the Gateway shut down cleanly.
- To show a logged terminal outcome, each cell was repeated with a loopback mirror serving a newer copy of the same catalog. Each check logged `remote model catalog applied` about 0.26 s after it started, followed by `/readyz` and `status` passing.

Not run: `openclaw@2026.9.7` plugin-deps-cleanup. At the previous head it failed inside the 2026.9.7 driver's retained-runtime verification (`Retained runtime entry does not reference its inventoried file: dist/a2ui-…mjs`), identically for a merge-base control package with none of this PR's commits.

Harness note for the 2026.9.7 cell: unchanged, the upgrade harness can't run against 2026.9.7. It seeds the retired `tools.toolSearch {mode:"code"}` setting, which 2026.9.7 rejects. The 2026.9.7 cell used the harness's existing Tool Search "absent" mode, a one-line local change that skips only that seed and its check. The 2026.9.6 cells used the unchanged harness.

**CI:** fully green on the final head ([run 36818367970](https://github.com/openclaw/openclaw/actions/runs/36818367970)). Earlier heads hit failures that reproduce on `main` in code this PR doesn't touch: `update-cli.target-schema` (main reproduction: https://github.com/openclaw/openclaw/pull/158000#issuecomment-5913464830), the type-suppression inventory, the Windows partition owner test and the Windows backup-rename test. Main has since fixed the last three. Config compatibility confirmation: https://github.com/openclaw/openclaw/pull/158000#issuecomment-5913462783.

No overlap with Pash/Sarah changes.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-10-01 13:27:55 +08:00
Sarah Fortune
7dc4a94490
docs: give Agents API its own onboarding guide (#162255)
Summary:
- Moves Agents API onboarding into a dedicated plugin guide and updates navigation, provider references, the plugin README, and release links.

Automerge notes:
- PR branch already contained follow-up commit before automerge: docs: address Agents API onboarding review

Validation:
- ClawSweeper review passed for head af284a9707b38cc6b72094559a4b14ffdac83019.
- Required merge gates passed before the squash merge.

Prepared head SHA: af284a9707b38cc6b72094559a4b14ffdac83019
Review: https://github.com/openclaw/openclaw/pull/162255#issuecomment-5922939944

Co-authored-by: Sarah Fortune <sjf@openai.com>
Approved-by: sjf-oa
2026-10-01 02:54:25 +00:00
Sarah Fortune
aa5ad42c0d
docs: clarify Agents API setup and hosted VM support (#162158)
Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-09-30 14:56:30 -07:00
RoboClaw
ddb870a8b5
fix: Daybreak hides supported xhigh and max reasoning (#162024)
Restore supported xhigh and max reasoning choices for Daybreak Blue and Red. Preserve requested aliases in Responses and honor declared account capabilities and Ultra opt-outs in the OpenAI plugin.

Cover capability selection, model materialization, discovery boundaries, and final Responses payloads with focused regressions. Authenticated provider observations and exact-head CI confirm the repaired contract.

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
2026-09-30 12:35:41 -07:00
RoboClaw
858993c1a4
feat(openai): support GPT-6.1 Sol (#161400)
* feat(openai): support GPT-6.1 Sol

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* feat(openai): support GPT-6.1 Sol

Worked on by:
- @steipete

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: 970e2aab-1efa-4534-be78-7b6ec08717b5

* fix(openai): preserve GPT-6.1 Sol request capabilities

Preserve mandatory reasoning through subscription discovery and configured Responses requests. Extract existing catalog readers and discovery coverage to respect file-size ratchets.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(openai): narrow discovered model request fixture

Require the discovered row before converting catalog modalities and optional context metadata to the typed chat runtime model.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

---------

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
2026-09-29 18:37:41 -07:00
Ayaan Zaidi
be271241b0
fix(providers): Vercel AI Gateway and Perplexity requests do not identify OpenClaw (#161018)
## What Problem This Solves

Fixes: requests to vendors that document app attribution do not identify OpenClaw. Vercel AI Gateway requests carry no attribution. Perplexity requests send an OpenRouter-style `HTTP-Referer`/`X-Title` pair that Perplexity does not document, rather than the integration header Perplexity does document.

## User Impact

User impact: OpenClaw requests are attributed wherever the vendor documents how:

- **Vercel AI Gateway** (`ai-gateway.vercel.sh`, any provider id): `HTTP-Referer: https://openclaw.ai`, `X-Title: OpenClaw`.
- **Perplexity web search** on `api.perplexity.ai`: `X-Pplx-Integration: openclaw/<version>`.
- **Perplexity web search through OpenRouter**: OpenRouter's standard OpenClaw attribution, replacing the separate "OpenClaw Web Search" title.

Custom proxy base URLs still get no attribution headers.

## Why This Change Was Made

The provider attribution policy stays the single owner. It gains Vercel AI Gateway and Perplexity entries, each with its docs URL and the vendor's own wording:
- Vercel: https://vercel.com/docs/ai-gateway/ecosystem/app-attribution ("AI Gateway reads two request headers when present: http-referer … x-title").
- Perplexity: https://docs.perplexity.ai/docs/getting-started/integrations/opencode (`X-Pplx-Integration: <client>/<version>`).

Vercel is selected by a new `vercel-ai-gateway` endpoint class, the same way OpenRouter is. The class is declared in the plugin manifest and mirrored into the official external provider catalog, so classification still works when the external plugin is not installed. The class exists only for attribution: catalog models routed through the gateway stay route-supported exactly as before, when the URL counted as a custom proxy.

Vercel AI Gateway uses the Anthropic Messages transport, which merges the policy's attribution headers since #160956, so the new policy entry is all it needs.

The Perplexity plugin now builds its headers through the public `resolveProviderRequestHeaders` SDK helper instead of hardcoding them. The isolated session stream forwards any documented (non-hidden) attribution the policy selects, not only OpenRouter's.

Not added:
- **Cohere**: `X-Client-Name` is documented for the native `api.cohere.com` chat API, but OpenClaw's chat path is the compatibility API on `api.cohere.ai`, where the header is not documented.
- **Kilo** `X-KiloCode-Version`: no verified documentation, and the existing Kilo header is duplicated between core and the plugin.
- **Requesty**: OpenClaw has no Requesty provider.
- **No documented attribution**: Cloudflare, Together, Fireworks, Groq, DeepInfra, Mistral, Anthropic, DeepSeek, Z.AI, Hugging Face, Moonshot, Venice, Chutes, Novita, LiteLLM and local runtimes publish none.

## Evidence

Final-head re-proof (rebased on main after #160956, built `dist` at `b179ec195bd`; the final head `d9ff8b2e136` adds only a test fixture type fix and a rebase onto main), `OPENCLAW_TELEMETRY=0`, same capture preload:
- OpenRouter bundled provider, `api: anthropic-messages`, `agent --local` with a real key → `POST openrouter.ai/api/v1/messages` with Referer, `X-OpenRouter-Title: OpenClaw`, `personal-agent,cli-agent`; reply `pong`; generation `gen-1790672764-Foux01snwfakVvYjdiD6` has `app_id` 2725608.
- `agent --local --model vercel-ai-gateway/anthropic/claude-haiku-4.5` (dummy key) → `POST ai-gateway.vercel.sh/v1/messages` with `http-referer: https://openclaw.ai`, `x-title: OpenClaw` (401, as expected).
- `openclaw infer web search --provider perplexity` (dummy key) → `POST api.perplexity.ai/search` with `x-pplx-integration: openclaw/2026.9.6` and no Referer/Title (401, as expected).

Earlier runs on the stacked branch:

Every run used `OPENCLAW_TELEMETRY=0`. Outgoing headers were captured by a throwaway `--import` preload (not committed) on undici's `undici:request:create` diagnostics channel. Base is #160956 (`55824fae493`).

Live OpenRouter runs, built `dist`, `openclaw agent --local` (OpenRouter key; `app_id` from `GET /api/v1/generation`):

| Route | Side | Headers | Generation | `app_id` |
| --- | --- | --- | --- | --- |
| custom id, `api: anthropic-messages`, `https://openrouter.ai/api` | before | none | `gen-1790662480-iGGLpF0HKH4hIBH36Ozu` | **null** |
| same | after | Referer, Title, `personal-agent,cli-agent` | `gen-1790662519-qK0CW0cAUacmTzrNU44D` | 2725608 |
| provider `openrouter` (chat completions) | after | same | `gen-1790662565-tQGEYs8oaJtonKqQU1hM` | 2725608 |
| Perplexity web search runtime via OpenRouter | before | Referer, `X-Title: OpenClaw Web Search` | `gen-1790659036-FuZzYNv2uvXAzytZSdd9` | 2725608 |
| same | after | Referer, `X-OpenRouter-Title: OpenClaw`, categories | `gen-1790659067-7vfkXWHIVn2LIcZQWXp8` | 2725608 |

Header capture only; no live credential was available for these vendors, so the requests got 401 with a dummy key. That is a live-credit gap: attribution could not be confirmed on the vendors' dashboards.

| Path | Before | After |
| --- | --- | --- |
| `agent --local`, `vercel-ai-gateway/anthropic/claude-haiku-4.5` → `POST ai-gateway.vercel.sh/v1/messages` | no attribution | `http-referer: https://openclaw.ai`, `x-title: OpenClaw` |
| `agent --local`, custom provider id with `baseUrl: https://ai-gateway.vercel.sh` | no attribution | same |
| Perplexity web search, `PERPLEXITY_API_KEY` → `POST api.perplexity.ai/search` | Referer + `X-Title: OpenClaw Web Search` | `x-pplx-integration: openclaw/2026.9.6` |
| Perplexity web search, direct key with a model override → `POST api.perplexity.ai/chat/completions` | same as above | `x-pplx-integration: openclaw/2026.9.6` |

Tests and checks:
- Targeted tests pass: provider-attribution, official-external-provider-endpoints (manifest/catalog mirror), provider-request-config, sessions/sdk, perplexity, vercel-ai-gateway, anthropic transport, and the new `model.configured-overrides.test.ts`.
- The route-support test fails without the fix.
- Focused wall times on the final head: `provider-attribution.test.ts` 33 tests 4.9s; `model.configured-overrides.test.ts` 1 test 39.6s cold (95% transform, the test itself runs in milliseconds).
- `pnpm tsgo:core` and `pnpm tsgo:extensions` pass; the `core.test.agents-other` tsgo shard passes on the final head.
- A fresh reviewer found that the new endpoint class dropped route support for Anthropic catalog models routed through the gateway. I reproduced it with the real metadata snapshot (supported `false`, previously `true`), fixed it in `8d86624a53`, and the re-review came back with no findings.

CI note: required ci-gate was red only from failures proven unrelated to this change; evidence: https://github.com/openclaw/openclaw/pull/161018#issuecomment-5888230678

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-29 18:27:32 +08:00
Ayaan Zaidi
cec7fe73dc
fix(openrouter): custom OpenRouter provider ids send no app attribution (#160956)
## What Problem This Solves

Fixes: OpenRouter requests carry inconsistent or missing app attribution. A custom provider id pointed at `https://openrouter.ai/api/v1` sends no attribution on normal agent turns. The bundled `openrouter` provider sends seven `X-OpenRouter-Categories` values, but OpenRouter honors at most two recognized categories per request and silently drops the rest. Isolated session streams sent a different set (`cli-agent` only) and only when install telemetry was enabled.

## User Impact

User impact: every OpenRouter request OpenClaw makes, whether through the bundled `openrouter` provider or a custom provider id whose `baseUrl` is OpenRouter, now sends the same attribution headers: `HTTP-Referer: https://openclaw.ai`, `X-OpenRouter-Title: OpenClaw` and `X-OpenRouter-Categories: personal-agent,cli-agent`. OpenRouter attribution no longer depends on the install telemetry setting. Custom proxy base URLs still get no OpenRouter attribution headers.

## Why This Change Was Made

The provider attribution policy is now the single owner of the OpenRouter header set. It selects OpenRouter attribution from the endpoint class (`openrouter.ai`), whatever the provider id, which matches how NVIDIA and Google attribution already work. The OpenRouter plugin stream wrapper and its image, video, speech and transcription request builders no longer add their own `HTTP-Referer`/`X-OpenRouter-Title` copies; those paths already go through the policy, which overrides the copies. The Anthropic Messages transport now merges the policy's headers the same way the OpenAI transports do, so removing the plugin wrapper's copies does not drop attribution on OpenRouter `anthropic-messages` requests. The isolated session stream reads the policy's header set as well.

## Evidence

Live runs used the built `dist` of each side (baseline `c34e838e49`, candidate `55824fae49`; the final head only adds the Anthropic Messages transport merge): `node openclaw.mjs agent --local --agent main --model <ref> --thinking off --json -m 'Reply with exactly: pong'` with a temp `OPENCLAW_HOME`/`OPENCLAW_STATE_DIR` and `OPENCLAW_TELEMETRY=0`. A throwaway `--import` preload (not committed) logged outgoing openrouter.ai request headers from undici's `undici:request:create` diagnostics channel. `app_id` comes from `GET /api/v1/generation?id=…`. Every run replied `pong`.

| Path | Side | Categories sent | Generation | `app_id` |
| --- | --- | --- | --- | --- |
| `agent --local`, provider `openrouter` | before | 7 values | `gen-1790655350-bnebR7u8xxS31Gnarann` | 2725608 |
| `agent --local`, provider `openrouter` | after | `personal-agent,cli-agent` | `gen-1790655479-medQeaj13NAK7rNePlW3` | 2725608 |
| `agent --local`, custom id `or`, `baseUrl: https://openrouter.ai/api/v1` | before | no attribution headers at all | `gen-1790655642-fykl8xuEgdCMP93lVYZK` | **null** (origin empty) |
| `agent --local`, custom id `or`, `baseUrl: https://openrouter.ai/api/v1` | after | `personal-agent,cli-agent` (+ Referer, Title) | `gen-1790655522-AadAufRx6Z1cPfLE3l6f` | 2725608 |

Isolated session stream (`createAgentSession` in `src/agents/sessions/sdk.ts`). A scratch script (not committed) sent one real request through `session.agent.streamFn`. Normal authenticated `agent --local` turns switch to the boundary-aware transport, so this stream only serves session-custom fall-through turns and SDK consumers; the script is how to reach it. The run used `api: anthropic-messages` at `https://openrouter.ai/api`, because that transport does not consult the policy and the session stream's headers are the only attribution:

| Side | `OPENCLAW_TELEMETRY` | Headers sent | Generation | `app_id` |
| --- | --- | --- | --- | --- |
| before | 0 | none | `gen-1790653951-OqOd5GB2eQtIoNpOvRNn` | **null** |
| before | 1 | Categories `cli-agent` | `gen-1790654009-mlLEe1AAfy0bfD9SyYih` | 2725608 |
| after | 0 | Referer, Title, `personal-agent,cli-agent` | `gen-1790653981-lchRBOjuKaAFhbUNaZfu` | 2725608 |
| after (custom id `or`, openai-completions) | 0 | Referer, Title, `personal-agent,cli-agent` | `gen-1790654058-P0DK1953pmZI88hZ71Jt` | 2725608 |

Bundled `openrouter` provider on `api: anthropic-messages` (`baseUrl: https://openrouter.ai/api`, model `anthropic/claude-haiku-4.5`), final head `53a26ec6e64`, same `agent --local` command with `OPENCLAW_TELEMETRY=0`: the request to `/api/v1/messages` carried `HTTP-Referer: https://openclaw.ai`, `X-OpenRouter-Title: OpenClaw`, `X-OpenRouter-Categories: personal-agent,cli-agent`; reply `pong`; generation `gen-1790669051-ibr3il50EvrfeHXM55tT` reports `app_id` 2725608. This is the path that previously got Referer/Title only from the plugin wrapper.

OpenRouter plugin media paths. A scratch harness (not committed) called the real speech `synthesize`, `listOpenRouterVideoModelCatalog`, image `generateImage` and audio `transcribeAudio` with a dummy key and captured the outgoing headers. Before this change, all four already sent the policy's header set, so the removed plugin literals were being overridden. After it, all four send `HTTP-Referer: https://openclaw.ai`, `X-OpenRouter-Title: OpenClaw` and `X-OpenRouter-Categories: personal-agent,cli-agent` to `/audio/speech`, `/videos/models`, `/images` and `/audio/transcriptions`.

Tests and checks:
- `node scripts/run-vitest.mjs` on provider-attribution, provider-request-config, stream-wrappers/proxy, sessions/sdk, and openrouter index/image/media/speech tests: all pass. The new custom-provider-id case in `provider-attribution.test.ts` fails on the baseline (`attributionHeaders` undefined).
- Focused local wall times on the final head (`pnpm test <files>`): `provider-attribution.test.ts` 31 tests 3.9s, `provider-request-config.test.ts` 36 tests 3.0s, `stream-wrappers/proxy.test.ts` 15 tests 5.1s, `sessions/sdk.test.ts` 37 tests 16.4s, `extensions/openrouter/` 18 files 285 tests 17.5s, `packages/ai/.../anthropic-transport-stream.test.ts` 144 tests 1.4s. The changed cases themselves each run in under 200ms.
- `pnpm tsgo:core` and `pnpm tsgo:extensions` pass.

Unchanged on purpose: the Cloudflare `User-Agent: openclaw` branch in the same session-stream helper still depends on install telemetry. It is not part of the attribution policy owner, and nothing requires changing it here.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-29 16:51:33 +08:00
Peter Steinberger
6d9efe2f5e
fix(setup): validate LM Studio and llama.cpp server URLs inline (#160720)
* fix(lmstudio): validate setup URLs before discovery

Reject malformed interactive endpoints in the provider-owned prompt instead
of sending Control UI and CLI operators into an unreachable-server retry.
Retain the authored draft and the existing HTTP(S) host-shorthand support.
Validate before the permissive path/default normalization can hide a missing
host. Reuse the existing host normalizer; no config or SDK surface changes.

The registered setup-entry regression fails against the original validator.
Real Gateway/Chromium proof rejects the invalid draft inline, then configures
the corrected local stub endpoint and sends chat through the selected model.
Owner and sibling coverage: 115 tests, 13.804 seconds wall; new case 2 ms.
Production delta +19 lines, tests +28, docs +3. The added code owns recoverable
prompt validation; existing prompt types move rather than being duplicated.

* fix(llama-cpp): validate server URLs in setup

Run the existing endpoint parser inside the provider-owned URL validator so
Control UI and CLI users can correct an invalid URL without losing setup.
Previously the parser threw after submission, closing the editable prompt
with a raw Invalid URL error. Preserve host shorthand and the existing
HTTP(S) and embedded-credential rules; no config or SDK surface changes.

The setup-entry regression fails against the original nonempty validator.
Real Gateway/Chromium proof keeps the draft with actionable inline guidance,
then activates the corrected local stub endpoint and sends chat through it.
Owner and sibling coverage: 115 tests, 13.804 seconds wall; new case 2 ms.
Production delta +10 lines, tests +29, docs +4. Growth converts the existing
throwing parser into a recoverable prompt result without duplicating policy.

* fix(lmstudio): reject credential-bearing setup URLs inline

* test(setup): use credential-free embedded-user URLs in validation fixtures

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-09-28 21:36:22 -07:00
Peter Steinberger
673a44e9bc
feat(anthropic): support Claude Sonnet 5.5 (#160847)
Add API and Claude CLI catalog entries with the published 1M/128K limits and
Sonnet 5 pricing, roll the bare `sonnet` alias to Sonnet 5.5 (`sonnet-5` stays
pinned), and give the model its own contract: `/think off` sends Anthropic's
`between_tools` setting instead of the rejected `disabled`, forced tool choices
relax to `auto`, retained thinking is prefix-bound with append-only runtime
context, a session moving from Sonnet 5 or Claude 4.x onto Sonnet 5.5 keeps its
reasoning while Opus 5, Fable, and Mythos reasoning is dropped, and direct
API-key requests opt into the safety refusal fallback to Sonnet 5. Fable 5.1 no
longer replays Sonnet 5.5 thinking, matching Anthropic's live replay behavior.
2026-09-29 02:49:52 +00:00
Bobby Bones
31dde0505c
feat: speak Gemini dialogues with two voices (#157465)
Add opt-in two-speaker Gemini 3.8 dialogue synthesis in the Google speech plugin. Preserve configured speaker turns, ordinary colon-prefixed prose, compact labels, and the existing single-voice default.

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
2026-09-28 18:22:36 -07:00
Bobby Bones
fd5a7172de
feat: speak with Gemini 3.8 Flash TTS (#157331)
Add opt-in Gemini 3.8 Flash and Flash-Lite TTS in the existing Google speech plugin.

Keep the implicit Gemini 3.1 default and older GenerateContent path unchanged. Send Gemini 3.8 transcript text separately from delivery style and structured speaker metadata through stateless Interactions requests, then reuse the existing PCM, WAV, and voice-note outputs.

Preserve the shared Google auth and response helpers, document the model-specific prompting behavior, and cover speaker/no-speaker requests, transcript handling, model selection, and audio output.

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
2026-09-28 15:23:34 -07:00
stevenlee-oai
3a3ce2be22
fix(openai): discover models for the selected SIWC account (#160027)
* fix(openai): discover models for the selected SIWC account

* test(openai): complete SIWC discovery fetch fixtures

* test(models): avoid shadowing catalog snapshot fixture

* fix(openai): avoid shadowing SIWC catalog rows

* fix(openai): keep SIWC catalog denials scoped to discovery
2026-09-28 11:15:09 -07:00
Peter Steinberger
6d6ea35f76
feat(video): add Kie AI, Z.AI, and Novita video generation (#160080)
* feat(kie): add Kie AI video generation provider

Add 13 documented Market video models with automatic text/image routing, local image uploads, body-level API errors, and bounded polling/downloads. Register API-key onboarding and discovery, wire live QA and CI filters, and document provider capabilities.

Validation: 34 mocked Kie cases pass; core/extension types, bundled registration, inventory, docs, workflow, formatting, and assertion-safety checks pass. Independent review found no actionable issues. The new test command took 186.18s including cold compilation. Live provider proof remains with the lead; Veo awaits API contract reconciliation.

* fix(config): allow Runway and Kie provider credentials in models.providers

Treat runway and kie as bundled provider overlays so apiKey credentials, including SecretRefs, validate without custom baseUrl or models fields. Keep credential resolution in the existing provider auth owner.

Validation: both SecretRef-only schema cases failed before the fix and pass afterward (28 schema cases). All 12 Runway cases pass, including a generated request using the real SDK resolver with a resolved file SecretRef snapshot. Core/extension type checks and the fresh independent review of both requested commits pass.

* feat(zai): add CogVideoX video generation

Extend the Z.AI plugin with regional async CogVideoX text-to-video and image-to-video generation. Reuse provider auth and HTTP helpers, support local image data URIs, and declare duration, geometry, audio, quality, and frame-rate controls.

Wire registration, live provider selection, release shards, generated inventory, and provider documentation. Mocked provider flows, focused contracts, typechecks, inventory, docs, formatting, workflow checks, lint, and assertion safety passed. Live proof remains with the lead.

* feat(novita): add Wan and Hailuo video generation

Extend the Novita plugin with native Wan 2.6 and Hailuo 2.3 async video routes, automatic family image routing, data URI image inputs, and model-specific capabilities. Default Wan output to silent and validate Hailuo duration/resolution combinations before submission.

Wire registration, live provider selection, release shards, generated inventory, and concise provider documentation. Mocked registered-provider flows, focused contracts, typechecks, inventory, docs, formatting, workflow checks, lint, and assertion safety passed. Live proof remains with the lead.

* fix(ci): repair video provider integration checks

Guard the native-live provider list before splitting and add Kie extension and documentation labeler coverage.

Regenerate the config baseline at exactly 4371 plugin entries. The 21 new paths all belong to the shared Kie plugin entry: root/config/enabled (3), hooks (6), llm (8), and subagent (4). Z.AI and Novita add no config entries; existing core, channel, and plugin entries are unchanged.

Focused labeler and native-live shard tests, config baseline, lint, formatting, and diff checks pass. The targeted TS2532 is removed; 19 inherited type diagnostics remain in untouched files. Workflow-planning has the same 17 unique failures and 432 passes on this branch and merge-base 7c346c1c65 because its fixture omits ci-static-step.sh.
2026-09-28 12:48:22 +00:00
Peter Steinberger
3412019bda
fix(qwen): animate local images with Wan image-to-video (#160123)
Qwen and Alibaba Wan video refused every local reference image with
"requires remote http(s) URLs", so agents could not animate attachments
or workspace images, and the default wan2.6-t2v model rejected a single
reference image instead of using its image-to-video sibling.

DashScope img_url accepts base64 data URIs (live-verified with
wan2.6-i2v), so the shared DashScope Wan path now sends local images as
data URIs under Model Studio's documented size limits (20 MB for Wan
2.5-2.7, 10 MB otherwise, applied to buffers and inline data URIs
alike). Reference videos remain remote-URL only. A Wan text-to-video
model plus exactly one image routes to its known i2v sibling. The model
catalog moves to dashscope-wan-models.ts to stay under the line cap.
2026-09-28 08:28:54 +00:00
Peter Steinberger
6541acaa57
fix(lmstudio): preserve JSON errors during non-interactive setup (#160196)
Throw actionable discovery and model-selection failures through the CLI error owner instead of exiting inside the provider validator. Remove the unreachable null branch while preserving read-only reset preflight.

Testbox proof: 20 failing CLI runs previously emitted empty stdout; all 20 candidate failures emit valid JSON and exit 1. Loaded-model setup succeeds in 10 fresh runs. Original code fails 11 provider regression cases; all 68 provider tests and the pinned changed-file gate pass.

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-09-28 00:10:06 -07:00
Peter Steinberger
317f4755b8
fix(fireworks): replace the retired GLM 5.2 Fast default router (#160102)
Fireworks retired accounts/fireworks/routers/glm-5p2-fast. Onboarding, the bundled catalog and the curated live selector now use the documented GLM 5.3 Fast router (Fast cached-input price 0.39/M); explicit operator pins of the old id are preserved.

Forward-port of release/2026.9.7 066fd58ec8.
2026-09-27 22:19:45 -07:00
stevenlee-oai
80714fa47c
fix(openai): clarify auth capabilities and SIWC limitations (#160023) 2026-09-27 20:49:47 -07:00
stevenlee-oai
ebd2bfc1dd
improve(openai): mark Sign in with ChatGPT as Beta (#160011) 2026-09-28 01:39:33 +00:00
stevenlee-oai
106752303d
fix(openai): keep SIWC credentials out of unsupported media requests (#159920)
* fix(openai): keep SIWC credentials out of unsupported media requests

* fix(openai): preserve token auth for custom embeddings
2026-09-27 16:26:55 -07:00
Peter Steinberger
4994ec4501
fix(openai): retire the Sora video provider after the API shutdown (#159543)
OpenAI shut down its Sora video API: sora-2 and sora-2-pro report
shutdown_date 2026-09-24 and POST/GET /v1/videos now return HTTP 404 for
every project. Every video_generate call routed to openai/* failed with an
opaque "OpenAI video generation failed (HTTP 404)", and because the bundled
openai plugin still advertised a configured video provider, agents on
OpenAI-only installs kept selecting it.

Remove the OpenAI video-generation provider, its manifest contract and
metadata, live-test defaults and workflow filters, and the docs that
advertised Sora. Stale openai/sora-* refs need no migration: the media
runtime already skips them with "No video-generation provider registered
for openai" and continues to configured fallbacks or auto-detected
providers.
2026-09-27 22:52:14 +00:00
Peter Steinberger
727e4ed263
fix(google): recover interrupted Interactions streams (#159198)
Emit the existing incomplete-stream contract so partial Interactions replies remain eligible for transient recovery. Centralize reader cancellation, lock release, and pending-work tracking in finally while preserving the original failure.

Regression proof on Blacksmith: six expected failures before the repair; all 101 focused and owner tests pass afterward. The full changed gate passes, and independent Codex review found no actionable P0-P2 findings.

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-09-26 15:18:11 -07:00
Sarah Fortune
099220cc93
fix(agentsapi): load OpenClaw persona and workspace context (#158328)
* test(agentsapi): cover Gateway instruction snapshots

* fix(agentsapi): load Gateway agent instructions for new sessions

* style(agentsapi): format instruction snapshot regression

* fix(agentsapi): declare the canonical attempt result

* test(agentsapi): use canonical fixture cloning

* refactor(agentsapi): use shared workspace preparation

* style(agentsapi): format shared preparation fixture

* docs(agentsapi): state the unimplemented context carrier gap

* style(agentsapi): format rebased instruction fixture

* fix(agentsapi): include OpenClaw persona and prompt context

* fix(agentsapi): preserve admitted tools in workspace context

* docs(agentsapi): document single-user MVP scope

---------

Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-09-25 22:39:27 -07:00
Sarah Fortune
b5e5571675
feat(agentsapi): transfer attachments and hosted files (#154229)
Enable two-way file transfer for the hosted Linux Agents API runtime. Upload admitted attachments to the hosted workspace and return completed files through managed reply media.

Preserve completed assistant replies when artifact transfer fails and enforce live attempt authority at the final outbound write. Document the initial Linux storage scope and deferred platform and failure-presentation limits.

Validation: focused regression tests, type checks, and real Linux ARM64 Docker Gateway transfers through Slack, with CSV, UTF-8, and binary downloads compared byte for byte. ClawSweeper reviewed the published head with no actionable findings.
2026-09-25 15:35:44 -07:00
Mark McDonald
e952abb4af
feat(gemini): add google-interactions api backend (#149880)
* feat: first pass of interactions impl

* retrieve API key consistently; fix thoughts & types

* correctly wire up google-interactions, order thought steps, fix base url

* add dev logging to verify iapi txns

* fix streamed partial arguments

* fix support for image payloads and tool results

* fix(ai): harden Gemini Interactions transport

* fix(ai): preserve Gemini Interactions reasoning effort

* fix(ai): preserve Interactions usage and adaptive reasoning

* fix(ui): retire created thinking claim before patch dispatch

* test(ui): prove rejected thinking update recovery

* fix(ai): validate Interactions tool arguments

* docs(google): explain Interactions selection

* fix(ai): share Interactions credential fallback

* fix(ai): preserve interactions stream metadata

* fix(ai): parse interactions events losslessly

* fix(google): register interactions provider hooks

* test: synchronize saturated CI fixtures

---------

Co-authored-by: Dallin Romney <dallinromney@gmail.com>
2026-09-25 13:47:11 -07:00
Rudra
2bbd9403b7
feat: register Telnyx as an official external provider (#116016)
* feat: register Telnyx as an official external provider

* docs(telnyx): acknowledge non-interactive setup risk

* fix(telnyx): pin the SDK-compatible 0.2.0 provider artifact

@telnyx/openclaw-provider@0.2.0 is built against the batched provider-catalog
SDK and loads on 2026.8.1+ hosts; 0.1.0 failed with sdk-incompatible. Pin the
npm artifact with its registry integrity, raise minHostVersion to >=2026.8.1,
drop the ClawHub pin until 0.2.0 is published there, and remove the docs warning.

* fix(telnyx): restore the ClawHub install source for 0.2.0

@telnyx/openclaw-provider@0.2.0 is now published on ClawHub under the telnyx
publisher with the same npm integrity and source commit, so the catalog can
offer both sources again. npm stays the default choice.

* test(telnyx): keep over-cap suites at their baseline line count

The line-cap ratchet (#149622) rejects growth in files already past the
max-lines cap. Move the Telnyx pin test into a vendor-pins sibling module,
and drop the CLI install and onboarding additions that re-proved what the
wecom pass-through and plugin-install-plan tests already cover.

---------

Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
2026-09-25 10:01:25 -07:00
Sarah Fortune
f80fef130a
feat(agentsapi): add OpenClaw tool support (#154208)
* feat(agentsapi): add OpenClaw tool support

* refactor(agentsapi): consume the focused tool runtime seam

* fix(agentsapi): persist host tool calls and results

* style(agentsapi): format transcript imports

# Conflicts:
#	extensions/agentsapi/agentsapi-messages.ts

* test(agentsapi): reconcile tool coverage after SDK rebase

* refactor(tokenjuice): derive runtime scope from manifest

* docs(agentsapi): require official npm SDK operations

* refactor(agentsapi): replace remaining raw HTTP with SDK

* refactor(agentsapi): separate harness lifecycle and attempts

* style(agentsapi): format extracted attempt module

* fix(agentsapi): distinguish transcript API attribution

* fix(plugins): default tool middleware to manifest runtimes

* refactor(agentsapi): remove unused readers and redundant media fallback

* test(agentsapi): restore existing media test placement

* refactor(agentsapi): remove unused surfaces and duplicate types

* refactor(codex): reuse shared tool telemetry types

* refactor(harness): consolidate tool result bookkeeping

* refactor(harness): remove unused replay flag

* refactor(agentsapi): reuse shared zero usage snapshot

* refactor(agentsapi): share terminal turn status check

* docs(plugins): clarify middleware runtime defaults

* fix(sdk): retain shipped messaging helper export

* fix(agentsapi): preserve saved sessions across tool upgrades

* docs(agentsapi): define exclusive Gateway session ownership

* refactor(codex): reuse shared app-server contracts

* fix(codex): import lifecycle type from its owner

* test(tokenjuice): avoid shadowing imported manifest

* fix(agentsapi): preserve steering during terminal tool cleanup

* fix(codex): align instruction helper with upstream owner

* fix(agentsapi): repair tool policy and transcript accounting

* style(agentsapi): format native prefix projection

* refactor(agentsapi): separate usage aggregation from message projection

---------

Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-09-25 08:43:32 -07:00
stevenlee-oai
739dc050ad
fix(openai): prioritize ChatGPT sign-in and clarify auth choices (#157792) 2026-09-24 23:10:32 -07:00
VasuBansal7576
104e67f55e
fix(auth): preserve Copilot tenant credentials during Doctor repair (#139131)
* fix(auth): preserve Copilot tenant credentials during Doctor repair

* test(auth): split Copilot ownership cases below file-size limit

* docs(copilot): clarify tenant credential sharing

Document that agents share Copilot credentials only when supported tenant scopes
match, and direct affected agents to authenticate for their intended tenant.

Co-authored-by: VasuBansal <148481383+VasuBansal7576@users.noreply.github.com>

* fix(auth): preserve Copilot tenant fences during peer settlement

Require refreshed shared Copilot credentials to match the fenced peer's
normalized routing scope before local fence removal. Cross-tenant peers remain
terminally fenced.

Co-authored-by: VasuBansal <148481383+VasuBansal7576@users.noreply.github.com>

* fix(auth): preserve Copilot tenant scope in ownership

Require Copilot OAuth ownership to validate normalized routing scope before
accepting an identical refresh generation. Same-tenant peers keep the shared
owner shortcut while cross-tenant credentials remain locally owned.

Co-authored-by: VasuBansal <148481383+VasuBansal7576@users.noreply.github.com>

* test(gateway): await plugin application receipt

* test(gateway): await node terminal events

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: VasuBansal <148481383+VasuBansal7576@users.noreply.github.com>
2026-09-24 17:40:17 -07:00
stevenlee-oai
67fd7e910b
feat(openai): add Sign in with ChatGPT through Responses (#148567)
* feat(auth): route renewable OAuth grants by provider policy

* feat(openai): add Sign in with ChatGPT preview

* feat(codex): bridge host-owned token sharing to Responses

* feat(status): show the selected model endpoint

* feat(openai): register and reuse SIWC OAuth clients

* docs(openai): explain authentication choices and identity

* refactor(siwc): clarify sign-in choices and credential preparation

* test(openai): await expired SIWC wizard before retry

* test(agents): await foreign maintenance startup

* feat(ui): show provider accounts and connection methods together

* test(ui): select API key from provider connection dialog
2026-09-24 15:19:53 -07:00
Orion
62580c13d1
fix(doctor): validate retired model successors on the selected route (#156435)
* fix(doctor): retain with warning when catalog successor is authoritatively unavailable

When doctor --fix processes a retired model reference, it resolves the
replacement from the manifest suppression rule. A successor that is
present in the plugin catalog but unavailable on this install (wrong
account tier, region, or plugin version) was previously written silently,
producing an unusable model reference that only fails at inference time.

Previous attempt (cleared-on-no-suppression-rule) was incorrect: an
undefined suppression result means no suppression applies, not that the
successor is absent. Valid catalog-only successors like xai/grok-4.7
have no suppression rule but are perfectly valid migrations.

Fix: evaluate the successor against the same auth-resolution context that
proved the source model's retirement. Only an authoritative negative
(availabilityAuthoritative=true, availability=false) blocks migration.
Unknown or transient availability (undefined) continues to migrate so
the operator can resolve auth after the reference is written.

When a successor is authoritatively unavailable, the repair now:
- Retains the original reference (rather than clearing to an inherited
  default, which could silently downgrade to a worse model)
- Emits an actionable warning naming the successor, the reason, and
  how to fix it

Fixes #156155

* fix(doctor): validate retired model successors before migration

Keep the saved model reference when its proposed successor is retired on the
selected route or incompatible with the selected account and runtime settings.
Evaluate the settings that repair will preserve without changing the input,
while allowing valid migrations during transient cooldowns or unknown readiness.

Consolidates #156317 into #156435 and repairs its route regression assertion
and unused type export. Related: #156155; native account-catalog exclusion is
not established by this change.

Co-authored-by: Bruce-Yii <298228875+Bruce-Yii@users.noreply.github.com>
Co-authored-by: Jonathan Sieling <jonathan@tailoredmonkey.com>

* fix(doctor): check installed-catalog membership before migrating successor

Add an authoritative installed-catalog membership check to the successor
guard so Doctor retains the saved model reference when the proposed
successor is proven absent from the installed plugin model catalog.

When the provider has catalog entries but the successor model is not among
them, Doctor now preserves the original reference and emits a warning
naming the absent successor and the required manual step. When catalog
membership cannot be determined (no catalog entries for the provider or
resolution failure), migration proceeds as before.

Addresses the remaining ClawSweeper finding at
retired-model-successor-guard.ts:106-110 and the installed-catalog
exclusion case in #156155.

Co-authored-by: Jonathan Sieling <jonathan@tailoredmonkey.com>

* fix(doctor): keep catalog hints non-authoritative

Retain route, account, runtime and retirement checks without treating a
missing manifest/config catalog row as native account exclusion. Preserve
the contributor follow-up in ancestry and the supported migration behavior.

Strengthen the existing config-repair regression; primary/fallback migration,
route-scoped allow-list retention and cooldown controls now pass. Native
account-catalog exclusion remains open under Refs #156155.

Co-authored-by: Bruce-Yii <298228875+Bruce-Yii@users.noreply.github.com>
Co-authored-by: Jonathan Sieling <jonathan@tailoredmonkey.com>

---------

Co-authored-by: Jonathan Sieling <jonathan@tailoredmonkey.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Bruce-Yii <298228875+Bruce-Yii@users.noreply.github.com>
2026-09-24 12:33:08 -07:00
Sarah Fortune
4c40118d65
feat(agentsapi): preserve native output and tool history (#156182)
* feat(agentsapi): project native output and reconcile saved history

* fix(agentsapi): retain observed usage after accounting read failures

ClawSweeper: "Retain observed usage when the final REST read fails"
https://github.com/openclaw/openclaw/pull/156182#issuecomment-5789452458

Merge final REST usage with observed usage by admitted turn ID. Canonical
records replace matching contributions, while omitted turns keep observed
usage and repeated accounting does not double count them.

* test(agentsapi): cover projection usage and authoritative session receipts

* refactor(agentsapi): keep projection implementation helpers module-local

* test(agentsapi): use the projection factory and preserve fixture types

---------

Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-09-23 21:18:27 -07:00
Bobby Bones
0ba669ef0b
fix(google): Talk on Gemini 3.8 Live closes the session on the first agent consult and ignores thinkingLevel (#152413)
Honor the Gemini 3.8 Live model contracts in realtime voice.

Extended Thinking now uses its supported thinking and NON_BLOCKING tool-response shape, keeps filler and tool phases active while interactionStatus is IN_PROGRESS, and completes only at IDLE. The Gateway provider and direct browser Talk transport retain transcript finality, interruption behavior, and the existing contracts for plain Gemini 3.8 and older Live models.

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
2026-09-23 10:25:59 -07:00
Serg
4f43e47518
fix(xai): give new Grok releases thinking levels and image input (#156397)
Recognize plain, latest-alias, and dated Grok release IDs using xAI's documented capability ranges, so newer releases receive supported thinking levels and image input without another exact-ID update. Keep Grok 4.20 and variant suffixes on their existing conservative paths, and share the release rule with Responses tool defaults.

Validation: 56 focused regression tests passed on the refreshed head; independent review found no P0/P1 issues.

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-09-23 13:18:28 +00:00
Peter Steinberger
99cfeee840
fix(anthropic): stop retaining MCP configs in the session catalog (#156266)
Project bounded Desktop metadata before cache and overlay retention, preserving the independent CLI index contract. Detach trimmed strings so short display values cannot keep large backing strings alive.

Synthetic full catalog proof reduced additional retained heap from 64.49 MiB to 0.482 MiB while preserving all 48 visible rows and metadata. Fixes #155754.

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-09-23 02:44:44 -07:00
Sarah Fortune
051c8871bc
feat(agentsapi): enable native live web search (#156306)
Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-09-23 07:54:02 +00:00
RoboClaw
5e5f9c2755
fix(anthropic): finish Opus 5.5 defaults and thinking display (#156093)
* fix(anthropic): finish Opus 5.5 defaults and thinking display

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* fix(anthropic): preserve authored model selections during CLI setup

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* test(qa): assert rolling Opus defaults without redundant resolution

Replace the ineffective real-time deadline probe with deterministic fake-clock coverage beyond the watchdog grace.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* test: align remaining Opus default fixtures

Keep explicit version pins, assert immutable numeric pricing, and consolidate PDF selection fixtures without dropping cases.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

---------

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
2026-09-22 23:59:18 -07:00
Sarah Fortune
31e9a02836
refactor(agentsapi): use the official OpenAI SDK (#156116)
* refactor(agentsapi): use the official OpenAI SDK

* fix(agentsapi): narrow SDK message items and update dependency lock

* refactor(agentsapi): use SDK client defaults

* test(agentsapi): identify requests without URL matching

* refactor(agentsapi): import SDK types directly

* fix(agentsapi): honor resolved reasoning effort

* docs(agentsapi): explain configured reasoning effort

* fix(agentsapi): preserve the official SDK endpoint

* fix(agentsapi): honor transport abort deadlines

* fix(agentsapi): preserve admitted SDK authentication

---------

Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-09-22 22:40:36 -07:00
Bobby Bones
7f0ea8e546
fix: SuperGrok usage shows No usage data when xAI omits creditUsagePercent (#155790)
<!--
Related: #155782

Required PR title:
fix: SuperGrok usage shows No usage data when xAI omits creditUsagePercent
-->

Related: #155782

## What Problem This Solves

Fixes: SuperGrok usage cards and `openclaw status --usage` show "No usage data" when xAI returns a valid weekly billing period that omits `creditUsagePercent`.

## User Impact

User impact: OAuth SuperGrok accounts now get a specific "Included usage omitted" snapshot, plus plan and prepaid facts, instead of a generic missing-telemetry error. Grok inference was already working; only quota display was wrong.

## Why This Change Was Made

xAI can omit a default-zero included-usage scalar on an otherwise valid weekly or monthly billing period. The parser now treats that as omitted included usage rather than inventing a percentage or reading on-demand pay-as-you-go counters as SuperGrok subscription quota. Legacy monthly `used` / `monthlyLimit` parsing and explicit `creditUsagePercent: 0` windows stay unchanged.

## Evidence

Exact head: `19aae5939ec429b30333e8db836bf6276d583895`. No code change in this proof pass.

Live usage-only billing probe against `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` (HTTP 200, 413 bytes). Isolated disposable Gateway on `127.0.0.1:63012` (`openclaw gateway run --dev`). Live operator Gateway on `127.0.0.1:18789` (pid 22298) was not restarted, modified, or used for this proof. No inference requests.

Sanitized live billing payload: valid weekly period, `creditUsagePercent` omitted, no legacy `used` / `monthlyLimit`:

```json
{
  "config": {
    "currentPeriod": {
      "type": "USAGE_PERIOD_TYPE_WEEKLY",
      "start": "2026-09-21T12:01:29.688516+00:00",
      "end": "2026-09-28T12:01:29.688516+00:00"
    },
    "onDemandCap": { "val": 0 },
    "onDemandUsed": { "val": 0 },
    "isUnifiedBillingUser": true,
    "prepaidBalance": { "val": 0 },
    "billingPeriodStart": "2026-09-21T12:01:29.688516+00:00",
    "billingPeriodEnd": "2026-09-28T12:01:29.688516+00:00"
  }
}
```

`has_creditUsagePercent`: false. Period recognized as weekly.

PR-head CLI after the same live account, isolated config/port/session store (exit 0):

```text
pnpm openclaw status --usage --timeout 30000
Usage:
  SuperGrok (SuperGrok)
    Included usage omitted
    Prepaid balance: $0.00
```

```text
pnpm openclaw models status
OAuth/token status
- xai usage: Included usage omitted
```

Before (same payload on current main / pre-fix adapter): `SuperGrok: No usage data`.

Focused tests: `node scripts/run-vitest.mjs extensions/xai/usage.test.ts --maxWorkers=1` — 12 passed. File wall 12.38s on one worker; individual cases 1–9ms.

Inspected, sanitized Provider Plans billing-card pair from exact PR-head Control UI (`19aae5939ec429b30333e8db836bf6276d583895`) with a local mocked `usage.status` renderer. Isolated Vite loopback `http://127.0.0.1:51133/`; live operator Gateway `127.0.0.1:18789` (pid 22298) was not used, restarted, or modified. No inference requests.

**Before** (pre-fix SuperGrok snapshot: `error: "No usage data"`):

![Before: SuperGrok Provider Plans card showing No usage data](https://github.com/user-attachments/assets/e315c018-edd5-4e58-99af-f6cbdebe6b66)

**After** (PR-head SuperGrok snapshot: plan + prepaid `$0.00` + `Included usage omitted`):

![After: SuperGrok Provider Plans card showing Included usage omitted](https://github.com/user-attachments/assets/b6157fb5-1cb8-4876-8e00-2b8321b29a13)

No emails, tokens, account identifiers, or private endpoints. Uploaded via the fork `user-attachments` endpoint after upstream `repository_id` returned 404 (no push on `openclaw/openclaw`).

Remaining semantic uncertainty: omitted `creditUsagePercent` is reported as omitted, not proven 0%. Cross-client reports (including [stablyai/orca#20826](https://github.com/stablyai/orca/issues/20826)) suggest xAI drops default-zero credit fields, but that is not a documented xAI contract.


Made with [Cursor](https://cursor.com)

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-23 10:06:33 +05:30
Sarah Fortune
e9fcdb4002
feat(openai): add explicit Agents API MVP harness (#151176)
OpenClaw needs an alternative OpenAI harness that owns a hosted agent session instead of connecting to a Codex app-server session.

Add an explicitly selected Agents API plugin for hosted Linux sessions with API-key authentication, text streaming, session reuse, steering, interruption, and native idle settlement. Reuse shared binding and transcript authority, and dispatch the shared completion hooks from the actual attempt result.

Validation: required final-head CI passed; ClawSweeper accepted the real Docker authority proof and reports no remaining findings. Native Linux/arm64 Docker flows verified hosted VM execution, steering, authorized interruption, queued-input cancellation, completion-hook delivery, successor isolation, and strict transcript rejection. No local unit tests, mocks, or fakes were used.

Token accounting is best effort when terminal events omit usage. Apps, connectors, custom executors, file transfer, image generation, and additional configuration remain outside this MVP.

Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-09-22 18:51:10 -07:00
Peter Steinberger
54655675d5
feat(openai): support GPT-6 Sol and Luna (#155967)
Add API and embedded ChatGPT routing, capability metadata, reasoning levels, and tiered pricing for the two public GPT-6 models. Preserve account-specific native Codex capabilities and Azure sampling. Related: #155937.
2026-09-22 22:11:33 +00:00
Peter Steinberger
42ec6202dc
feat(anthropic): support Claude Opus 5.5 (#155966)
* feat(anthropic): support Claude Opus 5.5

Add explicit API and Claude CLI catalog entries with published limits and pricing. Reuse the mandatory adaptive-thinking and prefix-preserving replay contracts, default to medium effort, and account for serving-model prices after fallback. Preserve Opus 5 defaults and aliases.

* fix(anthropic): normalize Opus 5.5 before plugin discovery

Keep cold model parsing aligned with the Anthropic manifest aliases and include Opus 5.5 in the existing live-priority and Claude CLI allowlist expectations.
2026-09-22 22:08:19 +00:00
Peter Steinberger
d5e6038577
feat: enable automatic Code Mode for preferred models (#155614)
* feat: enable automatic Code Mode for preferred models

* test: preserve parser boundary and automatic default expectations

* ci: refresh merge proof after upstream tooling type repair

Refresh the merge ref after main restored the tsx CLI shim declaration in 80b9608a25. No source changes.
2026-09-22 04:19:15 -07:00
Dallin Romney
35b95b2aa6 feat(xiaomi): add MiMo V2.6 support 2026-09-21 22:27:51 -07:00
Dallin Romney
2a2d28d578
feat(xai): add Grok 4.7 support (#155379) 2026-09-21 20:27:04 -07:00
RoboClaw
0391a39fde
fix(minimax): retain M3 Code Mode preference in model projections (#154664)
Reuse the existing model-definition builder for static and dynamic provider projections. Preserve the registered-provider regression and runtime prerequisite without dropping current test consumers.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
2026-09-21 03:00:10 -07:00
Peter Steinberger
b58603ee2b
fix(anthropic): avoid stale client version rejections on OAuth (#154098)
Direct Anthropic OAuth requests could fail with claude_code_version_too_old because they advertised Claude Code 2.1.75. Maintain a 2.1.278 client-version floor and prefer a newer installed stable CLI through the Anthropic plugin's shared, bounded probe. Both transports use one selected version for request headers and billing metadata; failed or slow probes fall back to the floor.

Preserve API-key and other provider routes without configuration, credential, dependency, or state migrations. Cover request identity and probe lifecycle, and repair the cold metadata fixture and OS-watch race encountered during validation. Focused tests and exact-head CI passed; candidate live OAuth acceptance remains unverified.

Thanks to @Cyb3rb1ade, @raghidtawil-lab, and @davidcittadini for the reports and controlled comparisons. The single-snapshot and header/billing boundary-test approach builds on @KrasimirKralev's prior work.

Fixes #94716. Related: #154016. Supersedes the approach in #150790 with credit; that PR remains open for maintainer follow-up. The separate model-allowlist issue #144903 is outside this repair.

Co-authored-by: Krasimir Kralev <263465593+KrasimirKralev@users.noreply.github.com>
2026-09-20 18:24:36 -07:00
RoboClaw
729c6b1cc3
feat: enable structured Tool Search by default (#154068)
Enable structured Tool Search when tools.toolSearch is unset, while preserving explicit settings, Code Mode precedence, native Codex discovery, and local-model limits. Repair prompt-policy guidance, callable hook authority, untrusted metadata handling, settlement-safe execution, and saved-result retention. Labs exposes the default and opt-out without rewriting configuration.

The rollout can add discovery turns and does not promise universal latency or token savings. The legacy Node bridge remains an explicit setting.

Validated with focused regressions, admitted OpenAI/SSE cases, actual Gateway and Chromium flows, static gates, independent review, and exact-head hosted CI.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: de7bc1dc-ea60-4fb4-a326-e5725d72be66
2026-09-20 16:59:54 -07:00
Ayaan Zaidi
3e48684b8f
fix: clarify Claude CLI and API model routes (#153707)
Distinguish resolved API, Claude CLI, and unknown configured routes in web and channel model pickers. Preview Default without the outgoing session runtime pin, preserve chat-only accessibility guidance, and explain account-specific billing without changing selection or authentication.

Keep route classification outside the eager startup graph. Apply the explicitly approved 32-byte recorded baseline adjustment for compressed bundle-hash variance; no added raw startup JavaScript or fixed-cap change.

Verified real Telegram Test Server model selection and default reset, full Control UI interactions with inspected before/after screenshots, focused command/UI regressions, and exact-head hosted CI. Source-bound native provider inference is not claimed for this presentation-only change.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-20 21:24:43 +05:30