- Raise typing-breaker default from 2 to 5 consecutive failures via a
named constant, keeping explicit overrides intact.
- Extend rich-plain-fallback trigger map to classify
RICH_MESSAGE_CONTENT_REQUIRED alongside existing invalid-entity
errors so both durable and streaming send funnels benefit.
- Add empty rendered-rich-HTML guards before sendRichMessage in both
the durable chunk loop (send.ts) and the streaming delivery funnel
(delivery.send.ts), preventing doomed Bot API calls and preserving
later valid chunks.
- Add regression tests for the default typing breaker, delivery-side
and durable-side RICH_MESSAGE_CONTENT_REQUIRED fallback behavior.
* @
fix(status): surface auto-fallback model in status and session_status (#96126)
When a session falls back to an alternate model via auto-fallback
(modelOverrideSource: "auto"), both `openclaw status` and `session_status`
silently showed the active fallback model without indicating it differs
from the configured primary. The mismatch gate used
hasUserPinnedModelSelection() which returns false for auto-fallback.
- status.summary.ts: widen mismatch gate from hasUserPinnedModelSelection
to entry?.modelOverride != null; emit distinct "fallback selected"
reason alongside existing "session override"
- status.command-sections.ts: add fallback-specific wording ("auto
fallback" / "check provider availability") while keeping the
modelSelectionReason filter intact (no false-positive null-reason rows)
- status-message.ts: add sessionHasAutoFallback detection for the
session_status RPC path; show "auto fallback" / "check provider" label
instead of "pinned session" / "clear /model default"
Co-Authored-By: Claude <noreply@anthropic.com>
@
* fix(status): narrow fallback detection to real fallback provenance
Replace entry?.modelOverride != null with
hasSessionAutoModelFallbackProvenance(entry) to avoid mislabeling
configured subagent automatic model selections as provider fallback.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(status): distinguish configured models from fallback
Co-authored-by: LZY3538 <liu.zhenye@xydigit.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gateway): persist agent request transcript media
Route inline and offloaded agent.request images through the canonical user-turn transcript recorder across embedded, CLI, and ACP runtimes. Share ordered media persistence with chat.send and cover media-only empty-reply turns.
Co-authored-by: Petros Dhespollari <info@peterdsp.dev>
* fix(gateway): avoid transcript media shadowing
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(anthropic): resolve thinking as disabled when legacy budget is below 1024
When adjustMaxTokensForThinking collapses the thinking budget below the
Anthropic minimum (1024), option resolution now sets thinkingEnabled to
false instead of always forcing it to true. This keeps every downstream
consumer (payload, replay, temperature, tool-choice) consistent — they
all see the same disabled state instead of an enabled flag with a
missing or API-rejected thinking block.
|| → ?? in both builders is defensive: the resolution layer already
prevents invalid budgets from reaching the builder through the normal
path, but ?? preserves an explicit zero when the builder is called
directly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(anthropic): guard raw streamAnthropic builder path against sub-minimum budgets
Add budget guards in both Anthropic payload builders so direct
streamAnthropic and bundled-plugin callers (e.g. Mantle) that bypass
option resolution also get the disabled-state rule instead of producing
API-rejected { type: "enabled", budget_tokens: < 1024 } requests.
Add proof-anthropic-thinking-budget.mts driving real production
functions with terminal output and negative control.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(anthropic): normalize legacy thinking budgets
Co-authored-by: Pick-cat <huang.ting3@xydigit.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(memory-host-sdk): handle stdout/stderr stream errors in runCliCommand
Register error handlers on stdout and stderr streams in
runCliCommand() to prevent uncaught exceptions when a pipe
breaks (e.g. EPIPE after child process exit).
Without these listeners, Node.js throws an uncaught exception
that crashes the process.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(memory-host-sdk): extract listenForChildOutputErrors helper
Refactor the inline stream error handling into a reusable
listenForChildOutputErrors helper, matching the pattern
established in the merged #101370 (agent-core).
The helper registers error listeners on stdout/stderr via a
shared loop, keeping the pattern consistent across the codebase.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(memory-host-sdk): harden qmd stream failures
* fix(memory-host-sdk): harden qmd stream failures
* docs(changelog): note qmd stream hardening
* chore: keep release changelog out of contributor PR
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix: show exported tool results in trace viewers
* fix(diagnostics-otel): emit semconv response key and execute_tool identity so trace viewers show tool results
---------
Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
* feat: add openclaw promos CLI for ClawHub promotional model offers
* fix: harden promos claim auth validation and sanitize remote promo text
* fix: enforce promo window client-side and validate slug contract
* fix: shell-safe model ref contract and explicit --api-key override
* fix: hold promotion aliases to the models alias contract
* docs: document argv-credential contract and env alternative for promos claim
* fix: enforce provider plugin enablement on the credential-reuse claim path
* fix: require plugin install before credential-reuse shortcut in promos claim
* fix: run runtime plugin repair on promo defaults and harden identifier parsing
* fix: distinct message for contract-invalid promo aliases
* fix(cli): validate promotion plugin contracts
* fix(cli): recheck promotion window before claim
* feat(cli): surface ClawHub promotions in models list via the hosted feed
Passive discovery for promotional model offers. A cadence-gated (24h),
fail-silent conditional GET of ClawHub's immutable promotions feed
snapshot (If-None-Match -> 304, short 2.5s timeout, unauthenticated so
CDN caches stay unfragmented) is cached in two new shared-state-DB
tables, fully separate from update_check_state. models list renders an
'Available via promotion' group for live offers whose models are not in
the user's configured set (including the zero-row fresh-install path),
tags claimed models promo / promo ended from provenance recorded at
claim time, and prints a one-time notice per newly seen offer; promos
list and claim mark offers as seen. Machine outputs stay clean, snapshot
sequence is monotonic against stale edges, and claims still revalidate
against the live API so the kill switch always wins.
* style: satisfy lint on promotions feed additions
no-useless-fallback-in-spread on the optional request headers and
no-map-spread in claim-provenance row mapping.
* fix(cli): harden promotions feed cache
* fix(cli): honor live promotion validity
* fix(ports): ignore malformed lsof listener pids
* fix(infra): strictly reject malformed lsof pid tokens
Number.parseInt truncated tokens like "111abc" to 111, silently accepting
a corrupted lsof pid record. Switch to parseStrictPositiveInteger so the
whole token must be a valid positive integer, and restore the deduped
listener test that a prior change had replaced instead of extended.
* fix(cli): strictly reject malformed lsof pid tokens
Harden the force-free lsof parser to reject digit-prefixed garbage like
p111abc instead of truncating it to pid 111, and cover the sibling parser
with a focused regression test.
* fix(infra): strictly parse stale lsof pid tokens
Harden the stale gateway cleanup parser to reject digit-prefixed garbage
like p111abc instead of truncating it to pid 111, and add a focused
regression test without dropping the existing argv verification coverage.
* fix(ports): fail closed on malformed lsof pids
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(media): allow aws-sdk auth mode for image and audio/video paths
Media understanding tools failed for amazon-bedrock deployments using
auth.mode "aws-sdk" (BYOK via role/SSO/profile creds). Each path called
requireApiKey, which throws on the empty-key sentinel before the AWS SDK
credential chain can resolve creds at call time.
The image path (image.ts) resolves auth via getApiKeyForModel; the
audio/video paths route through resolveProviderExecutionAuth. Both now
mirror the chat path's allowMissingApiKeyModes allowance: when the
resolved key is empty and the mode is aws-sdk, execute keyless and let
the SDK resolve credentials. The image path skips setRuntimeApiKey so no
empty-string secret is persisted; audio/video return the kind:"none"
execution auth so the runner bypasses key rotation.
Closes#72031
* fix(media): cover Bedrock PDF SDK auth
* fix(pdf): register plugin completion streams
* fix(media): recognize Bedrock SDK tool auth
* chore: move release note to PR
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agent-core): handle stdout/stderr stream errors in harness exec
Register error handlers on stdout and stderr streams in
NodeExecutionEnv.exec() to prevent uncaught exceptions when
a pipe breaks (e.g. EPIPE after child process exit).
Without these listeners, Node.js throws an uncaught exception
that crashes the entire agent-core process.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agent-core): address review — hoist-safe mock and curly lint
- Use vi.hoisted pattern for spawnMock to fix ReferenceError in CI
- Wrap one-line if guard in braces per repo curly rule
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: keep release notes in PR context
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(codex): scope app-server rate limits to the physical client
Replace the process-global rate-limit cache with a WeakMap keyed by the
physical app-server client, tracking per-limitId revisions. Rolling
account/rateLimits/updated notifications merge sparsely per the protocol
contract (credits/individualLimit/planType survive nulls), and
usage-limit errors only trust a snapshot for auth-profile blocking when
the same client observed a primary update during the failing turn's
startup. Fixes cross-client rate-limit bleed in usage-limit error
messages. A new client-runtime module installs the
account/chatgptAuthTokens/refresh handler and the rate-limit observer
once per physical client, replacing per-start inline handlers in
shared-client, run-attempt, and side-question.
* refactor(codex): split thread/resume subscription safety into thread-resume
Move the thread/resume request out of thread-lifecycle into a dedicated
thread-resume module that retires the exact physical client when resume
acceptance is indeterminate: only a structured RPC rejection proves
Codex holds no subscription, so any other failure abandons the client
instead of returning it to the shared pool. Resume responses naming a
different thread now fail closed (assertCodexThreadResumeSubscription),
and the fresh-thread fallback requires a released subscription unless
the resume was a proven RPC rejection.
* refactor(codex): replace client-factory positional DI with shared-client factory
Delete the lazy positional-argument CodexAppServerClientFactory and use
the options-object factory type exported from shared-client. Callers in
run-attempt, compact, bounded-turn, provider-capabilities, and the
web-search provider now default to getLeasedSharedCodexAppServerClient
directly; the lazy indirection was ineffective because those modules
already import shared-client statically.
* feat(codex): route app-server turn traffic through a keyed turn router
Install one turn router per physical app-server client and replace the
broad per-attempt notification/request fanout with explicit per-thread
routes. Attempt startup reserves the thread route (before thread/resume
on the resume path, so early notifications buffer instead of racing),
run-attempt activates it with receive-time, queued, and request
handlers, arms the route before turn/start, binds the accepted turn id
to flush buffered traffic in wire order, and releases the route on
cleanup. Requests for a pending turn wait for binding instead of being
auto-declined, native turn completion waits use route state instead of
scanning buffered notifications, and correlation readers now match the
canonical v2 wire shapes only (top-level threadId, nested turn.id). The
unscoped response-delta lease-count attribution and its client API are
deleted along with the retired correlation predicates.
* test(codex): reset the shared binding store between thread-lifecycle tests
SQLite bindings are keyed by session identity rather than the per-test
temp dir, so earlier tests leaked resumable threads into fresh-start
expectations. The old silent resume-failure fallback masked the leak;
subscription safety surfaces it.
* test(codex): reset the binding store between delivery-hint iterations
The loop reuses one session identity across iterations, so the previous
iteration's thread would resume against a harness that cannot serve it.