Skip to content

feat(devbox): add devbox.onEvict eviction notifications (SSE)#820

Merged
jrvb-rl merged 4 commits into
release-please--branches--main--changes--next--components--api-clientfrom
feat/devbox-on-evict-sse
Jul 24, 2026
Merged

feat(devbox): add devbox.onEvict eviction notifications (SSE)#820
jrvb-rl merged 4 commits into
release-please--branches--main--changes--next--components--api-clientfrom
feat/devbox-on-evict-sse

Conversation

@jrvb-rl

@jrvb-rl jrvb-rl commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Description

Follow-on to the server-side flex eviction-notification work (monorepo #10325 and its successor SSE endpoint). Adds the object-oriented client surface for eviction notifications.

New API: devbox.onEvict(callback) (and devbox.cancelOnEvict()) on the Devbox wrapper.

How it works (internal state management):

  • A per-client EvictionMonitor (src/sdk/eviction.ts) holds the interest set: Map<devbox_id, callback>. Monitors are looked up via a WeakMap<Runloop, EvictionMonitor> (getEvictionMonitor), so every Devbox built from the same generated client shares one monitor and thus one SSE connection. No Devbox/factory signatures changed.
  • The first onEvict across any devbox opens a single account-wide devboxes.watchEvictions() SSE stream via a background for await loop.
  • On each event: the id is matched against the interest set; unknown ids are discarded. The devbox is removed before its callback runs, so it fires at most once even if the server repeats a notification.
  • When the interest set empties, the stream's AbortController is aborted and the stream closed. Re-registering re-opens it.
  • The server replays all currently-pending evictions on connect, so late registrants are still notified.

Dependency / status

⚠️ Draft. This depends on the Stainless-generated devboxes.watchEvictions method and the DevboxEvictionEvent type (devbox_id, eviction_deadline_ms), which are generated downstream once the server endpoint lands. Those symbols are imported by assumed name from ../resources/devboxes/devboxes and will be reconciled once generation lands. No generated code was modified.

Note: jest.config.objects.js enforces 100% function coverage over src/sdk, so the object-oriented smoke test for onEvict/cancelOnEvict (plus a mocked tests/objects unit test) is required and will be added when the endpoint is available to exercise.

Testing

Deferred until the generated endpoint exists (see above).

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

❌ Object Smoke Tests Failed

Test Results

❌ Some smoke tests failed

Please fix the failing tests before checking coverage.

📋 View full test logs

@reflex-loop

reflex-loop Bot commented Jul 23, 2026

Copy link
Copy Markdown

Reflex agent status: Completed

The agent completed its work.

This PR was created by Reflex.

View the agent run →

This comment updates in place as the agent works.

@jrvb-rl

jrvb-rl commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Reconciled with the endpoint as landed:

  • callback is now callback(devbox, evictionDeadlineMs) (the Devbox object + Unix-ms deadline)
  • uses the real generated type DevboxEvictionEventView

Ordering: depends on runloopai/runloop#10404, which marks watch_evictions as streaming in openapi.stainless.yml so Stainless generates devboxes.watchEvictions() -> Stream<DevboxEvictionEventView>. Once that merges and Stainless regenerates (the .stats.yml bump lands the method + type), this rebases green.

@jrvb-rl
jrvb-rl force-pushed the feat/devbox-on-evict-sse branch from c5c33b0 to d8d6f4c Compare July 24, 2026 09:34
@jrvb-rl
jrvb-rl changed the base branch from main to release-please--branches--main--changes--next--components--api-client July 24, 2026 09:34
@jrvb-rl
jrvb-rl marked this pull request as ready for review July 24, 2026 10:04
@jrvb-rl
jrvb-rl requested review from james-rl and sid-rl July 24, 2026 10:04
@stainless-app
stainless-app Bot force-pushed the release-please--branches--main--changes--next--components--api-client branch from 3d5d772 to fc944fc Compare July 24, 2026 16:00
claude and others added 4 commits July 24, 2026 17:33
Adds an object-oriented `Devbox.onEvict(callback)` backed by a per-client
`EvictionMonitor`. The first registration opens a single account-wide
`watchEvictions` SSE stream; incoming notifications are matched against the local
interest set, the devbox is removed before its callback fires (at-most-once), and
the stream is closed once the interest set empties.

Depends on the Stainless-generated `devboxes.watchEvictions` method and
`DevboxEvictionEvent` type, which land downstream once the server endpoint merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Use the real generated event type DevboxEvictionEventView (the endpoint's view)
  in place of the assumed DevboxEvictionEvent.
- Invoke the callback as callback(devbox, evictionDeadlineMs): the Devbox object
  and the Unix millisecond deadline, instead of passing the raw event.

Requires the main-repo stainless config change that generates watchEvictions as
Stream<DevboxEvictionEventView> (runloopai/runloop#10404).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hermetic object-coverage test driving the shared EvictionMonitor via an in-memory
fake watch_evictions stream: asserts onEvict fires once with (devbox, deadline),
unmatched devbox ids are discarded, and cancelOnEvict aborts the stream. Gives the
object-coverage suite 100% function coverage of the eviction wrapper without a live
eviction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two bugs made devbox.onEvict never fire:

1. The generated watchEvictions() sends Accept: application/json, but the
   endpoint only streams for Accept: text/event-stream — otherwise it returns an
   empty text/plain 200. The monitor now forces the SSE Accept header.
2. The monitor ran the stream once and stopped. The server force-closes on
   leader change / slow consumer (and a long-lived HTTP/2 stream can drop), and
   expects the client to reconnect and re-read the snapshot. The monitor now
   reconnects with backoff until no devbox is still interested.

Mirrors the api-client-python fix, verified against dev (a forced flex drain
delivers on_evict for 20/20 devboxes before suspend).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jrvb-rl
jrvb-rl force-pushed the feat/devbox-on-evict-sse branch from 9f0afb1 to 3fcba8e Compare July 24, 2026 17:33
@jrvb-rl
jrvb-rl merged commit c4117b7 into release-please--branches--main--changes--next--components--api-client Jul 24, 2026
7 of 8 checks passed
@jrvb-rl
jrvb-rl deleted the feat/devbox-on-evict-sse branch July 24, 2026 18:39
@jrvb-rl jrvb-rl mentioned this pull request Jul 24, 2026
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.

3 participants