Files
r33drichards 605a7421e1 feat(cua-sandbox)!: require explicit pool names for Fleet images (#3215)
* docs(sandbox): document derived pool-name namespace collisions

Pool.apply() without name= (including via Sandbox.create(image, ...))
derives a deterministic cua-<hash> pool name from the image and
configuration, and uses it as the namespace. Identical configurations
hash to the same namespace for every account, so a common configuration
can collide with a namespace another credential owns and fail with 403
RBAC denials that fresh claim names cannot work around.

Add a section to the create-pool guide explaining the collision and the
fix: pass a unique explicit name= to Pool.apply() and claim from that
pool, or pass the named pool to Sandbox.create(pool=...).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kbiBCs8Wo3LtXCTEvV7dF

* feat(cua-sandbox)!: require explicit globally-unique pool names

Pool names double as Fleet namespaces and are globally unique across
accounts, so the SDK must never invent them. Previously Pool.apply()
without name= derived a deterministic cua-<sha256[:12]> name from the
image and configuration, so common configurations collided with
namespaces owned by other accounts and failed with opaque 403s that
fresh claim names could not work around.

- Pool.apply() (async and sync) now requires a non-empty name=.
- Sandbox.create(image) with Fleet credentials raises a ValueError
  directing callers to Pool.apply(image, name=...) followed by
  Sandbox.create(pool=..., name=...), instead of silently applying a
  derived pool.
- Sandbox.ephemeral(image) provisions its disposable pool under a
  random cua-eph-<hex> name; keep_pool=True now requires name= so
  later runs can find the kept pool. Claim names are unchanged: they
  are namespace-scoped, so the Fleet SDK's random claim-<petname>
  generation remains safe.
- 403s from pool/template reconciliation are mapped to a new
  PoolAccessDeniedError (exported from cua_sandbox and cua) that
  explains the global-uniqueness rule and likely causes; the rollback
  after a failed template reconcile is best-effort so it no longer
  masks the original error.
- README, the create-pool and Minecraft guides, and the Android RPS
  benchmark are updated to the explicit-pool pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kbiBCs8Wo3LtXCTEvV7dF

* fix(cua-sandbox): point pool-denied error at a new name and Discord support

Drop the speculative sizing/template-restriction clause from
PoolAccessDeniedError. The actionable advice is: try a new pool name,
and contact support on Discord (https://discord.gg/mVnXXpdE85) if that
does not work. Update the quoted message in the create-pool guide.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kbiBCs8Wo3LtXCTEvV7dF

* feat(cua-sandbox): adopt upstream Fleet SDK pool-access-denied error when available

Newer Fleet SDKs raise SdkError.PoolAccessDenied from the Rust core for
403s on pool-namespace writes, so every language binding shares one
message. Alias PoolAccessDeniedError to that type and construct it
directly when the installed cua-fleet provides it, keeping the local
PermissionError subclass and Status-403 mapping as the fallback for
older SDKs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kbiBCs8Wo3LtXCTEvV7dF

* fix(cua-sandbox): render pool-denial fallback message identically to the Rust SDK

The local PoolAccessDeniedError fallback repr-quoted the operation
('create pool') while cyclops-sdk's SdkError::PoolAccessDenied renders it
unquoted. Match the upstream rendering exactly so the message is
byte-identical whether it originates locally or from cua-fleet, and update
the docs sample output to the shared rendering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 10:41:41 -07:00
..
2026-04-20 21:30:12 -07:00
2026-04-20 21:30:12 -07:00
2026-04-20 21:30:12 -07:00