Documentation
¶
Overview ¶
Package tool owns the fail-closed capability surface the model calls. Every tool implements one interface, and every method that could parallelize badly or mutate state defaults to the conservative answer, so a tool that says nothing is treated as unsafe (doc 04 section 2).
Index ¶
- func HashBytes(b []byte) string
- func ResolveMutationPath(path string) string
- func UnifiedDiff(path, oldText, newText string) string
- type AntID
- type Attachment
- type BackgroundCloser
- type Base
- func (Base) CheckPermissions(context.Context, json.RawMessage, *ToolContext) PermissionResult
- func (Base) IsConcurrencySafe(json.RawMessage) bool
- func (Base) IsDestructive(json.RawMessage) bool
- func (Base) IsReadOnly(json.RawMessage) bool
- func (Base) MatchPrefix(json.RawMessage) PrefixMatcher
- func (Base) MaxResultSize() int
- type Diagnostic
- type DiskSpill
- type EditDisplay
- type FetchDisplay
- type FileEntry
- type FileState
- func (f *FileState) Apply(e *FileStateEffect)
- func (f *FileState) Clear()
- func (f *FileState) Entry(path string) (FileEntry, bool)
- func (f *FileState) Fresh(path, onDiskHash string) bool
- func (f *FileState) Hash(path string) string
- func (f *FileState) Set(path, hash string, mtime time.Time, lines int)
- type FileStateEffect
- type FindDisplay
- type FindFile
- type FindMatch
- type Journal
- type Pattern
- type PermissionResult
- type PrefixMatcher
- type ProgressFunc
- type Registry
- func (r *Registry) DeferredTools() []Tool
- func (r *Registry) ForAllowlist(allowed []string) *Registry
- func (r *Registry) Load(names ...string) []Tool
- func (r *Registry) Names() []string
- func (r *Registry) Register(t Tool) error
- func (r *Registry) RegisterDeferred(t Tool) error
- func (r *Registry) Resolve(name string) (Tool, bool)
- func (r *Registry) SchemaNames() []string
- type Result
- type Sandbox
- type SandboxSpec
- type Schema
- type ShDisplay
- type ShSub
- type SkillDeps
- type SkillDisplay
- type SpillRef
- type SpillStore
- type Tool
- type ToolContext
- type WriteDisplay
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HashBytes ¶
HashBytes is the content hash the map keys freshness on, shared by every tool that arms or checks the gate.
func ResolveMutationPath ¶
ResolveMutationPath resolves a write target the way the write tool does: symlinks resolved through the nearest existing ancestor, so a symlink pointing into a protected area cannot hide the area from the safety floor.
func UnifiedDiff ¶
UnifiedDiff renders a line-based unified diff between two versions of one file. The permission renderer (doc 05) and the edit display both lean on it, so it lives here rather than in a UI package.
Types ¶
type AntID ¶
type AntID string
AntID identifies the calling ant, for scoping side effects and attributing tokens in the ledger.
type Attachment ¶
Attachment is a non-text block a tool hands to the model. Unused until M7 gives read image passthrough; the field exists now so M7 is a capability flip, not a rewrite (doc 04 section 4.1).
type BackgroundCloser ¶
type BackgroundCloser interface {
CloseBackground() error
}
BackgroundCloser is implemented by a tool that owns detached processes it must reap when the session ends. The runner resolves it off the registry so it never has to name sh directly.
type Base ¶
type Base struct{}
Base carries the fail-closed defaults. Embed it and override only the methods whose answer is not the conservative one. A tool that embeds Base and overrides nothing is a serial, unsafe, non-destructive tool with a default size cap and no permission opinion, which is exactly the shape a half-finished tool must have (doc 04 section 2.4).
func (Base) CheckPermissions ¶
func (Base) CheckPermissions(context.Context, json.RawMessage, *ToolContext) PermissionResult
func (Base) IsConcurrencySafe ¶
func (Base) IsConcurrencySafe(json.RawMessage) bool
func (Base) IsDestructive ¶
func (Base) IsDestructive(json.RawMessage) bool
func (Base) IsReadOnly ¶
func (Base) IsReadOnly(json.RawMessage) bool
func (Base) MatchPrefix ¶
func (Base) MatchPrefix(json.RawMessage) PrefixMatcher
func (Base) MaxResultSize ¶
type Diagnostic ¶
type Diagnostic = lsp.Diagnostic
Diagnostic re-exports the language-server finding type so the typed tool displays carry diagnostics without the UI having to import the lsp package: the import-graph guard lets the UI reach the core only through event and tool (doc 02 section 1). It is an alias, so a value flows between tool and lsp with no conversion.
type DiskSpill ¶
type DiskSpill struct {
// contains filtered or unexported fields
}
DiskSpill writes spilled results into one directory, one numbered file per spill. The loop wires it to the nest's session state dir and calls Sweep when the session ends.
func NewDiskSpill ¶
NewDiskSpill builds a store over dir, creating it on first Put.
type EditDisplay ¶
type EditDisplay struct {
Path string
Diff string
Diagnostics []Diagnostic
}
EditDisplay is the typed data the UI renders for an edit: a real unified diff, syntax-highlighted by the TUI, plus any diagnostics the language server reported for the edited file, shown under the diff. Never sent to the model.
type FetchDisplay ¶
FetchDisplay is the typed data the UI renders for a fetch: the URL is the consequence a permission ask shows (doc 01 section 5.3).
type FileEntry ¶
type FileEntry struct {
Hash string // content hash at last read or write
Mtime time.Time // filesystem mtime at last read or write
Lines int // line count, for the unchanged-read stub
}
FileEntry is what the map remembers about one file.
type FileState ¶
type FileState struct {
// contains filtered or unexported fields
}
FileState is the per-session read-before-write map. read populates it, edit and write consult and refresh it. It is the session-level invariant that no mutation touches an unseen file (D8, doc 04 section 10). Safe for the loop's concurrent access.
func (*FileState) Apply ¶
func (f *FileState) Apply(e *FileStateEffect)
Apply folds a tool's FileStateEffect into the map. The loop calls it after every successful call that returned one.
func (*FileState) Clear ¶
func (f *FileState) Clear()
Clear empties the map. Compaction calls it before rebuilding the map for the restored working set (D8, doc 03 section 11): the ant only remembers reading what it actually re-read.
func (*FileState) Fresh ¶
Fresh reports whether the recorded hash still matches the content on disk. A missing entry means never-read; a mismatch means changed-since-read. The hash comparison is the whole check, no mtime, because cloud sync and antivirus bump mtimes without changing content (doc 04 section 10.2).
type FileStateEffect ¶
type FileStateEffect struct {
Path string // absolute, symlinks resolved
Hash string // content hash after the operation
Mtime time.Time // filesystem mtime after the operation
Lines int // line count, for the unchanged-read stub
}
FileStateEffect records what a successful read or write did to a file, so the loop can fold it into the session file-state map (doc 04 section 10). This is the arming step for the edit gate (D8).
type FindDisplay ¶
type FindDisplay struct {
Files []FindFile
Total int // matches before the cap
Capped bool // whether the limit truncated the set
}
FindDisplay is the typed match structure the TUI renders as a collapsible tree. It never goes to the model (doc 04 section 11.2).
type FindFile ¶
type FindFile struct {
Path string
Mtime time.Time
Matches []FindMatch // empty for a glob-only search
}
FindFile is one file's slice of the match set.
type Journal ¶
Journal is the slice of the append-only log a tool may write to. The colony's journal satisfies it.
type Pattern ¶
type Pattern struct {
Tool string // the tool name part, "sh" in sh(git:*)
Content string // the argument part, "git:*" in sh(git:*); empty is tool-wide
Source string // the pattern verbatim, journaled as written
}
Pattern is one permission rule pattern, kept as the user wrote it. The rule language proper lands with the pipeline (doc 05); tools only need enough shape here to answer MatchPrefix.
func (Pattern) CoversContent ¶
CoversContent reports whether this pattern's content part covers one content string (a path for write and edit, a URL for fetch), using the same three forms: exact, prefix with a trailing :*, wildcard. The prefix form is a plain string prefix here, so write(src/:*) covers anything under src/. In wildcards, * stops at a path separator and ** crosses them.
func (Pattern) CoversSubcommand ¶
CoversSubcommand reports whether this pattern's content part covers one normalized subcommand. Empty content is tool-wide and covers everything; "git commit:*" covers "git commit" and anything after it at a word boundary, so "git commit -m x" matches and "git commitx" does not; content with an inline * is a wildcard matched against the whole subcommand; content with neither must match exactly.
type PermissionResult ¶
type PermissionResult struct {
// contains filtered or unexported fields
}
PermissionResult is a tool's contribution to the permission pipeline. The pipeline (doc 05, slice 7) weighs it together with rules and mode; a tool cannot allow past a safety check from here (D15): an allow sets a pre-approval flag and still faces the safety floor.
func AllowResult ¶
func AllowResult() PermissionResult
AllowResult means the tool pre-approves this call. The pipeline still runs the safety floor before the approval lands.
func AskResult ¶
func AskResult(message string) PermissionResult
AskResult means the tool wants a human to decide this call.
func DenyResult ¶
func DenyResult(message string) PermissionResult
DenyResult means the tool refuses this call; the message tells the model why.
func Passthrough ¶
func Passthrough() PermissionResult
Passthrough means the tool has no opinion and the general rule and mode machinery decides. It is the Base default and the answer for every tool that has nothing early to say.
func (PermissionResult) IsAllow ¶
func (p PermissionResult) IsAllow() bool
IsAllow reports a tool pre-approval.
func (PermissionResult) IsAsk ¶
func (p PermissionResult) IsAsk() bool
IsAsk reports a tool escalation to a human.
func (PermissionResult) IsDeny ¶
func (p PermissionResult) IsDeny() bool
IsDeny reports a tool refusal.
func (PermissionResult) IsPassthrough ¶
func (p PermissionResult) IsPassthrough() bool
IsPassthrough reports whether the tool deferred entirely.
func (PermissionResult) Message ¶
func (p PermissionResult) Message() string
Message is the model-facing sentence for deny and ask.
type PrefixMatcher ¶
PrefixMatcher tests a permission pattern against one invocation. Only the tool knows how to parse its own arguments into the units a rule matches, which is why this lives on the tool.
type ProgressFunc ¶
type ProgressFunc func(chunk string)
ProgressFunc receives incremental output while a tool runs.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry resolves a tool by the name the model calls. The six core tools are registered at startup; skills and MCP tools attach later (doc 13) through the same Register call, so nothing about the registry knows they are special. It is deliberately dumb: it maps names to tools, rejects duplicates, filters to an ant's allowlist, and does nothing else (doc 04 section 13.1).
func (*Registry) DeferredTools ¶
DeferredTools lists the deferred tools not yet loaded, sorted by name, so the ant can announce them by name and the search step can match a query against them.
func (*Registry) ForAllowlist ¶
ForAllowlist returns the subset a worker ant may call, filtered by its card's Commands allowlist (doc 04 section 12.1). A tool absent from the allowlist is not in the returned registry, so the model never sees it in the tool list and cannot call it.
func (*Registry) Load ¶
Load marks deferred tools loaded and returns the ones that were newly loaded, so the search step can echo their schemas to the model. A name that is not a deferred tool is ignored, so a bad query loads nothing.
func (*Registry) RegisterDeferred ¶
RegisterDeferred adds a tool whose schema is withheld until the model loads it. The tool is announced by name only and becomes callable after Load, so a server with fifty tools the ant never touches costs a line of names and nothing more (doc 13, D20).
func (*Registry) SchemaNames ¶
SchemaNames lists the tools whose schema the model may see this turn: every non-deferred tool plus any deferred tool already loaded. A deferred tool not yet loaded is absent, so its schema never rides the request.
type Result ¶
type Result struct {
// Model is the text the model will see, before size capping and
// spillover.
Model string
// Display carries typed data for the UI renderer (a diff, a file
// list, an exit code). It is never sent to the model. Nil for tools
// with no rich view.
Display any
// Attachments are non-text blocks for the model, images for M7
// read. Empty for everything else in v1.
Attachments []Attachment
// StateEffect describes any file-state map change (a read or a
// write) so the loop can update the session invariant. Nil when the
// tool touched no file.
StateEffect *FileStateEffect
}
Result is what Call returns before serialization. It separates the model payload from any structured data the UI wants for rich rendering.
type Sandbox ¶
type Sandbox interface {
// Wrap returns a command configured to run under the sandbox, or the
// command unchanged when the sandbox is disabled for this invocation.
Wrap(ctx context.Context, cmd *exec.Cmd, spec SandboxSpec) (*exec.Cmd, error)
}
Sandbox wraps a command so it runs under OS-level isolation. The concrete seatbelt, namespace, or container implementation lives in doc 14; sh only knows this seam.
type SandboxSpec ¶
type SandboxSpec struct {
Cwd string // the one directory writes are allowed under, by default
AllowNet bool // whether the command may reach the network
AllowWrite []string // extra writable paths beyond cwd
DisableForce bool // an explicit escape hatch, gated by permission (doc 05)
}
SandboxSpec is the policy one invocation runs under.
type Schema ¶
type Schema struct {
Name string // matches Tool.Name
Description string // one line, budgeted; the model reads this
Params json.RawMessage // JSON Schema for the argument object
}
Schema is the model-facing description of a tool.
type ShDisplay ¶
type ShDisplay struct {
Command string
Description string
Output string
ExitCode int
Killed bool
Background string // the shell id when run in the background
}
ShDisplay is the typed data the UI renders for a shell result: the command, its output for the scrollable pane, and how it ended.
type ShSub ¶
ShSub is one subcommand of a compound command line. Raw keeps the text as written, redirections included, because the safety floor cares about a > target the rule matcher strips. Norm is the wrapper-stripped, env-stripped, redirection-stripped form the rule language matches.
func ShSplit ¶
ShSplit splits a command line into its subcommands, each carrying its raw and normalized form. An empty or unparseable command returns nil, and the callers treat nil as "matches nothing".
func (ShSub) MutationTargets ¶
MutationTargets returns the paths this subcommand could write, resolved against cwd, computed conservatively: redirection targets always count, and for a subcommand that is not known read-only every non-flag argument counts as a candidate. A harmless word resolves to a harmless path; the safety floor only acts on the candidates that land inside a protected area (doc 05 section 7).
type SkillDeps ¶
type SkillDeps struct {
// Lookup resolves a discovered skill by name; the bool is false for an
// unknown name.
Lookup func(name string) (*skill.Skill, bool)
// Names lists the discovered names, for near-match suggestions on a miss.
Names func() []string
// Matcher builds the allowed-tools gate for one skill, or nil when the
// skill declares no allowed-tools. It carries the doc 05 normalization.
Matcher func(s *skill.Skill) skill.Matcher
// Inline runs one gated command through the session's sh path. Nil
// disables inline execution entirely.
Inline skill.InlineRunner
// Grant adds a skill's allowed-tools to the session as always-allow rules,
// tagged with the skill name for the doc 05 audit trail.
Grant func(name string, rules []string)
// Trusted is false for an untrusted-content session (D19): no skill runs
// inline shell regardless of its scope.
Trusted bool
}
SkillDeps wires the skill tool to the discovered set and to the host's permission-gated seams. The runner builds it per session, because the matcher and the inline runner both close over that session's permission pipeline (doc 13 section 2.5).
type SkillDisplay ¶
SkillDisplay is the typed data the UI renders for an invocation: the name, whether it was a re-invocation, and any deferral or gate notes. Never sent to the model.
type SpillRef ¶
type SpillRef struct {
Path string // absolute path the model can read() or find over
}
SpillRef points at a spilled result on disk.
func ApplyResultBudget ¶
ApplyResultBudget caps the model-facing text and spills the remainder. The loop runs it after Call returns, before the result becomes a tool_result. The preview is head-heavy on purpose: the first three quarters of the budget is the head, the last quarter the tail, because the top of a command's output is usually what matters (doc 04 section 3.2). The returned ref is the zero value when nothing spilled; the loop surfaces it on the tool end event.
type SpillStore ¶
SpillStore persists oversized tool results and returns a reference the model can read back. Files live under the project nest and are swept on session end (doc 04 section 3.2).
type Tool ¶
type Tool interface {
// Name is the stable identifier the model calls and permission rules
// match. It is lower_snake for core tools: read, write, edit, sh,
// find, fetch.
Name() string
// Schema returns the JSON Schema for the argument object, plus the
// one-line description the model reads. The loop renders this into
// the tool list.
Schema() Schema
// ValidateInput checks the decoded arguments and returns a
// model-facing reason when they are invalid. It runs before
// permissions and shows no UI. A non-nil error here becomes a
// tool_result the model can read and correct.
ValidateInput(ctx context.Context, args json.RawMessage, tc *ToolContext) error
// CheckPermissions is the tool's hook into the doc 05 pipeline. It
// returns a PermissionResult the pipeline weighs together with rules
// and mode. The default is Passthrough, which means "defer entirely
// to the general system".
CheckPermissions(ctx context.Context, args json.RawMessage, tc *ToolContext) PermissionResult
// Call runs the tool. It streams progress through onProgress and
// returns a Result. Call must respect ctx cancellation promptly.
Call(ctx context.Context, args json.RawMessage, tc *ToolContext, onProgress ProgressFunc) (*Result, error)
// IsReadOnly reports whether this invocation only observes state.
// Default false. A read-only invocation may run in a plan-mode
// session.
IsReadOnly(args json.RawMessage) bool
// IsConcurrencySafe reports whether this invocation may run in
// parallel with other concurrency-safe tools in the same batch.
// Default false. The loop (doc 03) partitions on this.
IsConcurrencySafe(args json.RawMessage) bool
// IsDestructive reports whether this invocation is irreversible (rm,
// force push, DROP). Default false. The permission renderer (doc 05)
// leans on this to escalate the consequence it shows.
IsDestructive(args json.RawMessage) bool
// MatchPrefix returns a matcher the permission pipeline (doc 05)
// uses to test a rule pattern like sh(git commit:*) against this
// specific invocation.
MatchPrefix(args json.RawMessage) PrefixMatcher
// MaxResultSize is the byte ceiling on the model-facing result
// before it spills to disk. Zero means "no spill" (read uses this).
MaxResultSize() int
}
Tool is one typed, fail-closed capability the model can invoke by name.
func NewSh ¶
func NewSh() Tool
NewSh builds the sh tool with its own background-shell registry, one per session because the registry lives as long as the tool value.
func NewSkill ¶
NewSkill builds the skill tool over the discovered set and the session's gated seams.
func NewToolSearch ¶
NewToolSearch builds the search-and-load tool over a registry. Register it only when the registry actually has deferred tools, so a session with no MCP servers never shows the model a tool it cannot use.
type ToolContext ¶
type ToolContext struct {
// Cwd is the working directory sh inherits and paths resolve
// against. It persists across sh calls within a session; shell state
// does not.
Cwd string
// Files is the read-before-write file-state map (doc 04 section 10).
// read updates it, edit and write consult and refresh it.
Files *FileState
// Ant identifies the calling ant so the tool can scope side effects
// and the ledger can attribute tokens.
Ant AntID
// Namespace is the calling ant's memory namespace (doc 07), the scope
// the memory tools read and write. The loop sets it per turn from the
// card; empty means no memory is bound and the memory tools refuse.
Namespace string
// Sandbox is the seam sh runs commands through (doc 14). Nil in a
// trusted local run means "no sandbox"; non-nil wraps the command.
Sandbox Sandbox
// Spill is where oversized results are written (doc 04 section 3).
Spill SpillStore
// Journal records tool events for the append-only log (doc 01).
Journal Journal
// LSP is the language-server seam edit, write, and read use to fold
// compiler diagnostics into their results and to warm the server (doc
// 04 sections 2, 3, 6). Nil means no language server: every tool
// degrades to no diagnostics, never a failed edit.
LSP lsp.LSPClient
// Now is injected so tests and replay sets are deterministic.
Now func() time.Time
}
ToolContext is the session-scoped surface a tool is allowed to touch. The loop builds it per turn; a tool never reaches past it into globals. Inject a temp dir Cwd, a fresh FileState, and a fake clock, and a tool is fully exercisable without a real session (doc 04 section 2.3).
type WriteDisplay ¶
type WriteDisplay struct {
Path string
Content string
Created bool
Diagnostics []Diagnostic
}
WriteDisplay is the typed data the UI renders for a write: the whole new content, whether the file is new, and any diagnostics the language server reported for the written file. Never sent to the model.