Add persistent workspace shell sessions
Let agents inhabit a workspace across separate calls instead of only submitting one-shot execs. Add workspace shell open/read/write/signal/close across the CLI, Python SDK, and MCP server, with persisted shell records, a local PTY-backed mock implementation, and guest-agent support for real Firecracker workspaces. Mark the 2.5.0 roadmap milestone done, refresh docs/examples and the release metadata, and verify with uv lock, UV_CACHE_DIR=.uv-cache make check, and UV_CACHE_DIR=.uv-cache make dist-check.
This commit is contained in:
parent
2de31306b6
commit
3f8293ad24
28 changed files with 3265 additions and 81 deletions
|
|
@ -22,7 +22,7 @@ Networking: tun=yes ip_forward=yes
|
|||
|
||||
```bash
|
||||
$ uvx --from pyro-mcp pyro env list
|
||||
Catalog version: 2.4.0
|
||||
Catalog version: 2.5.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.
|
||||
|
|
@ -72,6 +72,7 @@ deterministic structured result.
|
|||
$ uvx --from pyro-mcp pyro demo
|
||||
$ 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 workspace shell open WORKSPACE_ID
|
||||
$ uvx --from pyro-mcp pyro mcp serve
|
||||
```
|
||||
|
||||
|
|
@ -96,13 +97,24 @@ $ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes --dest src
|
|||
$ 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=...
|
||||
|
||||
$ uvx --from pyro-mcp pyro workspace shell open WORKSPACE_ID
|
||||
[workspace-shell-open] workspace_id=... shell_id=... state=running cwd=/workspace cols=120 rows=30 execution_mode=guest_vsock
|
||||
|
||||
$ uvx --from pyro-mcp pyro workspace shell write WORKSPACE_ID SHELL_ID --input 'pwd'
|
||||
[workspace-shell-write] workspace_id=... shell_id=... state=running cwd=/workspace cols=120 rows=30 execution_mode=guest_vsock
|
||||
|
||||
$ uvx --from pyro-mcp pyro workspace shell read WORKSPACE_ID SHELL_ID
|
||||
/workspace
|
||||
[workspace-shell-read] workspace_id=... shell_id=... state=running cursor=0 next_cursor=... truncated=False execution_mode=guest_vsock
|
||||
```
|
||||
|
||||
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 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.
|
||||
workspace. Sync is non-atomic in `2.5.0`; if it fails partway through, delete and recreate the
|
||||
workspace. Use `pyro workspace exec` for one-shot commands and `pyro workspace shell *` when you
|
||||
need a persistent interactive PTY session in that same workspace.
|
||||
|
||||
Example output:
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ uvx --from pyro-mcp pyro env list
|
|||
Expected output:
|
||||
|
||||
```bash
|
||||
Catalog version: 2.4.0
|
||||
Catalog version: 2.5.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.
|
||||
|
|
@ -176,6 +176,7 @@ After the CLI path works, you can move on to:
|
|||
|
||||
- persistent workspaces: `pyro workspace create debian:12 --seed-path ./repo`
|
||||
- live workspace updates: `pyro workspace sync push WORKSPACE_ID ./changes`
|
||||
- interactive shells: `pyro workspace shell open WORKSPACE_ID`
|
||||
- MCP: `pyro mcp serve`
|
||||
- Python SDK: `from pyro_mcp import Pyro`
|
||||
- Demos: `pyro demo` or `pyro demo --network`
|
||||
|
|
@ -188,6 +189,10 @@ Use `pyro workspace ...` when you need repeated commands in one sandbox instead
|
|||
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 shell open WORKSPACE_ID
|
||||
pyro workspace shell write WORKSPACE_ID SHELL_ID --input 'pwd'
|
||||
pyro workspace shell read WORKSPACE_ID SHELL_ID
|
||||
pyro workspace shell close WORKSPACE_ID SHELL_ID
|
||||
pyro workspace logs WORKSPACE_ID
|
||||
pyro workspace delete WORKSPACE_ID
|
||||
```
|
||||
|
|
@ -196,8 +201,9 @@ Workspace commands default to the persistent `/workspace` directory inside the g
|
|||
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.
|
||||
is non-atomic in `2.5.0`; if it fails partway through, delete and recreate the workspace from its
|
||||
seed. Use `pyro workspace exec` for one-shot commands and `pyro workspace shell *` when you need
|
||||
an interactive PTY that survives across separate calls.
|
||||
|
||||
## Contributor Clone
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ Recommended surface:
|
|||
|
||||
- `vm_run`
|
||||
- `workspace_create(seed_path=...)` + `workspace_sync_push` + `workspace_exec` when the agent needs persistent workspace state
|
||||
- `open_shell` / `read_shell` / `write_shell` when the agent needs an interactive PTY inside that workspace
|
||||
|
||||
Canonical example:
|
||||
|
||||
|
|
@ -66,6 +67,7 @@ Recommended default:
|
|||
|
||||
- `Pyro.run_in_vm(...)`
|
||||
- `Pyro.create_workspace(seed_path=...)` + `Pyro.push_workspace_sync(...)` + `Pyro.exec_workspace(...)` when repeated workspace commands are required
|
||||
- `Pyro.open_shell(...)` + `Pyro.write_shell(...)` + `Pyro.read_shell(...)` when the agent needs an interactive PTY inside the workspace
|
||||
|
||||
Lifecycle note:
|
||||
|
||||
|
|
@ -76,12 +78,14 @@ Lifecycle note:
|
|||
`/workspace` that starts from host content
|
||||
- use `push_workspace_sync(...)` when later host-side changes need to be imported into that
|
||||
running workspace without recreating it
|
||||
- use `open_shell(...)` when the agent needs interactive shell state instead of one-shot execs
|
||||
|
||||
Examples:
|
||||
|
||||
- [examples/python_run.py](../examples/python_run.py)
|
||||
- [examples/python_lifecycle.py](../examples/python_lifecycle.py)
|
||||
- [examples/python_workspace.py](../examples/python_workspace.py)
|
||||
- [examples/python_shell.py](../examples/python_shell.py)
|
||||
|
||||
## Agent Framework Wrappers
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ Top-level commands:
|
|||
- `pyro workspace create`
|
||||
- `pyro workspace sync push`
|
||||
- `pyro workspace exec`
|
||||
- `pyro workspace shell open`
|
||||
- `pyro workspace shell read`
|
||||
- `pyro workspace shell write`
|
||||
- `pyro workspace shell signal`
|
||||
- `pyro workspace shell close`
|
||||
- `pyro workspace status`
|
||||
- `pyro workspace logs`
|
||||
- `pyro workspace delete`
|
||||
|
|
@ -50,6 +55,7 @@ Behavioral guarantees:
|
|||
- `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 shell *` manages persistent PTY sessions inside a started workspace.
|
||||
- `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.
|
||||
|
||||
|
|
@ -70,6 +76,11 @@ Supported public entrypoints:
|
|||
- `Pyro.create_vm(...)`
|
||||
- `Pyro.create_workspace(...)`
|
||||
- `Pyro.push_workspace_sync(workspace_id, source_path, *, dest="/workspace")`
|
||||
- `Pyro.open_shell(workspace_id, *, cwd="/workspace", cols=120, rows=30)`
|
||||
- `Pyro.read_shell(workspace_id, shell_id, *, cursor=0, max_chars=65536)`
|
||||
- `Pyro.write_shell(workspace_id, shell_id, *, input, append_newline=True)`
|
||||
- `Pyro.signal_shell(workspace_id, shell_id, *, signal_name="INT")`
|
||||
- `Pyro.close_shell(workspace_id, shell_id)`
|
||||
- `Pyro.start_vm(vm_id)`
|
||||
- `Pyro.exec_vm(vm_id, *, command, timeout_seconds=30)`
|
||||
- `Pyro.exec_workspace(workspace_id, *, command, timeout_seconds=30)`
|
||||
|
|
@ -93,6 +104,11 @@ Stable public method names:
|
|||
- `create_vm(...)`
|
||||
- `create_workspace(...)`
|
||||
- `push_workspace_sync(workspace_id, source_path, *, dest="/workspace")`
|
||||
- `open_shell(workspace_id, *, cwd="/workspace", cols=120, rows=30)`
|
||||
- `read_shell(workspace_id, shell_id, *, cursor=0, max_chars=65536)`
|
||||
- `write_shell(workspace_id, shell_id, *, input, append_newline=True)`
|
||||
- `signal_shell(workspace_id, shell_id, *, signal_name="INT")`
|
||||
- `close_shell(workspace_id, shell_id)`
|
||||
- `start_vm(vm_id)`
|
||||
- `exec_vm(vm_id, *, command, timeout_seconds=30)`
|
||||
- `exec_workspace(workspace_id, *, command, timeout_seconds=30)`
|
||||
|
|
@ -116,6 +132,9 @@ Behavioral defaults:
|
|||
- `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_workspace(...)` runs one command in the persistent workspace and leaves it alive.
|
||||
- `Pyro.open_shell(...)` opens a persistent PTY shell attached to one started workspace.
|
||||
- `Pyro.read_shell(...)` reads merged text output from that shell by cursor.
|
||||
- `Pyro.write_shell(...)`, `Pyro.signal_shell(...)`, and `Pyro.close_shell(...)` operate on that persistent shell session.
|
||||
|
||||
## MCP Contract
|
||||
|
||||
|
|
@ -140,6 +159,11 @@ Persistent workspace tools:
|
|||
- `workspace_create`
|
||||
- `workspace_sync_push`
|
||||
- `workspace_exec`
|
||||
- `shell_open`
|
||||
- `shell_read`
|
||||
- `shell_write`
|
||||
- `shell_signal`
|
||||
- `shell_close`
|
||||
- `workspace_status`
|
||||
- `workspace_logs`
|
||||
- `workspace_delete`
|
||||
|
|
@ -154,6 +178,7 @@ Behavioral defaults:
|
|||
- `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.
|
||||
- `workspace_exec` runs one command in a persistent `/workspace` and leaves the workspace alive.
|
||||
- `shell_open`, `shell_read`, `shell_write`, `shell_signal`, and `shell_close` manage persistent PTY shells inside a started workspace.
|
||||
|
||||
## Versioning Rule
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
This roadmap turns the agent-workspace vision into release-sized milestones.
|
||||
|
||||
Current baseline is `2.4.0`:
|
||||
Current baseline is `2.5.0`:
|
||||
|
||||
- workspace persistence exists and the public surface is now workspace-first
|
||||
- host crossing currently covers create-time seeding and later sync push
|
||||
- no shell, export, diff, service, snapshot, reset, or secrets contract exists yet
|
||||
- persistent PTY shell sessions exist alongside one-shot `workspace exec`
|
||||
- no export, diff, service, snapshot, reset, or secrets contract exists yet
|
||||
|
||||
Locked roadmap decisions:
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ also expected to update:
|
|||
## Milestones
|
||||
|
||||
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)
|
||||
2. [`2.5.0` PTY Shell Sessions](task-workspace-ga/2.5.0-pty-shell-sessions.md) - Done
|
||||
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)
|
||||
5. [`2.8.0` Named Snapshots And Reset](task-workspace-ga/2.8.0-named-snapshots-and-reset.md)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# `2.5.0` PTY Shell Sessions
|
||||
|
||||
Status: Done
|
||||
|
||||
## Goal
|
||||
|
||||
Add persistent interactive shells so an agent can inhabit a workspace instead
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue