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
|
|
@ -130,6 +130,67 @@ class Pyro:
|
|||
def logs_workspace(self, workspace_id: str) -> dict[str, Any]:
|
||||
return self._manager.logs_workspace(workspace_id)
|
||||
|
||||
def open_shell(
|
||||
self,
|
||||
workspace_id: str,
|
||||
*,
|
||||
cwd: str = "/workspace",
|
||||
cols: int = 120,
|
||||
rows: int = 30,
|
||||
) -> dict[str, Any]:
|
||||
return self._manager.open_shell(
|
||||
workspace_id,
|
||||
cwd=cwd,
|
||||
cols=cols,
|
||||
rows=rows,
|
||||
)
|
||||
|
||||
def read_shell(
|
||||
self,
|
||||
workspace_id: str,
|
||||
shell_id: str,
|
||||
*,
|
||||
cursor: int = 0,
|
||||
max_chars: int = 65536,
|
||||
) -> dict[str, Any]:
|
||||
return self._manager.read_shell(
|
||||
workspace_id,
|
||||
shell_id,
|
||||
cursor=cursor,
|
||||
max_chars=max_chars,
|
||||
)
|
||||
|
||||
def write_shell(
|
||||
self,
|
||||
workspace_id: str,
|
||||
shell_id: str,
|
||||
*,
|
||||
input: str,
|
||||
append_newline: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
return self._manager.write_shell(
|
||||
workspace_id,
|
||||
shell_id,
|
||||
input_text=input,
|
||||
append_newline=append_newline,
|
||||
)
|
||||
|
||||
def signal_shell(
|
||||
self,
|
||||
workspace_id: str,
|
||||
shell_id: str,
|
||||
*,
|
||||
signal_name: str = "INT",
|
||||
) -> dict[str, Any]:
|
||||
return self._manager.signal_shell(
|
||||
workspace_id,
|
||||
shell_id,
|
||||
signal_name=signal_name,
|
||||
)
|
||||
|
||||
def close_shell(self, workspace_id: str, shell_id: str) -> dict[str, Any]:
|
||||
return self._manager.close_shell(workspace_id, shell_id)
|
||||
|
||||
def delete_workspace(self, workspace_id: str) -> dict[str, Any]:
|
||||
return self._manager.delete_workspace(workspace_id)
|
||||
|
||||
|
|
@ -309,6 +370,64 @@ class Pyro:
|
|||
"""Return persisted command history for one workspace."""
|
||||
return self.logs_workspace(workspace_id)
|
||||
|
||||
@server.tool()
|
||||
async def shell_open(
|
||||
workspace_id: str,
|
||||
cwd: str = "/workspace",
|
||||
cols: int = 120,
|
||||
rows: int = 30,
|
||||
) -> dict[str, Any]:
|
||||
"""Open a persistent interactive shell inside one workspace."""
|
||||
return self.open_shell(workspace_id, cwd=cwd, cols=cols, rows=rows)
|
||||
|
||||
@server.tool()
|
||||
async def shell_read(
|
||||
workspace_id: str,
|
||||
shell_id: str,
|
||||
cursor: int = 0,
|
||||
max_chars: int = 65536,
|
||||
) -> dict[str, Any]:
|
||||
"""Read merged PTY output from a workspace shell."""
|
||||
return self.read_shell(
|
||||
workspace_id,
|
||||
shell_id,
|
||||
cursor=cursor,
|
||||
max_chars=max_chars,
|
||||
)
|
||||
|
||||
@server.tool()
|
||||
async def shell_write(
|
||||
workspace_id: str,
|
||||
shell_id: str,
|
||||
input: str,
|
||||
append_newline: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
"""Write text input to a persistent workspace shell."""
|
||||
return self.write_shell(
|
||||
workspace_id,
|
||||
shell_id,
|
||||
input=input,
|
||||
append_newline=append_newline,
|
||||
)
|
||||
|
||||
@server.tool()
|
||||
async def shell_signal(
|
||||
workspace_id: str,
|
||||
shell_id: str,
|
||||
signal_name: str = "INT",
|
||||
) -> dict[str, Any]:
|
||||
"""Send a signal to the shell process group."""
|
||||
return self.signal_shell(
|
||||
workspace_id,
|
||||
shell_id,
|
||||
signal_name=signal_name,
|
||||
)
|
||||
|
||||
@server.tool()
|
||||
async def shell_close(workspace_id: str, shell_id: str) -> dict[str, Any]:
|
||||
"""Close a persistent workspace shell."""
|
||||
return self.close_shell(workspace_id, shell_id)
|
||||
|
||||
@server.tool()
|
||||
async def workspace_delete(workspace_id: str) -> dict[str, Any]:
|
||||
"""Delete a persistent workspace and its backing sandbox."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue