Skip to content

Runner: on the inprocess sandbox, an attachment workspace copy is written to the runner disk and the agent cannot read it #7198

Description

@mmabrouk

Summary

On the inprocess sandbox provider, an attachment that degrades to a workspace copy is written to the runner pod's own disk, while the model's tools read from a different filesystem (the per-conversation Daytona command sandbox and its geesefs mount of the object store). The agent's read then fails with ENOENT and the model tells the user the file "didn't sync into the workspace files".

Reproduced on two independent deployments, so this is not environment specific.

Reproduction

  1. Create an agent with harness.kind = pi_core and sandbox.kind = inprocess.
  2. Attach any image to a chat message and ask the model to read it.
  3. The delivery record is outcome=workspace_only, and the agent's read <workingPath> returns ENOENT.

A differential probe created fresh agents and sent the same generated PNG on both stages:

Deployment Sandbox Model outcome agent could read the file
AWS staging inprocess openrouter/deepseek-v4-flash workspace_only No, ENOENT
GKE staging inprocess vertex_ai/gemini-3.7-flash workspace_only No, ENOENT
AWS staging daytona openrouter/deepseek-v4-flash workspace_only Yes, 4402 bytes
GKE staging daytona vertex_ai/gemini-3.7-flash workspace_only Yes, 4402 bytes

daytona works on both. inprocess fails on both.

Root cause

materializeWorkingCopy in services/runner/src/engines/sandbox_agent/attachments.ts chooses daytonaMaterialize whenever the sandbox handle exposes mkdirFs / writeFsFile / statFs.

For the inprocess provider that handle is InProcessHarnessHost (services/runner/src/engines/inprocess/harness-host.ts). Its writeFsFile, mkdirFs and statFs use Node fs against the runner process's own disk. The class says so itself:

// ---- Runner files: callers are runner code, never the model; the runner's own disk

The model's tools do not run there. engines/inprocess/conversation-workspace.ts states the design:

All of Pi's tools run in the sandbox, on its mount of the drive; the runner opens no path the model chose and mounts nothing.

and runProcess on that same host object is documented as "the command sandbox, never the runner host". So the attachment is written on one filesystem and read from another.

Verified on a live runner pod: no /dev/fuse, no geesefs process, no FUSE mounts, and the attachment is absent both from the pod's local session directory and from the object store under the session working directory.

Impact

Every non-native attachment is unreadable on inprocess. That includes documents, and images whenever native delivery is unavailable. inprocess is auto-enabled wherever daytona is enabled, so this reaches normal users.

Suggested direction

Materialize the working copy through the command sandbox's filesystem (the store-backed mount) rather than the runner host disk. Either route materializeWorkingCopy to the command sandbox handle, or stop exposing host fs methods on the object the attachment chain receives, so the capability check cannot select the wrong target.

Acceptance

  • An inprocess agent can read an attached file that was delivered as a workspace copy.
  • A regression test covers inprocess specifically. The existing coverage passes because daytona writes to the right place.

Workaround

Set the agent's sandbox to Daytona.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions