Desktop automations silently fall back to workspace-write sandbox regardless of app configuration

Open 💬 19 comments Opened Mar 20, 2026 by kilo-code-bot[bot]
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

Desktop automations (scheduled/recurring tasks) start threads with workspace-write sandbox even when the application is configured for full access (danger-full-access). The sandbox policy only corrects to the intended value after a user manually enters the chat UI, at which point the desktop app injects the correct permissions via turn/start with sandbox_policy override. This makes unattended Git automations (pull, branch, commit, PR) fail with permission denied on .git/ paths, and forces manual intervention in workflows designed to be autonomous.

Root Cause (confirmed via source analysis)

The app-server protocol's thread/start RPC accepts an optional sandbox parameter in ThreadStartParams ([codex-rs/app-server-protocol/src/protocol/v2.rs:2481](<https://github.com/openai/codex/blob/main/codex-rs/app-server-protocol/src/protocol/v2.rs#L2481>)). When omitted, the app-server resolves the sandbox policy from the layered config (config.toml, CLI overrides, project trust) via derive_sandbox_policy() ([codex-rs/core/src/config/mod.rs:1747](<https://github.com/openai/codex/blob/main/codex-rs/core/src/config/mod.rs#L1747>)).

The desktop app stores its sandbox preference in .codex-global-state.json (e.g., agent-mode: full-access, skip-full-access-confirm: true), which is not part of the app-server's config resolution chain. The app-server has no mechanism to read this file.

Two code paths diverge:

<!-- linear:table-colwidths:266,266,266 -->
| Path | Behavior | Why |
| -- | -- | -- |
| Interactive chat (thread/start + turn/start) | Correct sandbox | Desktop app sends sandbox: "danger-full-access" in thread/start or overrides via turn/start with sandbox_policyOp::OverrideTurnContext |
| Automation scheduler | Wrong sandbox (workspace-write) | Automation launcher sends thread/start WITHOUT the sandbox parameter → app-server falls back to config.toml default (typically workspace-write) |

Why it "fixes itself" after chat interaction:<br>When the user opens the chat UI, the desktop app sends turn/start with sandbox_policy: "danger-full-access", which triggers Op::OverrideTurnContext ([codex-rs/app-server/src/codex_message_processor.rs:6023](<https://github.com/openai/codex/blob/main/codex-rs/app-server/src/codex_message_processor.rs#L6023>)) to override the running thread's sandbox policy.

Impact

  • Breaks unattended Git automations (permission denied on .git/FETCH_HEAD, .git/refs/heads lockfiles)
  • Causes false Permission denied failures on workspace writes
  • Forces manual intervention in workflows designed to be autonomous
  • Creates user confusion: "I set full access, why is it restricted?"

Evidence from local state

From .codex-global-state.json:

{
  "agent-mode": "full-access",
  "skip-full-access-confirm": true
}

From rollout JSONL (first turn context of automation):

{
  "sandbox_policy": {"type": "workspace-write"},
  "network_access": false
}

From rollout JSONL (after chat re-entry, same conversation):

{
  "developer_instructions": "... danger-full-access ..."
}

From state_5.sqlite threads table — mixed sandbox policies for the same automation title:

  • Some runs: {"type":"danger-full-access"}
  • Other runs: {"type":"workspace-write", "network_access": false}

Related Issues

This issue is part of a broader pattern of sandbox policy propagation failures across the Codex platform. The following issues describe related or overlapping symptoms:

Automations and background execution

<!-- linear:table-colwidths:200,200,200,200 -->
| Issue | Title | Status | Relationship |
| -- | -- | -- | -- |
| openai/codex#14590 | Desktop automations can start in workspace-write and switch to danger-full-access after chat interaction | OPEN | This issue's predecessor — same root cause |
| openai/codex#12919 | Scheduled automations cannot access GitHub API in default sandbox despite allowlisted gh api and loaded token | OPEN | Direct symptom of this bug — automation gets CODEX_SANDBOX_NETWORK_DISABLED=1 and GH_TOKEN=missing because sandbox policy is wrong |
| openai/codex#14741 | Automations fail to use MCP servers | OPEN | Direct symptom — MCP servers are sandbox-restricted in automations because the sandbox defaults to workspace-write |
| openai/codex#15161 | Thread-level sandbox in Codex App | OPEN | Feature request that would partially address this — per-thread sandbox override would let automations declare their own sandbox |
| [#10695](<https://github.com/openai/codex/issues/10695>) | Codex App cannot use Github Fix CI skill due to keychain and GH_TOKEN env being inaccessible in its sandbox | OPEN | Overlapping — desktop app sandbox restricts env/keychain access for skill execution |

Sandbox policy propagation failures

<!-- linear:table-colwidths:200,200,200,200 -->
| Issue | Title | Status | Relationship |
| -- | -- | -- | -- |
| openai/codex#15305 | Review-mode subagent ignores runtime sandbox override and falls back to config defaults | OPEN | Same pattern, different surface — review subagent inherits stale config instead of parent thread's effective runtime sandbox override |
| openai/codex#14068 | codex app-server tool commands run in read-only sandbox despite --dangerously-bypass-approvals-and-sandbox | OPEN | Same pattern, different surface — app-server's --dangerously-bypass flag doesn't propagate to tool child processes; strace confirms codex-linux-sandbox is invoked with {"type":"read-only"} |
| openai/codex#15309 | Approved escalated commands still inherit restricted network policy in interactive CLI sessions | OPEN | Same pattern, different surface — approval flow drops filesystem sandbox but preserves restricted NetworkSandboxPolicy, so approved commands still see CODEX_SANDBOX_NETWORK_DISABLED=1 |
| openai/codex#12996 | Codex.app injecting restricted network access | OPEN | Overlapping — desktop app injects restricted network policy that overrides config.toml network_access = true |
| [#10856](<https://github.com/openai/codex/issues/10856>) | codex app can't touch web, but codex cli can | OPEN | Overlapping — desktop app has different network/DNS behavior than CLI, likely due to sandbox policy differences |

Broader sandbox inconsistency issues

<!-- linear:table-colwidths:200,200,200,200 -->
| Issue | Title | Status | Relationship |
| -- | -- | -- | -- |
| openai/codex#5038 (closed) | VS Code extension ignores approval_policy="never" and randomly asks for approval | CLOSED | Historical — earlier manifestation of the same class of problem: client-side config not propagating to the agent runtime |
| openai/codex#5090 (closed) | Regression: sandbox/network enforcement breaks Codex autonomy | CLOSED | Historical — follow-up to openai/codex#5038, same pattern of sandbox/network enforcement ignoring configured permissions |
| openai/codex#9298 | Requires permissions escalation for every network request even when network_access = true | OPEN | Overlapping — network sandbox enforcement ignores configured policy |
| [#10099](<https://github.com/openai/codex/issues/10099>) | VS Code OpenAI codex keeps asking for permissions | OPEN | Overlapping — approval policy not persisting across sessions |
| openai/codex#14338 | Allow writable gitdir for current worktree in sandboxed workspace-write mode | OPEN | Tangential — workspace-write sandbox is too restrictive for Git operations, compounding this issue's impact |
| openai/codex#12716 | Allow list for commands instead of danger-full-access + deny list | OPEN | Feature request — would provide a middle ground between full-access and workspace-write |

Architecture Detail

How sandbox policy is resolved (current behavior)

thread/start (caller provides sandbox?)
  │
  ├─ YES → ConfigOverrides.sandbox_mode = Some(DangerFullAccess)
  │         └─ derive_sandbox_policy(sandbox_mode_override=Some(...))
  │              └─ resolved = DangerFullAccess  ✓
  │
  └─ NO  → ConfigOverrides.sandbox_mode = None
            └─ derive_sandbox_policy(sandbox_mode_override=None)
                 ├─ check profile sandbox_mode    → None (no profile set)
                 ├─ check config.toml sandbox_mode → None (not set)
                 ├─ check project trust            → trusted? workspace-write
                 └─ default                        → ReadOnly or WorkspaceWrite  ✗

How the interactive path works (correct)

Desktop App (interactive)
  ├─ Reads .codex-global-state.json → agent-mode = "full-access"
  ├─ Maps to SandboxMode::DangerFullAccess
  ├─ thread/start { sandbox: "danger-full-access" }
  │   └─ ConfigOverrides.sandbox_mode = Some(DangerFullAccess)  ✓
  └─ turn/start { sandbox_policy: "danger-full-access" }
      └─ Op::OverrideTurnContext  ✓

How the automation path fails (broken)

Desktop App (automation scheduler)
  ├─ Reads .codex-global-state.json → agent-mode = "full-access"
  ├─ (does NOT pass sandbox to thread/start)
  ├─ thread/start { /* no sandbox field */ }
  │   └─ ConfigOverrides.sandbox_mode = None  ✗
  │       └─ derive_sandbox_policy → defaults to workspace-write  ✗
  └─ Later: user opens chat UI
      └─ turn/start { sandbox_policy: "danger-full-access" }
          └─ Op::OverrideTurnContext  ✓ (fixes it retroactively)

Proposed Fix

The protocol already supports the required behavior — ThreadStartParams.sandbox exists and accepts the full range of sandbox modes. No protocol changes are needed. The fix is in the desktop app (closed-source):

Option A: Always pass sandbox in thread/start (recommended)

The automation launcher should read the app's configured sandbox preference and pass it in every thread/start call:

{ "method": "thread/start", "params": {
    "cwd": "/path/to/workspace",
    "sandbox": "danger-full-access",
    ...
} }

This is the simplest fix and guarantees every thread gets the intended policy regardless of config.toml.

Option B: Persist sandbox preference to config.toml

When the user changes the sandbox mode in the desktop app UI, write sandbox_mode to config.toml via the existing config/batch/write API:

{ "method": "config/batch/write", "params": {
    "edits": [{ "keyPath": "sandbox_mode", "value": "danger-full-access" }],
    "reloadUserConfig": true
} }

This makes the preference visible to all thread-creation paths automatically, including those that don't supply an explicit sandbox override.

Option C: Per-automation sandbox configuration

Add a sandbox mode selector to the automation setup UI (related: openai/codex#15161). When creating/editing an automation, the user selects the desired sandbox mode. The launcher stores this value and passes it in thread/start. This gives the most flexibility but requires UI changes.

Documentation Fix (submitted)

A documentation PR has been submitted that clarifies the ThreadStartParams.sandbox fallback behavior and documents the two strategies above for clients that manage their own sandbox preferences. The PR adds:

  • Doc comment to ThreadStartParams.sandbox explaining the fallback to config.toml
  • "Sandbox defaults for clients with external preferences" section in the app-server README

Acceptance Criteria

  1. An automation configured to run with full access starts its thread with danger-full-access sandbox from the first turn, without requiring user interaction
  2. The thread's rollout JSONL shows sandbox_policy: {"type": "danger-full-access"} in the first turn context, not workspace-write
  3. Git operations (git pull, branch creation, commit, PR) succeed in the automation's first turn without permission errors
  4. The behavior is consistent: the automation sandbox policy does not change between the "background" phase and the "chat UI" phase

View original on GitHub ↗

19 Comments

github-actions[bot] contributor · 4 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #14590

Powered by Codex Action

m13v · 2 months ago

this UI-layer state vs scheduler path desync is a classic desktop agent pattern. hit it on a mac agent project earlier this year where Accessibility checks passed interactively but failed silently from a launchd run because the TCC prompt context never fired. root cause was the same shape, privileged config lives in a process the scheduler never hydrates from. what worked for us was passing the full policy envelope on thread spawn rather than falling back to config.toml defaults. the self-heal after chat re-entry proves the override path works, it's just not wired into the unattended launch.

sinshev · 2 months ago

Additional sanitized reproduction from today.

Environment:

  • Codex Desktop automation run
  • Platform: Darwin arm64
  • CLI observed in rollout metadata: 0.125.0-alpha.3
  • Local shell Codex CLI: codex-cli 0.30.0
  • GitHub CLI: gh 2.90.0

Automation configuration:

kind = "cron"
execution_environment = "local"
cwds = ["/path/to/private/workspace"]

Observed behavior:

  • The automation was explicitly configured with execution_environment = "local".
  • The automation run still started with the injected permission block:
  • sandbox_mode = read-only
  • Network access is restricted
  • Approval policy is currently never
  • In that automation run:
  • gh auth status reported the stored token as invalid.
  • gh issue list ... / gh api ... failed with error connecting to api.github.com.
  • curl https://api.github.com/... failed with Could not resolve host: api.github.com.
  • Writing the automation memory file failed with operation not permitted.
  • In a normal interactive/full-access Codex session on the same machine shortly afterward:
  • gh auth status succeeded using the same account/keyring.
  • the token had repo and workflow scopes.
  • gh could access the target private repository.

Why this matters:

This is not just a stale GitHub token. The same host/keyring works outside the automation run. The automation runtime appears to ignore or fail to propagate the local execution environment into the first turn's effective sandbox/network policy. Because the automation run has approval_policy = never, the agent cannot recover by requesting escalation.

Expected behavior:

For a cron automation configured with execution_environment = "local", the first turn should start with the effective local/full-access permissions needed for local keyring access, network access, and writes to the configured automation memory path, or the UI/config should expose a separate explicit automation sandbox/network selector if local is not intended to mean that.

ZepPellN · 2 months ago

Additional sanitized reproduction from a Codex Desktop scheduled automation on macOS.

Environment:

  • Codex Desktop scheduled automation / cron task
  • Platform: macOS / Darwin arm64
  • Automation config includes execution_environment = "local" and a workspace cwd
  • The CLI being called is a local Twitter/X CLI that authenticates via macOS Keychain/browser-cookie backed state

Observed behavior:

  • In a normal interactive terminal/Codex session on the same machine, the live auth preflight succeeds.
  • twitter status --yaml reports authenticated successfully.
  • The same check also succeeds with a stripped environment: env -i HOME=/Users/<user> USER=<user> PATH=... twitter status --yaml.
  • In the scheduled automation runtime, the same workflow repeatedly fails at the Twitter auth step with not_authenticated.
  • Other symptoms in scheduled runs match sandbox/permission isolation rather than bad credentials: writes to automation memory/state or vault paths can fail with PermissionError / Operation not permitted.

Why this matters:

This is not a missing credential, stale login, or PATH/env problem. The credential is valid and readable from the same host/user in an interactive session, even with a minimal environment. The scheduled automation runtime appears to run in a different effective permission context that cannot reliably access macOS Keychain/browser-cookie backed auth, despite being configured as local execution.

Current workaround:

The automation now has to hard-stop on a live auth preflight before fetching or writing any state, otherwise it can overwrite valid timeline data with an empty/partial result. That prevents data corruption but means the scheduled automation cannot reliably run unattended.

Expected behavior:

For scheduled automations configured with local/full-access execution, the first turn should have the same effective local permissions needed for Keychain/browser-cookie backed CLI auth and configured workspace/state writes, or the UI should expose a separate explicit automation permission model and make clear that unattended runs cannot access those resources.

orangepropeller · 2 months ago

Can confirm again.

WingCH · 2 months ago

same issue

Arconte112 · 2 months ago

same issue

eddial · 2 months ago

same issue

yuchaocheung-pixel · 2 months ago

Adding another Windows repro from Codex Desktop App 26.430.10722 observed on 2026-05-07.

Environment:

  • Platform: Windows / Codex Desktop App
  • UI sandbox setting: Full access
  • config.toml contains sandbox_mode = "danger-full-access"
  • [windows] sandbox = "elevated"
  • Relevant workspaces are trusted
  • Approval policy: on-request

Observed behavior:

  • In normal tool execution, without an explicit escalation/local-permission run, outbound socket operations are still blocked.
  • A Python market-data TCP client fails during its initial connection with PermissionError: [WinError 10013].
  • A simple Python SSL socket connection to an external mail server port also fails with the same PermissionError: [WinError 10013].
  • The exact same commands succeed when run with escalated/local permissions.
  • Existing desktop automations that previously fetched external data and sent email started failing after the app upgrade because the default automation/tool execution environment still behaves as network/socket restricted.
  • As a workaround, the Codex cron automations had to be paused and replaced with a local runner process outside the Codex sandbox.

Expected behavior:

  • If the UI/config says Full access / danger-full-access, the initial tool and automation execution should not inherit a network-disabled/socket-restricted sandbox.
  • At minimum, the Desktop App should expose a separate, accurate "network/socket access" status/setting, because the current Full access UI implies this should work while outbound sockets are still blocked.

This looks related to #12996 and #13373, but on Windows the directly observable failure is WinError 10013 rather than only DNS/GitHub API failures.

rgerhards · 2 months ago

This is also a big problem for me and makes current automations almost useless. At least I need abilty for git operations (like git pull) and access to at least a select set of websites.

josh-abram · 2 months ago

Experiencing this behaviour in Windows for Automates as well. For me, it also causes the permissions dropdown in the Codex desktop app as well as plan mode to be greyed out completely.

rajohns08 · 2 months ago

Same issue - this makes automations pointless for me

SergeNS-mne · 2 months ago

I can confirm this issue affects Codex Desktop automations that need outbound SSH.

In my case, the same SSH command worked in an interactive Codex thread, but failed in a scheduled desktop automation with:

ssh: connect to host <host> port 22: Operation not permitted

Local state_5.sqlite showed the automation thread was started with:

{"type":"workspace-write","network_access":false}

while the interactive thread had a less restrictive effective policy.

A practical workaround that fixed it for me was adding this to ~/.codex/config.toml and restarting Codex Desktop:

sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = true

After restart, the same scheduled automation successfully connected over SSH while keeping the filesystem sandbox as workspace-write.

So the issue is not limited to Git operations. It also affects automations that need SSH/network access. A good product-level fix would be for the automation launcher to pass the intended sandbox/network policy at thread/start, or persist the Desktop UI sandbox/network preference into config.toml so background automations resolve the same policy as interactive threads.

Crempa · 1 month ago

We are seeing the same problem on macOS with Codex Desktop scheduled automations, and it makes automations very hard to use for anything that needs local state, SSH, or durable memory writes.

Relevant config in ~/.codex/config.toml:

sandbox_mode = "danger-full-access"
approval_policy = "never"

The automation itself is configured as local:

kind = "cron"
execution_environment = "local"

But the effective first-turn context does not match the config. From our local rollout JSONL:

// interactive/manual Codex thread
"sandbox_policy": { "type": "danger-full-access" }

// scheduled automation runs
"sandbox_policy": { "type": "read-only" }

An earlier scheduled run of the same automation also started as:

{
  "type": "workspace-write",
  "writable_roots": [
    "/Users/<user>/.codex/automations/<automation-id>",
    "/Users/<user>/Workspace/.../.git"
  ],
  "network_access": false
}

Observed failures in scheduled automation:

  • local write needed by the tool failed: Operation not permitted: '/Users/<user>/.tlap-ai-sysadmin/runs/.audit.lock'
  • fallback temp directory creation failed: mkdir: /private/tmp/...: Operation not permitted
  • outbound SSH failed: ssh: connect to host <host> port 22: Operation not permitted
  • writing automation memory failed: operation not permitted: /Users/<user>/.codex/automations/<automation-id>/memory.md

The exact same prompt run manually in a new Codex chat succeeds, including the SSH-based audit and local report writes. So this looks like the automation launcher is not propagating the configured full-access/local policy into the actual first turn.

trajan-b · 1 month ago

Same issue

KIDA-MNESIA · 1 month ago

Same issue here.

Environment:

  • Codex Desktop: 26.611.61753 / bundle 4008
  • Bundled CLI: codex-cli 0.140.0-alpha.19
  • Platform: macOS / Darwin arm64
  • User config contains approval_policy = "never" and sandbox_mode = "danger-full-access"; codex doctor reports unrestricted filesystem, network enabled, approval Never for normal interactive sessions.

Observed in a scheduled local automation:

  • The run still launched with a managed/read-only permission context instead of full access.
  • brew upgrade failed before upgrading because it could not write Homebrew cache files (Operation not permitted).
  • npm update -g -d could not reach registry.npmjs.org during the automation run (ENOTFOUND / restricted network behavior), while the current interactive environment has network enabled.

This matches the described mismatch where interactive sessions honor full access, but unattended Desktop automations do not consistently inherit the same sandbox/network policy.

baptisteArno · 17 days ago

Additional evidence from a projectless scheduled automation on macOS.

Environment:

  • Codex Desktop: 26.623.101652, bundle 4674
  • Rollout CLI version: 0.142.5
  • Platform: macOS 26.5.1, Darwin arm64
  • User config contains:
approval_policy = "never"
sandbox_mode = "danger-full-access"
CODEX_HOME = "/Users/baptistearno/.codex"

Automation config:

id = "daily-sync"
kind = "cron"
execution_environment = "local"
cwds = ["~"]

Observed on 2026-07-03:

  1. The scheduled automation created a generated projectless cwd:
~/Documents/Codex/2026-07-03/sync-une-journ-e-compl-te
  1. The first automated turn in rollout 019f26ff-669b-7151-a54a-c67acd13d88d started with:
{
  "cwd": "~/Documents/Codex/2026-07-03/sync-une-journ-e-compl-te",
  "workspace_roots": ["~/Documents/Codex/2026-07-03/sync-une-journ-e-compl-te"],
  "approval_policy": "never",
  "sandbox_policy": { "type": "read-only" },
  "permission_profile": {
    "type": "managed",
    "file_system": {
      "type": "restricted",
      "entries": [{ "path": { "type": "special", "value": { "kind": "root" } }, "access": "read" }]
    },
    "network": "restricted"
  }
}
  1. The same thread/rollout, after reopening/re-entering from the UI, had a later turn context with:
{
  "sandbox_policy": { "type": "danger-full-access" },
  "permission_profile": { "type": "disabled" }
}

No config change happened between those two contexts.

  1. Same-minute scheduled automations launched from the trusted project ~/Dev/typebot.io started correctly with:
{
  "sandbox_policy": { "type": "danger-full-access" },
  "permission_profile": { "type": "disabled" }
}

Impact of the first-turn projectless read-only sandbox:

  • Workspace writes failed (touch work/write-test and shell here-doc temp creation both returned Operation not permitted).
  • Keychain-backed secrets that normally load through shell env were empty/unavailable (STRAVA_CLIENT_SECRET, STRAVA_REFRESH_TOKEN, Dougs, Gmail), so external API phases could not run.
  • macOS Calendar/JXA and Swift/EventKit were unusable in that context.
  • Automation memory writes failed, so the unattended job could not record its own failure.

Source/doc cross-check:

  • The public app-server protocol already supports ThreadStartParams.sandbox.
  • In @openai/codex@0.142.5, SandboxMode defaults to read-only.
  • ConfigToml::derive_permission_profile only falls back to workspace-write when the active project has a trusted or untrusted project decision; otherwise it uses the default sandbox mode.

This looks like the desktop automation scheduler/projectless path is starting the thread without passing the configured sandbox: "danger-full-access" and without anchoring the run to a trusted cwd, so app-server falls back to the read-only default for the first automation turn. Once the thread is opened in the UI, the correct full-access context is injected retroactively.

Liu-Da · 15 days ago

same issue

PZZZB · 10 days ago

Additional reproduction: cross-thread follow-ups also lose Full Access

This propagation bug is not limited to scheduled automations. I can reproduce the same policy downgrade when the Codex Desktop App sends a follow-up to an existing task through send_message_to_thread.

Environment

  • Codex Desktop App: 26.707.3748.0
  • Platform: Microsoft Windows NT 10.0.22631.0 x64
  • App permission selector: Full access

Reproduction

  1. Start from a source task whose effective turn context is:

``json
{
"approval_policy": "never",
"sandbox_policy": {"type": "danger-full-access"},
"permission_profile": {"type": "disabled"}
}
``

  1. Use the Desktop App's send_message_to_thread tool to send a follow-up to an existing long-running task.
  2. Inspect the first target turn created by the cross-thread delivery. It starts as:

``json
{
"approval_policy": "on-request",
"sandbox_policy": {
"type": "workspace-write",
"network_access": false
},
"permission_profile": {
"type": "managed",
"network": "restricted"
}
}
``

  1. Open the same target task and manually send continue with Full access still selected.
  2. The next direct/manual turn immediately returns to danger-full-access + approval_policy: never + disabled permission profile, with no config change.

Observed scope

  • 9 unique restricted turns in the affected long-running task.
  • Every restricted turn was a cross-thread <codex_delegation> follow-up.
  • Direct/manual turns in the same task consistently used Full access.
  • The rollout contains 159 exec_command calls with sandbox_permissions: "require_escalated" after these restricted turns.
  • On Windows, the managed sandbox path also frequently emits:

``text
CreateProcessWithLogonW failed: 1385
``
The agent then retries with escalation, causing repeated permission prompts even though the user selected Full access.

Expected behavior

A cross-thread follow-up should use either:

  1. the destination task's permission mode selected in the UI, or
  2. the source turn's effective sandbox/approval policy,

but it should not silently fall back to workspace-write/on-request.

At minimum, send_message_to_thread should expose explicit sandbox/approval overrides. This is also consistent with the requested API shape in #14923.

This appears to be the same underlying client-side policy propagation class described in this issue, but through the cross-thread follow-up launcher instead of the automation scheduler. All task IDs, local paths, and account details are intentionally redacted.