Skip to content

fix(tools): honor tools.fs.workspaceOnly=false for host write/edit#28822

Merged
velvet-shark merged 3 commits intoopenclaw:mainfrom
lailoo:fix/fs-workspace-only-28763
Feb 27, 2026
Merged

fix(tools): honor tools.fs.workspaceOnly=false for host write/edit#28822
velvet-shark merged 3 commits intoopenclaw:mainfrom
lailoo:fix/fs-workspace-only-28763

Conversation

@lailoo
Copy link

@lailoo lailoo commented Feb 27, 2026

Summary

Fixes #28763 by honoring the tools.fs.workspaceOnly=false configuration for host write and edit operations.

Problem

After upgrading to 2026.2.26, FS tools remained restricted to the agent workspace even when tools.fs.workspaceOnly=false was set with sandbox mode off. The issue was that while the workspace guard wrapper was conditionally applied based on the config, the underlying host operations (createHostWriteOperations and createHostEditOperations) always called toRelativePathInRoot, which throws an error for paths outside the workspace.

Changes

  • Modified createHostWriteOperations and createHostEditOperations to accept a workspaceOnly parameter
  • When workspaceOnly=false, these functions now use direct file operations instead of enforcing workspace boundaries
  • When workspaceOnly=true (default), the original workspace boundary enforcement is preserved
  • Updated createHostWorkspaceWriteTool and createHostWorkspaceEditTool to pass the workspaceOnly option
  • Updated the tool creation in pi-tools.ts to pass the workspaceOnly config to the host operations

Test Plan

Added comprehensive test coverage in src/agents/pi-tools.workspace-only-false.test.ts:

  • ✅ Write outside workspace when workspaceOnly=false
  • ✅ Edit outside workspace when workspaceOnly=false
  • ✅ Read outside workspace when workspaceOnly=false
  • ✅ Block write outside workspace when workspaceOnly=true

All tests pass. Existing tests remain passing.

Effect on User Experience

Users who set tools.fs.workspaceOnly=false with sandbox off can now use FS tools (read, write, edit) to access files outside the workspace as expected. The default behavior (workspace-only) remains unchanged for security.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 27, 2026

Greptile Summary

Fixed filesystem tools to correctly honor the tools.fs.workspaceOnly=false configuration setting. The root cause was that while the workspace guard wrapper was conditionally applied based on the config, the underlying host operations always called toRelativePathInRoot, which throws errors for paths outside the workspace.

The fix properly threads the workspaceOnly option through the call chain from pi-tools.tscreateHostWorkspaceWriteTool/createHostWorkspaceEditToolcreateHostWriteOperations/createHostEditOperations. When workspaceOnly=false, these operations now use direct filesystem calls (path.resolve + fs.*) instead of the workspace-boundary-enforcing helpers.

Key changes:

  • Modified createHostWriteOperations and createHostEditOperations to conditionally bypass workspace validation when workspaceOnly=false
  • Updated tool creation in pi-tools.ts to pass the workspaceOnly config option
  • Added comprehensive test coverage in pi-tools.workspace-only-false.test.ts
  • Default behavior (workspace-only) preserved for security

The implementation correctly maintains the security model: filesystem tools are restricted to the workspace by default, and users must explicitly opt-in to broader filesystem access by setting tools.fs.workspaceOnly=false (which only works when sandbox mode is off).

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk - it's a focused bug fix with comprehensive test coverage and no breaking changes.
  • The implementation correctly fixes the reported issue while preserving secure defaults. The code follows existing patterns, has comprehensive test coverage (write, edit, read operations both inside and outside workspace), and maintains backward compatibility. The security model is sound: workspace restrictions remain the default, and the new behavior only activates when users explicitly configure workspaceOnly=false with sandbox mode off.
  • No files require special attention.

Last reviewed commit: 4c6a046

Copy link

@nikolasdehor nikolasdehor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct fix for the reported regression. Security model is preserved — user must explicitly opt in via workspaceOnly=false. Good test coverage for both modes.

@velvet-shark velvet-shark self-assigned this Feb 27, 2026
@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation channel: slack Channel integration: slack scripts Repository scripts commands Command implementations channel: feishu Channel integration: feishu size: XL and removed size: S labels Feb 27, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d61965861

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +124 to +126
const pending = INDEX_CACHE_PENDING.get(key);
if (pending) {
return pending;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include purpose in pending memory-manager deduplication

The new INDEX_CACHE_PENDING fast-path returns any in-flight manager promise using only agentId/workspace/settings as the key, so a concurrent purpose: "status" request can now force a regular (purpose: "default") caller to reuse a status-only instance. In MemoryIndexManager construction, status-only mode sets dirty differently, so this race can skip the initial full sync and serve stale memory index results until another trigger runs. Before this change, concurrent calls could create separate instances per purpose; with the pending map they are always collapsed to whichever purpose started first.

Useful? React with 👍 / 👎.

@velvet-shark velvet-shark force-pushed the fix/fs-workspace-only-28763 branch from 3d61965 to 83d4329 Compare February 27, 2026 23:51
@openclaw-barnacle openclaw-barnacle bot added size: M and removed docs Improvements or additions to documentation channel: slack Channel integration: slack scripts Repository scripts commands Command implementations channel: feishu Channel integration: feishu size: XL labels Feb 27, 2026
@velvet-shark velvet-shark merged commit 1725839 into openclaw:main Feb 27, 2026
22 checks passed
@velvet-shark
Copy link
Member

Merged via squash.

Thanks @lailoo!

wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Mar 1, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Mar 1, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
ansh pushed a commit to vibecode/openclaw that referenced this pull request Mar 2, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
steipete pushed a commit to Sid-Qin/openclaw that referenced this pull request Mar 2, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
safzanpirani pushed a commit to safzanpirani/clawdbot that referenced this pull request Mar 2, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
steipete pushed a commit to Sid-Qin/openclaw that referenced this pull request Mar 2, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
venjiang pushed a commit to venjiang/openclaw that referenced this pull request Mar 2, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
justinhuangcode added a commit to justinhuangcode/openclaw that referenced this pull request Mar 2, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
justinhuangcode added a commit to justinhuangcode/openclaw that referenced this pull request Mar 2, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
justinhuangcode added a commit to justinhuangcode/openclaw that referenced this pull request Mar 2, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
steipete pushed a commit to justinhuangcode/openclaw that referenced this pull request Mar 2, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
steipete pushed a commit that referenced this pull request Mar 2, 2026
PR #28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to #31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
steipete pushed a commit to liuxiaopai-ai/openclaw that referenced this pull request Mar 2, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
steipete added a commit that referenced this pull request Mar 2, 2026
* fix(plugins): fallback install entrypoints for legacy manifests

* Voice Call: enforce exact webhook path match

* Tests: isolate webhook path suite and reset cron auth state

* chore: keep #31930 scoped to voice webhook path fix

* fix: add changelog for exact voice webhook path match (#31930) (thanks @afurm)

* fix: handle HTTP 529 (Anthropic overloaded) in failover error classification

Classify Anthropic's 529 status code as "rate_limit" so model fallback
triggers reliably without depending on fragile message-based detection.

Closes #28502

* fix: add changelog for HTTP 529 failover classification (#31854) (thanks @bugkill3r)

* fix(slack): guard against undefined text in includes calls during mention handling

* fix: add changelog for mentions/slack null-safe guards (#31865) (thanks @stone-jin)

* fix(memory-lancedb): pass dimensions to embedding API call

- Add dimensions parameter to Embeddings constructor
- Pass dimensions to OpenAI embeddings.create() API call
- Fixes dimension mismatch when using custom embedding models like DashScope text-embedding-v4

* fix: add regression for memory-lancedb dimensions pass-through (#32036) (thanks @scotthuang)

* fix(telegram): guard malformed native menu specs

* fix: harden plugin command registration + telegram menu guard (#31997) (thanks @liuxiaopai-ai)

* fix(gateway): restart heartbeat on model config changes

* fix: add changelog credit for heartbeat model reload (#32046) (thanks @stakeswky)

* test(process): replace no-output timer subprocess with spawn mock

* test(perf): trim repeated setup in cron memory and config suites

* test(perf): reduce per-case setup in script and git-hook tests

* fix(slack): scope debounce key by message timestamp to prevent cross-thread collisions

Top-level channel messages from the same sender shared a bare channel
debounce key, causing concurrent messages in different threads to merge
into a single reply on the wrong thread. Now the debounce key includes
the message timestamp for top-level messages, matching how the downstream
session layer already scopes by canonicalThreadId.

Extracted buildSlackDebounceKey() for testability.

Closes #31935

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden slack debounce key routing and ordering (#31951) (thanks @scoootscooob)

* fix(openrouter): skip reasoning.effort injection for x-ai/grok models

x-ai/grok models on OpenRouter do not support the reasoning.effort
parameter and reject payloads containing it with "Invalid arguments
passed to the model." Skip reasoning injection for these models, the
same way we already skip it for the dynamic "auto" routing model.

Closes #32039

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for openrouter x-ai reasoning guard (#32054) (thanks @scoootscooob)

* fix(agents): scope volcengine-plan/byteplus-plan auth lookup to profile resolution

The configure flow stores auth credentials under `provider: "volcengine"`,
but the coding model uses `volcengine-plan` as its provider. Add a scoped
`normalizeProviderIdForAuth` function used only by `listProfilesForProvider`
so coding-plan variants resolve to their base provider for auth credential
lookup without affecting global provider routing.

Closes #31731

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tools): honor fsPolicy.workspaceOnly in image/pdf tool localRoots

PR #28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to #31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for fsPolicy image/pdf propagation (#31882) (thanks @justinhuangcode)

* fix: skip Telegram command sync when menu is unchanged (#32017)

Hash the command list and cache it to disk per account. On restart,
compare the current hash against the cached one and skip the
deleteMyCommands + setMyCommands round-trip when nothing changed.
This prevents 429 rate-limit errors when the gateway restarts
several times in quick succession.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(telegram): scope command-sync hash cache by bot identity (#32059)

* fix: normalize coding-plan providers in auth order validation

* feat(security): Harden Docker browser container chromium flags (#23889) (#31504)

* Gateway: honor OPENCLAW_GATEWAY_URL override for remote/local calls

* Agents: fix sandbox sessionKey usage for PI embedded subagent calls

* Sandbox: tighten browser container Chromium runtime flags

* fix: add sandbox browser defaults for container hardening

* docs: expand sandbox browser default flags list

* fix: make sandbox browser flags optional and preserve gateway env auth overrides

* docs: scope PR 31504 changelog entry

* style: format gateway call override handling

* fix: dedupe sandbox browser chrome args

* fix: preserve remote tls fingerprint for env gateway override

* fix: enforce auth for env gateway URL override

* chore: document gateway override auth security expectations

* fix(delivery): strip HTML tags for plain-text messaging surfaces

Models occasionally produce HTML tags in their output. While these render
fine on web surfaces, they appear as literal text on WhatsApp, Signal,
SMS, IRC, and Telegram.

Add sanitizeForPlainText() utility that converts common inline HTML to
lightweight-markup equivalents and strips remaining tags. Applied in the
outbound delivery pipeline for non-HTML surfaces only.

Closes #31884
See also: #18558

* fix(outbound): harden plain-text HTML sanitization paths (#32034)

* fix(security): harden file installs and race-path tests

* matrix: bootstrap crypto runtime when npm scripts are skipped

* fix(matrix): keep plugin register sync while bootstrapping crypto runtime (#31989)

* perf(runtime): reduce cron persistence and logger overhead

* test(perf): use prebuilt plugin install archive fixtures

* test(perf): increase guardrail scan read concurrency

* fix(queue): restart drain when message enqueued after idle window

After a drain loop empties the queue it deletes the key from
FOLLOWUP_QUEUES.  If a new message arrives at that moment
enqueueFollowupRun creates a fresh queue object with draining:false
but never starts a drain, leaving the message stranded until the
next run completes and calls finalizeWithFollowup.

Fix: persist the most recent runFollowup callback per queue key in
FOLLOWUP_RUN_CALLBACKS (drain.ts).  enqueueFollowupRun now calls
kickFollowupDrainIfIdle after a successful push; if a cached
callback exists and no drain is running it calls scheduleFollowupDrain
to restart immediately.  clearSessionQueues cleans up the callback
cache alongside the queue state.

* fix: avoid stale followup drain callbacks (#31902) (thanks @Lanfei)

* fix(synology-chat): read cfg from outbound context so incomingUrl resolves

* fix: require openclaw.extensions for plugin installs (#32055) (thanks @liuxiaopai-ai)

---------

Co-authored-by: Andrii Furmanets <furmanets.andriy@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Saurabh <skmishra1991@gmail.com>
Co-authored-by: stone-jin <1520006273@qq.com>
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: User <user@example.com>
Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: justinhuangcode <justinhuangcode@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: AytuncYildizli <cryptosquanch@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: white-rm <zhang.xujin@xydigit.com>
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
* fix(plugins): fallback install entrypoints for legacy manifests

* Voice Call: enforce exact webhook path match

* Tests: isolate webhook path suite and reset cron auth state

* chore: keep openclaw#31930 scoped to voice webhook path fix

* fix: add changelog for exact voice webhook path match (openclaw#31930) (thanks @afurm)

* fix: handle HTTP 529 (Anthropic overloaded) in failover error classification

Classify Anthropic's 529 status code as "rate_limit" so model fallback
triggers reliably without depending on fragile message-based detection.

Closes openclaw#28502

* fix: add changelog for HTTP 529 failover classification (openclaw#31854) (thanks @bugkill3r)

* fix(slack): guard against undefined text in includes calls during mention handling

* fix: add changelog for mentions/slack null-safe guards (openclaw#31865) (thanks @stone-jin)

* fix(memory-lancedb): pass dimensions to embedding API call

- Add dimensions parameter to Embeddings constructor
- Pass dimensions to OpenAI embeddings.create() API call
- Fixes dimension mismatch when using custom embedding models like DashScope text-embedding-v4

* fix: add regression for memory-lancedb dimensions pass-through (openclaw#32036) (thanks @scotthuang)

* fix(telegram): guard malformed native menu specs

* fix: harden plugin command registration + telegram menu guard (openclaw#31997) (thanks @liuxiaopai-ai)

* fix(gateway): restart heartbeat on model config changes

* fix: add changelog credit for heartbeat model reload (openclaw#32046) (thanks @stakeswky)

* test(process): replace no-output timer subprocess with spawn mock

* test(perf): trim repeated setup in cron memory and config suites

* test(perf): reduce per-case setup in script and git-hook tests

* fix(slack): scope debounce key by message timestamp to prevent cross-thread collisions

Top-level channel messages from the same sender shared a bare channel
debounce key, causing concurrent messages in different threads to merge
into a single reply on the wrong thread. Now the debounce key includes
the message timestamp for top-level messages, matching how the downstream
session layer already scopes by canonicalThreadId.

Extracted buildSlackDebounceKey() for testability.

Closes openclaw#31935

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden slack debounce key routing and ordering (openclaw#31951) (thanks @scoootscooob)

* fix(openrouter): skip reasoning.effort injection for x-ai/grok models

x-ai/grok models on OpenRouter do not support the reasoning.effort
parameter and reject payloads containing it with "Invalid arguments
passed to the model." Skip reasoning injection for these models, the
same way we already skip it for the dynamic "auto" routing model.

Closes openclaw#32039

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for openrouter x-ai reasoning guard (openclaw#32054) (thanks @scoootscooob)

* fix(agents): scope volcengine-plan/byteplus-plan auth lookup to profile resolution

The configure flow stores auth credentials under `provider: "volcengine"`,
but the coding model uses `volcengine-plan` as its provider. Add a scoped
`normalizeProviderIdForAuth` function used only by `listProfilesForProvider`
so coding-plan variants resolve to their base provider for auth credential
lookup without affecting global provider routing.

Closes openclaw#31731

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tools): honor fsPolicy.workspaceOnly in image/pdf tool localRoots

PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for fsPolicy image/pdf propagation (openclaw#31882) (thanks @justinhuangcode)

* fix: skip Telegram command sync when menu is unchanged (openclaw#32017)

Hash the command list and cache it to disk per account. On restart,
compare the current hash against the cached one and skip the
deleteMyCommands + setMyCommands round-trip when nothing changed.
This prevents 429 rate-limit errors when the gateway restarts
several times in quick succession.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(telegram): scope command-sync hash cache by bot identity (openclaw#32059)

* fix: normalize coding-plan providers in auth order validation

* feat(security): Harden Docker browser container chromium flags (openclaw#23889) (openclaw#31504)

* Gateway: honor OPENCLAW_GATEWAY_URL override for remote/local calls

* Agents: fix sandbox sessionKey usage for PI embedded subagent calls

* Sandbox: tighten browser container Chromium runtime flags

* fix: add sandbox browser defaults for container hardening

* docs: expand sandbox browser default flags list

* fix: make sandbox browser flags optional and preserve gateway env auth overrides

* docs: scope PR 31504 changelog entry

* style: format gateway call override handling

* fix: dedupe sandbox browser chrome args

* fix: preserve remote tls fingerprint for env gateway override

* fix: enforce auth for env gateway URL override

* chore: document gateway override auth security expectations

* fix(delivery): strip HTML tags for plain-text messaging surfaces

Models occasionally produce HTML tags in their output. While these render
fine on web surfaces, they appear as literal text on WhatsApp, Signal,
SMS, IRC, and Telegram.

Add sanitizeForPlainText() utility that converts common inline HTML to
lightweight-markup equivalents and strips remaining tags. Applied in the
outbound delivery pipeline for non-HTML surfaces only.

Closes openclaw#31884
See also: openclaw#18558

* fix(outbound): harden plain-text HTML sanitization paths (openclaw#32034)

* fix(security): harden file installs and race-path tests

* matrix: bootstrap crypto runtime when npm scripts are skipped

* fix(matrix): keep plugin register sync while bootstrapping crypto runtime (openclaw#31989)

* perf(runtime): reduce cron persistence and logger overhead

* test(perf): use prebuilt plugin install archive fixtures

* test(perf): increase guardrail scan read concurrency

* fix(queue): restart drain when message enqueued after idle window

After a drain loop empties the queue it deletes the key from
FOLLOWUP_QUEUES.  If a new message arrives at that moment
enqueueFollowupRun creates a fresh queue object with draining:false
but never starts a drain, leaving the message stranded until the
next run completes and calls finalizeWithFollowup.

Fix: persist the most recent runFollowup callback per queue key in
FOLLOWUP_RUN_CALLBACKS (drain.ts).  enqueueFollowupRun now calls
kickFollowupDrainIfIdle after a successful push; if a cached
callback exists and no drain is running it calls scheduleFollowupDrain
to restart immediately.  clearSessionQueues cleans up the callback
cache alongside the queue state.

* fix: avoid stale followup drain callbacks (openclaw#31902) (thanks @Lanfei)

* fix(synology-chat): read cfg from outbound context so incomingUrl resolves

* fix: require openclaw.extensions for plugin installs (openclaw#32055) (thanks @liuxiaopai-ai)

---------

Co-authored-by: Andrii Furmanets <furmanets.andriy@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Saurabh <skmishra1991@gmail.com>
Co-authored-by: stone-jin <1520006273@qq.com>
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: User <user@example.com>
Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: justinhuangcode <justinhuangcode@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: AytuncYildizli <cryptosquanch@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: white-rm <zhang.xujin@xydigit.com>
dorgonman pushed a commit to kanohorizonia/openclaw that referenced this pull request Mar 3, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
dawi369 pushed a commit to dawi369/davis that referenced this pull request Mar 3, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dawi369 pushed a commit to dawi369/davis that referenced this pull request Mar 3, 2026
* fix(plugins): fallback install entrypoints for legacy manifests

* Voice Call: enforce exact webhook path match

* Tests: isolate webhook path suite and reset cron auth state

* chore: keep openclaw#31930 scoped to voice webhook path fix

* fix: add changelog for exact voice webhook path match (openclaw#31930) (thanks @afurm)

* fix: handle HTTP 529 (Anthropic overloaded) in failover error classification

Classify Anthropic's 529 status code as "rate_limit" so model fallback
triggers reliably without depending on fragile message-based detection.

Closes openclaw#28502

* fix: add changelog for HTTP 529 failover classification (openclaw#31854) (thanks @bugkill3r)

* fix(slack): guard against undefined text in includes calls during mention handling

* fix: add changelog for mentions/slack null-safe guards (openclaw#31865) (thanks @stone-jin)

* fix(memory-lancedb): pass dimensions to embedding API call

- Add dimensions parameter to Embeddings constructor
- Pass dimensions to OpenAI embeddings.create() API call
- Fixes dimension mismatch when using custom embedding models like DashScope text-embedding-v4

* fix: add regression for memory-lancedb dimensions pass-through (openclaw#32036) (thanks @scotthuang)

* fix(telegram): guard malformed native menu specs

* fix: harden plugin command registration + telegram menu guard (openclaw#31997) (thanks @liuxiaopai-ai)

* fix(gateway): restart heartbeat on model config changes

* fix: add changelog credit for heartbeat model reload (openclaw#32046) (thanks @stakeswky)

* test(process): replace no-output timer subprocess with spawn mock

* test(perf): trim repeated setup in cron memory and config suites

* test(perf): reduce per-case setup in script and git-hook tests

* fix(slack): scope debounce key by message timestamp to prevent cross-thread collisions

Top-level channel messages from the same sender shared a bare channel
debounce key, causing concurrent messages in different threads to merge
into a single reply on the wrong thread. Now the debounce key includes
the message timestamp for top-level messages, matching how the downstream
session layer already scopes by canonicalThreadId.

Extracted buildSlackDebounceKey() for testability.

Closes openclaw#31935

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden slack debounce key routing and ordering (openclaw#31951) (thanks @scoootscooob)

* fix(openrouter): skip reasoning.effort injection for x-ai/grok models

x-ai/grok models on OpenRouter do not support the reasoning.effort
parameter and reject payloads containing it with "Invalid arguments
passed to the model." Skip reasoning injection for these models, the
same way we already skip it for the dynamic "auto" routing model.

Closes openclaw#32039

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for openrouter x-ai reasoning guard (openclaw#32054) (thanks @scoootscooob)

* fix(agents): scope volcengine-plan/byteplus-plan auth lookup to profile resolution

The configure flow stores auth credentials under `provider: "volcengine"`,
but the coding model uses `volcengine-plan` as its provider. Add a scoped
`normalizeProviderIdForAuth` function used only by `listProfilesForProvider`
so coding-plan variants resolve to their base provider for auth credential
lookup without affecting global provider routing.

Closes openclaw#31731

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tools): honor fsPolicy.workspaceOnly in image/pdf tool localRoots

PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for fsPolicy image/pdf propagation (openclaw#31882) (thanks @justinhuangcode)

* fix: skip Telegram command sync when menu is unchanged (openclaw#32017)

Hash the command list and cache it to disk per account. On restart,
compare the current hash against the cached one and skip the
deleteMyCommands + setMyCommands round-trip when nothing changed.
This prevents 429 rate-limit errors when the gateway restarts
several times in quick succession.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(telegram): scope command-sync hash cache by bot identity (openclaw#32059)

* fix: normalize coding-plan providers in auth order validation

* feat(security): Harden Docker browser container chromium flags (openclaw#23889) (openclaw#31504)

* Gateway: honor OPENCLAW_GATEWAY_URL override for remote/local calls

* Agents: fix sandbox sessionKey usage for PI embedded subagent calls

* Sandbox: tighten browser container Chromium runtime flags

* fix: add sandbox browser defaults for container hardening

* docs: expand sandbox browser default flags list

* fix: make sandbox browser flags optional and preserve gateway env auth overrides

* docs: scope PR 31504 changelog entry

* style: format gateway call override handling

* fix: dedupe sandbox browser chrome args

* fix: preserve remote tls fingerprint for env gateway override

* fix: enforce auth for env gateway URL override

* chore: document gateway override auth security expectations

* fix(delivery): strip HTML tags for plain-text messaging surfaces

Models occasionally produce HTML tags in their output. While these render
fine on web surfaces, they appear as literal text on WhatsApp, Signal,
SMS, IRC, and Telegram.

Add sanitizeForPlainText() utility that converts common inline HTML to
lightweight-markup equivalents and strips remaining tags. Applied in the
outbound delivery pipeline for non-HTML surfaces only.

Closes openclaw#31884
See also: openclaw#18558

* fix(outbound): harden plain-text HTML sanitization paths (openclaw#32034)

* fix(security): harden file installs and race-path tests

* matrix: bootstrap crypto runtime when npm scripts are skipped

* fix(matrix): keep plugin register sync while bootstrapping crypto runtime (openclaw#31989)

* perf(runtime): reduce cron persistence and logger overhead

* test(perf): use prebuilt plugin install archive fixtures

* test(perf): increase guardrail scan read concurrency

* fix(queue): restart drain when message enqueued after idle window

After a drain loop empties the queue it deletes the key from
FOLLOWUP_QUEUES.  If a new message arrives at that moment
enqueueFollowupRun creates a fresh queue object with draining:false
but never starts a drain, leaving the message stranded until the
next run completes and calls finalizeWithFollowup.

Fix: persist the most recent runFollowup callback per queue key in
FOLLOWUP_RUN_CALLBACKS (drain.ts).  enqueueFollowupRun now calls
kickFollowupDrainIfIdle after a successful push; if a cached
callback exists and no drain is running it calls scheduleFollowupDrain
to restart immediately.  clearSessionQueues cleans up the callback
cache alongside the queue state.

* fix: avoid stale followup drain callbacks (openclaw#31902) (thanks @Lanfei)

* fix(synology-chat): read cfg from outbound context so incomingUrl resolves

* fix: require openclaw.extensions for plugin installs (openclaw#32055) (thanks @liuxiaopai-ai)

---------

Co-authored-by: Andrii Furmanets <furmanets.andriy@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Saurabh <skmishra1991@gmail.com>
Co-authored-by: stone-jin <1520006273@qq.com>
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: User <user@example.com>
Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: justinhuangcode <justinhuangcode@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: AytuncYildizli <cryptosquanch@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: white-rm <zhang.xujin@xydigit.com>
OWALabuy pushed a commit to kcinzgg/openclaw that referenced this pull request Mar 4, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OWALabuy pushed a commit to kcinzgg/openclaw that referenced this pull request Mar 4, 2026
* fix(plugins): fallback install entrypoints for legacy manifests

* Voice Call: enforce exact webhook path match

* Tests: isolate webhook path suite and reset cron auth state

* chore: keep openclaw#31930 scoped to voice webhook path fix

* fix: add changelog for exact voice webhook path match (openclaw#31930) (thanks @afurm)

* fix: handle HTTP 529 (Anthropic overloaded) in failover error classification

Classify Anthropic's 529 status code as "rate_limit" so model fallback
triggers reliably without depending on fragile message-based detection.

Closes openclaw#28502

* fix: add changelog for HTTP 529 failover classification (openclaw#31854) (thanks @bugkill3r)

* fix(slack): guard against undefined text in includes calls during mention handling

* fix: add changelog for mentions/slack null-safe guards (openclaw#31865) (thanks @stone-jin)

* fix(memory-lancedb): pass dimensions to embedding API call

- Add dimensions parameter to Embeddings constructor
- Pass dimensions to OpenAI embeddings.create() API call
- Fixes dimension mismatch when using custom embedding models like DashScope text-embedding-v4

* fix: add regression for memory-lancedb dimensions pass-through (openclaw#32036) (thanks @scotthuang)

* fix(telegram): guard malformed native menu specs

* fix: harden plugin command registration + telegram menu guard (openclaw#31997) (thanks @liuxiaopai-ai)

* fix(gateway): restart heartbeat on model config changes

* fix: add changelog credit for heartbeat model reload (openclaw#32046) (thanks @stakeswky)

* test(process): replace no-output timer subprocess with spawn mock

* test(perf): trim repeated setup in cron memory and config suites

* test(perf): reduce per-case setup in script and git-hook tests

* fix(slack): scope debounce key by message timestamp to prevent cross-thread collisions

Top-level channel messages from the same sender shared a bare channel
debounce key, causing concurrent messages in different threads to merge
into a single reply on the wrong thread. Now the debounce key includes
the message timestamp for top-level messages, matching how the downstream
session layer already scopes by canonicalThreadId.

Extracted buildSlackDebounceKey() for testability.

Closes openclaw#31935

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden slack debounce key routing and ordering (openclaw#31951) (thanks @scoootscooob)

* fix(openrouter): skip reasoning.effort injection for x-ai/grok models

x-ai/grok models on OpenRouter do not support the reasoning.effort
parameter and reject payloads containing it with "Invalid arguments
passed to the model." Skip reasoning injection for these models, the
same way we already skip it for the dynamic "auto" routing model.

Closes openclaw#32039

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for openrouter x-ai reasoning guard (openclaw#32054) (thanks @scoootscooob)

* fix(agents): scope volcengine-plan/byteplus-plan auth lookup to profile resolution

The configure flow stores auth credentials under `provider: "volcengine"`,
but the coding model uses `volcengine-plan` as its provider. Add a scoped
`normalizeProviderIdForAuth` function used only by `listProfilesForProvider`
so coding-plan variants resolve to their base provider for auth credential
lookup without affecting global provider routing.

Closes openclaw#31731

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tools): honor fsPolicy.workspaceOnly in image/pdf tool localRoots

PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add changelog credit for fsPolicy image/pdf propagation (openclaw#31882) (thanks @justinhuangcode)

* fix: skip Telegram command sync when menu is unchanged (openclaw#32017)

Hash the command list and cache it to disk per account. On restart,
compare the current hash against the cached one and skip the
deleteMyCommands + setMyCommands round-trip when nothing changed.
This prevents 429 rate-limit errors when the gateway restarts
several times in quick succession.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(telegram): scope command-sync hash cache by bot identity (openclaw#32059)

* fix: normalize coding-plan providers in auth order validation

* feat(security): Harden Docker browser container chromium flags (openclaw#23889) (openclaw#31504)

* Gateway: honor OPENCLAW_GATEWAY_URL override for remote/local calls

* Agents: fix sandbox sessionKey usage for PI embedded subagent calls

* Sandbox: tighten browser container Chromium runtime flags

* fix: add sandbox browser defaults for container hardening

* docs: expand sandbox browser default flags list

* fix: make sandbox browser flags optional and preserve gateway env auth overrides

* docs: scope PR 31504 changelog entry

* style: format gateway call override handling

* fix: dedupe sandbox browser chrome args

* fix: preserve remote tls fingerprint for env gateway override

* fix: enforce auth for env gateway URL override

* chore: document gateway override auth security expectations

* fix(delivery): strip HTML tags for plain-text messaging surfaces

Models occasionally produce HTML tags in their output. While these render
fine on web surfaces, they appear as literal text on WhatsApp, Signal,
SMS, IRC, and Telegram.

Add sanitizeForPlainText() utility that converts common inline HTML to
lightweight-markup equivalents and strips remaining tags. Applied in the
outbound delivery pipeline for non-HTML surfaces only.

Closes openclaw#31884
See also: openclaw#18558

* fix(outbound): harden plain-text HTML sanitization paths (openclaw#32034)

* fix(security): harden file installs and race-path tests

* matrix: bootstrap crypto runtime when npm scripts are skipped

* fix(matrix): keep plugin register sync while bootstrapping crypto runtime (openclaw#31989)

* perf(runtime): reduce cron persistence and logger overhead

* test(perf): use prebuilt plugin install archive fixtures

* test(perf): increase guardrail scan read concurrency

* fix(queue): restart drain when message enqueued after idle window

After a drain loop empties the queue it deletes the key from
FOLLOWUP_QUEUES.  If a new message arrives at that moment
enqueueFollowupRun creates a fresh queue object with draining:false
but never starts a drain, leaving the message stranded until the
next run completes and calls finalizeWithFollowup.

Fix: persist the most recent runFollowup callback per queue key in
FOLLOWUP_RUN_CALLBACKS (drain.ts).  enqueueFollowupRun now calls
kickFollowupDrainIfIdle after a successful push; if a cached
callback exists and no drain is running it calls scheduleFollowupDrain
to restart immediately.  clearSessionQueues cleans up the callback
cache alongside the queue state.

* fix: avoid stale followup drain callbacks (openclaw#31902) (thanks @Lanfei)

* fix(synology-chat): read cfg from outbound context so incomingUrl resolves

* fix: require openclaw.extensions for plugin installs (openclaw#32055) (thanks @liuxiaopai-ai)

---------

Co-authored-by: Andrii Furmanets <furmanets.andriy@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Saurabh <skmishra1991@gmail.com>
Co-authored-by: stone-jin <1520006273@qq.com>
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: User <user@example.com>
Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: justinhuangcode <justinhuangcode@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: AytuncYildizli <cryptosquanch@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: white-rm <zhang.xujin@xydigit.com>
sachinkundu pushed a commit to sachinkundu/openclaw that referenced this pull request Mar 6, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
* fix(plugins): fallback install entrypoints for legacy manifests

* Voice Call: enforce exact webhook path match

* Tests: isolate webhook path suite and reset cron auth state

* chore: keep openclaw#31930 scoped to voice webhook path fix

* fix: add changelog for exact voice webhook path match (openclaw#31930) (thanks @afurm)

* fix: handle HTTP 529 (Anthropic overloaded) in failover error classification

Classify Anthropic's 529 status code as "rate_limit" so model fallback
triggers reliably without depending on fragile message-based detection.

Closes openclaw#28502

* fix: add changelog for HTTP 529 failover classification (openclaw#31854) (thanks @bugkill3r)

* fix(slack): guard against undefined text in includes calls during mention handling

* fix: add changelog for mentions/slack null-safe guards (openclaw#31865) (thanks @stone-jin)

* fix(memory-lancedb): pass dimensions to embedding API call

- Add dimensions parameter to Embeddings constructor
- Pass dimensions to OpenAI embeddings.create() API call
- Fixes dimension mismatch when using custom embedding models like DashScope text-embedding-v4

* fix: add regression for memory-lancedb dimensions pass-through (openclaw#32036) (thanks @scotthuang)

* fix(telegram): guard malformed native menu specs

* fix: harden plugin command registration + telegram menu guard (openclaw#31997) (thanks @liuxiaopai-ai)

* fix(gateway): restart heartbeat on model config changes

* fix: add changelog credit for heartbeat model reload (openclaw#32046) (thanks @stakeswky)

* test(process): replace no-output timer subprocess with spawn mock

* test(perf): trim repeated setup in cron memory and config suites

* test(perf): reduce per-case setup in script and git-hook tests

* fix(slack): scope debounce key by message timestamp to prevent cross-thread collisions

Top-level channel messages from the same sender shared a bare channel
debounce key, causing concurrent messages in different threads to merge
into a single reply on the wrong thread. Now the debounce key includes
the message timestamp for top-level messages, matching how the downstream
session layer already scopes by canonicalThreadId.

Extracted buildSlackDebounceKey() for testability.

Closes openclaw#31935


* fix: harden slack debounce key routing and ordering (openclaw#31951) (thanks @scoootscooob)

* fix(openrouter): skip reasoning.effort injection for x-ai/grok models

x-ai/grok models on OpenRouter do not support the reasoning.effort
parameter and reject payloads containing it with "Invalid arguments
passed to the model." Skip reasoning injection for these models, the
same way we already skip it for the dynamic "auto" routing model.

Closes openclaw#32039


* fix: add changelog credit for openrouter x-ai reasoning guard (openclaw#32054) (thanks @scoootscooob)

* fix(agents): scope volcengine-plan/byteplus-plan auth lookup to profile resolution

The configure flow stores auth credentials under `provider: "volcengine"`,
but the coding model uses `volcengine-plan` as its provider. Add a scoped
`normalizeProviderIdForAuth` function used only by `listProfilesForProvider`
so coding-plan variants resolve to their base provider for auth credential
lookup without affecting global provider routing.

Closes openclaw#31731


* fix(tools): honor fsPolicy.workspaceOnly in image/pdf tool localRoots

PR openclaw#28822 fixed the Write/Edit tools to respect `tools.fs.workspaceOnly`,
but the image and PDF tools still unconditionally include default local
roots (`~/.openclaw/media`, `~/.openclaw/agents`, etc.) when computing
the `localRoots` allowlist for non-sandbox mode.

When `fsPolicy.workspaceOnly` is true, restrict `localRoots` to only the
workspace directory so that files outside the workspace are rejected by
`assertLocalMediaAllowed()`.

Relates to openclaw#31716


* fix: add changelog credit for fsPolicy image/pdf propagation (openclaw#31882) (thanks @justinhuangcode)

* fix: skip Telegram command sync when menu is unchanged (openclaw#32017)

Hash the command list and cache it to disk per account. On restart,
compare the current hash against the cached one and skip the
deleteMyCommands + setMyCommands round-trip when nothing changed.
This prevents 429 rate-limit errors when the gateway restarts
several times in quick succession.


* fix(telegram): scope command-sync hash cache by bot identity (openclaw#32059)

* fix: normalize coding-plan providers in auth order validation

* feat(security): Harden Docker browser container chromium flags (openclaw#23889) (openclaw#31504)

* Gateway: honor OPENCLAW_GATEWAY_URL override for remote/local calls

* Agents: fix sandbox sessionKey usage for PI embedded subagent calls

* Sandbox: tighten browser container Chromium runtime flags

* fix: add sandbox browser defaults for container hardening

* docs: expand sandbox browser default flags list

* fix: make sandbox browser flags optional and preserve gateway env auth overrides

* docs: scope PR 31504 changelog entry

* style: format gateway call override handling

* fix: dedupe sandbox browser chrome args

* fix: preserve remote tls fingerprint for env gateway override

* fix: enforce auth for env gateway URL override

* chore: document gateway override auth security expectations

* fix(delivery): strip HTML tags for plain-text messaging surfaces

Models occasionally produce HTML tags in their output. While these render
fine on web surfaces, they appear as literal text on WhatsApp, Signal,
SMS, IRC, and Telegram.

Add sanitizeForPlainText() utility that converts common inline HTML to
lightweight-markup equivalents and strips remaining tags. Applied in the
outbound delivery pipeline for non-HTML surfaces only.

Closes openclaw#31884
See also: openclaw#18558

* fix(outbound): harden plain-text HTML sanitization paths (openclaw#32034)

* fix(security): harden file installs and race-path tests

* matrix: bootstrap crypto runtime when npm scripts are skipped

* fix(matrix): keep plugin register sync while bootstrapping crypto runtime (openclaw#31989)

* perf(runtime): reduce cron persistence and logger overhead

* test(perf): use prebuilt plugin install archive fixtures

* test(perf): increase guardrail scan read concurrency

* fix(queue): restart drain when message enqueued after idle window

After a drain loop empties the queue it deletes the key from
FOLLOWUP_QUEUES.  If a new message arrives at that moment
enqueueFollowupRun creates a fresh queue object with draining:false
but never starts a drain, leaving the message stranded until the
next run completes and calls finalizeWithFollowup.

Fix: persist the most recent runFollowup callback per queue key in
FOLLOWUP_RUN_CALLBACKS (drain.ts).  enqueueFollowupRun now calls
kickFollowupDrainIfIdle after a successful push; if a cached
callback exists and no drain is running it calls scheduleFollowupDrain
to restart immediately.  clearSessionQueues cleans up the callback
cache alongside the queue state.

* fix: avoid stale followup drain callbacks (openclaw#31902) (thanks @Lanfei)

* fix(synology-chat): read cfg from outbound context so incomingUrl resolves

* fix: require openclaw.extensions for plugin installs (openclaw#32055) (thanks @liuxiaopai-ai)

---------

Co-authored-by: Andrii Furmanets <furmanets.andriy@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Saurabh <skmishra1991@gmail.com>
Co-authored-by: stone-jin <1520006273@qq.com>
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: User <user@example.com>
Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: justinhuangcode <justinhuangcode@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: AytuncYildizli <cryptosquanch@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: white-rm <zhang.xujin@xydigit.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: tools.fs.workspaceOnly=false does not allow write/read/edit outside workspace (sandbox off)

3 participants