Skip to content

docs(sandbox): add GET /api/sandbox/reconnect reference#195

Merged
sweetmantech merged 1 commit into
mainfrom
docs/sandbox-reconnect
May 7, 2026
Merged

docs(sandbox): add GET /api/sandbox/reconnect reference#195
sweetmantech merged 1 commit into
mainfrom
docs/sandbox-reconnect

Conversation

@sweetmantech
Copy link
Copy Markdown
Collaborator

@sweetmantech sweetmantech commented May 7, 2026

Summary

Adds the OpenAPI spec and reference page for GET /api/sandbox/reconnect — the third Session Sandboxes endpoint required for the chat loading-UX cutover. Sequel to:

The matching api PR will follow once this lands.

Why now

Per the gap analysis on the open-agents → api cutover: `/reconnect` is the missing endpoint that drives the loading-state UX on session re-entry (user closes tab, comes back). Unlike `GET /api/sandbox/status` which is a DB-only read, `/reconnect` runs a live runtime probe inside the sandbox (`sandbox.exec("pwd")`) so the UI can distinguish:

  • A truly-alive sandbox we can resume immediately
  • A DB row that says "active" but the runtime is gone (need to flip the UI to offer resume-from-snapshot)
  • A session with no sandbox yet

Without this endpoint, returning to a hibernated session leaves the UI stuck in a polling loop.

Response shape

{
  \"status\": \"connected\" | \"expired\" | \"no_sandbox\",
  \"hasSnapshot\": boolean,
  \"expiresAt\": number,        // present only when status=\"connected\"
  \"lifecycle\": { /* SandboxLifecycle */ }
}

status distinguishes three operational outcomes (errors come back as 4xx with {status, error}):

Value Meaning
connected Live probe succeeded. expiresAt reflects the freshly-probed expiry, which may differ from lifecycle.sandboxExpiresAt if the sandbox extended itself between writes.
expired Recorded runtime state is no longer reachable. UI should offer resume-from-snapshot if hasSnapshot is true, otherwise fresh-create.
no_sandbox Session has never had a sandbox provisioned.

Refactor included

The inline lifecycle envelope previously on SandboxStatusResponse is extracted into a shared SandboxLifecycle component schema. Both /status and /reconnect now $ref the same definition, so the field set can't drift between them. Previously inline; now one source of truth.

File layout

File Change
api-reference/openapi/sandbox.json + /api/sandbox/reconnect path; + ReconnectResponse schema; + SandboxLifecycle component (status response refactored to $ref)
api-reference/sandbox/reconnect.mdx new frontmatter-only reference page
docs.json nav adds reconnect under "Session Sandboxes"

Net diff: +156 / -47 lines.

Test plan

  • `npx mintlify@latest dev` renders without errors
  • New "Reconnect to session sandbox" page appears under Agents & Sandboxes › Session Sandboxes
  • Reconnect page renders the sessionId query param + 200 / 400 / 401 / 403 / 404 responses
  • Status page still renders the lifecycle envelope after the $ref extraction (regression check)

What follows

After this lands:

  • api PR: handler under `apps/api/app/api/sandbox/reconnect/`, reusing `hasRuntimeSandboxState` + the abstraction's `connectSandbox` for the live probe
  • open-agents PR: point UI at `RECOUPABLE_API_BASE_URL` for the reconnect call, delete the local handler

🤖 Generated with Claude Code


Summary by cubic

Adds the OpenAPI spec and docs page for GET /api/sandbox/reconnect to power session re-entry by running a live sandbox probe. Also extracts a shared SandboxLifecycle schema used by both status and reconnect responses.

  • New Features

    • Added GET /api/sandbox/reconnect to the OpenAPI spec with sessionId query param.
    • Returns status (connected | expired | no_sandbox), hasSnapshot, optional expiresAt, and lifecycle.
    • New reference page at api-reference/sandbox/reconnect.mdx; added to Session Sandboxes nav.
  • Refactors

    • Extracted inline lifecycle envelope into a shared SandboxLifecycle component schema.
    • Updated /api/sandbox/status to $ref SandboxLifecycle to keep fields consistent.

Written for commit c41992f. Summary will update on new commits.

The third Session Sandboxes endpoint required for the chat loading-UX
cutover. Unlike GET /api/sandbox/status (DB-only read), /reconnect
runs a live runtime probe inside the sandbox to verify it is
reachable. Used by the chat UI on session re-entry / tab refocus to
decide whether to flip out of "loading sandbox..." or surface a
"resume" affordance.

Response distinguishes three operational outcomes via `status`:
- `connected` — runtime probe succeeded; `expiresAt` reflects the
  freshly-probed expiry
- `expired` — recorded runtime state is no longer reachable; UI
  should offer resume-from-snapshot or fresh-create
- `no_sandbox` — session has never had a sandbox provisioned

Refactor: extracted the inline `lifecycle` envelope previously on
`SandboxStatusResponse` into a shared `SandboxLifecycle` component
schema. Both `/status` and `/reconnect` now reference one source of
truth, and we won't drift the field set between them.

Files:
- api-reference/openapi/sandbox.json: new path + ReconnectResponse +
  SandboxLifecycle component (status response now $refs it)
- api-reference/sandbox/reconnect.mdx: frontmatter-only reference page
- docs.json: nav adds reconnect under "Session Sandboxes"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 14 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7431a0f0-1bed-43cc-b5e9-f7394855c997

📥 Commits

