Persist room-event observations as durable bare user transcript rows and carry an ambient transcript watermark through session state so Telegram chat windows only include the unpersisted gap.
Fixes#99257
Fail closed when managed OpenAI OAuth refresh fails instead of silently falling back to stale external Codex CLI credentials.
Make managed provider OAuth authoritative after bootstrap, preserve API-key and non-OpenAI external CLI behavior, and surface targeted re-auth guidance without exposing profile IDs in group/channel replies.
Fixes#99120.
Co-authored-by: Eva <239388517+100yenadmin@users.noreply.github.com>
* fix(config/sessions): narrow reply-session initialization revision to identity fields
The initialization guard compared the full persisted session entry, so
background touches to updatedAt, heartbeat timestamps, context-budget
metadata, etc. produced false-positive stale-snapshot conflicts and the
"reply session initialization conflicted" error.
Only sessionId and sessionFile matter for detecting a session rotation.
Narrow the revision to those identity fields and add a regression test.
Fixes#98672
* fix(config/sessions): merge current metadata when reply-init identity guard passes
* fix(config/sessions): preserve only snapshot-drifted metadata in reply-init commit
* fix: preserve cleared reply-session metadata
* fix: allow same-session reply initialization drift
---------
Co-authored-by: moguangyu5-design <moguangyu5-design@users.noreply.github.com>
Co-authored-by: Josh Lehman <josh@martian.engineering>
5.22 already drops the tool-error WARNING text via payloads.ts, but the
error tool-result payload was still delivered as channel progress unless
sourceReplyDeliveryMode was message_tool_only. Operators who opt into
messages.suppressToolErrors expect no tool-error noise in chat at all.
Add a config-gated early-return in the onToolResult dispatch path so the
visible progress delivery is dropped too, matching the warning-text policy.
No-op unless messages.suppressToolErrors is true. Folds the mac-mini deploy
hotfix into the tracked branch.
(cherry picked from commit a973410e6d3ddbbaa4c3fb308ad4247274a9633a)
Co-authored-by: amittell <mittell@me.com>
matchLevelDirective consumed the token after a level directive
(/think, /verbose, /trace, /fast, /reasoning, /elevated) as the level
argument unconditionally, and extractLevelDirective stripped it from the
body whether or not it was a valid level. So a message like
"/verbose explain quantum computing" reached the agent as
"quantum computing", silently dropping the user's first word, and the
whitespace scan crossed newlines so "/verbose\nSummarize this" lost
"Summarize".
Treat the trailing token as the directive argument only when it
normalizes to a valid level or is the sole remaining token (preserving
the unrecognized-level hint and the default/inherit clear sentinels).
When two or more words follow, the directive acts argument-less and the
message text is preserved intact, matching the exec and queue parsers
that stop at an unrecognized token.
Gap finding: tempdir-8h9i0j1k, tempdir-9i0j1k2l
Replace bare fs.mkdtemp/mkdtempSync calls in 4 test files with the
shared createSuiteTempRootTracker() helper, adding proper cleanup
where none existed.
Background: the project has standardized on shared temp dir helpers
since 88b87d893d (withTempDir) -> 13df67ebc8 (createSuiteTempRootTracker)
-> 06431fd99b / #87298 (CI guard + docs guidance). This PR continues
that migration for the highest-risk files.
Files changed:
- src/skills/lifecycle/install-fallback.test.ts: variable overwrite risk
- src/auto-reply/reply/session-hooks-context.test.ts: helper leaks temp dirs
- src/auto-reply/reply/abort.test.ts: createAbortConfig leaks root dir
- src/auto-reply/inbound.test.ts: test cases without cleanup
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Preserve shared reasoning suppression by default while letting Telegram opt into durable reasoning payloads only when it has a deliverable reasoning lane. Covers persistent /reasoning on, separate reasoning stream lanes, and progress-stream suppression.\n\nVerification:\n- node scripts/run-vitest.mjs src/auto-reply/reply/dispatch-from-config.test.ts extensions/telegram/src/bot-message-dispatch.test.ts\n- git diff --check upstream/main...HEAD\n- .agents/skills/autoreview/scripts/autoreview --mode branch --base upstream/main --stream-engine-output\n- CI run 28411526182 green, including QA Smoke CI and check-test-types\n- Real behavior proof run 28411676681 passed\n\nPR: #97875
* fix(models): don't advise models.providers[] registration for runtime-bound models
When an agent's configured model is bound to an agent runtime (e.g. the
"codex" runtime, whose catalog comes from the OpenAI ChatGPT-account
app-server), and that model id is no longer offered by the runtime, model
resolution fails with "Unknown model: <provider>/<model>". The appended hint
told users to register the model in models.providers[].models[].
For runtime-owned models that advice is misleading: adding the registration
makes resolution "succeed" only for the request to be rejected later by the
provider — e.g. OpenAI returns 400 "model is not supported when using Codex
with a ChatGPT account" once a model id such as gpt-5.3-codex is deprecated.
Detect the agentRuntime binding on the configured agents.defaults.models entry
and instead point the user at the runtime's live catalog (openclaw models list
--provider <id>) and at switching the configured default to an available model.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(reply): show a clear "model unavailable" reply instead of generic failure
When a configured model is retired/renamed by the provider, model resolution
fails (run.ts throws a typed FailoverError with reason "model_not_found") and
the agent runner falls back to the generic "Something went wrong … use /new"
reply. That copy is actively misleading for this failure: retrying or starting
a new session can never help, because the model id itself must be changed in
config. Users on a deprecated model (e.g. gpt-5.3-codex on a Codex ChatGPT
account) just see the generic message on every message and on /new.
Classify this failure into a dedicated user-facing reply that explains the
model is unavailable and points at the config. Detection is structural — it
keys off the typed FailoverError reason, not provider error text — so it stays
robust as provider wording changes; free-text rejections without a typed reason
remain the responsibility of the failover layer that owns error classification.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: recover terminal session status on visible inbound turns (#86827)
When a group chat session enters a terminal status (failed/timeout/killed),
subsequent visible inbound messages now automatically recover the session by
clearing stale lifecycle fields while preserving the session ID and transcript
continuity.
Changes:
- session.ts: detect terminal status on visible turns and clear status/
startedAt/endedAt/runtimeMs/abortedLastRun without rotating sessionId
- dispatch-from-config.ts: force-clear stale active reply operations for
terminal sessions and retry admission once
- agent.ts: mirror terminal recovery in the agent API dispatch path
- kernel.ts: add zero-count visible dispatch warning diagnostic
- types.ts: add 'warning' to ChannelTurnLogEvent event union
* fix: guard terminal recovery from concurrent force-clear race
When two visible messages arrive against the same terminal session
snapshot, the second turn could force-fail the first turn's freshly
admitted recovery operation, dropping the very message the recovery
path exists to save.
Add a terminalRecovery flag on ReplyOperation that is set after a
recovery turn clears the proven stale leftover and admits its own
operation. The force-clear branch now skips operations marked as
in-flight terminal recoveries, letting concurrent turns fall through
to normal busy/wait handling instead.
Add a two-turn regression test that gates the first recovery turn
open, races a second visible turn against the same terminal snapshot,
and asserts neither turn's operation is incorrectly killed.
Also fix missing FinalizedMsgContext import in kernel.ts.
* fix: avoid return value in Promise executor to satisfy lint
* fix: gate terminal force-clear to visible reply turns
A heartbeat/control turn can pass the early active-run short-circuit, reach
the terminal force-clear branch, and abort an in-flight visible recovery
operation that a concurrent visible turn just admitted (before that op is
marked terminalRecovery). Gate the force-clear to visible reply turns so
non-visible turns fall through to normal busy/skip handling instead of
killing the recovery they are meant to protect.
Adds a focused regression test exercising a heartbeat turn that reaches the
force-clear branch against a terminal session snapshot with an active
recovery operation present; it must leave that operation intact and skip.
* fix: verify session identity before terminal force-clear
* fix: mark clean no-stale terminal recovery to survive concurrent visible turn
The same-session terminal-recovery race could still drop a reply when two
visible turns raced the same failed snapshot. The terminalRecovery marker was
set only on the post-force-clear re-admission path, never on the clean no-stale
admission path. So when the first turn admitted cleanly (no stale op to clear),
its recovery op stayed terminalRecovery=false and a second concurrent visible
turn force-cleared it, dropping the first reply (#86827).
Consolidate marking to the single owned-admission choke point that both the
clean no-stale admission and the re-admission-after-force-clear flow through.
Genuine stale leftovers from the original failed run never pass through this
admission, so they stay unmarked and remain force-clearable.
Add a no-stale regression twin to the existing stale-race test.
* fix: suppress zero-count visible-dispatch warning for observed-delivery turns
maybeWarnZeroCountVisibleDispatch re-implemented a partial visibility
check that omitted observedReplyDelivery, so visible turns delivered via
the observed-delivery path (queuedFinal=false, zero counts,
observedReplyDelivery=true) falsely tripped the silent-drop sentinel and
emitted a bogus zero-count-visible-dispatch warning/event.
Use the canonical hasVisibleChannelTurnDispatch helper for the warning
suppression so all non-count delivery paths (observedReplyDelivery,
fallback, summary, queuedFinal) are honored. Add regression tests
covering the observed-delivery case (no warning) and a genuinely empty
visible dispatch (still warns).
* test: read persisted session store in failed-group recovery test
---------
Co-authored-by: 忻役 <xinyi@mininglamp.com>
Summary:
- The PR imports `truncateUtf16Safe` and uses it for the Codex usage-limit preview and verbose working-label truncation paths in auto-reply.
- PR surface: Source +2. Total +2 across 2 files.
- Reproducibility: yes. Current main uses raw `slice(0, N)` at both reported user-facing truncation sites, and ... ludes terminal before/after output showing dangling surrogates before the fix and safe truncation after it.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 74a0a32ed9.
- Required merge gates passed before the squash merge.
Prepared head SHA: 74a0a32ed9
Review: https://github.com/openclaw/openclaw/pull/97299#issuecomment-4820038635
Co-authored-by: zenglingbiao <zeng.lingbiao@xydigit.com>
Approved-by: takhoffman
* fix(auth): suppress recovery hint for format failures
* test(auth): cover format failure recovery copy
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>