Documentation
¶
Overview ¶
Package tool holds dax-coding's tools: read, write, edit, glob, grep, ls and bash. They are written against the agenttool contract, so the same values run under agentturn, under any other Open Responses loop, or behind an MCP server. They act through a workspace.Workspace, never os or os/exec, so they run unchanged on this machine (workspace.Local), in a container or on a remote runtime, and the policy's checks of their calls read that workspace's files too.
Any extension uses this package for its own tools: Files to turn a path the model gives into the workspace's and confine it (ReadFile, WriteFile, Update, Stat, ReadDir), DefaultEnv for the environment of a local workspace's processes, and BashSubjects, PathSubjects and Analyzer to read a command line or a path as the policy does. Each of dax's tools makes the facts claim (agenttool.Factual) from that same analysis: what a call would touch, and bash's stamped plan; the session takes the policy's subjects and the stamp from the claims.
Index ¶
- Constants
- Variables
- func Bash(f *Files, opts ...BashOption) agenttool.Tool
- func BashSubjects(f *Files, maxFile int64) agentpolicy.Subjects
- func Builtins(f *Files, maxRead int64, bash ...BashOption) []agenttool.Tool
- func ChildEnv(base, pass []string, secrets ...string) []string
- func DefaultEnv(pass []string, secrets ...string) []string
- func Edit(ws *Files, opts ...ReadOption) agenttool.Tool
- func Glob(ws *Files) agenttool.Tool
- func Grep(ws *Files) agenttool.Tool
- func LS(ws *Files) agenttool.Tool
- func PathCalls(f *Files, name string) ([]agenttool.FactCall, error)
- func PathSubjects(f *Files, field, def string) agentpolicy.Subjects
- func Read(ws *Files, opts ...ReadOption) agenttool.Tool
- func ReadOnly(f *Files, maxRead int64) []agenttool.Tool
- func StampArgs(ctx context.Context, an *Analyzer, args json.RawMessage) (out json.RawMessage, changed bool, err error)
- func Text(r agenttool.Result) string
- func Write(ws *Files) agenttool.Tool
- type Analyzer
- type BashArgs
- type BashOption
- type Check
- type EditArgs
- type Files
- func (w *Files) Dir() string
- func (w *Files) ReadDir(path string) ([]fs.DirEntry, error)
- func (w *Files) ReadFile(path string, max int64) ([]byte, error)
- func (w *Files) Rel(path string) (string, error)
- func (w *Files) Resolve(path string) (string, error)
- func (w *Files) Stat(path string) (fs.FileInfo, error)
- func (w *Files) Update(path string, max int64, fn func(old []byte) ([]byte, error)) (string, error)
- func (w *Files) Workspace() workspace.Workspace
- func (w *Files) WriteFile(path string, data []byte) (string, error)
- type GlobArgs
- type GrepArgs
- type LSArgs
- type ReadArgs
- type ReadOption
- type StageCheck
- type WriteArgs
Constants ¶
const DefaultMaxRead = 2 << 20
DefaultMaxRead is the most bytes of one file read scans, and the largest file edit will rewrite.
Variables ¶
var ErrLinksUnknown = errors.New("the workspace cannot say where its links lead")
ErrLinksUnknown is Resolve's error where the workspace cannot say where a link leads.
var ErrOutside = workspace.ErrOutside
ErrOutside is what a path that leaves the workspace gets, whether it names another directory outright, climbs out with "..", or goes out through a symbolic link.
Functions ¶
func Bash ¶
func Bash(f *Files, opts ...BashOption) agenttool.Tool
Bash returns a tool that runs a shell command at the workspace's root, on whichever machine the workspace is, with the environment the workspace gives its processes. It is sequential: a batch that contains a shell command runs one call at a time, so a command never races a concurrent edit of the same file.
Its facts claim is what a call would touch, from the same analysis the policy's bash rules read (BashSubjects), and, for a line the analysis allows unasked, the arguments carrying the stamp of that plan and of those facts: the tool then runs only that plan, and only while the line still touches what it was allowed on (command). A line outside the analysis that writes through a redirect carries the stamp of the files its redirects write, and runs as typed only while they are the same.
func BashSubjects ¶
func BashSubjects(f *Files, maxFile int64) agentpolicy.Subjects
BashSubjects returns the subjects splitter of the bash tool for the workspace f. What it decides is who may auto-run:
- A command line inside the safe subset (see Analyzer) is one subject per stage, each its words joined by single spaces, so an allow rule for a command can match its stage. A stage of a command the check governs (git, ls, cat ...) whose arguments are not acceptable also gets a sentinel subject, which no rule matches, so `git log --output=x` is not covered by a rule for `git log`. So does a git stage in a repository whose configuration names a program, and the question says which.
- Any other command line is cut into the parts a rule can still name, so a deny or ask rule for rm reaches `git status; rm x`, and a sentinel subject is added, so no allow rule for a command ever covers it. A redirect to a file is a write of the file it opens, links followed (redirectCalls). The cut is for the question the user is asked and for deny and ask rules; it is a best-effort reading of bash, not a parser, and nothing is allowed because of it.
A command with an unterminated quote is an error, which blocks the call, and so are arguments with a key that is command or dax_stamp in another case (exactKeys), which the tool would read and the analysis would not. The analysis runs under the context the policy passes, the decision's, so the git-config check it runs through the workspace's Exec is cancelled with the decision.
func Builtins ¶
func Builtins(f *Files, maxRead int64, bash ...BashOption) []agenttool.Tool
Builtins returns read, write, edit, glob, grep, ls and bash, in the order the system prompt lists them. The file tools are confined to f's workspace; bash runs at its root but is not confined.
func ChildEnv ¶
ChildEnv is base without its credentials: every variable isSecret names, and every one in secrets, except those in pass. Bash commands and MCP servers get this environment, so a test, a build script or a server cannot read the provider's key from it. secrets is for a key whose variable the user named and isSecret may not recognise.
func DefaultEnv ¶
DefaultEnv is the current environment through ChildEnv.
func Edit ¶
func Edit(ws *Files, opts ...ReadOption) agenttool.Tool
Edit returns a tool that replaces one exact occurrence of a string.
func Glob ¶
Glob returns a tool that lists files whose path matches a doublestar-style pattern, sorted. It skips .git and common vendored directories.
func Grep ¶
Grep returns a tool that searches files for a regular expression and returns path:line:text for each matching line.
func LS ¶
LS returns a tool that lists one directory: names sorted, directories with a trailing slash, files with their size.
func PathCalls ¶ added in v0.0.9
PathCalls is what a read of name, a path as a file tool takes it, would touch, as dax-coding's file tools claim it: name normalised against the workspace's root, and what its links lead to, read through f's workspace. The calls name no tool (Tool is empty, the tool that reads), except the one a workspace that cannot read links adds, which names a tool no rule names, so it asks. It is exported for an extension whose tool reads a workspace file by another name, dax-skills' skill tool reading a file of a project's skill, so its facts claim can name the file as a read would (see extension.Extension.HeldTo).
func PathSubjects ¶
func PathSubjects(f *Files, field, def string) agentpolicy.Subjects
PathSubjects returns the subjects splitter of a file tool: its one subject is the path in field, normalised against the workspace's root (see normalizePath), so a rule written for docs/** meets docs/../.git/x as .git/x, ./.env and a/../.env as .env and the absolute path of a file as the same name. The name the model used is matched, and so is what its links lead to, read through the workspace: a file called notes.txt that is a link to .env is .env to the rules. A workspace that cannot read links adds a subject no rule allows, so the call asks. A path that leaves the workspace gets a subject no rule names, so it asks. When the field is absent the subject is def, which is the directory a search defaults to. Arguments with a key that is field in another case are an error, which blocks the call (exactKeys): the tool would decode that key, and this reads field.
func Read ¶
func Read(ws *Files, opts ...ReadOption) agenttool.Tool
Read returns a tool that reads a file with line numbers. It streams the file: memory is bounded by the output and one line, not by the file, and it scans at most the size cap, so a multi-gigabyte file costs what a small one does. Past the cap, use grep to find the line.
func StampArgs ¶
func StampArgs(ctx context.Context, an *Analyzer, args json.RawMessage) (out json.RawMessage, changed bool, err error)
StampArgs is the policy's side of an auto-allowed bash call: when the command line analyses as Auto, the arguments come back carrying the stamp of the plan that was approved and of the facts it was approved on (planStamp), and the bash tool will run only that plan on those facts. Any other call comes back with a stamp the model supplied, which is not valid, removed. changed says the arguments differ. Arguments with a key that is command or dax_stamp in another case are an error (exactKeys).
Types ¶
type Analyzer ¶
type Analyzer struct {
// Files is the workspace the commands run in, at its root.
Files *Files
// MaxFile is the largest file cat, head, tail, wc and grep may be
// given; zero is DefaultMaxRead.
MaxFile int64
// ConfigKey reads the exec-bearing key a repository's git
// configuration names in dir, absolute in the workspace's namespace;
// nil runs git in the workspace (execConfigKey). strict is set for
// a line that will run as typed, without the auto-allow environment.
ConfigKey func(ctx context.Context, dir string, strict bool) (string, error)
}
Analyzer decides which command lines dax runs without asking. It is an allow-list: a command line is auto-allowed only if it parses in the safe subset (parsePlan) and every stage is one of the commands below, run with arguments that can be shown to be read-only and to stay in the workspace. A stage whose command is one of these but whose arguments fail is not merely unlisted: it is a stage no allow rule may cover, because a rule for "git log" must not allow `git log --output=x`.
Every look it takes at a file goes through the workspace (Files), and git's configuration is read by running git there, so a command that will run in a container is checked against the container's files.
type BashArgs ¶
type BashArgs struct {
Command string `json:"command" desc:"The command to run"`
Timeout int `json:"timeout_seconds,omitempty" desc:"Kill the command after this many seconds (default 120)"`
// Stamp is set by dax when the policy allowed the command without
// asking; a value that dax did not set makes the call fail.
Stamp string `json:"dax_stamp,omitempty" desc:"Set by dax; leave it out"`
}
BashArgs are the arguments of the bash tool.
type BashOption ¶
type BashOption func(*bashConfig)
BashOption configures the bash tool.
func WithMaxFile ¶
func WithMaxFile(n int64) BashOption
WithMaxFile sets the largest file the auto-allowed cat, head, tail, wc and grep may be given; see Analyzer.
type Check ¶
type Check struct {
// Parsed is whether the line is in the safe subset. When it is not,
// Stages is empty and nothing is auto-allowed.
Parsed bool
Stages []StageCheck
// Auto is whether the whole line runs without asking.
Auto bool
// contains filtered or unexported fields
}
Check is the verdict on a command line.
type EditArgs ¶
type EditArgs struct {
Path string `json:"path" desc:"File path"`
Old string `json:"old_string" desc:"Exact text to find"`
New string `json:"new_string" desc:"Replacement text"`
// Stamp is the stamp of the facts the policy decided this call on
// (dax sets it); the tool runs only if they still hold.
Stamp string `json:"dax_stamp,omitempty" desc:"Set by dax; leave it out"`
}
EditArgs are the arguments of the edit tool.
type Files ¶
type Files struct {
// contains filtered or unexported fields
}
Files is the file tools' view of a workspace.Workspace: the paths the model writes, absolute or relative to the workspace's root, turned into the workspace's names, and the lock that keeps a write from landing between another's read and write. The workspace does the rest, so the same tools act on this machine's directory (workspace.Local), a container or a remote runtime alike.
bash is not confined by it: a shell can reach anything the workspace's processes can. That is the policy's to decide.
A session makes one Files for its workspace, and every tool that writes shares it, so the lock is one lock.
func (*Files) ReadFile ¶
ReadFile reads a regular file no larger than max bytes; zero or less is DefaultMaxRead.
func (*Files) Rel ¶
Rel turns a path the model gave, absolute or relative to the workspace, into a name relative to its root, cleaned. A path that names somewhere else is ErrOutside. It checks the name alone; the Root's own checks catch a link that leaves.
func (*Files) Resolve ¶
Resolve is the name, relative to the workspace's root, that path leads to with every link on its way followed, read through the workspace's file system, as the policy's checks follow them. A path or a link that leads out of the workspace is ErrOutside; a workspace whose file system cannot read links, or a chain of links too long to follow, is ErrLinksUnknown, so a caller that must know where a name leads fails toward refusing. A name that does not exist is judged by where its directory leads. An extension that offers a project's files through a tool other than the file tools (dax-skills) uses it to keep a link from reaching past what it offers.
func (*Files) Update ¶
Update reads a regular file no larger than max bytes (zero or less is DefaultMaxRead), and writes what fn makes of its content, holding the write lock across both, as the edit tool does. An error from fn leaves the file as it was. It returns the file's name relative to the workspace.
type GlobArgs ¶
type GlobArgs struct {
Pattern string `` /* 159-byte string literal not displayed */
Path string `` /* 135-byte string literal not displayed */
MaxResults int `json:"max_results,omitempty" desc:"Maximum paths to return (default 1000)"`
// Stamp is the stamp of the facts the policy decided this call on
// (dax sets it); the tool runs only if they still hold.
Stamp string `json:"dax_stamp,omitempty" desc:"Set by dax; leave it out"`
}
GlobArgs are the arguments of the glob tool.
type GrepArgs ¶
type GrepArgs struct {
Pattern string `json:"pattern" desc:"Regular expression (Go RE2 syntax)"`
Path string `json:"path,omitempty" desc:"File or directory to search, relative to the workspace (default the workspace root)"`
Include string `` /* 152-byte string literal not displayed */
IgnoreCase bool `json:"ignore_case,omitempty" desc:"Match case-insensitively"`
MaxResults int `json:"max_results,omitempty" desc:"Maximum matching lines to return (default 200)"`
// Stamp is the stamp of the facts the policy decided this call on
// (dax sets it); the tool runs only if they still hold.
Stamp string `json:"dax_stamp,omitempty" desc:"Set by dax; leave it out"`
}
GrepArgs are the arguments of the grep tool.
type LSArgs ¶
type LSArgs struct {
Path string `json:"path,omitempty" desc:"Directory to list, relative to the workspace (default the workspace root)"`
// Stamp is the stamp of the facts the policy decided this call on
// (dax sets it); the tool runs only if they still hold.
Stamp string `json:"dax_stamp,omitempty" desc:"Set by dax; leave it out"`
}
LSArgs are the arguments of the ls tool.
type ReadArgs ¶
type ReadArgs struct {
Path string `json:"path" desc:"File path, relative to the workspace or absolute inside it"`
Offset int `json:"offset,omitempty" desc:"1-based line to start at (default 1)"`
Limit int `json:"limit,omitempty" desc:"Maximum lines to return (default 2000)"`
// Stamp is the stamp of the facts the policy decided this call on
// (dax sets it); the tool runs only if they still hold.
Stamp string `json:"dax_stamp,omitempty" desc:"Set by dax; leave it out"`
}
ReadArgs are the arguments of the read tool.
type ReadOption ¶
type ReadOption func(*readConfig)
ReadOption configures the read tool.
func WithMaxRead ¶
func WithMaxRead(n int64) ReadOption
WithMaxRead sets the most bytes one read call scans, which is also the largest file edit will rewrite. Zero or less is DefaultMaxRead.
type StageCheck ¶
type StageCheck struct {
// Words are the stage's words as text; Text what a person is asked
// about; Match what an allow rule is matched against.
Words []string
Text, Match string
// Governed is set for a command this check has opinions about.
Governed bool
// OK is whether the arguments of a governed command are acceptable.
OK bool
// Reads are the files the stage reads, relative to the workspace,
// for the policy's path rules.
Reads []string
// Why says what was wrong, or, for a git stage, the key of the
// repository's configuration that names a program.
Why string
}
StageCheck is the verdict on one stage of a command line.
type WriteArgs ¶
type WriteArgs struct {
Path string `json:"path" desc:"File path"`
Content string `json:"content" desc:"Full file content"`
// Stamp is the stamp of the facts the policy decided this call on
// (dax sets it); the tool runs only if they still hold.
Stamp string `json:"dax_stamp,omitempty" desc:"Set by dax; leave it out"`
}
WriteArgs are the arguments of the write tool.