Commit Graph

9224 Commits

Author SHA1 Message Date
SidQin-cyber
20523b918a fix(gateway): allow trusted-proxy control-ui auth to skip device pairing
Control UI connections authenticated via gateway.auth.mode=trusted-proxy were
still forced through device pairing because pairing bypass only considered
shared token/password auth (sharedAuthOk). In trusted-proxy deployments,
this produced persistent "pairing required" failures despite valid trusted
proxy headers.

Treat authenticated trusted-proxy control-ui connections as pairing-bypass
eligible and allow missing device identity in that mode.

Fixes #25293

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-24 14:17:54 +00:00
SidQin-cyber
99d854db82 fix(agents): await block-reply flush before tool execution starts
handleToolExecutionStart() flushed pending block replies and then called
onBlockReplyFlush() as fire-and-forget (`void`). This created a race where
fast tool results (especially media on Telegram) could be delivered before
the text block that preceded the tool call.

Await onBlockReplyFlush() so the block pipeline finishes before tool
execution continues, preserving delivery order.

Fixes #25267

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-24 14:11:40 +00:00
Peter Steinberger
4d124e4a9b feat(security): warn on likely multi-user trust-model mismatch 2026-02-24 14:03:19 +00:00
Peter Machona
097a6a83a0 fix(cli): replace stale doctor/restart command hints (#24485)
* fix(cli): replace stale doctor and restart hints

* fix: add changelog for CLI hint updates (#24485) (thanks @chilu18)

---------

Co-authored-by: Muhammed Mukhthar CM <mukhtharcm@gmail.com>
2026-02-24 14:49:59 +05:30
Vincent Koc
4b316c33db Auto-reply: normalize stop matching and add multilingual triggers (#25103)
* Auto-reply tests: cover multilingual abort triggers

* Auto-reply: normalize multilingual abort triggers

* Gateway: route chat stop matching through abort parser

* Gateway tests: cover chat stop parsing variants

* Auto-reply tests: cover Russian and German stop words

* Auto-reply: add Russian and German abort triggers

* Gateway tests: include Russian and German stop forms

* Telegram tests: route Russian and German stop forms to control lane

* Changelog: note multilingual abort stop coverage

* Changelog: add shared credit for abort shortcut update
2026-02-24 01:07:25 -05:00
Peter Steinberger
31f2bf9519 test: fix gate regressions 2026-02-24 04:39:53 +00:00
Peter Steinberger
2d6d6797d8 test: fix post-merge config and tui command-handler tests 2026-02-24 04:38:21 +00:00
justinhuangcode
6ea1607f1c test(discord): add regression tests for reasoning tag stripping in stream
Verify that partial stream updates containing <thinking> tags are stripped
before reaching the draft preview, and that pure "Reasoning:\n" partials
are suppressed entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:37:30 +00:00
justinhuangcode
e8a4d5d9bd fix(discord): strip reasoning tags from partial stream preview
When streamMode is "partial", reasoning/thinking block content can leak
into the Discord draft preview because the partial text is forwarded to
the draft stream without filtering.  Apply `stripReasoningTagsFromText`
before updating the draft and skip pure-reasoning messages (those
starting with "Reasoning:\n") so internal thinking traces never reach
the user-visible preview.

Fixes #24532

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:37:30 +00:00
Peter Steinberger
5ac70b36a4 test: make shell-env trust-path test platform-safe (#24991) (thanks @stakeswky) 2026-02-24 04:34:49 +00:00
Peter Steinberger
d3ecc234da test: align flaky CI expectations after main changes (#24991) (thanks @stakeswky) 2026-02-24 04:34:49 +00:00
Peter Steinberger
d427d09b5e fix: align reasoning payload typing for #24991 (thanks @stakeswky) 2026-02-24 04:34:49 +00:00
User
7d76c241f8 fix: suppress reasoning payloads from generic channel dispatch path
When reasoningLevel is 'on', reasoning content was being sent as a
visible message to WhatsApp and other non-Telegram channels via two
paths:
1. Block reply: emitted via onBlockReply in handleMessageEnd
2. Final payloads: added to replyItems in buildEmbeddedRunPayloads

Telegram has its own dispatch path (bot-message-dispatch.ts) that
splits reasoning into a dedicated lane and handles suppression.
The generic dispatch-from-config.ts path used by WhatsApp, web, etc.
had no such filtering.

Fix:
- Add isReasoning?: boolean flag to ReplyPayload
- Tag reasoning payloads at both emission points
- Filter isReasoning payloads in dispatch-from-config.ts for both
  block reply and final reply paths

Telegram is unaffected: it uses its own deliver callback that detects
reasoning via the 'Reasoning:\n' prefix and routes to a separate lane.

Fixes #24954
2026-02-24 04:34:49 +00:00
Workweaver Ralph
b9e587fb63 fix(tui): guard sendMessage when disconnected; reset readyPromise on close
(cherry picked from commit df827c3eef34ca02cfe5c57a1eabcd9c8e5a4ec1)
2026-02-24 04:33:51 +00:00
Marco Di Dionisio
83689fc838 fix: include trusted-proxy in sharedAuthOk check
In trusted-proxy mode, sharedAuthResult is null because hasSharedAuth
only triggers for token/password in connectParams.auth. But the primary
auth (authResult) already validated the trusted-proxy — the connection
came from a CIDR in trustedProxies with a valid userHeader. This IS
shared auth semantically (the proxy vouches for identity), so operator
connections should be able to skip device identity.

Without this fix, trusted-proxy operator connections are rejected with
"device identity required" because roleCanSkipDeviceIdentity() sees
sharedAuthOk=false.

(cherry picked from commit e87048a6a650d391e1eb5704546eb49fac5f0091)
2026-02-24 04:33:51 +00:00
zerone0x
bc52d4a459 fix(openrouter): skip reasoning effort injection for 'auto' routing model
The 'auto' model on OpenRouter dynamically routes to any underlying model
OpenRouter selects, including reasoning-required endpoints. Previously,
OpenClaw would unconditionally inject `reasoning.effort: "none"` into
every request when the thinking level was "off", which causes a 400 error
on models where reasoning is mandatory and cannot be disabled.

Root cause:
- openrouter/auto has reasoning: false in the built-in catalog
- With thinking level "off", createOpenRouterWrapper injects
  `reasoning: { effort: "none" }` via mapThinkingLevelToOpenRouterReasoningEffort
- For any OpenRouter-routed model that requires reasoning this results in:
  "400 Reasoning is mandatory for this endpoint and cannot be disabled"
- The reasoning: false is then persisted back to models.json on every
  ensureOpenClawModelsJson call, so manually removing it has no lasting effect

Fix:
- In applyExtraParamsToAgent, when provider is "openrouter" and the model
  id is "auto", pass undefined as thinkingLevel to createOpenRouterWrapper
  so no reasoning.effort is injected at all, letting OpenRouter's upstream
  model handle it natively
- Add an explanatory comment in buildOpenrouterProvider clarifying that the
  reasoning: false catalog value does NOT cause effort injection for "auto"

Users who need explicit reasoning control should target a specific model
id (e.g. openrouter/deepseek/deepseek-r1) rather than the auto router.

Fixes #24851

(cherry picked from commit aa554397980972d917dece09ab03c4cc15f5d100)
2026-02-24 04:33:51 +00:00
Ben Marvell
eae13d9367 test(agents): update test to match universal tool-result repair for OpenAI
The previous test asserted that OpenAI-responses sessions would NOT get
synthetic tool results for orphaned tool calls. With repairToolUseResultPairing
now running universally, the correct behavior is that orphaned tool calls
get a synthetic tool_result — matching what OpenAI actually requires.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 2edb0ffe0bf96e9e415c03458ff9cee6bf29bcbe)
2026-02-24 04:33:51 +00:00
Ben Marvell
252079f001 fix(agents): repair orphaned tool results for OpenAI after history truncation
repairToolUseResultPairing was gated behind !isOpenAi, skipping orphaned
tool_result cleanup for OpenAI providers. When limitHistoryTurns truncated
conversation history, tool_result messages whose matching tool_call was
before the truncation point survived and were sent as function_call_output
items with stale call_id references. OpenAI rejects these with:
"No tool call found for function call output with call_id ..."

Enable the repair universally — all providers need it after truncation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 97b065aa6e56fff97414bee26a6b6fc5a33f019a)
2026-02-24 04:33:50 +00:00
chilu18
424ba72cad fix(config): add actionable guidance for dmPolicy open allowFrom mismatch
(cherry picked from commit d3bfbdec5dc5c85305caa0f129f5d4b3c504f559)
2026-02-24 04:33:50 +00:00
chilu18
8c8374defa fix(cron): treat embedded error payloads as run failures
(cherry picked from commit 50fd31c070e8b466db6d81c70b285fd631df1c05)
2026-02-24 04:33:50 +00:00
Marc Gratch
75969ed5c4 fix(plugins): pass session context to before_compaction hook in subscribe handler
The handleAutoCompactionStart handler was calling runBeforeCompaction with
only messageCount and an empty hook context. Plugins receiving this hook
could not identify the session or snapshot the transcript during
auto-compaction.

The other call site in compact.ts already passes the full payload
(messages, sessionFile, sessionKey). This aligns the subscribe handler
to do the same using ctx.params.session and ctx.params.sessionKey.

(cherry picked from commit 318a19d1a1a428ff1be2e03f51777c3829c6e322)
2026-02-24 04:33:50 +00:00
Marcus Castro
58ce0a89ec fix(cli): load plugin registry for configure and onboard commands (#17266)
(cherry picked from commit 644badd40df6eb36847ee7baf36e02ae07bdac74)
2026-02-24 04:33:50 +00:00
JackyWay
792bd6195c fix: recognize Bedrock as Anthropic-compatible in transcript policy
(cherry picked from commit 3b5154081cdd6f9ff94b35c50b8f57714f9ad381)
2026-02-24 04:33:50 +00:00
github-actions[bot]
3823587ada fix(agents): allow empty edit replacement text
(cherry picked from commit 3c21fc30d38ae69f59c7200cfae76642473b2f03)
2026-02-24 04:33:50 +00:00
Glucksberg
fd7ca4c394 fix: normalize input peer.kind in resolveAgentRoute (#22730)
The input peer.kind from channel plugins was used as-is without
normalization via normalizeChatType(), while the binding side correctly
normalized. This caused "dm" !== "direct" mismatches in
matchesBindingScope, making plugins that use "dm" as peerKind fail to
match bindings configured with "direct".

Normalize both peer.kind and parentPeer.kind through normalizeChatType()
so that "dm" and "direct" are treated equivalently on both sides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit b0c96702f5531287f857410303b2c3cc698a1441)
2026-02-24 04:33:50 +00:00
HCL
24e52f53e4 fix(cli): resolve --url option collision in browser cookies set
When addGatewayClientOptions registers --url on the parent browser
command, Commander.js captures it before the cookies set subcommand
can receive it. Switch from requiredOption to option and resolve
via inheritOptionFromParent, matching the existing pattern used
for --target-id.

Fixes #24811

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 96fcb963ec6ef4254898aa2afa91d85b61ce677a)
2026-02-24 04:33:50 +00:00
Brian Mendonca
d51a4695f0 Deny cron tool on /tools/invoke by default
(cherry picked from commit 816a6b3a4df5bf8436f08e3fc8fa82411e3543ac)
2026-02-24 04:33:50 +00:00
Peter Steinberger
f9de17106a refactor(browser): share relay token + options validation tests 2026-02-24 04:23:22 +00:00
Mitch McAlister
8bcd405b1c fix: add .int() to runTimeoutSeconds zod schema for consistency
Matches convention used by all other *Seconds/*Ms timeout fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:22:43 +00:00
Mitch McAlister
5710d72527 feat(agents): configurable default runTimeoutSeconds for subagent spawns
When sessions_spawn is called without runTimeoutSeconds, subagents
previously defaulted to 0 (no timeout). This adds a config key at
agents.defaults.subagents.runTimeoutSeconds so operators can set a
global default timeout for all subagent runs.

The agent-provided value still takes precedence when explicitly passed.
When neither the agent nor the config specifies a timeout, behavior is
unchanged (0 = no timeout), preserving backwards compatibility.

Updated for the subagent-spawn.ts refactor (logic moved from
sessions-spawn-tool.ts to spawnSubagentDirect).

Closes #19288

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:22:43 +00:00
Peter Steinberger
803e02d8df fix: adapt landed fixups to current type and approval constraints 2026-02-24 04:20:30 +00:00
Peter Steinberger
dd14daab15 fix(telegram): allowlist api.telegram.org in media SSRF policy 2026-02-24 04:20:30 +00:00
zerone0x
ac6cec7677 fix(providers): strip trailing /v1 from Anthropic baseUrl to prevent double-path
The pi-ai Anthropic provider constructs the full API endpoint as
`${baseUrl}/v1/messages`. If a user configures
`models.providers.anthropic.baseUrl` with a trailing `/v1`
(e.g. "https://api.anthropic.com/v1"), the resolved URL becomes
"https://api.anthropic.com/v1/v1/messages" which the Anthropic API
rejects with a 404 / connection failure.

This regression appeared in v2026.2.22 when @mariozechner/pi-ai bumped
from 0.54.0 to 0.54.1, which started appending the /v1 segment where
the previous version did not.

Fix: in normalizeModelCompat(), detect anthropic-messages models and
strip a single trailing /v1 (with optional trailing slash) from the
configured baseUrl before it is handed to pi-ai. Models with baseUrls
that do not end in /v1 are unaffected. Non-anthropic-messages models
are not touched.

Adds 6 unit tests covering the normalisation scenarios.

Fixes #24709

(cherry picked from commit 4c4857fdcb3506dc277f9df75d4df5879dca8d41)
2026-02-24 04:20:30 +00:00
Marcus Castro
01c1f68ab3 fix(hooks): decouple message:sent internal hook from mirror param
(cherry picked from commit 1afd7030f8e5e9dda682f1de5942a9662ac7dbcf)
2026-02-24 04:20:30 +00:00
User
c7bf0dacb8 chore: remove unused isMinimal param from buildSkillsSection
Address review feedback: isMinimal is no longer referenced after the
early-return guard was removed in the parent commit.

(cherry picked from commit 2efe04d301c386f1c7dc93d4ae60de8fac8a63b2)
2026-02-24 04:20:30 +00:00
User
2398b51378 fix: include available_skills in isolated cron agentTurn sessions (closes #24888)
buildSkillsSection() had an early-return guard on isMinimal that silently
dropped the entire <available_skills> block for any session using
promptMode="minimal" — which includes all isolated cron agentTurn sessions
(isCronSessionKey → promptMode="minimal" in attempt.ts:497-500).

Fix: remove the isMinimal guard from buildSkillsSection so that skills are
emitted whenever a non-empty skillsPrompt is provided, regardless of mode.
Memory, docs, reply-tags, and other verbose sections remain gated on isMinimal.

Tests added:
- "includes skills in minimal prompt mode when skillsPrompt is provided (cron regression)"
- "omits skills in minimal prompt mode when skillsPrompt is absent"
- Updated existing minimal-mode test expectation to match corrected behaviour.

(cherry picked from commit 66af86e7eede75721a0439cff595209aa4548eff)
2026-02-24 04:20:30 +00:00
zerone0x
c69fc383b9 fix(config): surface helpful chown hint on EACCES when reading config
When the gateway is deployed in a Docker/container environment using a
1-click hosting template, the openclaw.json config file can end up owned
by root (mode 600) while the gateway process runs as the non-root 'node'
user. This causes a silent EACCES failure: the gateway starts with an
empty config and Telegram/Discord bots stop responding.

Before this fix the error was logged as a generic 'read failed: ...'
message with no indication of how to recover.

After this fix:
- EACCES errors log a clear, actionable error to stderr (visible in
  docker logs) with the exact chown command to run
- The config snapshot issue message also includes the chown hint so
  'openclaw gateway status' / Control UI surface the fix path
- process.getuid() is used to include the current UID in the hint;
  falls back to '1001' on platforms where it is unavailable

Fixes #24853

(cherry picked from commit 0a3c572c4175953b0d1284993642b1689678fce4)
2026-02-24 04:20:30 +00:00
SidQin-cyber
f3459d71e8 fix(exec): treat shell exit codes 126/127 as failures instead of completed
When a command exits with code 127 (command not found) or 126 (not
executable), the exec tool previously returned status "completed" with
the error buried in the output text. This caused cron jobs to report
status "ok" and never increment consecutiveErrors, silently swallowing
failures like `python: command not found` across multiple daily cycles.

Now these shell-reserved exit codes are classified as "failed", which
propagates through the cron pipeline to properly increment
consecutiveErrors and surface the issue for operator attention.

Fixes #24587

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 2b1d1985ef09000977131bbb1a5c2d732b6cd6e4)
2026-02-24 04:20:30 +00:00
damaozi
c6bb7b0c04 fix(whatsapp): groupAllowFrom sender filter bypassed when groupPolicy is allowlist (#24670)
(cherry picked from commit af06ebd9a63c5fb91d7481a61fcdd60dac955b59)
2026-02-24 04:20:30 +00:00
Brian Mendonca
3f5e7f8156 fix(gateway): consume allow-once approvals to prevent replay
(cherry picked from commit 6adacd447c61b7b743d49e8fabab37fb0b2694c5)
2026-02-24 04:20:30 +00:00
HeMuling
3c13f4c2b4 test(subagents): mock sessions store in steer-restart coverage 2026-02-24 04:17:56 +00:00
HeMuling
d0e008d460 chore(status): clarify bootstrap file semantics 2026-02-24 04:17:56 +00:00
HeMuling
c3b3065cc9 fix(subagents): reconcile orphaned restored runs 2026-02-24 04:17:56 +00:00
Peter Steinberger
cd3927ad67 fix(sessions): preserve allow-any subagent model overrides (#21088) (thanks @Slats24) 2026-02-24 04:16:32 +00:00
Slats
87dd896963 fix: sessions_sspawn model override ignored for sub-agents
Fix bug where sessions_spawn model parameter was ignored, causing sub-agents
   to always use the parent's default model.

   The allowAny flag from buildAllowedModelSet() was not being captured or used.

   🤖 AI-assisted (Claude) - fully tested locally

   Fixes #17479, #6295, #10963
2026-02-24 04:16:32 +00:00
Peter Steinberger
f6b4baa776 test(telegram): align stop-phrase sequential key expectation (#25034) 2026-02-24 04:16:17 +00:00
Keith
b2719d00ff fix(subagents): restore isInternalMessageChannel guard in resolveAnnounceOrigin
Restores the narrower internal-channel guard from PR #22223 (fe57bea08) that was
inadvertently reverted by f555835b0.

The original !isDeliverableMessageChannel() check strips the requester's channel
whenever it is not in the registered deliverable set. This causes delivery
failures for plugin channels whose adapter ID differs from their plugin ID (e.g.
"gmail" vs "openclaw-gmail"): the requester origin is discarded and the announce
falls back to stale session routes — typically WhatsApp — resulting in a timeout
followed by an E.164 format error.

Replacing with isInternalMessageChannel() limits stripping to explicitly internal
channels (webchat), preserving the requester origin for all external channels
regardless of whether they are currently in the deliverable list.

Fixes: #22223 regression introduced in f555835b0

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-02-24 04:13:40 +00:00
Sahil Satralkar
420d8c663c Tests/Typing: stabilize subagent completion routing changes 2026-02-24 04:12:25 +00:00
Sahil Satralkar
8796c78b3d Gateway: propagate message target and thread headers into tools invoke context 2026-02-24 04:12:25 +00:00
Sahil Satralkar
f9ffd41cfa Subagents: fallback completion announce to internal session when outbound route is incomplete 2026-02-24 04:12:25 +00:00