pinesandbox 0.3.18 → 0.3.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,36 +1,34 @@
1
- # PineSandbox Computer SDK (Ruby)
1
+ # Pine Computer Ruby SDK
2
2
 
3
3
  `pinesandbox` is the Ruby server SDK for Pine Computers. Create and reconnect
4
4
  Computers, run agent tasks, manage files, and authorize desktop access from
5
5
  your backend. Stop/Start restores saved cookies, learned skills, and localStorage.
6
6
  Pause/Resume retains live tabs, sessions, and memory when enabled for your project.
7
7
 
8
- See the [integration guide](https://pinesandbox.io/docs/integration) for setup,
8
+ See the [integration guide](https://console.pinecomputer.io/docs/integration) for setup,
9
9
  authentication, and examples. This README covers the Ruby API.
10
10
 
11
11
  ## Install
12
12
 
13
- Admitted users download the versioned Ruby SDK bundle from
14
- <https://pinesandbox.io/sdks/ruby>. Extract it under `vendor/`, then reference
15
- the bundle from your `Gemfile`:
16
-
17
- ```ruby
18
- gem "pinesandbox", path: "vendor/pinesandbox-ruby-sdk-<version>"
13
+ ```sh
14
+ bundle add pinesandbox
19
15
  ```
20
16
 
21
- Use the directory name of the bundle you extracted; the download page shows the
22
- exact line for the latest release.
23
-
24
- No GitHub account or package token is required. See the
25
- [integration guide §Install](https://pinesandbox.io/docs/integration#1-install)
26
- for the download and extraction commands.
17
+ Bundler pins it to the latest release's compatibility line (`"~> 0.3.<patch>"`), so
18
+ `bundle update` takes later patches but not a new minor version. Using it needs a project key (`pk_…`) from the
19
+ [Console](https://console.pinecomputer.io). Pine Computer is in beta:
20
+ [request access](https://pinecomputer.io/#access) if you don't have one yet. Each
21
+ release is also an archive on the [Ruby SDK page](https://console.pinecomputer.io/sdks/ruby),
22
+ for vendoring.
27
23
 
28
24
  The current beta SDK is on the `0.3` compatibility line.
29
25
 
30
26
  ## Create a Computer
31
27
 
32
- A **Computer** is the identity and saved browser state you keep. A **runtime** is
33
- one running allocation of it. A **session** is work inside that runtime.
28
+ A **Computer** is the identity and saved browser state you keep. A **session** is
29
+ work inside the running Computer.
30
+ [Terms](https://console.pinecomputer.io/docs/how-a-computer-works#terms) defines
31
+ the rest.
34
32
 
35
33
  The examples use `store` for your application's encrypted database; it is not an
36
34
  SDK object. Save credentials **before** Create, then save the returned binding.
@@ -49,7 +47,7 @@ store.save_credentials(credentials.id, credentials.to_json)
49
47
  computer = client.create_computer(credentials)
50
48
  store.save_binding(computer.id, computer.binding.to_json)
51
49
 
52
- session = computer.create_session(name: "main", browser: true)
50
+ session = computer.create_session(browser: true) # Pine picks the name: save session.name
53
51
  session.create_tab(url: "https://example.com")
54
52
  ```
55
53
 
@@ -58,11 +56,12 @@ The two records have different lifetimes:
58
56
  | Save | When | Used for |
59
57
  |---|---|---|
60
58
  | `credentials.to_json` | Before Create; again if you change keys | Starting this Computer again and decrypting its saved state |
61
- | `computer.binding.to_json` | After each successful Create or Start | Reconnecting to that exact runtime from any backend |
59
+ | `computer.binding.to_json` | After each successful Create or Start | Reconnecting to the running Computer from any backend |
62
60
 
63
61
  Both contain secrets. Keep them encrypted on your backend, never in a browser.
64
62
  For a one-off script, `client.with_computer { |computer| … }` creates and stops the
65
- Computer for you, raising if Stop cannot be confirmed.
63
+ Computer for you, raising if Stop cannot be confirmed. When the block raises, its
64
+ error propagates and an unconfirmed Stop is only warned about.
66
65
 
67
66
  ## Reuse it from another backend
68
67
 
@@ -75,12 +74,13 @@ state = computer.status # e.g. :running, :paused, :resuming
75
74
  ```
76
75
 
77
76
  Reconnect reconstructs a handle locally. It makes no request, creates nothing,
78
- and does not replay work. Its next call targets the saved runtime, even if another
79
- backend has since replaced it. Store the binding revision alongside its JSON in
80
- your existing Computer record. Save a replacement only if its revision is newer
81
- (or it is the identical binding); a delayed reply must not overwrite a newer one.
82
- After confirmed Stop, clear the binding only if the stored revision and runtime
83
- still match the one stopped. Keep that revision and the durable credentials.
77
+ and does not replay work. Its next call goes to the Computer sandbox the binding
78
+ names, even if another backend has since started the Computer again. Store the binding
79
+ revision alongside its JSON in your existing Computer record. Save a replacement
80
+ only if its revision is newer (or it is the identical binding); a delayed reply
81
+ must not overwrite a newer one.
82
+ After confirmed Stop, clear the binding only if the stored revision and sandbox
83
+ id still match the one stopped. Keep that revision and the durable credentials.
84
84
  Session records belong to a binding revision and cannot be reused after Start.
85
85
 
86
86
  To continue a session, also save its name and token when you create it, then use
@@ -93,10 +93,10 @@ worker can wait for existing work rather than submit it again. See
93
93
 
94
94
  | Intent | Call | Result |
95
95
  |---|---|---|
96
- | Finish using compute | `computer.stop` | Saves supported state, releases the runtime and ends its sessions |
97
- | Start a stopped Computer | `client.start_computer(credentials)` | Restores saved state into a new runtime; create new sessions |
98
- | Keep live memory for later | `computer.pause` | Retains the runtime and sessions; requires project opt-in |
99
- | Continue paused work | `computer.resume` | Restores the retained runtime and sessions |
96
+ | Finish using compute | `computer.stop` | Saves supported state, shuts the Computer down and ends its sessions |
97
+ | Start a stopped Computer | `client.start_computer(credentials)` | Runs the Computer from its saved state; create new sessions |
98
+ | Keep live memory for later | `computer.pause` | Keeps the Computer and its sessions in memory; requires project opt-in |
99
+ | Continue paused work | `computer.resume` | Brings back the paused Computer and its sessions |
100
100
  | Observe after a connection failure | `computer.status` | Reads state without changing or renewing it |
101
101
 
102
102
  ```ruby
@@ -107,13 +107,14 @@ computer = client.start_computer(credentials)
107
107
  store.save_binding(computer.id, computer.binding.to_json)
108
108
  ```
109
109
 
110
- Stop/Start is also how an application replaces an old runtime. An SDK upgrade or
110
+ Stop/Start is also how an application updates a Computer. An SDK upgrade or
111
111
  Pine image rollout does not replace a running Computer. Start can receive a
112
112
  compatible older warm revision during rollout; it does not guarantee a build.
113
113
 
114
- `attach_computer` remains an alias for Start. **Use Reconnect for an existing
115
- runtime.** A failed Resume does not fall back to Start. Stop/Start restores saved
116
- browser state, not live tabs, session files or page memory. Export files first.
114
+ `attach_computer` remains an alias for Start. **Use Reconnect for a Computer
115
+ that's running or paused.** A failed Resume does not fall back to Start.
116
+ Stop/Start restores saved browser state, not live tabs, session files or page
117
+ memory. Export files first.
117
118
  [Persistence and teardown](#persistence--teardown) covers completion, recovery and
118
119
  permanent deletion.
119
120
 
@@ -121,19 +122,17 @@ permanent deletion.
121
122
 
122
123
  `timeout:` is the inactivity window (default five minutes), not an HTTP request
123
124
  timeout. Work renews it; reading status, watching events and other observation do
124
- not. It starts at allocation, so a one-minute window can expire during slow setup.
125
+ not. It starts when the Computer starts, so a one-minute window can expire during
126
+ slow setup.
125
127
 
126
- Projects can opt into a lifetime policy. The standard allowance is one minute to
127
- 24 hours of idle time and seven days of maximum runtime age; approved project
128
- limits can be higher. Existing allocations retain their admitted terms.
129
- Enrollment applies to new allocations from Ruby 0.3.13 / Go 0.3.15 and later,
130
- including integrations that do not use the new policy methods. A requested
131
- timeout above the allowance, including the SDK default, is rejected. Before
132
- enrolling a project, check its integrations' requested timeouts and handling of
133
- maximum age. Deployment alone does not enroll projects or alter existing runtimes.
128
+ Projects have a lifetime policy unless Pine removed it for one. The standard
129
+ allowance is one minute to 24 hours of idle time and seven days of maximum age;
130
+ approved project limits can be higher. A project without one has no maximum age
131
+ and no idle maximum. A running Computer keeps the terms it started with.
132
+ A requested timeout above the allowance, including the SDK default, is rejected.
134
133
 
135
134
  ```ruby
136
- details = computer.runtime_details
135
+ details = computer.sandbox_details
137
136
  if details.lifecycle
138
137
  updated = computer.set_idle_timeout(seconds: 3600)
139
138
  puts updated.lifecycle.idle_timeout_seconds
@@ -141,67 +140,71 @@ if details.lifecycle
141
140
  end
142
141
  ```
143
142
 
144
- For an enrolled runtime, changing idle timeout takes effect now while running,
145
- or on the next Resume while paused. It cannot extend maximum age or snapshot
146
- retention. Legacy runtimes keep their previous expiry clock while paused; enrolled
147
- runtimes use snapshot retention and maximum age. An identical value does nothing.
143
+ For a Computer started after enrollment, changing idle timeout takes effect now
144
+ while running, or on the next Resume while paused. It cannot extend maximum age or
145
+ snapshot retention. Computers started before enrollment keep their previous expiry
146
+ clock while paused; those started after use snapshot retention and maximum age. An
147
+ identical value does nothing.
148
148
  After a conflict or lost response,
149
- read `runtime_details` before deciding whether to edit again.
149
+ read `sandbox_details` before deciding whether to edit again.
150
150
 
151
151
  Maximum age includes paused time. Graceful Stop begins twelve minutes before the
152
152
  hard deadline; expiry is not proof that a final save succeeded. Plan Stop/Start
153
153
  at a safe point before that deadline when you need uninterrupted work.
154
154
 
155
- `runtime_details` describes the current allocation, including optional lifecycle
156
- terms and its opaque `runtime_revision`. Missing lifecycle means the runtime
157
- has no admitted project lifetime policy. `client.project_policy` optionally reads
158
- allowances for future allocations; ordinary operations do not require that call.
155
+ `sandbox_details` describes the current Computer sandbox, including optional
156
+ lifecycle terms and its opaque `runtime_revision`. Missing lifecycle means the Computer
157
+ was started without a project lifetime policy. `client.project_policy` optionally reads
158
+ allowances for future starts; ordinary operations do not require that call.
159
159
 
160
- `ready_timeout:` bounds allocation/readiness and `bind_ready_timeout:` bounds the
161
- following handshake. They do not change runtime expiry. For example,
162
- `timeout: 28_800, ready_timeout: 120` requests eight hours of idle time with a
163
- two-minute readiness wait. These waits are separate from transport retries and
164
- cleanup; see the [integration guide](https://pinesandbox.io/docs/integration).
160
+ `ready_timeout:` bounds waiting for the Computer to start and become ready, and
161
+ `bind_ready_timeout:` bounds the following handshake. They do not change the
162
+ Computer's expiry. For example, `timeout: 28_800, ready_timeout: 120` requests
163
+ eight hours of idle time with a two-minute readiness wait. These waits are
164
+ separate from transport retries and cleanup; see the
165
+ [integration guide](https://console.pinecomputer.io/docs/integration).
165
166
 
166
167
  ## Handle failures without replacing live work
167
168
 
168
- - A network error or `alive? == false` does not prove the runtime is gone.
169
- Read `status`; its `NotFoundError` confirms runtime-record absence. A
170
- data-plane `RuntimeGoneError` alone does not: paused/restoring Computers can
169
+ - A network error or `alive? == false` does not prove the Computer has stopped.
170
+ Read `status`; its `NotFoundError` confirms that it has. A
171
+ data-plane `SandboxGoneError` alone does not: paused/restoring Computers can
171
172
  temporarily have no route.
172
- - `RuntimeRetainedError` (a `BindError`) means Start found an existing runtime.
173
- Match its `runtime_id` to your saved binding, or reload the latest binding from
173
+ - `SandboxRetainedError` (a `BindError`) means Start found the Computer still
174
+ running or paused.
175
+ Match its `sandbox_id` to your saved binding, or reload the latest binding from
174
176
  your database. Reconnect, Resume or finish Stop as appropriate; an ID alone
175
177
  cannot reconstruct a missing token.
176
178
  - A Stop timeout means completion is unconfirmed, not canceled. Keep the binding
177
179
  and observe or retry Stop. Older servers report `StopSaveError` for a failed or
178
- unconfirmed save; current servers still release the runtime.
180
+ unconfirmed save; current servers still shut the Computer down.
179
181
  - Serialize Create/Start per Computer in your application. On
180
182
  `BindingRevisionConflictError`, reload the winner's saved binding instead of
181
183
  retrying with a higher revision.
182
184
  - If a registered Computer's Start reply is lost before saving its binding,
183
185
  serialize recovery, call `client.stop_computer(id)`, then Start separately with
184
- the saved credentials. This releases the runtime whose token was lost.
186
+ the saved credentials. This stops the Computer whose token was lost.
185
187
  - An interrupted first Create can fail before registering the Computer. Stop-by-ID
186
188
  then returns true, but Start raises `UnknownComputerError`. For that initial
187
189
  creation, retry Create with your saved credentials after confirming the project
188
190
  and ID are correct and the identity was not deleted. Do not treat every unknown
189
- ID as permission to create. An allocation abandoned before registration may
190
- remain until expiry; Stop-by-ID cannot discover it.
191
+ ID as permission to create. A Computer sandbox started before registration may
192
+ run until it expires; Stop-by-ID cannot find it.
191
193
  - On `AttachAuthorizationCommittedError`, registration has succeeded. Retain its
192
194
  non-nil credentials and reconcile the registered Computer before Start. Private
193
195
  keys cannot be recovered from an ID.
194
196
 
195
- Allocation retries within one call reuse its create ID. A fresh call is a new
196
- attempt. Failed attach attempts clean up known allocations best-effort; process
197
- crashes rely on expiry. Longer requested timeouts can leave abandoned compute
198
- running longer. See [Errors](#errors) for the error types.
197
+ Retries within one call reuse its create ID. A fresh call is a new attempt. A
198
+ failed attach shuts down the Computer sandbox it started, on a best-effort
199
+ basis; after a process crash, that Computer sandbox runs until it expires.
200
+ Longer requested timeouts can leave abandoned compute running longer. See
201
+ [Errors](#errors) for the error types.
199
202
 
200
203
  ## Stored credentials and older integrations
201
204
 
202
205
  The project API key stays on your backend. Computer credentials contain the state
203
206
  key, current and retained capture keys, prior state keys and ephemeral mode.
204
- Bindings contain the runtime ID, Computer token and binding revision. The SDK
207
+ Bindings contain the sandbox ID, Computer token and binding revision. The SDK
205
208
  handles short-lived project and attach authorization internally.
206
209
 
207
210
  `computer.credentials` returns an independent copy. Credentials and bindings use
@@ -219,8 +222,8 @@ For integrations using older storage or field-based methods:
219
222
  - Starting from a credentials value requires custom credential providers to
220
223
  implement `current_binding` and `credentials`. Bare callables still support
221
224
  Create and the field-based Attach form with an explicit binding revision.
222
- - `control_plane` and sandbox handles are SDK internals. Integrations use the
223
- Computer methods above.
225
+ - `control_plane` and the handles it returns are SDK internals. Integrations use
226
+ the Computer methods above.
224
227
 
225
228
  ## Sessions
226
229
 
@@ -312,7 +315,7 @@ same.
312
315
 
313
316
  `run` raises `409` if a turn is already running, or a person or your own agent
314
317
  holds the drive slot. Routes: `/v2/sessions/{name}/agent/*` in the
315
- [Computer API reference](https://pinesandbox.io/spec/computer).
318
+ [Computer API reference](https://console.pinecomputer.io/spec/computer).
316
319
 
317
320
  ### Activity
318
321
 
@@ -350,11 +353,11 @@ observation; do not guess from nearby events. A text-only update leaves the last
350
353
  captured screen and its original attribution visible. Keep historical selection
351
354
  separate from following new frames; the event ID identifies each image for replay.
352
355
 
353
- This SDK requires a Computer runtime with the public V2 agent contract. There is
356
+ This SDK requires a Computer that serves the public V2 agent contract. There is
354
357
  no fallback to native trace. Activity includes progress, questions and results;
355
358
  native commands, reasoning and provider metadata are internal-only. Upgrade
356
- agent consumers before moving them to the new runtime: its legacy agent routes
357
- reject ordinary bindings.
359
+ agent consumers before you restart their Computers on the new release: its legacy
360
+ agent routes reject ordinary bindings.
358
361
 
359
362
  Paged history uses the same typed events as streaming. Scope every saved cursor
360
363
  to its Task; after a session replacement, discover the new Task before replaying
@@ -387,47 +390,42 @@ drive.upload_file("input[type=file]", "resume.pdf") # set a file input (deposi
387
390
 
388
391
  `observe` is read-only (allowed even while Pine's agent runs — you can watch);
389
392
  `computer_use` / `upload_file` take the drive slot (`409` if Pine's agent or a person
390
- holds it). Pine's resident agent can use private DOM/browser tools through
391
- `session.delegate`; direct customer shell execution runs public programs and
392
- works with session files. It cannot invoke Pine's private browser implementation.
393
- Raw CDP is an image-owned runtime transport and is intentionally absent from
394
- the SDK surface. Tab helpers
393
+ holds it). Shell commands run ordinary programs and work with the session's
394
+ files. Tab helpers
395
395
  (`create_tab`/`tabs`/`patch_tab`/`close_tab`), `focus!`, and `epoch` round out the manual
396
396
  surface. Public routes are defined by the
397
- [Computer API reference](https://pinesandbox.io/spec/computer).
397
+ [Computer API reference](https://console.pinecomputer.io/spec/computer).
398
398
 
399
- ## Files & artifacts
399
+ ## Files
400
400
 
401
- A session has a **workdir** (where `exec` runs and the agent works), an inbound
402
- **attachments** root (files you hand the agent), and an **artifact registry** (the
403
- session's managed outputs).
401
+ A session has a **workdir** (where `exec` runs and the agent works) and an inbound
402
+ **attachments** root (files you hand the agent), which appears inside the workdir as
403
+ `attachments/`.
404
404
 
405
405
  ```ruby
406
406
  # Read the workdir (what the agent produced / your shell wrote):
407
407
  session.list_files(pattern: "*.csv") # => [FileEntry, ...]
408
408
  session.read_file("out/report.csv") # => bytes (binary encoding)
409
+ session.open_file("out/export.zip") { |chunk| io.write(chunk) } # stream it without buffering
409
410
 
410
- # Deposit a material the agent should use. It lands in attachments/ and surfaces
411
- # to the agent via the materials preamble — a sibling of the workdir, so it is NOT
412
- # read_file-able (read_file serves the workdir).
411
+ # Give the agent an input file. It lands in attachments/ (readable as
412
+ # read_file("attachments/brief.md")), and the agent sees it when its next turn starts.
413
413
  session.write_file("brief.md", File.read("brief.md"))
414
414
 
415
- # Artifacts — the session's managed outputs, each with metadata + a download.
416
- art = session.upload_artifact("logo.png", File.binread("logo.png")) # => Models::Artifact
417
- session.list_artifacts # all, newest-first
418
- session.list_artifacts(turn_id: "turn_…") # just one agent turn's outputs
419
- session.read_artifact(art.id) # => bytes
420
- session.zip_artifacts # => a .zip of all artifacts (bytes)
421
- session.open_artifact(art.id) { |chunk| io.write(chunk) } # stream it without buffering
415
+ # The files an ended turn produced, by workdir path:
416
+ turn.result.artifacts.each do |file|
417
+ path = file.fetch("relative_path")
418
+ save(path, session.read_file(path))
419
+ end
422
420
  ```
423
421
 
424
- `created_by` names the producer: `upload` (you), `download` (a browser download,
425
- auto-indexed), `agent` (a turn's outputs, durably captured at the turn's result). Only
426
- `agent` artifacts carry a `turn_id`, so the `turn_id` filter scopes to one turn's
427
- outputs; uploads/downloads always appear unfiltered. Uploads are immutable +
428
- id-addressed (a repeated filename yields a distinct artifact). Routes: `/files`,
429
- `/artifacts[/{zip,artifact_id}]` in the
430
- [Computer API reference](https://pinesandbox.io/spec/computer).
422
+ A turn's result lists its files by `relative_path` in the workdir, with size, SHA-256
423
+ and media type. They aren't copies: read them when the turn ends, because a later turn
424
+ or shell command can change them. A browser download isn't one of them; to get one
425
+ back, ask for it in the goal, and the agent copies it into the workdir and lists it.
426
+ Without a block, `open_file` returns an Enumerator of the chunks. Session files last
427
+ until the Computer stops; save what you need first. Routes: `/files` in the
428
+ [Computer API reference](https://console.pinecomputer.io/spec/computer).
431
429
 
432
430
  ## Skills — `learn` / `teach` (and when each blocks)
433
431
 
@@ -477,8 +475,8 @@ gate: nothing learned reaches another agent until you activate it.
477
475
 
478
476
  ```ruby
479
477
  computer.checkpoint # synchronous state save (content-hash dedup) — before a risky step
480
- computer.stop # save cookies, learned skills and localStorage, then delete the sandbox
481
- computer.kill # delete without an acknowledged save
478
+ computer.stop # save cookies, learned skills and localStorage, then shut the Computer down
479
+ computer.kill # shut the Computer down without waiting for a save
482
480
  client.delete_computer(computer) # PERMANENT: kill if attached, then delete id + saved state
483
481
  ```
484
482
 
@@ -487,10 +485,10 @@ client.delete_computer(computer) # PERMANENT: kill if attached, then delete id
487
485
  state is purged after 24 hours, and the id is never reusable. Deleting an unknown,
488
486
  already-deleted, or other project's id also returns `true`. There is no undelete.
489
487
 
490
- `stop` submits graceful Stop to the runtime controller, then waits up to 390s
491
- (`wait_timeout:`) for compute release. The controller tries to save supported state
488
+ `stop` submits graceful Stop to Pine's controller, then waits up to 390s
489
+ (`wait_timeout:`) for the Computer to shut down. The controller tries to save supported state
492
490
  before deletion and continues if the SDK process disappears. Saving is best effort:
493
- a save that fails or is not confirmed by the deadline still releases the runtime,
491
+ a save that fails or is not confirmed by the deadline still shuts the Computer down,
494
492
  and `stop` returns true; the next Start restores the last saved state. Reconstruct
495
493
  the saved handle with `reconnect_computer` to observe or join the same operation from
496
494
  another backend. Only an older server keeps the source and raises
@@ -498,49 +496,49 @@ another backend. Only an older server keeps the source and raises
498
496
  failure from an unconfirmed outcome after the Computer's services restart.
499
497
  A `false` return means completion is unconfirmed. Keep the saved binding for retry.
500
498
  After Stop returns true, clear the stored binding only if its revision and
501
- runtime still match the handle stopped; a concurrent Start may have replaced it.
499
+ sandbox id still match the handle stopped; a concurrent Start may have replaced it.
502
500
  Keep the revision fence, identity, state key and capture keypairs for future Start. Ephemeral
503
- bindings skip persistence; the runtime's binding record decides this.
501
+ bindings skip persistence; Pine's binding record decides this.
504
502
 
505
- If a Start response was lost before saving the runtime token, use
506
- `client.stop_computer(id)` to stop the currently bound runtime with project
503
+ If a Start response was lost before saving the Computer token, use
504
+ `client.stop_computer(id)` to stop the Computer with project
507
505
  authorization alone, then call Start separately. This lookup selects one binding;
508
506
  it never follows a concurrent newer binding. Private keys remain necessary for
509
507
  Start and cannot be recovered from the ID.
510
508
 
511
- A true result proves compute release or authoritative runtime absence. It does
512
- not prove a final save by a runtime that disappeared, and provider snapshot cleanup
513
- can still be running. Start restores only the latest committed state. Cookies,
514
- learned skills and compatible localStorage survive supported saved-state recovery;
515
- tabs, session files, page memory and IndexedDB do not.
509
+ A true result proves the Computer has shut down or was already gone.
510
+ It does not prove a final save when the Computer had already gone, and provider
511
+ snapshot cleanup can still be running. Start restores only the latest committed
512
+ state. Cookies, learned skills and compatible localStorage survive supported
513
+ saved-state recovery; tabs, session files, page memory and IndexedDB do not.
516
514
 
517
- `start_computer` reads the current binding revision once, checks that the old
518
- runtime is gone or released, waiting for an already accepted deletion if needed,
515
+ `start_computer` reads the current binding revision once, checks that the
516
+ Computer has stopped, waiting for an already accepted shutdown if needed,
519
517
  and creates a new binding. A conflict stays a conflict;
520
518
  it never adopts a newer revision and displaces another worker's winner. For a
521
- recorded failed runtime, explicitly call
522
- `client.recover_computer(id, key: key, capture_keypair: keypair)`.
523
- Recovery accepts possible unsaved-state loss, releases the observed failed runtime
524
- with a version precondition, waits up to 90s for compute release, then Starts. It
525
- refuses healthy running/paused runtimes and an unresolved capture timeout. If
526
- release times out, retry later; no replacement has been allocated. `kill` remains
519
+ Computer you recorded as failed, explicitly call
520
+ `client.recover_computer(credentials)`.
521
+ Recovery accepts possible unsaved-state loss, shuts the failed Computer down
522
+ with a version precondition, waits up to 90s for it to shut down, then Starts. It
523
+ refuses healthy running or paused Computers and an unresolved capture timeout. If
524
+ shutting it down times out, retry later; nothing new has been started. `kill` remains
527
525
  an unconditional destructive operation.
528
526
 
529
- This SDK's retryable allocation and server-owned Stop require the updated backend
530
- and a compatible Computer runtime; neither requires Pause opt-in. Existing
527
+ This SDK's retryable Start and server-owned Stop require the updated backend
528
+ and a compatible Computer; neither requires Pause opt-in. Existing
531
529
  Computer create, capture/Finish and delete calls remain supported for applications
532
530
  keeping their prior SDK. Complete the backend rollout before upgrading, including
533
531
  the conditional-release support used by recovery. A pool image update does not
534
532
  upgrade an already-running Computer: stop old-image bindings with the prior SDK
535
533
  before upgrading their caller. Integrations using the removed generic
536
- public sandbox operations must migrate to the Computer contract; the private Ruby
534
+ public lifecycle operations must migrate to the Computer contract; the private Ruby
537
535
  `ControlPlane#create` adapter is removed as well.
538
536
 
539
- For projects opted in to Pause/Resume on snapshot-capable runtimes:
537
+ For projects opted in to Pause/Resume, on Computers that support snapshots:
540
538
 
541
539
  ```ruby
542
540
  computer.pause(timeout: 360) # waits for capture and confirmed source stop
543
- computer.resume(timeout: 360) # waits for restored routing and runtime recovery
541
+ computer.resume(timeout: 360) # waits until the Computer is restored and reachable
544
542
  ```
545
543
 
546
544
  Pause is disabled by default. Pine enables it for a project on request;
@@ -548,41 +546,45 @@ refresh the project token after it is enabled. Disabling the
548
546
  feature prevents new pauses but preserves Resume and Stop for retained memory.
549
547
  Ordinary Stop/Start remains available without the opt-in.
550
548
 
551
- The same sandbox handle, session capabilities and retained process memory remain.
552
- A pool image update does not change the captured runtime. Calls include acceptance
549
+ The same Computer handle, session capabilities and retained process memory remain.
550
+ A pool image update does not change a paused Computer. Calls include acceptance
553
551
  and completion in one bounded timeout. A timeout leaves the outcome unconfirmed;
554
- retry against the retained handle. The controller restores the same paused
555
- runtime before Stop can save it. Snapshot retention and admitted maximum age still apply; legacy expiry is unchanged.
552
+ retry against the retained handle. The controller restores the paused Computer
553
+ before Stop can save it. Snapshot retention and admitted maximum age still apply;
554
+ legacy expiry is unchanged.
556
555
 
557
556
  Do not rescue a failed `resume` by automatically calling `start_computer`.
558
557
  Retrying Resume keeps the existing memory-recovery intent; Start explicitly
559
558
  accepts recovery from the last saved state with new sessions and possible loss
560
559
  of unsaved work. An unavailable status or timeout alone does not prove that
561
- memory is lost. The [Computer API reference](https://pinesandbox.io/spec/computer)
560
+ memory is lost. The [Computer API reference](https://console.pinecomputer.io/spec/computer)
562
561
  defines the observed states and recovery choices: retry Resume, refresh the
563
562
  status, or Start from saved state.
564
563
 
565
564
  ## Errors
566
565
 
566
+ [Troubleshooting](https://console.pinecomputer.io/docs/troubleshooting) says what
567
+ each common error means and how to fix it.
568
+
567
569
  Three typed families, one per credential exchange (see `lib/pinesandbox/errors.rb`):
568
570
 
569
- Data-plane runtime loss raises `PineSandbox::RuntimeGoneError`; it preserves the
570
- wire diagnostics and original `ApiError` cause. Errors about a resource on a
571
- live Computer remain distinct, including `PineSandbox::SessionNotFound` and
572
- `PineSandbox::ControlNotHeld`.
571
+ A Computer that can no longer be reached raises `PineSandbox::SandboxGoneError`;
572
+ it preserves the wire diagnostics and original `ApiError` cause. Errors about a
573
+ resource on a live Computer remain distinct, including
574
+ `PineSandbox::SessionNotFound` and `PineSandbox::ControlNotHeld`.
573
575
 
574
576
  - **`BindError`** — `Computer#attach` outcomes: `BindAuthError`,
575
- `ComputerAlreadyAttachedError`, `RuntimeUnusableError`, `SavedStateUnavailableError`,
577
+ `ComputerAlreadyAttachedError`, `SandboxUnusableError`, `SavedStateUnavailableError`,
576
578
  `BindTimeoutError`; plus `TokenRejectedError` on a post-bind 401 for the
577
- Computer token (`ct_` not recognized — on a live runtime it is
578
- `binding_auth_lost`; re-attach only when the runtime is confirmed gone). A 401
579
+ Computer token (`ct_` not recognized — on a running Computer it is
580
+ `binding_auth_lost`; re-attach only when the Computer is confirmed stopped). A 401
579
581
  for a session token is an ordinary `UnauthorizedError`. If `create_computer` raises
580
- `AttachAuthorizationCommittedError`, retain its non-nil `#credentials` and inspect `#runtime_id` before deciding
581
- whether to stop or recover the failed runtime. Credential inspection redacts private material.
582
+ `AttachAuthorizationCommittedError`, retain its non-nil `#credentials` and inspect `#sandbox_id` before deciding
583
+ whether to stop or recover the failed Computer. Credential inspection redacts private material.
582
584
  - **`AttachCredentialsError`** — the portal attach-credentials flow:
583
585
  `UnknownComputerError` (404 — deleted or cross-project),
584
586
  `ProjectAccessDenied` (403), and `BindingRevisionConflictError` (412 — another start changed the binding; `#current_binding_revision` /
585
- `#current_runtime_id` identify the winner. Do not retry automatically). A bad `pk_` (401),
587
+ `#current_sandbox_id` identify the winner. Do not retry automatically). A bad `pk_` (401),
586
588
  rate limit (429), and server errors are the base `AttachCredentialsError`.
587
589
  Inspect the stable problem `#problem_type` and optional safe `#reason`; raw
588
590
  internal exceptions are never part of the API contract.
@@ -591,9 +593,9 @@ live Computer remain distinct, including `PineSandbox::SessionNotFound` and
591
593
  A `Retry-After` longer than the mint's 10-second backoff cap ends the mint at
592
594
  once with that error instead of retrying early.
593
595
 
594
- The old names `SandboxGoneError`, `SandboxFailedError`, `PodPoisonedError`,
595
- `BrokerUnreachableError`, `SandboxHandle` and the `sandbox_id` readers keep
596
- working as deprecated aliases; they warn when Ruby's deprecation warnings are on.
596
+ The old names `PodPoisonedError` (now `SandboxUnusableError`) and
597
+ `BrokerUnreachableError` (now `SavedStateUnavailableError`) keep working as
598
+ deprecated aliases; they warn when Ruby's deprecation warnings are on.
597
599
 
598
600
  ## Troubleshooting — reporting an issue
599
601
 
@@ -601,7 +603,7 @@ The SDK supports saved Computer diagnostics. They require the diagnostics backen
601
603
 
602
604
  ```ruby
603
605
  # Persist one key per logical request if you need to retry after a lost response.
604
- report = client.create_diagnostic(computer_id: saved_id, session: "main",
606
+ report = client.create_diagnostic(computer_id: saved_id, session: saved_session_name,
605
607
  idempotency_key: diagnostic_request_key)
606
608
  support_reference = report.fetch("id")
607
609
  # Read later. Only poll queued/running reports, with a bounded deadline.
@@ -628,17 +630,17 @@ begin
628
630
  session.agent.run("book the 9am slot")
629
631
  rescue PineSandbox::ApiError => e
630
632
  logger.error(e.message)
631
- # => "… (runtime=sbx_abc123, op=POST /v2/sessions/main/agent/run, request_id=req_abc123)"
633
+ # => "… (sandbox=<sandbox-id>, op=POST /v2/sessions/main/agent/run, request_id=req_abc123)"
632
634
  Report.open(computer_id: computer.id, session: session.name,
633
635
  at: Time.now.utc, request_id: e.request_id, op: e.op)
634
636
  end
635
637
  ```
636
638
 
637
639
  Each error class exposes exactly its own typed context (the message always carries the same
638
- data): Computer API **`ApiError`** → `#runtime_id` / `#op` / `#request_id`; control-plane errors
640
+ data): Computer API **`ApiError`** → `#sandbox_id` / `#op` / `#request_id`; control-plane errors
639
641
  → `#op` / `#request_id`; portal **`ControlTokenError`** / **`AttachCredentialsError`** → `#op`
640
642
  / `#request_id`. The suffix grammar is uniform `key=value` across the Go and Ruby SDKs; Ruby
641
- Computer API errors use **`runtime=`** because the Ruby SDK holds the runtime id, where the Go SDK
643
+ Computer API errors use **`sandbox=`** because the Ruby SDK holds the sandbox id, where the Go SDK
642
644
  uses **`host=`** because its client holds the Computer host (same Computer, different handle
643
645
  each SDK already has).
644
646
 
@@ -655,10 +657,10 @@ to find the failure:
655
657
 
656
658
  ## Known limitations
657
659
 
658
- - Pause/Resume requires a qualified snapshot-capable Computer runtime.
659
- Other runtimes reject it as unsupported. Snapshot loss, expiry or
660
- failed recovery surfaces an error; it never silently falls back to a fresh
661
- runtime. Enrolled runtimes use snapshot retention and maximum age while paused; legacy expiry is unchanged.
660
+ - Pause/Resume requires a Computer that supports snapshots; others reject it as
661
+ unsupported. Snapshot loss, expiry or failed recovery surfaces an error; it
662
+ never silently falls back to a fresh start. Computers started after enrollment
663
+ use snapshot retention and maximum age while paused; legacy expiry is unchanged.
662
664
 
663
665
  ## Develop
664
666