Documentation
¶
Overview ¶
Package agentworkspace is where an agent's tools act: a file tree, processes, a working directory and an environment. A session runs over one Workspace, whichever machine it is on, and every tool that touches files or runs a command goes through it, so a tool written for one runs unchanged on another. Local is the working directory on this machine; a container or a remote runtime is another implementation of the same interface.
The interface is the one the openhands-workspace study settled on: a small value with a file system, one-shot execution and a descriptor the session records. A workspace that can run a process for longer than one call, with pipes to it, is also a Starter.
The package is pre-1.0: its API may change in a minor version.
Index ¶
- Constants
- Variables
- func Read(w Workspace, name string, max int64) ([]byte, error)
- type Command
- type Descriptor
- type Local
- func (l *Local) Close() error
- func (l *Local) Descriptor() Descriptor
- func (l *Local) Env() []string
- func (l *Local) Exec(ctx context.Context, c Command) (*Output, error)
- func (l *Local) FS() fs.FS
- func (l *Local) Real() string
- func (l *Local) Remove(_ context.Context, name string) error
- func (l *Local) Root() string
- func (l *Local) Start(ctx context.Context, c Command) (Process, error)
- func (l *Local) WriteFile(_ context.Context, name string, data []byte, perm fs.FileMode) error
- type Output
- type Process
- type Starter
- type Workspace
Constants ¶
const ( KindLocal = "local" KindContainer = "container" KindRemote = "remote" )
Kinds a Descriptor names, as agentsession's env entry records them.
Variables ¶
var ErrOutside = errors.New("path is outside the workspace")
ErrOutside is what a name that leaves the workspace gets, whether it names another directory outright, climbs out with "..", or goes out through a symbolic link.
Functions ¶
Types ¶
type Command ¶
type Command struct {
// Args is argv; Args[0] is resolved in the workspace.
Args []string
// Dir is relative to Root; "" is Root. A Dir that leaves the
// workspace, by name or through a link, is ErrOutside.
Dir string
// Env is added to Workspace.Env.
Env []string
// Stdin is the process's standard input; nil is none. Start refuses
// it: a started process's input is Process.Stdin.
Stdin []byte
// Timeout ends the process, and everything it started, after this
// long; zero is no limit but the context's.
Timeout time.Duration
// Stream, when set, receives the output the caller is not otherwise
// given, as it arrives. For Exec that is standard output and
// standard error, interleaved, and Output holds neither. For Start
// it is standard error, standard output being Process.Stdout; nil
// discards it.
Stream io.Writer
}
Command is one process to run.
type Descriptor ¶
type Descriptor struct {
// Kind is KindLocal, KindContainer or KindRemote.
Kind string
// Ref is what the harness resolves to it: an image digest, a host,
// an instance ID; empty for the local machine.
Ref string
// Root is Workspace.Root.
Root string
}
Descriptor is what the session records about a workspace, in its env entry: agentsession's workspace kind and ref, and the root, which is the entry's cwd.
type Local ¶
type Local struct {
// contains filtered or unexported fields
}
Local is a directory on this machine. Every file operation goes through an os.Root, which the kernel-facing layer of the standard library keeps from following a name out of the directory, so a link inside it that points outside is refused rather than followed. A process it runs is not confined: a shell reaches whatever the user does, and the policy is what stands in front of it.
Local runs on Unix: its processes run in process groups of their own, and its files are opened without blocking.
func NewLocal ¶
NewLocal opens dir as a workspace whose processes start with env and nothing else: nil is an empty environment, not this process's. A product passes this process's environment without its credentials.
func (*Local) Close ¶
Close ends every process Start started that is not closed yet, as Process.Close does, and releases the directory handle.
func (*Local) Descriptor ¶
func (l *Local) Descriptor() Descriptor
Descriptor names the local machine.
func (*Local) Env ¶
Env is the environment its processes start with, a copy: a caller that changes it changes nothing a later process starts with.
func (*Local) Exec ¶
Exec runs a command in its own process group, so a timeout or a cancel kills what it started too, and waits at most two seconds for output a killed child's children still hold open.
func (*Local) Real ¶
Real is the directory with its links resolved, in which an absolute path the model wrote may be spelled.
func (*Local) Start ¶
Start runs a command in its own process group, with pipes for its standard input and output, and returns at once. Its standard error goes to Command.Stream, or nowhere. The process, and everything it started, is ended by Process.Close, by the end of ctx, by Command.Timeout, or by the workspace's Close; when it exits by itself, anything left in its group is killed.
type Output ¶
type Output struct {
Stdout, Stderr []byte
// ExitCode is the process's status; -1 when it was killed.
ExitCode int
// TimedOut reports that Command.Timeout ended it.
TimedOut bool
}
Output is how a process ended.
type Process ¶
type Process interface {
// Stdin is the process's standard input. Closing it is end of file
// for the process.
Stdin() io.WriteCloser
// Stdout is the process's standard output. It reaches end of file
// once the process and everything it started have closed it.
Stdout() io.ReadCloser
// Wait waits for the process to exit and returns its status, -1 if
// a signal ended it. err is the context's error when the context or
// Command.Timeout ended it, and otherwise nil unless waiting itself
// failed; an exit status other than zero is not an error. Wait may
// be called any number of times and returns the same each time.
Wait() (status int, err error)
// Signal sends sig to the process and to everything it started. It
// is os.ErrProcessDone once the process has exited.
Signal(sig os.Signal) error
// Close ends the process, if it is still running, and everything it
// started, and releases the pipes. It first closes Stdin and gives
// the process a moment to exit, as the MCP stdio transport's
// shutdown asks, then terminates it, then kills it. It returns once
// the process has exited, and may be called more than once.
Close() error
}
Process is a process started in a workspace. Its methods are safe for concurrent use: one goroutine may read Stdout while another writes Stdin and a third waits.
type Starter ¶
type Starter interface {
Workspace
// Start starts cmd and returns at once. The process lives no longer
// than its owner: Process.Close, the end of ctx, cmd.Timeout and
// the workspace's Close each end it and everything it started, and
// when it exits by itself, what it started goes with it. A caller
// that is done with it calls Close, even after it exited.
Start(ctx context.Context, cmd Command) (Process, error)
}
Starter is a Workspace that can run a process for longer than one call, with pipes to it: an MCP server over stdio, a persistent shell. It is an interface of its own, as fs.ReadLinkFS is beside fs.FS, so that a workspace that only stores files, or a runtime whose API runs one command at a time, is still a Workspace; a caller asks for it with a type assertion, or calls Start.
type Workspace ¶
type Workspace interface {
// Root is the absolute path of the working directory as the
// workspace's own processes see it: the checkout on this machine,
// /workspace in a container. It is what the model is told and what
// the session records as its cwd.
Root() string
// FS reads the workspace's files. Opening a name never blocks: a
// FIFO or a device opens, or fails, at once, and a tool checks what
// it opened is a regular file. A name that leaves the workspace is
// an error that errors.Is ErrOutside. An FS that can tell a link
// from what it leads to implements fs.ReadLinkFS; one that cannot
// leaves a check that must follow links unable to, and so asking.
FS() fs.FS
// WriteFile creates or replaces a regular file, creating the
// directories above it. It refuses a FIFO or a device where the file
// is, and does not block on one.
WriteFile(ctx context.Context, name string, data []byte, perm fs.FileMode) error
// Remove deletes a file or an empty directory.
Remove(ctx context.Context, name string) error
// Env is the environment every process in the workspace starts
// with, the provider's key and other credentials already removed.
Env() []string
// Exec runs one command to completion in a fresh process.
Exec(ctx context.Context, cmd Command) (*Output, error)
// Descriptor is what the session records about where the tools ran.
Descriptor() Descriptor
// Close releases the workspace. It is used until Close and not after.
Close() error
}
Workspace is where a session's tools act. Names given to it are fs.ValidPath names relative to Root; turning a path the model wrote into one is the tool's.