Skip to content

feat(api-client, sdk): track sandbox snapshot captures by ID#73

Closed
mu-hashmi wants to merge 10 commits into
mainfrom
refactor/snapshot-from-sandbox
Closed

feat(api-client, sdk): track sandbox snapshot captures by ID#73
mu-hashmi wants to merge 10 commits into
mainfrom
refactor/snapshot-from-sandbox

Conversation

@mu-hashmi

@mu-hashmi mu-hashmi commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

This updates snapshot-from-sandbox clients to track the Snapshot accepted by the API instead of inferring completion from Sandbox state.

  • Decode the 202 response as SnapshotDto, add CAPTURING, and regenerate all six API clients.
  • Update TypeScript, Python sync/async, Ruby, Go, and Java SDKs to poll the returned Snapshot ID until ACTIVE.
  • Surface Snapshot ID, terminal state, and errorReason for ERROR and BUILD_FAILED.
  • Stop waiting on local timeout or cancellation without cancelling the server-side capture.
  • Keep TypeScript polling on the main API client rather than the Sandbox toolbox proxy.
  • Add per-language regression coverage and refresh generated SDK documentation.

Verification:

  • TypeScript focused tests: 58 passed.
  • Python sync/async tests: 44 passed.
  • Ruby specs: 46 passed.
  • Focused Go and Java tests passed.
  • All six generated API clients built successfully.
  • Full client build passed across 24 projects.
  • SDK documentation generated successfully for all five languages.

Summary by cubic

Snapshot-from-sandbox now returns a SnapshotDto, and all SDKs poll that snapshot by ID until it becomes ACTIVE. Added a "capturing" state, clearer failure/timeout messages, and kept TypeScript polling on the main API.

  • New Features

    • API returns 202 with SnapshotDto for POST /sandbox/{id}/snapshot; 409 on name conflicts.
    • Added SnapshotState "capturing"; SDKs poll the accepted snapshot ID and surface terminal state and errorReason.
    • TypeScript keeps polling via the main API client (not the toolbox).
  • Migration

    • Handle 202 responses and decode SnapshotDto instead of assuming 200/Sandbox.
    • Track Snapshot state (including "capturing") rather than Sandbox "snapshotting".
    • Handle 409 when a snapshot name already exists.
    • Treat timeout=0 as no timeout; negative timeouts are invalid in SDKs.
    • TypeScript: if constructing Sandbox manually, pass a SnapshotsApi instance alongside SandboxApi.

Written for commit 6d4928c. Summary will update on new commits.

Review in cubic

Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>
Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>

@beta-devin-ai-integration beta-devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review (Beta)

Signed-off-by: Muhammad Hashmi <mhashmi@berkeley.edu>

@beta-devin-ai-integration beta-devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review (Beta)

Comment on lines +105 to +114
String existingSandboxId = System.getenv("DAYTONA_EXISTING_SANDBOX_ID");
if (existingSandboxId != null && !existingSandboxId.isEmpty()) {
sandbox = daytona.get(existingSandboxId);
} else {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setName(sandboxName);
params.setLanguage("python");
params.setLabels(Collections.singletonMap("purpose", "e2e-test"));
sandbox = daytona.create(params, 60);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Java end-to-end test breaks when reusing an existing sandbox

When an existing sandbox is reused via the new environment-variable branch (E2ETest.java:105-114), the sandbox is fetched instead of created, so its real name never matches the freshly generated sandboxName, and the first ordered test's name check then fails.
Impact: Anyone who points the end-to-end suite at a pre-existing sandbox gets a spurious test failure (and the reused sandbox is also deleted in teardown).

How the name mismatch is introduced

In setUp, sandboxName is always assigned a unique value (E2ETest.java:97). When DAYTONA_EXISTING_SANDBOX_ID is set, setUp takes the daytona.get(existingSandboxId) branch and never applies sandboxName to the sandbox. The ordered test sandboxLifecycleHasIdNameAndOrganizationId asserts assertThat(sandbox.getName()).isEqualTo(sandboxName) (E2ETest.java:169), which cannot hold for a pre-existing sandbox whose name differs from the generated one. Additionally, tearDown calls sandbox.delete() (E2ETest.java:148), which would delete the reused sandbox.

Prompt for agents
In E2ETest.setUp, when the DAYTONA_EXISTING_SANDBOX_ID branch is taken (daytona.get(existingSandboxId)), the sandbox's name will not equal the freshly generated sandboxName, so the Order(1) test sandboxLifecycleHasIdNameAndOrganizationId's assertion assertThat(sandbox.getName()).isEqualTo(sandboxName) will fail. Consider updating sandboxName from the fetched sandbox (e.g. sandboxName = sandbox.getName()) when reusing an existing sandbox, or make the name assertion conditional on whether a sandbox was created vs. reused. Also review tearDown's sandbox.delete() so a reused, pre-existing sandbox is not unintentionally deleted.
Open in Devin Review (Beta)

Was this helpful? React with 👍 or 👎 to provide feedback.

@mu-hashmi mu-hashmi closed this Jul 17, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant