Skip to content

Fixes #397: Abort sandbox exec on turn cancellation - #407

Open
rohanmalhotracodes wants to merge 9 commits into
truefoundry:mainfrom
rohanmalhotracodes:fix-sandbox-exec-abort
Open

rohanmalhotracodes wants to merge 9 commits into
truefoundry:mainfrom
rohanmalhotracodes:fix-sandbox-exec-abort

Conversation

@rohanmalhotracodes

@rohanmalhotracodes rohanmalhotracodes commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #397

Fixes sandbox turn cancellation so an in-flight sandbox exec can stop promptly when the turn is cancelled, instead of waiting for the command timeout.

sanity check added a video

Screen.Recording.2026-08-22.at.8.07.24.PM.mov

Changes

  • Thread the turn AbortSignal into SandboxOptions and SandboxExecParams.
  • Pass the signal only on the user-facing sandbox exec path, not init, skills, mkdir, uploads, downloads, or general tool execution.
  • Make Daytona abort force-stop the sandbox with sandbox.stop(timeout, true) and avoid recovery/retry of the cancelled command.
  • Make TFY abort the /exec fetch when the turn signal aborts.
  • Make Local abort kill the active exec process tree through the existing killExecTree path without deleting the sandbox root.
  • Add focused coverage for exec signal propagation and TFY fetch abort behavior.
  • Add a changeset for @truefoundry/trueforge-core and @truefoundry/trueforge.

How was this tested?

  • git diff --check
  • pnpm --filter @truefoundry/trueforge-core test -- sandboxBridgeTimeout tfyLayout
  • pnpm --filter @truefoundry/trueforge-core typecheck
  • pnpm --filter @truefoundry/trueforge typecheck
  • pnpm --filter @truefoundry/trueforge test

Checklist

  • I have read the contributing guidelines
  • pnpm build, pnpm test, pnpm typecheck, pnpm lint:ci, and pnpm format:check pass locally
  • Tests added/updated where it makes sense
  • No hand-edits to generated code (packages/trueforge-sdk, .github/fern/openapi/openapi.json, docs/openapi.json) — fork PRs omit SDK regen; maintainers regenerate after merge
  • Docs / .env.example updated if configuration or behavior changed

Acceptance Critera

  • 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.


Note

Medium Risk
Changes cancellation and teardown paths across Daytona, TFY, and local sandbox exec; incorrect abort handling could stop sandboxes early or skip legitimate recovery, but scope is limited to turn-cancel on user exec.

Overview
Fixes turn cancellation waiting on long sandbox exec timeouts by threading the turn AbortSignal from the turns API into Sandbox and SandboxExecParams, and stopping work when that signal aborts.

The signal is passed only for agent exec tool calls—not sandbox init, skill setup, uploads, or other internal provider.exec paths. Providers return SANDBOX_EXEC_ABORTED instead of hanging or mis-reporting failures.

Daytona registers abort to sandbox.stop(..., true), skips executeWithSandboxRecovery retry when the turn was cancelled, and evicts stale SDK cache entries without destroying the remote sandbox id. TFY ties turn abort to the /exec fetch (distinct from client timeout AbortError). Local kills the supervised process tree via killExecTree and surfaces abort without removing the sandbox root.

onSignalAbort now returns a cleanup disposer; isSignalAborted is exported from core. Tests cover signal propagation, TFY fetch abort, Daytona no-retry-on-abort, and local smoke abort behavior.

Reviewed by Cursor Bugbot for commit 1d5b7b9. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1d5b7b9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@truefoundry/trueforge-core Patch
@truefoundry/trueforge Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@debajyoti-truefoundry

Copy link
Copy Markdown
Contributor

@rohanmalhotracodes

Thanks for your contribution.

Can you attach a video that showcases

  1. Ask the agent to execute sleep 120.
  2. You click cancel on the UI, and the turn cancels promptly.

Please add a before-and-after video by changing the branch in between.

@chiragjn chiragjn added the core label Aug 24, 2026
@debajyoti-truefoundry

Copy link
Copy Markdown
Contributor

@rohanmalhotracodes

Please also confirm that you have covered all Acceptance Criteria present on #397

Feel free to copy them into this PR description.

@rohanmalhotracodes

Copy link
Copy Markdown
Contributor Author

@rohanmalhotracodes

Thanks for your contribution.

Can you attach a video that showcases

  1. Ask the agent to execute sleep 120.
  2. You click cancel on the UI, and the turn cancels promptly.

Please add a before-and-after video by changing the branch in between.

Before

Screen.Recording.2026-08-24.at.5.33.37.PM.mov

After

Screen.Recording.2026-08-24.at.5.45.34.PM.mov

@thesujai thesujai left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The video you uploaded is not very satisfactory.

For instance - From the video it is not very clear that next turn can be created after the cancellation or not(using the same sandbox, or code mode works etc)

You should test the acceptance criteria mention in the Issue

Comment thread packages/trueforge/src/sandbox/local/core/hostRun.ts Outdated
Comment thread packages/trueforge-core/src/core/sandbox/provider/DaytonaProvider.ts Outdated
@rohanmalhotracodes

Copy link
Copy Markdown
Contributor Author

Added coverage for the

  • Daytona: aborting an exec does not trigger sandbox recovery/restart or retry the cancelled command on DaytonaError.
  • Local: aborting a long exec returns promptly, then the same existingSandboxId can run another command and read the existing filesystem state.

I also reran pnpm smoke:local-sandbox, which passes and covers Local sandbox reuse plus Code Mode UDS behavior.

@thesujai

thesujai commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Just a note: #409 also adds signal to download method

@rohanmalhotracodes

Copy link
Copy Markdown
Contributor Author

Before

Screen.Recording.2026-08-25.at.1.03.35.PM.mov

After

Screen.Recording.2026-08-25.at.1.07.47.PM.mov
Screen.Recording.2026-08-25.at.1.08.14.PM.mov

@debajyoti-truefoundry @thesujai PTAL

@rohanmalhotracodes

Copy link
Copy Markdown
Contributor Author

Thanks for the heads-up. I kept this PR scoped to #397 and only pass the turn abort signal through the sandbox exec path. I did not add/forward the turn signal to downloadFile, since the issue explicitly says to support abort on exec only and not other sandbox operations.

@rohanmalhotracodes

Copy link
Copy Markdown
Contributor Author

sorry to bother here can I get more issues assigned please I cannot see help wanted issues as the newer issues have not yet been approved @govindavashishtha @debajyoti-truefoundry, I have got the chat deletion issue merged so I do have time to work on more

@thesujai

thesujai commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

@thesujai thesujai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@thesujai

Copy link
Copy Markdown
Contributor

This looks good.
Only thing is when cancelling a sandbox when it is initializing(in the first turn), it takes time because we are not passing the cancel signal to the initialization methods, but this is out of scope for this PR, will track that in a followup issue.

@rohanmalhotracodes

Copy link
Copy Markdown
Contributor Author

@chiragjn PTAL

@mstevens843

Copy link
Copy Markdown

I independently reviewed b476ce42ff15a1bc10c2e56fc55cf1a73a6ade7d and reran sandboxBridgeTimeout, tfyLayout, and daytonaSnapshotRegistration: all 12 tests passed.

I also ran two isolated local tests using the real provider and UDS transport. They confirmed child-process termination and stopped output after cancellation, a subsequent operation against the same sandbox identity, and a corrupt MCP tools cache triggering a real refetch and repair.

Review tests and reproduction notes

No issues found within the reviewed scope. These additional checks are at the provider/transport layer, not a complete session/turn lifecycle. I did not run live Daytona/TFY providers. The broader local smoke suite was blocked earlier by an unrelated pip-policy assertion, so I am not claiming that whole suite passed.

First-turn initialization cancellation remains the separate question in #433.

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop the sandbox when the cancel API cancels a turn

6 participants