Documentation
¶
Overview ¶
Package shelltool provides a shell command execution tool that can be registered with an agent. It mirrors the .NET LocalShellTool / LocalShellExecutor design: approval-in-the-loop is the default security boundary; an allow/deny Policy offers a best-effort pre-execution guardrail.
Security ¶
Running agent-generated shell commands is inherently dangerous. This package provides two complementary controls:
Policy: an allow/deny list of regular expressions checked before commands reach the shell. The policy is a UX guardrail, NOT a security boundary — a determined model can trivially work around regex checks.
Approval-in-the-loop: NewLocal returns a Local that reports approval is required through tool.ApprovalRequiredTool, so the harness tool-approval middleware prompts a human before every execution. This is the primary security control. Pass LocalConfig.AcknowledgeUnsafe = true only when you have an independent isolation mechanism (e.g. a Docker container) and understand the risk.
Usage ¶
t, err := shelltool.NewLocal(shelltool.LocalConfig{})
if err != nil {
// handle invalid configuration
}
env := shelltool.NewEnvironmentProvider(t, shelltool.EnvironmentProviderConfig{})
cfg := agent.Config{
Tools: []tool.Tool{t},
ContextProviders: []agent.ContextProvider{env},
}
Index ¶
- func DefaultShellEnvironmentInstructions(snapshot ShellEnvironmentSnapshot) string
- type EnvironmentProvider
- func (p *EnvironmentProvider) CurrentSnapshot() (ShellEnvironmentSnapshot, bool)
- func (p *EnvironmentProvider) Invoked(ctx context.Context, invoked agent.InvokedContext) error
- func (p *EnvironmentProvider) Invoking(ctx context.Context, invoking agent.InvokingContext) ([]*message.Message, []agent.Option, error)
- func (p *EnvironmentProvider) Refresh(ctx context.Context) (ShellEnvironmentSnapshot, error)
- type EnvironmentProviderConfig
- type Executor
- type Local
- func (t *Local) ApprovalRequired() bool
- func (t *Local) Call(ctx context.Context, args string) (any, error)
- func (t *Local) Close() error
- func (t *Local) Description() string
- func (t *Local) Initialize(ctx context.Context) error
- func (t *Local) Name() string
- func (t *Local) ReturnSchema() any
- func (t *Local) Run(ctx context.Context, command string) (Result, error)
- func (t *Local) Schema() any
- type LocalConfig
- type Mode
- type Policy
- type PolicyConfig
- type Result
- type ShellEnvironmentSnapshot
- type ShellFamily
- type ShellRequest
- type ToolVersion
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultShellEnvironmentInstructions ¶
func DefaultShellEnvironmentInstructions(snapshot ShellEnvironmentSnapshot) string
DefaultShellEnvironmentInstructions renders shell environment guidance for an agent.
Types ¶
type EnvironmentProvider ¶
type EnvironmentProvider struct {
// contains filtered or unexported fields
}
EnvironmentProvider probes a local shell and injects shell-specific instructions through an agent.ContextProvider.
func NewEnvironmentProvider ¶
func NewEnvironmentProvider(executor Executor, config EnvironmentProviderConfig) *EnvironmentProvider
NewEnvironmentProvider creates a shell environment provider backed by executor.
func (*EnvironmentProvider) CurrentSnapshot ¶
func (p *EnvironmentProvider) CurrentSnapshot() (ShellEnvironmentSnapshot, bool)
CurrentSnapshot returns the most recently captured snapshot, if one exists.
func (*EnvironmentProvider) Invoked ¶
func (p *EnvironmentProvider) Invoked(ctx context.Context, invoked agent.InvokedContext) error
Invoked implements agent.ContextProvider by delegating to the wrapped provider. The wrapped provider is configured without a Store, so this is a no-op on success.
func (*EnvironmentProvider) Invoking ¶
func (p *EnvironmentProvider) Invoking(ctx context.Context, invoking agent.InvokingContext) ([]*message.Message, []agent.Option, error)
Invoking implements agent.ContextProvider by delegating to the wrapped provider, applying this provider's context/instructions to the invocation.
func (*EnvironmentProvider) Refresh ¶
func (p *EnvironmentProvider) Refresh(ctx context.Context) (ShellEnvironmentSnapshot, error)
Refresh forces a re-probe and stores the new snapshot.
type EnvironmentProviderConfig ¶
type EnvironmentProviderConfig struct {
// SourceID identifies context injected by this provider.
// Defaults to "shell_environment".
SourceID string
// ProbeTools lists CLI tools whose --version output should be probed.
// Nil uses a small default list; an empty non-nil slice disables tool probes.
ProbeTools []string
// OverrideFamily forces the reported shell family when non-nil.
OverrideFamily *ShellFamily
// ProbeTimeout bounds each individual probe. Nil uses a 5 second default.
// Negative values are invalid.
ProbeTimeout *time.Duration
// InstructionsFormatter renders a snapshot into agent instructions.
// Defaults to [DefaultShellEnvironmentInstructions].
InstructionsFormatter func(ShellEnvironmentSnapshot) string
}
EnvironmentProviderConfig configures an EnvironmentProvider.
type Executor ¶
type Executor interface {
Initialize(context.Context) error
Run(context.Context, string) (Result, error)
}
Executor runs shell commands for environment probing.
type Local ¶
type Local struct {
// contains filtered or unexported fields
}
Local runs shell commands on behalf of an agent.
func NewLocal ¶
func NewLocal(opts LocalConfig) (*Local, error)
NewLocal returns a local shell command tool for an agent.
func (*Local) ApprovalRequired ¶
ApprovalRequired reports whether calls should require human approval.
func (*Local) Call ¶
Call unmarshals the command argument, executes it via Run, and returns model-formatted output.
func (*Local) Description ¶
Description returns the model-facing description of the shell tool.
func (*Local) Initialize ¶
Initialize starts a persistent shell early; it is a no-op in stateless mode.
func (*Local) ReturnSchema ¶
ReturnSchema returns nil because the tool yields free-form model-formatted output.
type LocalConfig ¶
type LocalConfig struct {
// Shell is an optional override for the shell binary path. When empty,
// the AGENT_FRAMEWORK_SHELL environment variable is consulted; if that is
// also unset, the OS default is used (/bin/bash on POSIX, pwsh/cmd on
// Windows). Mutually exclusive with [LocalConfig.ShellArgv].
Shell string
// ShellArgv overrides the shell launch argv. The first element is the
// shell binary; remaining elements are passed as a launch-time prefix
// before the standard -c / -Command / persistent suffix. Mutually
// exclusive with [LocalConfig.Shell].
ShellArgv []string
// Mode selects stateless-per-call or persistent-shell execution.
// Defaults to [ModePersistent].
Mode Mode
// WorkingDirectory is the initial working directory for the shell.
// When empty, defaults to the current process working directory.
WorkingDirectory string
// ConfineWorkingDirectory controls whether each persistent command is
// prefixed with a cd/Set-Location back to [LocalConfig.WorkingDirectory].
// The default is true.
ConfineWorkingDirectory *bool
// Environment contains extra environment variables for the spawned shell.
// A nil value removes an inherited variable. A nil map means no overrides.
Environment map[string]*string
// CleanEnvironment starts the shell with only a small allowlist of
// inherited variables (PATH, HOME, USER, USERNAME, USERPROFILE,
// SystemRoot, TEMP, TMP) before applying [LocalConfig.Environment].
CleanEnvironment bool
// Timeout is the per-command deadline. Nil means no timeout. Negative values
// are invalid. 30s is the recommended value.
Timeout *time.Duration
// MaxOutputBytes caps each captured output stream per command.
// Defaults to 64 KiB. Output beyond this limit is
// silently truncated and [Result.Truncated] is set.
MaxOutputBytes int
// Policy is an optional allow/deny filter checked before the command
// reaches the shell. Nil means allow everything.
Policy *Policy
// AcknowledgeUnsafe opts out of the default approval-required gate.
// When false (the default), the returned tool's ApprovalRequired method
// reports true so the harness prompts a human before every execution. Set
// this to true only when you have an independent isolation mechanism and
// accept the risk.
AcknowledgeUnsafe bool
}
LocalConfig configures the shell tool returned by NewLocal.
type Mode ¶
type Mode int
Mode controls whether each call spawns a fresh shell or reuses a single long-lived shell process.
const ( // ModePersistent keeps a single shell alive across calls so that // directory changes, exported variables, and history persist. A // persistent executor MUST NOT be shared across concurrent users or // agent sessions. ModePersistent Mode = iota // ModeStateless spawns a new shell process for every command. Safe to // share across concurrent calls; no state leaks between invocations. ModeStateless )
type Policy ¶
type Policy struct {
// contains filtered or unexported fields
}
Policy is a layered allow/deny pattern filter for shell commands.
The regex filter is a UX guardrail, NOT a security boundary. It is intended to fast-fail commands operators would rather reject before execution while the primary isolation is approval-in-the-loop or container sandboxing.
A policy constructed with no patterns allows any non-empty command. Allow patterns are checked before deny patterns, so an allow match short-circuits evaluation and skips the deny list.
type PolicyConfig ¶
type PolicyConfig struct {
// DenyList contains patterns that trigger a deny outcome. Nil or empty
// disables the deny list.
DenyList []string
// AllowList contains explicit-allow patterns. A match here short-circuits
// the deny list.
AllowList []string
}
PolicyConfig configures a Policy.
type Result ¶
type Result struct {
// Stdout is the captured standard output, possibly truncated.
Stdout string
// Stderr is the captured standard error, possibly truncated.
Stderr string
// ExitCode is the exit status reported by the process. -1 if the process
// did not exit cleanly.
ExitCode int
// Duration is how long the command ran end-to-end.
Duration time.Duration
// Truncated is true when stdout or stderr was truncated.
Truncated bool
// TimedOut is true when the command was killed for exceeding the timeout.
TimedOut bool
}
Result is the outcome of a single shell command invocation.
func (Result) FormatForModel ¶
FormatForModel returns a single text block combining stdout, stderr, status flags, and the exit code — suitable for returning to the language model.
type ShellEnvironmentSnapshot ¶
type ShellEnvironmentSnapshot struct {
Family ShellFamily
OSDescription string
ShellVersion string
WorkingDirectory string
ToolVersions map[string]ToolVersion
}
ShellEnvironmentSnapshot is a point-in-time view of the shell environment the agent is using.
type ShellFamily ¶
type ShellFamily int
ShellFamily identifies the shell syntax family in use.
const ( // ShellFamilyUnknown means no shell family has been selected or detected. ShellFamilyUnknown ShellFamily = iota // ShellFamilyPOSIX is a POSIX-style shell such as bash, sh, or zsh. ShellFamilyPOSIX // ShellFamilyPowerShell is PowerShell, including pwsh and Windows PowerShell. ShellFamilyPowerShell )
func (ShellFamily) String ¶
func (f ShellFamily) String() string
type ShellRequest ¶
type ShellRequest struct {
// Command is the full command line that the agent wants to run.
Command string
// WorkingDirectory is the optional working directory the command will
// execute in, if known.
WorkingDirectory string
}
ShellRequest is a shell command awaiting a policy decision.
type ToolVersion ¶
ToolVersion reports whether a CLI was found and, when available, the first non-empty line from its --version output.