Skip to content

Stop the sandbox when the cancel API cancels a turn #397

Description

@debajyoti-truefoundry

What problem are you trying to solve?

When a user cancels a turn, the turn today waits for any running sandbox execution until that execution times out.

Proposed solution

Support abort on the exec tool only.
Do not add abort to other sandbox operations.

Give AbortSignal to Sandbox at construct time through SandboxOptions.
The factory already receives the signal.

Current behavior:

  • TurnSandboxFactory input includes signal: AbortSignal.
  • TurnResourceResolver.resolveSandbox sends signal to the factory.
  • The turn sandbox factory in turns.ts does not use the signal. It only reads spec, existingSandboxId, and tracing.
  • buildTurnSandbox and SandboxOptions do not take a signal.

Plan:

  1. Add optional signal?: AbortSignal to SandboxOptions. Keep that signal on the Sandbox instance when present.
  2. Add optional signal?: AbortSignal to SandboxExecParams. From Sandbox, send this.signal only on the exec tool path (handleExec → provider.exec). Do not send the turn signal on init, skills, or mkdir. Do not send abort through executeToolCalls.
  3. Change the factory and buildTurnSandbox so that they send the factory signal into new Sandbox(...).
  4. Update provider implementations so that they honor params.signal when present.
  5. Each provider uses the signal as follows:
  • Daytona: When the signal aborts, force-stop the sandbox with sandbox.stop(timeout, true) from local @daytona/sdk. force: true sends SIGKILL. The next turn can use the same sandbox id again. restoreExistingSandbox already calls start when the sandbox is not started. The filesystem stays. Process memory and Code Mode state do not stay.
  • TFY: In TFYSandboxProvider, abort the HTTP fetch when the turn signal aborts or when the exec timeout ends. How you combine the signals is an implementation detail.
  • Local: When the signal aborts, stop the current exec process tree. runSupervisorSession already uses killExecTree for timeout and for output overflow. killExecTree sends SIGKILL to the process group. See packages/trueforge/src/sandbox/local/core/hostRun.ts. Send params.signal into runSupervisorSession. Call the same killExecTree when the signal aborts. Do not delete the sandbox root.

When the cancel API aborts the turn signal, the exec tool can stop before the command timeout ends.

This change does not apply to freezeTurn with reason cancelled-for-next-turn.

Acceptance criteria

  • Client cancel during a long sandbox exec ends the turn without a wait for the full exec timeout. This applies to Daytona and Local.
  • After a Daytona cancel, executeWithSandboxRecovery must not start the sandbox and run the same cancelled command again on DaytonaError.
  • After a Daytona / Local stop, the next turn can reuse existingSandboxId. The sandbox starts. The filesystem is available. Any commands run successfully.
  • After a Daytona / Local stop, Code Mode works in the next turn.
  • After a Daytona / Local stop during Code Mode MCP use, the next turn recovers on-disk state without a hard failure.
    • If the mcp_client tools cache {server}.tools.json is corrupt or truncated, the client deletes it and refetches over NATS.
  • freezeTurn with reason cancelled-for-next-turn does not change.

Alternatives considered

  • Add a stop method on Sandbox and SandboxProvider instead of abort on exec. Do not do this now. Abort on the exec tool is enough for this cancel path.

Additional context

  • Callers send AbortSignal into the agent loop and into TurnSandboxFactory. The signal is not stored on Sandbox. The signal is not sent to provider.exec.
  • TFY aborts the HTTP fetch only when the exec timeout ends in TFYSandboxProvider.postExec. Requirement: also abort when the turn signal aborts.
  • Daytona executeCommand has no abort parameter. Force stop is on the sandbox method stop in local @daytona/sdk@0.204.1.
  • Sandbox also calls provider.exec for init, skills, and mkdir. Those paths stay without the turn abort signal.

Activity

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

Metadata

Metadata

Labels

enhancementNew feature or requesthelp wantedMaintainers will accept community code contributions for this issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions