Refresh docs and examples for workspaces

Rewrite the user-facing persistent sandbox story around pyro workspace ..., including the install guide, first-run transcript, integrations notes, and public contract reference.

Rename the Python example to examples/python_workspace.py and update the docs to use the new workspace create, sync, exec, status, logs, and delete flows with seed_path/workspace_id terminology.

Mark the 2.4.0 workspace-contract pivot as done in the roadmap now that the shipped CLI, SDK, MCP, docs, and tests all use the workspace-first surface.
This commit is contained in:
Thales Maciel 2026-03-12 01:22:26 -03:00
parent 48b82d8386
commit 2de31306b6
9 changed files with 148 additions and 150 deletions

View file

@ -22,7 +22,7 @@ Networking: tun=yes ip_forward=yes
```bash
$ uvx --from pyro-mcp pyro env list
Catalog version: 2.3.0
Catalog version: 2.4.0
debian:12 [installed|not installed] Debian 12 environment with Git preinstalled for common agent workflows.
debian:12-base [installed|not installed] Minimal Debian 12 environment for shell and core Unix tooling.
debian:12-build [installed|not installed] Debian 12 environment with Git and common build tools preinstalled.
@ -70,18 +70,18 @@ deterministic structured result.
```bash
$ uvx --from pyro-mcp pyro demo
$ uvx --from pyro-mcp pyro task create debian:12 --source-path ./repo
$ uvx --from pyro-mcp pyro task sync push TASK_ID ./changes
$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo
$ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes
$ uvx --from pyro-mcp pyro mcp serve
```
`pyro demo` proves the one-shot create/start/exec/delete VM lifecycle works end to end.
When you need repeated commands in one sandbox, switch to `pyro task ...`:
When you need repeated commands in one sandbox, switch to `pyro workspace ...`:
```bash
$ uvx --from pyro-mcp pyro task create debian:12 --source-path ./repo
Task: ...
$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo
Workspace ID: ...
Environment: debian:12
State: started
Workspace: /workspace
@ -90,18 +90,19 @@ Execution mode: guest_vsock
Resources: 1 vCPU / 1024 MiB
Command count: 0
$ uvx --from pyro-mcp pyro task sync push TASK_ID ./changes --dest src
[task-sync] task_id=... mode=directory source=... destination=/workspace/src entry_count=... bytes_written=... execution_mode=guest_vsock
$ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes --dest src
[workspace-sync] workspace_id=... mode=directory source=... destination=/workspace/src entry_count=... bytes_written=... execution_mode=guest_vsock
$ uvx --from pyro-mcp pyro task exec TASK_ID -- cat src/note.txt
hello from synced task
[task-exec] task_id=... sequence=1 cwd=/workspace execution_mode=guest_vsock exit_code=0 duration_ms=...
$ uvx --from pyro-mcp pyro workspace exec WORKSPACE_ID -- cat src/note.txt
hello from synced workspace
[workspace-exec] workspace_id=... sequence=1 cwd=/workspace execution_mode=guest_vsock exit_code=0 duration_ms=...
```
Use `--source-path` when the task should start from a host directory or a local
Use `--seed-path` when the workspace should start from a host directory or a local
`.tar` / `.tar.gz` / `.tgz` archive instead of an empty `/workspace`. Use
`pyro task sync push` when you need to import later host-side changes into a started task.
Sync is non-atomic in `2.3.0`; if it fails partway through, delete and recreate the task.
`pyro workspace sync push` when you need to import later host-side changes into a started
workspace. Sync is non-atomic in `2.4.0`; if it fails partway through, delete and recreate the
workspace.
Example output:

View file

@ -83,7 +83,7 @@ uvx --from pyro-mcp pyro env list
Expected output:
```bash
Catalog version: 2.3.0
Catalog version: 2.4.0
debian:12 [installed|not installed] Debian 12 environment with Git preinstalled for common agent workflows.
debian:12-base [installed|not installed] Minimal Debian 12 environment for shell and core Unix tooling.
debian:12-build [installed|not installed] Debian 12 environment with Git and common build tools preinstalled.
@ -174,29 +174,30 @@ pyro run debian:12 -- git --version
After the CLI path works, you can move on to:
- persistent workspaces: `pyro task create debian:12 --source-path ./repo`
- live task updates: `pyro task sync push TASK_ID ./changes`
- persistent workspaces: `pyro workspace create debian:12 --seed-path ./repo`
- live workspace updates: `pyro workspace sync push WORKSPACE_ID ./changes`
- MCP: `pyro mcp serve`
- Python SDK: `from pyro_mcp import Pyro`
- Demos: `pyro demo` or `pyro demo --network`
## Persistent Task Workspace
## Persistent Workspace
Use `pyro task ...` when you need repeated commands in one sandbox instead of one-shot `pyro run`.
Use `pyro workspace ...` when you need repeated commands in one sandbox instead of one-shot `pyro run`.
```bash
pyro task create debian:12 --source-path ./repo
pyro task sync push TASK_ID ./changes --dest src
pyro task exec TASK_ID -- cat src/note.txt
pyro task logs TASK_ID
pyro task delete TASK_ID
pyro workspace create debian:12 --seed-path ./repo
pyro workspace sync push WORKSPACE_ID ./changes --dest src
pyro workspace exec WORKSPACE_ID -- cat src/note.txt
pyro workspace logs WORKSPACE_ID
pyro workspace delete WORKSPACE_ID
```
Task commands default to the persistent `/workspace` directory inside the guest. If you need the
task identifier programmatically, use `--json` and read the `task_id` field. Use `--source-path`
when the task should start from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive.
Use `pyro task sync push` for later host-side changes to a started task. Sync is non-atomic in
`2.3.0`; if it fails partway through, delete and recreate the task from its seed.
Workspace commands default to the persistent `/workspace` directory inside the guest. If you need
the identifier programmatically, use `--json` and read the `workspace_id` field. Use `--seed-path`
when the workspace should start from a host directory or a local `.tar` / `.tar.gz` / `.tgz`
archive. Use `pyro workspace sync push` for later host-side changes to a started workspace. Sync
is non-atomic in `2.4.0`; if it fails partway through, delete and recreate the workspace from its
seed.
## Contributor Clone