Reviewing files that changed from the base of the PR and between 8d61fc6 and c41992f.

📒 Files selected for processing (3)
  • api-reference/openapi/sandbox.json
  • api-reference/sandbox/reconnect.mdx
  • docs.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/sandbox-reconnect

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 3 files

@sweetmantech sweetmantech merged commit c44dea0 into main May 7, 2026
3 checks passed
@sweetmantech sweetmantech deleted the docs/sandbox-reconnect branch May 7, 2026 16:46
sweetmantech added a commit to recoupable/api that referenced this pull request May 7, 2026
* feat(sandbox): port GET /api/sandbox/reconnect from open-agents

Implements the live runtime probe endpoint required for the chat
loading-UX cutover on session re-entry / tab refocus. Matches the
contract documented in recoupable/docs#195 (now merged on main).

Unlike GET /api/sandbox/status (DB-only read), /reconnect actually
runs `sandbox.exec("pwd")` inside the runtime so the UI can
distinguish a truly-alive sandbox from a DB row whose sandbox no
longer exists.

Behavior:
- 200 status="no_sandbox" when sandbox_state lacks runtime metadata
  (delegates to hasRuntimeSandboxState — the same gate /status uses)
- 200 status="connected" with sandbox.expiresAt when the probe succeeds
- 200 status="expired" when the probe throws — also clears
  sandbox_state on the session row and sets lifecycle_state to
  "hibernated" so subsequent /status reads agree with the probe
- hasSnapshot derived from snapshot_url across all three outcomes
- 4xx for auth (401), missing sessionId (400), forbidden (403),
  not-found (404) — same envelope as /status

Files follow the existing api conventions established by PR #522:
- app/api/sandbox/reconnect/route.ts: thin GET delegation + OPTIONS
- lib/sandbox/getSandboxReconnectHandler.ts: handler logic
- Reuses validateAuthContext, selectSessions, hasRuntimeSandboxState,
  buildLifecycle, connectSandbox, updateSession
- Error envelope { status, error } matches sessions PRs

TDD red -> green:
- Handler tests cover auth fail, missing sessionId, 404, 403,
  no_sandbox, hasSnapshot derivation, connected (with expiresAt),
  expired (with state-clear assertion), and lifecycle envelope shape
- Thin route shell test asserts delegation
- Suite: 2516 -> 2526 (+10 net new tests), pnpm lint:check clean

Out of scope (deferred per the gap analysis):
- Transient vs unavailable error distinction. open-agents preserves
  runtime state on transient errors (network blip != dead sandbox).
  v1 treats every probe failure as expired, which is safer for the
  loading UX (user can retry). Worth porting once we have a real
  signal that this is happening in production.
- Stale-state lifecycle workflow kick (open-agents' /status has it
  too — needs workflow infra in api).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(sandbox): extract noSandboxResponse to its own file (SRP)

Per review feedback on PR #525 — pulls the inline `noSandboxResponse`
helper out of `getSandboxReconnectHandler.ts` into its own file so it
can be reused by future endpoints (e.g., `/snapshot` resume) and so
the handler file stops carrying response-shape construction logic.

The narrowed `ReconnectBody` type in the handler now only covers the
two outcomes the handler actually constructs locally (`connected` /
`expired`); the `no_sandbox` shape lives with its builder.

TDD red -> green: 3 unit tests for the extracted helper covering 200
status, hasSnapshot derivation, and lifecycle envelope projection.
Suite: 2526 -> 2529.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(sandbox): extract validateSandboxReconnectRequest + fix preview build

Two changes bundled:

1) SRP per review feedback (sweetmantech) — extract the auth +
   sessionId-from-query + session lookup + ownership check pre-flight
   from `getSandboxReconnectHandler` into its own
   `validateSandboxReconnectRequest.ts`. Mirrors the
   `validateCreateSandboxBody` pattern: returns either a 4xx
   NextResponse describing the first failure, or `{ row, auth }` for
   the handler to consume.

2) Type fix for `next build` — `connectSandbox(row.sandbox_state as
   SandboxState)` failed to compile against `Json` (union includes
   primitives + arrays); cast through `unknown` first. The
   `hasRuntimeSandboxState` gate above ensures the runtime shape is
   safe at the call site, so the double cast is justified — comment
   added explaining why.

The vitest pass alone wasn't enough to catch the type error — `next
build` runs a separate `tsc` step that the test runner skips. Caught
by the Vercel preview build failing on the previous commit.

TDD red -> green:
- 5 unit tests for the new validator covering auth fail (passes
  through), missing sessionId (400), session not found (404),
  ownership mismatch (403), and happy-path return shape ({row, auth})
- Existing handler tests pass unchanged — the module-level mocks for
  validateAuthContext / selectSessions still intercept the calls now
  that they're behind the new validator
- Suite: 2529 -> 2534, pnpm lint:check clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sidneyswift added a commit that referenced this pull request May 7, 2026
Pulls in three commits from main:
- Sandbox API: GET /api/sandbox/reconnect added (PR #195) — wired into our
  Sessions group alongside sandbox/create + sandbox/status
- Sandbox API: POST /api/sandbox request schema dropped branch +
  isNewBranch (PR #194) — auto-merged
- Music-video workflow: substantive content updates (PR #193) — async
  pipeline section, sandbox file fetch path,
  resolution. Took main's version verbatim then re-applied this PR's
  prose sweep (no em-dashes, no 'recipe' synonym for 'workflow').
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant