Skip to content

feat(MARSOHS-1935): add persistent workspaces to the agents resources - #750

Open
sanpj2292 wants to merge 8 commits into
mainfrom
prao/MARSOHS-1935/feat.workspaces
Open

sanpj2292 wants to merge 8 commits into
mainfrom
prao/MARSOHS-1935/feat.workspaces

Conversation

@sanpj2292

@sanpj2292 sanpj2292 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds persistent workspaces to the hand-written agents resources: a separate set of files with its own id that outlives sessions and is attached to one session at a time. Ticket: MARSOHS-1935.

The API is behind a per-team feature flag and is not generally available yet, so calls return 501 for teams that are not enrolled.

Changes

  • client.agents.workspaces (sync and async) with create, list, get and delete for /v2/agents/workspaces.
    • create(size_limit_gib, *, name=None, idempotency_key=None) sends the Idempotency-Key header only when a key is given. A new workspace answers 201 and a replay answers 200; both return the workspace.
    • list(*, page_size=None, page_token=None, state=None) lists only workspaces in one state when state is given; any other value is a 400 from the server.
    • create raises ValueError for a non-integer or a size below 1. The upper bound is left to the server, because a team limit can lower it.
    • Errors use the existing mapping: 401 ClientAuthenticationError, 404 ResourceNotFoundError, 409 ResourceExistsError, and 422 or 501 HttpResponseError with the server's message.
    • WorkspaceState holds the six state constants (including DELETING). An unknown state stays a plain string.
  • Attaching a workspace to a session: a workspace is chosen per session and is never part of a manifest.
    • create_from_config(..., workspace_id=None) sends it as a body field, only when set. This is the only way to attach a workspace.
    • create_from_manifest and agents.start accept no workspace_id. Their docstrings say to save the manifest as a config and use create_from_config.
  • Example: examples/agents/persistent_workspace.py creates a workspace with an idempotency key, creates a session from a config with it attached, removes the session, waits until the workspace is available, and attaches it to a second session.
  • Docs: the two Sphinx pages list the new modules.

The agents packages are hand-written and are preserved by make generate (see the clean target in the Makefile), so nothing here is overwritten by regeneration.

Testing

  • make lint-tests passes (black and pylint 10.00).
  • The agents test suite passes (199 passed, 1 skipped), including the new test_workspaces.py and test_async_workspaces.py, which cover 201 and 200 replay, the header only when given, paging parameters, get and delete, 401, 404, 409, 422 and 501, the session body field, and that the manifest paths accept no workspace_id.
  • Not run locally: the Python 3.8 and 3.9 matrix, the Sphinx build, and integration tests.
  • black --check examples fails on 10 existing example files; they fail the same way on main and are not part of this PR.

Notes

  • doctl and godo changes for the same ticket are in separate PRs.

Rename (MARSOHS-2336)

The size is now size_limit_gib (was size_gibibytes), to match the API change in digitalocean/cthulhu#182026. Nothing released changes, because the workspace calls are only in this PR. This should merge after the API change is deployed.

sanpj2292 and others added 3 commits October 6, 2026 11:52
Add client.agents.workspaces (sync and async) with create, list, get and
delete for persistent workspaces under /v2/agents/workspaces. Create accepts
an optional idempotency_key that is sent as the Idempotency-Key header only
when given, so a retried create is safe.

A session names its workspace per session, never inside a manifest:
workspace_id is a body field on create_from_config and a query parameter on
create_from_manifest and start.

Add WorkspaceState constants, a runnable example of the create, attach,
remove and re-attach flow, and tests for both clients.
…config

A workspace is chosen per session and never travels with a manifest, so
workspace_id is removed from create_from_manifest and from start, in the sync
and async clients. create_from_config keeps workspace_id as a body field and is
now the only way to attach a workspace; the docstrings point to it.

Replace the query-parameter tests with tests that those two methods accept no
workspace_id and send none.
@sanpj2292
sanpj2292 marked this pull request as ready for review October 6, 2026 12:12
sanpj2292 and others added 5 commits October 8, 2026 00:11
list takes state to return only workspaces in one state, on both the sync and
async clients. A page can hold fewer workspaces than page_size, even none,
while next_page_token is set, so callers keep requesting pages until it is
empty. The workspace example now lists the available workspaces.
…elete and create docs

The API has a sixth workspace state, DELETING, shown while a delete runs, and
accepts it as the list state filter. Add WorkspaceState.DELETING and update the
docstrings: delete is a 409 while a session holds the workspace or it is attaching
or being saved, and create is a 409 for a key whose workspace is being deleted.
The API now calls the workspace size an upper limit on a disk that is billed on
use, and names it size_limit_gib in the create body and the workspace answer
(digitalocean/cthulhu#182026). create_workspace takes size_limit_gib instead of
size_gibibytes, sends it under that name, and the error messages follow.

The workspace calls are in this PR and have no release yet, so nothing that is
released changes.
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.

2 participants