View file

@ -16,7 +16,7 @@ That keeps the model-facing contract small:
- one ephemeral VM
- automatic cleanup
Move to `task_*` only when the agent truly needs repeated commands in one workspace across
Move to `workspace_*` only when the agent truly needs repeated commands in one workspace across
multiple calls.
## OpenAI Responses API
@ -30,7 +30,7 @@ Best when:
Recommended surface:
- `vm_run`
- `task_create(source_path=...)` + `task_sync_push` + `task_exec` when the agent needs persistent workspace state
- `workspace_create(seed_path=...)` + `workspace_sync_push` + `workspace_exec` when the agent needs persistent workspace state
Canonical example:
@ -65,23 +65,23 @@ Best when:
Recommended default:
- `Pyro.run_in_vm(...)`
- `Pyro.create_task(source_path=...)` + `Pyro.push_task_sync(...)` + `Pyro.exec_task(...)` when repeated workspace commands are required
- `Pyro.create_workspace(seed_path=...)` + `Pyro.push_workspace_sync(...)` + `Pyro.exec_workspace(...)` when repeated workspace commands are required
Lifecycle note:
- `Pyro.exec_vm(...)` runs one command and auto-cleans the VM afterward
- use `create_vm(...)` + `start_vm(...)` only when you need pre-exec inspection or status before
that final exec
- use `create_task(source_path=...)` when the agent needs repeated commands in one persistent
- use `create_workspace(seed_path=...)` when the agent needs repeated commands in one persistent
`/workspace` that starts from host content
- use `push_task_sync(...)` when later host-side changes need to be imported into that running
workspace without recreating the task
- use `push_workspace_sync(...)` when later host-side changes need to be imported into that
running workspace without recreating it
Examples:
- [examples/python_run.py](../examples/python_run.py)
- [examples/python_lifecycle.py](../examples/python_lifecycle.py)
- [examples/python_task.py](../examples/python_task.py)
- [examples/python_workspace.py](../examples/python_workspace.py)
## Agent Framework Wrappers
@ -100,7 +100,7 @@ Recommended pattern:
- keep the framework wrapper thin
- map one-shot framework tool input directly onto `vm_run`
- expose `task_*` only when the framework truly needs repeated commands in one workspace
- expose `workspace_*` only when the framework truly needs repeated commands in one workspace
Concrete example:

View file

@ -19,12 +19,12 @@ Top-level commands:
- `pyro env prune`
- `pyro mcp serve`
- `pyro run`
- `pyro task create`
- `pyro task sync push`
- `pyro task exec`
- `pyro task status`
- `pyro task logs`
- `pyro task delete`
- `pyro workspace create`
- `pyro workspace sync push`
- `pyro workspace exec`
- `pyro workspace status`
- `pyro workspace logs`
- `pyro workspace delete`
- `pyro doctor`
- `pyro demo`
- `pyro demo ollama`
@ -46,15 +46,12 @@ Behavioral guarantees:
- `pyro run` fails if guest boot or guest exec is unavailable unless `--allow-host-compat` is set.
- `pyro run`, `pyro env list`, `pyro env pull`, `pyro env inspect`, `pyro env prune`, and `pyro doctor` are human-readable by default and return structured JSON with `--json`.
- `pyro demo ollama` prints log lines plus a final summary line.
- `pyro task create` auto-starts a persistent workspace.
- `pyro task create --source-path PATH` seeds `/workspace` from a host directory or a local
`.tar` / `.tar.gz` / `.tgz` archive before the task is returned.
- `pyro task sync push TASK_ID SOURCE_PATH [--dest WORKSPACE_PATH]` imports later host-side
directory or archive content into a started task workspace.
- `pyro task exec` runs in the persistent `/workspace` for that task and does not auto-clean.
- `pyro task logs` returns persisted command history for that task until `pyro task delete`.
- Task create/status results expose `workspace_seed` metadata describing how `/workspace` was
initialized.
- `pyro workspace create` auto-starts a persistent workspace.
- `pyro workspace create --seed-path PATH` seeds `/workspace` from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive before the workspace is returned.
- `pyro workspace sync push WORKSPACE_ID SOURCE_PATH [--dest WORKSPACE_PATH]` imports later host-side directory or archive content into a started workspace.
- `pyro workspace exec` runs in the persistent `/workspace` for that workspace and does not auto-clean.
- `pyro workspace logs` returns persisted command history for that workspace until `pyro workspace delete`.
- Workspace create/status results expose `workspace_seed` metadata describing how `/workspace` was initialized.
## Python SDK Contract
@ -71,17 +68,17 @@ Supported public entrypoints:
- `Pyro.inspect_environment(environment)`
- `Pyro.prune_environments()`
- `Pyro.create_vm(...)`
- `Pyro.create_task(...)`
- `Pyro.push_task_sync(task_id, source_path, *, dest="/workspace")`
- `Pyro.create_workspace(...)`
- `Pyro.push_workspace_sync(workspace_id, source_path, *, dest="/workspace")`
- `Pyro.start_vm(vm_id)`
- `Pyro.exec_vm(vm_id, *, command, timeout_seconds=30)`
- `Pyro.exec_task(task_id, *, command, timeout_seconds=30)`
- `Pyro.exec_workspace(workspace_id, *, command, timeout_seconds=30)`
- `Pyro.stop_vm(vm_id)`
- `Pyro.delete_vm(vm_id)`
- `Pyro.delete_task(task_id)`
- `Pyro.delete_workspace(workspace_id)`
- `Pyro.status_vm(vm_id)`
- `Pyro.status_task(task_id)`
- `Pyro.logs_task(task_id)`
- `Pyro.status_workspace(workspace_id)`
- `Pyro.logs_workspace(workspace_id)`
- `Pyro.network_info_vm(vm_id)`
- `Pyro.reap_expired()`
- `Pyro.run_in_vm(...)`
@ -94,17 +91,17 @@ Stable public method names:
- `inspect_environment(environment)`
- `prune_environments()`
- `create_vm(...)`
- `create_task(...)`
- `push_task_sync(task_id, source_path, *, dest="/workspace")`
- `create_workspace(...)`
- `push_workspace_sync(workspace_id, source_path, *, dest="/workspace")`
- `start_vm(vm_id)`
- `exec_vm(vm_id, *, command, timeout_seconds=30)`
- `exec_task(task_id, *, command, timeout_seconds=30)`
- `exec_workspace(workspace_id, *, command, timeout_seconds=30)`
- `stop_vm(vm_id)`
- `delete_vm(vm_id)`
- `delete_task(task_id)`
- `delete_workspace(workspace_id)`
- `status_vm(vm_id)`
- `status_task(task_id)`
- `logs_task(task_id)`
- `status_workspace(workspace_id)`
- `logs_workspace(workspace_id)`
- `network_info_vm(vm_id)`
- `reap_expired()`
- `run_in_vm(...)`
@ -112,15 +109,13 @@ Stable public method names:
Behavioral defaults:
- `Pyro.create_vm(...)` and `Pyro.run_in_vm(...)` default to `vcpu_count=1` and `mem_mib=1024`.
- `Pyro.create_task(...)` defaults to `vcpu_count=1` and `mem_mib=1024`.
- `Pyro.create_workspace(...)` defaults to `vcpu_count=1` and `mem_mib=1024`.
- `allow_host_compat` defaults to `False` on `create_vm(...)` and `run_in_vm(...)`.
- `allow_host_compat` defaults to `False` on `create_task(...)`.
- `Pyro.create_task(..., source_path=...)` seeds `/workspace` from a host directory or a local
`.tar` / `.tar.gz` / `.tgz` archive before the task is returned.
- `Pyro.push_task_sync(...)` imports later host-side directory or archive content into a started
task workspace.
- `allow_host_compat` defaults to `False` on `create_workspace(...)`.
- `Pyro.create_workspace(..., seed_path=...)` seeds `/workspace` from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive before the workspace is returned.
- `Pyro.push_workspace_sync(...)` imports later host-side directory or archive content into a started workspace.
- `Pyro.exec_vm(...)` runs one command and auto-cleans that VM after the exec completes.
- `Pyro.exec_task(...)` runs one command in the persistent task workspace and leaves the task alive.
- `Pyro.exec_workspace(...)` runs one command in the persistent workspace and leaves it alive.
## MCP Contract
@ -140,27 +135,25 @@ Advanced lifecycle tools:
- `vm_network_info`
- `vm_reap_expired`
Task workspace tools:
Persistent workspace tools:
- `task_create`
- `task_sync_push`
- `task_exec`
- `task_status`
- `task_logs`
- `task_delete`
- `workspace_create`
- `workspace_sync_push`
- `workspace_exec`
- `workspace_status`
- `workspace_logs`
- `workspace_delete`
Behavioral defaults:
- `vm_run` and `vm_create` default to `vcpu_count=1` and `mem_mib=1024`.
- `task_create` defaults to `vcpu_count=1` and `mem_mib=1024`.
- `workspace_create` defaults to `vcpu_count=1` and `mem_mib=1024`.
- `vm_run` and `vm_create` expose `allow_host_compat`, which defaults to `false`.
- `task_create` exposes `allow_host_compat`, which defaults to `false`.
- `task_create` accepts optional `source_path` and seeds `/workspace` from a host directory or a
local `.tar` / `.tar.gz` / `.tgz` archive before the task is returned.
- `task_sync_push` imports later host-side directory or archive content into a started task
workspace, with an optional `dest` under `/workspace`.
- `workspace_create` exposes `allow_host_compat`, which defaults to `false`.
- `workspace_create` accepts optional `seed_path` and seeds `/workspace` from a host directory or a local `.tar` / `.tar.gz` / `.tgz` archive before the workspace is returned.
- `workspace_sync_push` imports later host-side directory or archive content into a started workspace, with an optional `dest` under `/workspace`.
- `vm_exec` runs one command and auto-cleans that VM after the exec completes.
- `task_exec` runs one command in a persistent `/workspace` and leaves the task alive.
- `workspace_exec` runs one command in a persistent `/workspace` and leaves the workspace alive.
## Versioning Rule

View file

@ -25,7 +25,7 @@ also expected to update:
## Milestones
1. [`2.4.0` Workspace Contract Pivot](task-workspace-ga/2.4.0-workspace-contract-pivot.md)
1. [`2.4.0` Workspace Contract Pivot](task-workspace-ga/2.4.0-workspace-contract-pivot.md) - Done
2. [`2.5.0` PTY Shell Sessions](task-workspace-ga/2.5.0-pty-shell-sessions.md)
3. [`2.6.0` Structured Export And Baseline Diff](task-workspace-ga/2.6.0-structured-export-and-baseline-diff.md)
4. [`2.7.0` Service Lifecycle And Typed Readiness](task-workspace-ga/2.7.0-service-lifecycle-and-typed-readiness.md)

View file

@ -1,5 +1,7 @@
# `2.4.0` Workspace Contract Pivot
Status: Done
## Goal
Make the public product read as a workspace-first sandbox instead of a
@ -16,7 +18,7 @@ task-flavored alpha by replacing the `task_*` surface with `workspace_*`.
- `pyro workspace delete`
- SDK:
- `create_workspace`
- `sync_push_workspace`
- `push_workspace_sync`
- `exec_workspace`
- `status_workspace`
- `logs_workspace`