feat(sandbox): accept branch + isNewBranch in POST /api/sandbox body#536
feat(sandbox): accept branch + isNewBranch in POST /api/sandbox body#536sweetmantech wants to merge 5 commits intotestfrom
Conversation
Closes the remaining open-agents parity gaps in the two read endpoints
that the chat UI hits on session re-entry / tab refocus.
**Status handler (`GET /api/sandbox/status`)**
- **Failed-state self-heal**: when `hasRuntimeSandboxState` matches but
`lifecycle_state === "failed"`, recovers to `active` + clears
`lifecycle_error` + refreshes `sandbox_expires_at`. Without this,
the UI gets stuck on "Paused" after a transient lifecycle eval
hiccup even though the runtime is still alive.
- **`hasSnapshot` recognizes hibernated state**: now true when
`snapshot_url` is set OR `lifecycle_state === "hibernated"` AND the
state has a resumable name. Previously only checked `snapshot_url`,
so paused-but-resumable sessions reported `hasSnapshot: false`.
**Reconnect handler (`GET /api/sandbox/reconnect`)**
- **Transient-error preservation**: only collapses to `expired` when
the probe error matches a known permanent-failure pattern
(`isSandboxUnavailableError`: 404 / 410 / "sandbox not found" /
"sandbox is stopped" / "sandbox probe failed" / "expected a stream
of command data"). Anything else (502 / connection reset / timeout)
is treated as transient: runtime state is preserved, response is
`connected` with a conservative `safeExpiresAt` (only forwarded if
still in the future). This avoids forcing a full sandbox rebuild
on a flaky network.
- **Aggressive cleanup gating**: not-found errors drop the resume
handle (sandbox is gone-gone, can't be brought back), but other
unavailable errors keep it via `clearUnavailableSandboxState` so a
future provision can reuse the name.
- **Expires sync**: on a successful probe, `sandbox_expires_at` is
refreshed from the live SDK state — without it the FE timer drifts
from reality.
- **Lifecycle recovery**: on a successful probe, if the row was in
`lifecycle_state: "failed"`, recovers to `active` + clears
`lifecycle_error`.
**New helpers (each its own SRP file with a vitest red→green pass):**
- `isSandboxNotFoundError` — 404 / sandbox-not-found patterns
- `isSandboxUnavailableError` — broader permanent-failure dispatcher
- `clearSandboxResumeState` — collapses state to just `{ type }`
- `clearUnavailableSandboxState` — picks between resume-clear and
state-clear based on the error class
Tests: 2622 / 2622 pass. Lint + tsc clean for changed files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per review: the inline helper in getSandboxReconnectHandler.ts is its own concern (read epoch-ms expiresAt off a sandbox state) and belongs in a dedicated file alongside the other state predicates. Adds `lib/sandbox/getStateExpiresAt.ts` with a focused test (numeric match, non-numeric reject, null/scalar guard). Reconnect handler now imports from the new path; no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…atus-reconnect-parity
Closes the last open-agents parity gap on the provision endpoint.
The web client already sends both fields with every request — they
were being silently dropped by the Zod schema, which meant sandboxes
provisioned via api always landed on the repo's default branch.
Real-world impact this unlocks:
- Sessions with a recorded `branch` (e.g. "develop") now check out
that branch on provision, matching what's persisted on the row.
- Sessions with `isNewBranch: true` (e.g. session bootstrap minted
`xy/abcd1234`) now create a fresh ref off the repo's default,
matching open-agents' behavior.
Body routing matches `connectSandbox.state.source`:
- `isNewBranch=false` (or omitted) → forwards as `{ branch }`
- `isNewBranch=true` → forwards as `{ newBranch }`
TDD: red tests for both branches + a kept-default case → green via
schema extension and a small spread in the source object. The prior
"strips unknown branch input" test is replaced with a "preserves
branch/isNewBranch" test plus a rejection test for non-boolean
isNewBranch.
Tests 2629 / 2629 pass. Lint + tsc clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Closing per author — branch / isNewBranch will be treated as ignored concepts going forward, no need to plumb them through the api. |
Summary
Closes the last open-agents parity gap on the provision endpoint. The web client already sends both fields with every request — they were being silently dropped by the Zod schema, so sandboxes provisioned via api always landed on the repo's default branch.
Body routing matches `connectSandbox.state.source`:
Why this matters now
Precursor for the open-agents → api cutover. Without this, cutting over POST /api/sandbox would silently break:
Test plan
🤖 Generated with Claude Code
Summary by cubic
POST /api/sandboxnow acceptsbranchandisNewBranchso sandboxes can check out an existing branch or create a new one, matching the web client/open-agents behavior. Omitting both fields still uses the repo’s default branch.branchand booleanisNewBranch; rejects non-boolean values.isNewBranch=true→ forward as{ newBranch };isNewBranch=falseor omitted → forward as{ branch }; both omitted → no branch fields sent.Written for commit 98d1c1f. Summary will update on new commits.