stdio

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package stdio is the process-boundary transport for the Agent Client Protocol bridge: it carries a *protocol.Conn over a process's stdin and stdout. Serve wires an agent process's own standard streams to a Conn already built by the caller (the agent side); Spawn starts and supervises a child process whose stdin/stdout carry the other end of that same Conn (the client side, used by acp/client).

This package is stdlib-only plus acp/protocol: it never imports github.com/looprig/harness or github.com/looprig/core, directly or transitively (see acp/CLAUDE.md).

spawn.go is the client side of the stdio transport: it starts a child process whose stdin/stdout carry the other end of a protocol.Conn, and supervises its teardown. The process-group handling here mirrors the proven pattern in foreignloops/driver/claude/claude.go: configure the child as its own process-group leader, escalate SIGINT -> grace period -> SIGKILL(group) on teardown, and reap it exactly once. Unsupported platforms (see process_unsupported.go) fail before any child is started; they never fall back to weaker supervision.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Serve

func Serve(ctx context.Context, r io.Reader, w io.Writer, conn *protocol.Conn) error

Serve runs the agent-side stdio transport loop. conn must already be built by the caller over r and w (typically protocol.NewConn(r, w, opts), with r, w the same os.Stdin/os.Stdout) with any handlers registered; Serve's job is to translate ctx cancellation — of which Conn itself has no notion — into the transport closure that unblocks Conn's blocked read/write on r/w, and to return once the connection is over by either cause.

Serve blocks until ctx is done or conn ends on its own (the peer disconnected, or something else closed conn). Either way, it ensures conn is closed and returns: ctx.Err() if ctx caused the return, or nil if conn ended on its own — ordinary peer disconnect is not itself an error worth reporting to Serve's caller.

Types

type Command

type Command struct {
	// Path is the absolute, cleaned path to the executable.
	Path string
	// Args is the argument list (excluding argv[0]).
	Args []string
	// Env is the child's complete environment. A nil or empty Env starts the
	// child with an empty environment, never this process's ambient one.
	Env []string
	// Dir is the child's working directory. Empty means "inherit this
	// process's current working directory" (as os/exec does by default); a
	// non-empty Dir must be an absolute, cleaned path.
	Dir string
}

Command describes a child process to spawn. Every field is validated at the boundary: Path must be an absolute, cleaned path (never resolved via PATH lookup or a shell); Env is the child's complete environment (an explicit whitelist assembled by the caller) and is never merged with this process's ambient environment, even when nil or empty.

type CommandError

type CommandError struct{ Field, Reason string }

CommandError reports an invalid Command discovered before any child is started.

func (*CommandError) Error

func (e *CommandError) Error() string

type ExitError

type ExitError struct {
	// Err is the underlying error from the child's Wait (typically an
	// *exec.ExitError, or the escalated-kill outcome).
	Err error
	// Stderr is the bounded tail of the child's stderr at the time it exited.
	Stderr []byte
}

ExitError reports a child's abnormal exit together with the last stderrRingCapacity bytes of its stderr, kept only for diagnosis and never parsed for protocol meaning.

func (*ExitError) Error

func (e *ExitError) Error() string

func (*ExitError) Unwrap

func (e *ExitError) Unwrap() error

type PlatformError

type PlatformError struct{ GOOS string }

PlatformError reports an OS without the process-group supervision Spawn requires. Spawn returns this before starting a child; it never degrades to weaker supervision.

func (*PlatformError) Error

func (e *PlatformError) Error() string

type Proc

type Proc struct {
	Stdin  io.WriteCloser
	Stdout io.Reader
	// contains filtered or unexported fields
}

Proc is a supervised child process. Stdin and Stdout carry the raw transport bytes for a protocol.Conn built on top of them; Stderr is never exposed as a stream — it is drained internally to a bounded ring and surfaced only via ExitError.

func Spawn

func Spawn(ctx context.Context, cmd Command) (*Proc, error)

Spawn starts cmd as the leader of its own process group and returns a Proc supervising it. It validates cmd, checks platform support, and honors ctx: a context already canceled (or one that is canceled before the child exits) triggers the same SIGINT -> grace -> SIGKILL(group) teardown that Kill performs, run asynchronously so ctx cancellation never blocks on it.

The returned Proc's Stdin/Stdout are pipes to the child; the child's stderr is drained internally (never exposed) to a bounded ring surfaced via ExitError on abnormal exit.

func (*Proc) Kill

func (p *Proc) Kill() error

Kill tears the process group down: SIGINT, a closeGrace grace period for a cooperative exit, then SIGKILL to the group, followed by the single Wait reap. It is idempotent — concurrent or repeated calls all observe the same result — and always returns after the child has been reaped, so it never leaves a zombie behind.

func (*Proc) Signal

func (p *Proc) Signal(sig os.Signal) error

Signal sends sig to the entire process group (the leader and every descendant that has not changed its own group).

func (*Proc) Wait

func (p *Proc) Wait() error

Wait blocks until the child exits and reaps it. This is the single call to the underlying *exec.Cmd.Wait — safe to call concurrently and any number of times, including after Kill (which calls it internally as its own final step): every caller observes the same cached result. A non-nil result is always an *ExitError carrying the stderr tail captured at exit.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL