Repository navigation
Cross-workspace delegation runs Pi in Supervised when the caller is in Auto #272
Description
Summary
A task delegated to a linked workspace inherits the caller's runtime mode without checking which modes the target provider offers. When the caller runs in Auto and the task targets Pi (which offers Supervised, Auto-accept edits and Full access, but not Auto), the thread on the other workspace is stored as auto. RuntimePolicy then runs it as approval-required, so Pi asks before every edit, write and command.
The same delegate_task within one workspace steps down to the closest supported mode (Auto-accept edits). The cross-workspace path skips that step. The behaviour comes from upstream code; the fork carries it unchanged.
Code path
apps/server/src/peer/RemoteDelegation.ts:243: whenruntimeModeis omitted orinherit, the mode is left out of the forwardedt3_thread_launchand "left to the other side".- On the target, the hub caller is a linked client.
clientModeCeiling(McpInvocationContext.ts) caps it at min(link access, calling agent's mode).t3_thread_launch(mcp/toolkits/project/handlers.ts:193) starts from the project'sdefaultRuntimeMode(defaultfull-access) and falls back to the ceiling.McpToolAccess.startsThreadsandresolveRuntimeModeget no target, so nothing checks the provider's supported modes. orchestration-v2/RuntimePolicy.tsproviderRuntimeMode: a mode outside the provider'ssupportedRuntimeModesbecomesapproval-required.- Pi receives
T3_PI_RUNTIME_MODE=approval-required(mcpInjection.ts, which also mapsautotoapproval-required), and itstool_callhook asks for everything except read-only tools.
By contrast, local delegate_task (OrchestratorMcpService.ts:2220) passes the resolved target to resolveRuntimeMode, which steps an inherited unsupported mode down to the most permissive supported one (delegatedTaskModes.ts:66). For an explicitly requested unsupported mode it fails with "would run as approval-required".
A plain t3_thread_launch with a Pi modelSelection from an Auto caller takes the same unchecked path (from code reading; not reproduced separately).
Reproduction (throwaway scratch tests, not committed)
Server chain: the t3_thread_launch handler with a linked client (linked: true, narrowedTo: auto), then RuntimePolicy.layerFromProjectStore with Pi's snapshot modes, then buildPiRpcLaunch. The calling agent is in Auto in every row.
| link access | B project default | delegate_task runtimeMode | stored on B | provider policy | T3_PI_RUNTIME_MODE |
|---|---|---|---|---|---|
| full-access | full-access | (omitted) | auto |
approval-required | approval-required |
| full-access | auto | (omitted) | auto |
approval-required | approval-required |
| auto | full-access | (omitted) | auto |
approval-required | approval-required |
| auto | auto | (omitted) | auto |
approval-required | approval-required |
| full-access / auto | auto-accept-edits | (omitted) | auto-accept-edits | auto-accept-edits | auto-accept-edits |
| auto-accept-edits | any | (omitted) | auto-accept-edits | auto-accept-edits | auto-accept-edits |
| full-access / auto / auto-accept-edits | any | auto-accept-edits |
auto-accept-edits | auto-accept-edits | auto-accept-edits |
| approval-required | any | (omitted) | approval-required | approval-required | approval-required |
| approval-required | any | auto-accept-edits |
— | runtime_mode_escalation_denied |
— |
resolveRuntimeMode("auto", undefined, {Pi target}) gives auto-accept-edits (local path). resolveRuntimeMode("auto", undefined) gives auto (remote path).
Pi hook (loadMcpBridge from mcpBridge.testkit.ts), showing which tool calls ask:
T3_PI_RUNTIME_MODE |
read / grep / ls | edit / write | bash |
|---|---|---|---|
| approval-required | runs | asks | asks |
| auto-accept-edits | runs | runs | asks |
| full-access | runs | runs | runs |
Workaround
Have the calling agent pass runtimeMode: "auto-accept-edits" to delegate_task explicitly. The link's access must be at least Auto-accept edits. An explicit mode also skips the target project's default.
Fix direction (upstream)
Resolve the remote launch's mode against the target provider's supported modes, as local delegate_task does. For example, t3_thread_launch could pass the resolved provider's supportedRuntimeModes to resolveRuntimeMode. Per the fork's working practices this stays unfixed here unless upstream fixes it; close this issue with the upstream commit.
Not checked live with a real Pi in two workspaces.
Activity
henrychu04 commentedon Oct 28, 2026
Upstream triage (upstream/main 9826dbb, 2026-10-28)
Reproduced on a clean upstream checkout with throwaway tests, which were deleted afterwards.
Linked environments end to end
Built on
RemoteDelegation.test.ts's harness: a laptop and a box served over HTTP, a real peer link atfull-access, a realdelegate_taskfrom the laptop parent, and the box'sThreadLaunchServicemocked to capture the launch. The stored mode then goes throughRuntimePolicy.layerFromProjectStorewith each driver's provider-level modes (Pistatus.ts; Grok, Fx, Musestatus.ts/acpSupport.ts).delegate_taskgivesruntimeMode: "auto"For Pi,
buildPiRpcLaunchthen setsT3_PI_RUNTIME_MODE=approval-required.The linked path differs from same-environment delegation wherever the inherited mode is missing but a broader mode than Supervised is offered: Auto→Pi and Full access→Fx. An explicit unsupported mode is accepted instead of refused.
t3_thread_launchwith a Pi model (handler harness)full-access, agent narrowed toauto)autoauto, no link (upstream only)autoBoth run as
approval-required. No peer link is needed to reach it.Cause and history
delegate_task.resolveRuntimeModetakes the target'ssupportedRuntimeModes, anddocs/orchestration-v2/orchestrator-mcp-server.mdstates the rule: "When the target listsruntimeModes, an explicitruntimeModeoutside that list is refused, and an inherited mode steps down to the broadest mode the target offers."delegate_task) came later. It resolves explicit modes against the parent only (resolveRuntimeMode(parent.thread.runtimeMode, input.runtimeMode), no target) and leaves omitted modes to the other side. There,t3_thread_launch→McpToolAccess.startsThreads→resolveRuntimeMode(caller.limits.runtimeMode, requested)also has no target, and so does the project-default step intoolkits/project/handlers.ts. fix(server): delegated Muse tasks no longer ask approval for every command pingdotgg/t3code#18065's rule therefore never applies on the linked path, nor to anyt3_thread_launch.resolveCompatibleRuntimeMode, bd84b17) shows such a thread as Supervised, so the UI is accurate there. The mode is just narrower than the parent's and than what same-environment delegation would give.Fix direction for upstream
Apply pingdotgg#18065's target check where the launch resolves its mode.
t3_thread_launch(startsThreadsand the project-default step) could pass the resolvedmodelSelection's providersupportedRuntimeModestoresolveRuntimeMode. That covers the linked delegation, the forwarded launch and local launches in one place. Optionally, the laptop side could also check an explicit mode against the target'sruntimeModesfromorchestrator_capabilitieswithenvironmentId.Not reported upstream yet.
henrychu04 commentedon Oct 28, 2026
Upstream PR: pingdotgg#26682. It applies pingdotgg#18065's target-mode check in
t3_thread_launch, which covers linkeddelegate_task, forwarded launches and local launches. Close this issue with the upstream commit once it lands and is synced.