Skip to content

Redesign guest ProcessService and FileSystemService - #65

Draft
Jaana Dogan (rakyll) wants to merge 1 commit into
mainfrom
proto
Draft

Redesign guest ProcessService and FileSystemService#65
Jaana Dogan (rakyll) wants to merge 1 commit into
mainfrom
proto

Conversation

@rakyll

@rakyll Jaana Dogan (rakyll) commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

ProcessService is rebuilt around a five-RPC surface with streaming stdin,
POSIX signals, and an output stream that carries the exit status:

  • StartProcess returns the full Process resource. The request gains
    stdin (open a pipe) and timeout (per-process SIGKILL deadline).
  • StreamProcessOutput streams a oneof of stdout/stderr chunks and ends
    with a final exit message carrying the finished Process, so clients
    no longer poll GetProcess for the exit code. Offsets past the end of
    the spool suppress output, which makes "follow with max offsets" the
    way to wait for exit without transferring data.
  • WriteProcessInput is client-streaming; stdin stays open across calls
    until a message sets close, enabling interactive use.
  • SignalProcess delivers any standard POSIX signal (new Signal enum) to
    the process group and replaces KillProcess.

Process now carries command and pid, a two-state ProcessState
(RUNNING/EXITED), and a single exit_code using the shell convention
(128 + signal number when killed by a signal). The old COMPLETED/
FAILED/TERMINATED statuses are gone.

Guest process tracker: return plain sentinel errors mapped to gRPC codes
by the service layer, wake output streamers on write via a notifier
instead of polling every 50ms, and map the Signal enum to host syscall
signals by name so tests pass on non-Linux hosts.

FileSystemService: ReadFile streams ReadFileResponse{chunk} instead of
FileChunk{data}. ReadFileRequest gains mode: when set and the file is
missing, the guest creates it empty with that mode (parent directories
included) and ends the stream without chunks; when zero, a missing file
remains NOT_FOUND; existing files are never modified. WriteFileRequest
gains seek_offset: zero keeps the replace-the-file behaviour, a
positive offset writes in place from that byte, zero-filling any gap
and preserving bytes outside the written range; negative offsets are
INVALID_ARGUMENT. Fields are reordered to path, mode, seek_offset,
chunk, with path/mode/seek_offset read from the first message only.

Update the API proxy, the MCP shell tool (new stdin and timeout_seconds
params), the Go SDK (Process handle with Info/Wait/Output/Stdin/Signal/
Kill, Env.Run taking a ShellRequest, Env.WriteFileAt), the Python SDK
(Process class, Signal/ProcessState enums, ProcessExitedError,
write_file seek_offset), the CLI shell command (--stdin, --timeout),
regenerated Go and Python stubs, and READMEs. Add server, SDK, fake,
and e2e tests throughout.

This is a breaking change to the alpha API.

@rakyll
Jaana Dogan (rakyll) force-pushed the proto branch 2 times, most recently from 954feca to 0d24b66 Compare September 12, 2026 05:13
@rakyll Jaana Dogan (rakyll) changed the title Redesign ProcessService: stdin streaming, signals, exit-carrying output stream Redesign guest ProcessService and FileSystemService Sep 12, 2026
ProcessService is rebuilt around a five-RPC surface with streaming stdin,
POSIX signals, and an output stream that carries the exit status:

- StartProcess returns the full Process resource. The request gains
  `stdin` (open a pipe) and `timeout` (per-process SIGKILL deadline).
- StreamProcessOutput streams a oneof of stdout/stderr chunks and ends
  with a final `exit` message carrying the finished Process, so clients
  no longer poll GetProcess for the exit code. Offsets past the end of
  the spool suppress output, which makes "follow with max offsets" the
  way to wait for exit without transferring data.
- WriteProcessInput is client-streaming; stdin stays open across calls
  until a message sets `close`, enabling interactive use.
- SignalProcess delivers any standard POSIX signal (new Signal enum) to
  the process group and replaces KillProcess.

Process now carries command and pid, a two-state ProcessState
(RUNNING/EXITED), and a single exit_code using the shell convention
(128 + signal number when killed by a signal). The old COMPLETED/
FAILED/TERMINATED statuses are gone.

Guest process tracker: return plain sentinel errors mapped to gRPC codes
by the service layer, wake output streamers on write via a notifier
instead of polling every 50ms, and map the Signal enum to host syscall
signals by name so tests pass on non-Linux hosts.

FileSystemService: ReadFile streams ReadFileResponse{chunk} instead of
FileChunk{data}. ReadFileRequest gains `mode`: when set and the file is
missing, the guest creates it empty with that mode (parent directories
included) and ends the stream without chunks; when zero, a missing file
remains NOT_FOUND; existing files are never modified. WriteFileRequest
gains `seek_offset`: zero keeps the replace-the-file behaviour, a
positive offset writes in place from that byte, zero-filling any gap
and preserving bytes outside the written range; negative offsets are
INVALID_ARGUMENT. Fields are reordered to path, mode, seek_offset,
chunk, with path/mode/seek_offset read from the first message only.

Update the API proxy, the MCP shell tool (new stdin and timeout_seconds
params), the Go SDK (Process handle with Info/Wait/Output/Stdin/Signal/
Kill, Env.Run taking a ShellRequest, Env.WriteFileAt), the Python SDK
(Process class, Signal/ProcessState enums, ProcessExitedError,
write_file seek_offset), the CLI shell command (--stdin, --timeout),
regenerated Go and Python stubs, and READMEs. Add server, SDK, fake,
and e2e tests throughout.

This is a breaking change to the alpha API.
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.

1 participant