fix(tools): honor tools.fs.workspaceOnly=false for host write/edit#28822
fix(tools): honor tools.fs.workspaceOnly=false for host write/edit#28822velvet-shark merged 3 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryFixed filesystem tools to correctly honor the The fix properly threads the Key changes:
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 Confidence Score: 5/5
Last reviewed commit: 4c6a046 |
nikolasdehor
left a comment
There was a problem hiding this comment.
Correct fix for the reported regression. Security model is preserved — user must explicitly opt in via workspaceOnly=false. Good test coverage for both modes.
There was a problem hiding this comment.
💡 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".
| const pending = INDEX_CACHE_PENDING.get(key); | ||
| if (pending) { | ||
| return pending; |
There was a problem hiding this comment.
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 👍 / 👎.
3d61965 to
83d4329
Compare
…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
…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
…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
…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
…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
…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
…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
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>
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>
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>
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>
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>
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(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>
…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
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(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>
…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
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(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>
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(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>
…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
…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
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(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>
Summary
Fixes #28763 by honoring the
tools.fs.workspaceOnly=falseconfiguration 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=falsewas 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 (createHostWriteOperationsandcreateHostEditOperations) always calledtoRelativePathInRoot, which throws an error for paths outside the workspace.Changes
createHostWriteOperationsandcreateHostEditOperationsto accept aworkspaceOnlyparameterworkspaceOnly=false, these functions now use direct file operations instead of enforcing workspace boundariesworkspaceOnly=true(default), the original workspace boundary enforcement is preservedcreateHostWorkspaceWriteToolandcreateHostWorkspaceEditToolto pass theworkspaceOnlyoptionpi-tools.tsto pass theworkspaceOnlyconfig to the host operationsTest Plan
Added comprehensive test coverage in
src/agents/pi-tools.workspace-only-false.test.ts:workspaceOnly=falseworkspaceOnly=falseworkspaceOnly=falseworkspaceOnly=trueAll tests pass. Existing tests remain passing.
Effect on User Experience
Users who set
tools.fs.workspaceOnly=falsewith 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.