Documentation
¶
Overview ¶
Package tools implements what turns dcode into a coding agent rather than a chat: reading, searching, editing and running.
This is the package with the most consequence per line. A wrong edit here corrupts a user's work silently, so several rules are enforced structurally rather than trusted to the model:
- edit only operates on a file read in this session and unchanged since;
- an ambiguous match fails instead of guessing;
- Declare reports what a call would touch without doing any of it, so policy decides before there is anything to undo.
Spec: docs/specs/architecture/tool-suite/202608072337-*.
Index ¶
- Constants
- func Reporter(ctx context.Context, kind string, total int) func(done int)
- func UnifiedDiff(before, after, path string) string
- func WithProgress(ctx context.Context, r Report) context.Context
- type BackgroundRunner
- type Bash
- type BashInput
- type Delegator
- type DonePropose
- type DoneProposeCriterion
- type DoneProposeInput
- type Edit
- type EditInput
- type Explore
- type ExploreInput
- type Fetch
- type FetchInput
- type Glob
- type GlobInput
- type Grep
- type GrepInput
- type Handle
- type Limits
- type LocalRunner
- type Meta
- type Plan
- type PlanInput
- type Process
- type ProcessInput
- type Read
- type ReadInput
- type Registry
- type Remember
- type RememberInput
- type Report
- type Result
- type Runner
- type State
- func (s *State) AddProcess(command string, h Handle) string
- func (s *State) Adopt(child *State)
- func (s *State) BeginCycle()
- func (s *State) BeginTurn()
- func (s *State) ChangedSinceRead(read func(path string) (string, error)) []string
- func (s *State) CheckEditable(path, current string) *ToolError
- func (s *State) Close()
- func (s *State) MarkRead(path, content string, msgIdx int)
- func (s *State) MarkWritten(path string)
- func (s *State) Plan() []protocol.PlanItem
- func (s *State) ReadPaths() []string
- func (s *State) Snapshot(path string)
- func (s *State) Undo() (restored, refused []string, err error)
- func (s *State) UndoCycle() (restored, refused []string, err error)
- func (s *State) WasRead(path string) bool
- func (s *State) WriteSeq() uint64
- func (s *State) Written() []string
- type Symbol
- type SymbolInput
- type Tool
- type ToolError
- type Write
- type WriteInput
Constants ¶
const ( // EchoDiffNever returns the count alone. EchoDiffNever = "never" // EchoDiffMulti returns the diff only where the model cannot derive the // result: a replace_all that hit more than one occurrence. EchoDiffMulti = "multi" // EchoDiffAlways returns it on every edit. Expensive in an append-only // history, and useful for debugging a model that is editing wrongly. EchoDiffAlways = "always" )
The three modes of DCODE_EDIT_ECHO_DIFF.
const ( KindDef = "def" KindRef = "ref" KindAny = "any" )
The three kinds.
const ( CodeFileNotRead = "file_not_read" CodeFileChanged = "file_changed" CodeNoMatch = "no_match" CodeAmbiguousMatch = "ambiguous_match" CodeNoOpEdit = "no_op_edit" CodeNotFound = "not_found" CodeInvalidPattern = "invalid_pattern" CodeTimeout = "timeout" CodePlanMultiActive = "plan_multiple_active" CodePlanNoReason = "plan_blocked_no_reason" CodeDenied = "denied" CodeBadInput = "bad_input" )
Error codes. Everything but denied is recoverable: the model reads the error and corrects itself, which is the whole reason the messages are written for a reader rather than for a log.
const DiffContext = 3
DiffContext is how many unchanged lines surround each change. Three is the convention every reviewer already reads.
const DiffMaxLines = 400
DiffMaxLines caps the diff carried on an event.
A whole-file rewrite would otherwise produce a payload larger than the file, and the client can only show a screenful anyway. The cap is generous enough that an ordinary edit is never touched.
const DoneProposeFile = "done.toml"
DoneProposeFile is what a proposal is written to, inside the spec folder.
const GrepMaxContextLines = 10
GrepMaxContextLines caps how much surrounding text one match may carry.
A ceiling rather than a configuration key, for the same reason the match limit is one: the number that matters is "enough to understand the match", and past a handful more lines stop informing and start costing. Asking for five thousand is asking for the file, which is what grep exists not to be.
const ProgressStep = 25
ProgressStep is how often a scan reports.
Every file would put one event per file into the log and the record — a scan of ten thousand files writing ten thousand lines nobody reads. Every twenty-five keeps a hundred-file scan honest at four updates and a large one from flooding, and the final count arrives with the result either way.
const SymbolLimit = "textual match on symbol boundary; does not resolve interface or dynamic dispatch"
SymbolLimit is appended to every result, always.
A textual search cannot see a call made through an interface, a function pointer, reflection, or a name assembled at run time. That is a false negative, and a false negative is the worst failure mode there is: an empty result looks exactly like a complete answer. An agent that concludes "there are no other callers" from this and renames half of them does so with confidence.
Same principle as RN-5 on truncation, and for the same reason: output must not look complete when it is not.
Variables ¶
This section is empty.
Functions ¶
func Reporter ¶ added in v0.2.0
Reporter counts and reports on the step, so a caller writes one line in its loop instead of the same arithmetic in each tool.
func UnifiedDiff ¶
UnifiedDiff renders the change between two texts.
Empty when nothing changed: a diff with no hunks is noise, and the caller uses the empty string to mean "nothing to show".
Types ¶
type BackgroundRunner ¶
type BackgroundRunner interface {
Start(ctx context.Context, workdir, command string) (Handle, error)
}
BackgroundRunner starts a command that outlives the call that started it.
Separate from Runner rather than a method on it: a foreground command is waited on and a background one is not, and the two return different things. Folding them together would give one method two meanings decided by a flag, which is the shape Declare exists to keep out of this package.
type Bash ¶
type Bash struct {
Runner Runner
Workdir string
Timeout time.Duration
// Background starts a command that is not waited on. Nil means this build
// cannot start one, and asking for it is refused rather than silently run
// in the foreground — which would freeze the session, the exact failure
// the flag exists to prevent.
Background BackgroundRunner
// Settle is how long a background start watches before reporting. It is
// not a readiness check: it answers "did it die immediately", which is the
// common failure and the one a model cannot otherwise see.
Settle time.Duration
}
Bash runs a shell command.
func (Bash) Description ¶
func (Bash) Schema ¶
func (Bash) Schema() json.RawMessage
type BashInput ¶
type BashInput struct {
Command string `json:"command"`
Timeout int `json:"timeout_seconds,omitempty"`
Background bool `json:"background,omitempty"`
}
BashInput is the argument shape.
type Delegator ¶
type Delegator interface {
// Explore answers one question, reading and — when owns is non-empty —
// writing the paths it names.
//
// The implementation excludes this tool from the child's registry, so
// nesting stays impossible by absence. It also decides the mode: read-only
// when owns is empty, and otherwise the PARENT's mode, intersected with
// the owned paths. The caller never chooses either, which is why owns is a
// request rather than a grant.
Explore(ctx context.Context, task, path string, owns []string) (conclusion string, read, wrote, unread []string, truncated bool, err error)
}
Delegator runs a read-only sub-turn and reports what it found.
An interface for the same reason Bash takes a Runner: the loop owns turns and this package owns tools, and having either import the other would close a cycle. The tool declares and validates; the loop decides what a child turn is allowed to be.
type DonePropose ¶ added in v0.13.0
type DonePropose struct {
// Spec is the folder the proposal is for, absolute. Set by whoever built
// the qualifying turn: the model does not get to choose which folder its
// proposal lands in.
Spec string
// Submit records the proposal. It does not measure it and does not write
// anything: the loop does both, after the turn.
//
// Injected because it keeps this package from importing the loop, which
// imports this one — and because what happens to a proposal is not a
// tool's business. A tool is the boundary the model reaches through.
Submit func(ctx context.Context, in DoneProposeInput) (string, error)
}
DonePropose is how the model hands over a definition of done it derived.
A tool, and not prose, for the reason everything else in this harness is a tool: the call IS how the thing gets done, and there is no other way to do it. A model that describes criteria in a sentence has described them.
It is available only in a qualifying turn. A tool that can redefine done, within reach of a working turn, is the shortest way out of a loop — the agent rewrites the ruler instead of meeting it.
func (DonePropose) Declare ¶ added in v0.13.0
func (d DonePropose) Declare(json.RawMessage) (policy.Request, error)
Declare touches nothing, and that is the design rather than a shortcut.
The first version declared a write to the spec folder, which was honest about the consequence and wrong about the actor. A qualifying turn runs in plan mode — read-only, because working out what you will be measured by is reading — and read-only denies every write with no exception. So a tool that declared one was denied, and the model correctly reported that it could not propose.
Nothing here writes. The proposal is RECORDED, the turn ends, and the LOOP measures it and writes it down afterwards, under the boundary the work will actually run under. Keeping the write out of the turn is what lets plan mode stay a guarantee with no hole in it — and it measures the criteria somewhere they can actually run, which read-only is not.
func (DonePropose) Description ¶ added in v0.13.0
func (DonePropose) Description() string
func (DonePropose) Execute ¶ added in v0.13.0
func (d DonePropose) Execute(ctx context.Context, input json.RawMessage, _ *State) (Result, error)
func (DonePropose) Name ¶ added in v0.13.0
func (DonePropose) Name() string
func (DonePropose) Schema ¶ added in v0.13.0
func (DonePropose) Schema() json.RawMessage
type DoneProposeCriterion ¶ added in v0.13.0
type DoneProposeCriterion struct {
Name string `json:"name"`
Command string `json:"command"`
ExitCode int `json:"exit_code,omitempty"`
Expects string `json:"expects"`
Why string `json:"why,omitempty"`
}
DoneProposeCriterion is one candidate.
type DoneProposeInput ¶ added in v0.13.0
type DoneProposeInput struct {
Criteria []DoneProposeCriterion `json:"criteria"`
Protected []string `json:"protected,omitempty"`
}
DoneProposeInput is the argument shape.
type Edit ¶
type Edit struct{}
Edit replaces an exact string. The most consequential tool in the set.
func (Edit) Description ¶
func (Edit) Schema ¶
func (Edit) Schema() json.RawMessage
type EditInput ¶
type EditInput struct {
Path string `json:"path"`
OldString string `json:"old_string"`
NewString string `json:"new_string"`
ReplaceAll bool `json:"replace_all,omitempty"`
// Edits is the batch form. When present it wins, and the four fields above
// are ignored: two ways of saying the same call in one call is ambiguity
// nobody can resolve from the outside.
Edits []editOp `json:"edits,omitempty"`
}
EditInput is the argument shape.
type Explore ¶
type Explore struct {
Delegator Delegator
}
Explore delegates reading, so the cost of it does not come back.
dcode has one head. A task crossing twenty files reads all twenty in the same window, and each one pushes the last out; by file fifteen compaction hits and what was learned at file three becomes two lines of summary, if it survives at all.
Delegating inverts that. A child reads the twenty files in ITS window and returns half a page. The gain is not speed — it is that the cost of the reading does not come back.
func (Explore) Description ¶
func (Explore) Schema ¶
func (Explore) Schema() json.RawMessage
type ExploreInput ¶
type ExploreInput struct {
Task string `json:"task"`
Path string `json:"path,omitempty"`
// Owns are the paths the child may write, and it is the only way a child
// writes at all.
//
// Absent is the read-only child that already existed, so nothing changes
// for anyone already delegating. Present is a request, never a grant: the
// loop intersects it with what the parent may already write, and a child
// can only ever end up narrower than its parent.
//
// There is still no mode field, and the absence is still the guarantee.
// What the model passes is the task and the paths, and both may only
// narrow.
Owns []string `json:"owns,omitempty"`
}
ExploreInput is the argument shape.
The child receives the TASK, not the parent's history. That is the whole point: a copied history would return exactly the cost delegation exists to avoid.
There is no mode field, and that absence is the guarantee. Read-only is fixed where the sub-turn is constructed, so it is not something the model passes and not something a caller forgets.
type Fetch ¶
type Fetch struct {
// Client is injected so a test never reaches the network and so a
// deployment can supply its own timeouts and proxy.
Client *http.Client
// Limit caps the body. A document that does not fit is truncated and says
// so, like every other output here.
Limit int
}
Fetch reads a document off the web.
The tool suite put network access out of scope from the beginning, and the reason was sound at the time: a network tool without a permission model is a hole. That premise is gone. Consent is asked once per project and kept in grants, the policy already treats network as an axis of its own, and the evaluator already refuses what was not granted.
A fetch, not a browser. It returns the text at a URL, which is what reading a changelog or a library's documentation needs. Search is a different capability with a different failure mode — a model given a search box will answer from whatever came back first — and it waits for evidence that fetching alone is not enough.
Unlike bash, this runs in this process rather than behind the sandbox, so the gate is the policy verdict and not the operating system. That is the same guarantee every other tool here relies on: `read` is kept inside the workspace by the resolver, not by seatbelt. bash is the exception because a shell command is opaque, and this one is not.
func (Fetch) Declare ¶
Declare reports the crossing and no path.
Always the network, whatever the URL looks like. Reading the string to decide is the reasoning bash already rejected: there is no reading of it that answers whether this one reaches out, because all of them do.
func (Fetch) Description ¶
func (Fetch) Schema ¶
func (Fetch) Schema() json.RawMessage
type FetchInput ¶
type FetchInput struct {
URL string `json:"url"`
}
FetchInput is the argument shape.
type Glob ¶
type Glob struct{}
Glob finds files by pattern.
func (Glob) Description ¶
func (Glob) Schema ¶
func (Glob) Schema() json.RawMessage
type Grep ¶
type Grep struct{}
Grep searches file contents.
func (Grep) Description ¶
func (Grep) Schema ¶
func (Grep) Schema() json.RawMessage
type GrepInput ¶
type GrepInput struct {
Pattern string `json:"pattern"`
Path string `json:"path,omitempty"`
Glob string `json:"glob,omitempty"`
ContextLines int `json:"context_lines,omitempty"`
}
GrepInput is the argument shape.
type Handle ¶
type Handle interface {
// Output is everything written so far, stdout and stderr together.
Output() string
// Exited reports the exit code, and whether it has exited at all.
Exited() (code int, done bool)
// Stop ends it. Calling it on a command that already exited does nothing.
Stop()
}
Handle is a command that is still running, or one that has finished and whose output nobody has collected yet.
type Limits ¶
type Limits struct {
ReadMaxLines int
ReadMaxLineLength int
GlobMaxResults int
GrepMaxMatches int
SymbolMaxMatches int
MaxToolOutput int
RespectGitignore bool
RequireReadBefore bool
AtomicWrite bool
// EditEchoDiff decides when the diff of an edit goes back to the MODEL.
// It always goes to the client, in every mode.
EditEchoDiff string
}
Limits are the per-tool caps.
type LocalRunner ¶
type LocalRunner struct{}
LocalRunner executes through a plain shell. It exists for the development entry point only; the sandbox package supplies the real one.
type Meta ¶
type Meta struct {
// Lines read, or matched, or written.
Lines int
// Files touched or matched.
Files int
// Added and Removed are line counts for an edit or a write.
Added int
Removed int
// ExitCode of a command. Meaningful only when HasExit is set, because a
// successful command exits zero and zero is also the empty value.
ExitCode int
HasExit bool
// Diff is the unified diff of a change, for a client to render.
//
// It never reaches the model: the model wrote the edit and already knows
// what it changed, so putting the diff in the history would pay tokens for
// something nobody reads. It rides on the event instead.
Diff string
}
Meta is the structured account of what a tool call did. Every field is optional: a tool reports what applies to it and leaves the rest zero.
type Plan ¶
type Plan struct{}
Plan maintains the session plan. It touches no file and runs nothing: it changes session state only, which is why its policy verdict is always allow.
func (Plan) Description ¶
func (Plan) Schema ¶
func (Plan) Schema() json.RawMessage
type Process ¶
type Process struct{}
Process reads and stops the commands bash started in the background.
A separate tool from bash, and the reason is the policy verdict rather than the tool count. bash declares the network and a write to the workspace, because a shell command is opaque and the worst case is what gets declared. Reading a buffer this process already owns crosses nothing at all. Folded together, every read of a log would queue an approval for a boundary it does not touch, and a question asked for no reason is how people learn to answer without reading.
func (Process) Declare ¶
Declare reports nothing, because nothing is touched: no path, no network, no command run. The verdict is always allow.
func (Process) Description ¶
func (Process) Schema ¶
func (Process) Schema() json.RawMessage
type ProcessInput ¶
ProcessInput is the argument shape.
type Read ¶
type Read struct{}
Read returns file contents with line numbers, so the model and edit share one frame of reference.
func (Read) Description ¶
func (Read) Schema ¶
func (Read) Schema() json.RawMessage
type ReadInput ¶
type ReadInput struct {
Path string `json:"path"`
Offset int `json:"offset,omitempty"`
Limit int `json:"limit,omitempty"`
}
ReadInput is the argument shape.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds the enabled tools.
type Remember ¶
type Remember struct {
// Commit and Today are handed in rather than read here, so the provenance a
// memory carries agrees with the repository state the prefix already
// described. Two readings of git in one session can disagree, and
// disagreeing about where the session is is worse than not knowing.
Commit string
Today string
}
Remember writes something learned where the next session will read it.
A tool rather than something that happens on its own, and the distinction is the design: reading what was learned is a fact the model needs every turn, so it lives in the prefix; writing one is an act with consequences, and an act that happens without being asked is an act nobody authorised.
func (Remember) Description ¶
func (Remember) Schema ¶
func (Remember) Schema() json.RawMessage
type RememberInput ¶
type RememberInput struct {
Kind string `json:"kind"`
Subject string `json:"subject"`
Body string `json:"body"`
}
RememberInput is the argument shape.
type Report ¶ added in v0.2.0
Report says how far a call has got. Total 0 means the total is not known yet, which is honest for a walk that has not finished enumerating.
type Result ¶
type Result struct {
Output string
IsError bool
Code string
Truncated bool
Remaining int
// Meta is what a client needs to render the call in one line without
// parsing Output.
//
// Parsing prose to rebuild a number the tool already knew is how a UI
// silently breaks when the wording changes. The tool states it once.
Meta Meta
}
Result is what a tool hands back to the loop.
type Runner ¶
type Runner interface {
Run(ctx context.Context, workdir, command string) (stdout string, exitCode int, err error)
}
Runner executes a command inside the sandbox. The tool never reaches for os/exec directly: every execution goes through the boundary, and an interface is what makes that structural instead of a rule to remember.
type State ¶
type State struct {
Resolver *policy.Resolver
Limits Limits
// contains filtered or unexported fields
}
State is the per-session state tools share.
func NewState ¶
NewState builds session state for a session offering these tools.
The names are a parameter rather than a setter with a default, because every default here is a guess about what the model can reach, and a guess that is wrong is an error message sending it somewhere that does not exist.
func (*State) AddProcess ¶
AddProcess records a started command and returns the identifier the model will use to reach it again.
The identifier is a sequence, not a clock. A timestamp would ride into tool output and make two otherwise identical sessions differ byte-for-byte, which is the same reason MarkRead takes a message index instead of a time.
func (*State) Adopt ¶
Adopt takes over what a delegated child recorded, so the turn that asked for the work is the turn that can undo it.
Undo is per turn and delegation happens inside one. Without this the parent's undo would reach everything except the part it delegated, and undoing half of a division of work leaves a tree nobody designed.
Three things move, and each for its own reason:
- the snapshots, so there is something to put back. The parent's own snapshot wins where both have one: the first of the turn is what the turn started from, and the child's later one records a state the turn itself produced.
- the file records, because undo refuses a file that moved since the turn left it, and that judgement needs the hash of what was actually written. Here the child's wins, because the child wrote last and the disk agrees with it.
- the written set, because "did this session change anything" is the fact the definition of done reads, and work done by a child is still work done.
func (*State) BeginCycle ¶ added in v0.16.0
func (s *State) BeginCycle()
BeginCycle marks a point inside a turn that UndoCycle can come back to.
A turn holds many verification cycles, and BeginTurn's snapshot set spans all of them. Undoing at turn scope after one bad cycle would throw away every good cycle that came before it, which is why the loop could not be given the undo it already had.
It takes a SECOND layer of snapshots — how each already-touched path stands right now — and leaves the turn's own layer alone. One layer was tried and is wrong: a path written in cycle one and again in cycle two would keep cycle two's content, so the bad cycle's write survived the undo of the bad cycle. Two layers because they answer different questions: the turn's is "how did this look before the model started", the cycle's is "how did it look before this attempt".
func (*State) BeginTurn ¶
func (s *State) BeginTurn()
BeginTurn starts a new set of undoable changes.
A new turn replaces the old set rather than adding to it. Keeping them would let one undo reach back through work the person already read and accepted, and "undo" that goes further than the last thing is not undo.
func (*State) ChangedSinceRead ¶
ChangedSinceRead reports files whose content on disk no longer matches what the model was shown.
The reader is injected so the check is testable without a filesystem, and so the caller decides what "on disk" means. A file that has become unreadable is not reported as changed: it is a different failure, and the tool that touches it next will say so precisely.
func (*State) CheckEditable ¶
CheckEditable enforces the read-before-edit invariant.
This is the rule that prevents the most expensive failure the product can produce: blind-editing a file whose contents the model assumed. Without it the agent silently overwrites work the user did in parallel.
func (*State) Close ¶
func (s *State) Close()
Close stops every background process this session started.
This is where "a process dies with the session" stops being a rule someone has to remember and becomes ownership: the table lives in session state, and state that goes away takes the processes with it. It is also what makes approving a long command need no separate question about duration — the authorisation and the process have the same lifetime, so there is no window in which someone consented to an instant and granted an era.
func (*State) MarkRead ¶
MarkRead records that path was read with this content.
msgIdx is the position in history, deliberately not a timestamp: a clock value here would leak into tool output and invalidate the prompt cache on every call.
func (*State) MarkWritten ¶
MarkWritten records that a tool changed path.
Separate from MarkRead on purpose. MarkRead is also called right after a write, to keep the read-before-edit invariant satisfied for the next edit — so it cannot answer "did this session change anything", which is the fact the definition of done needs.
func (*State) ReadPaths ¶
WasRead reports whether path has been read this session. ReadPaths returns the workspace-relative paths this session opened, sorted.
It is what a delegated turn hands back beside its conclusion: it does not prove the child understood, but it proves it looked, and it turns "trust me" into something a person can spot-check.
func (*State) Snapshot ¶
Snapshot records how a file stood before this turn changes it.
Called by the tools that write, before they write. Only the first call per path per turn is kept: the first is what the turn started from, and a later one would record a state the turn itself produced.
A file that is not there is recorded as not there, which is what makes undoing a creation mean removing it.
func (*State) Undo ¶
Undo puts back what the last turn changed, and reports what it would not touch.
A file that changed on disk after the turn left it is refused, never overwritten. Undoing over somebody's own edit would throw away their work to restore something older, which is the opposite of what undo is for — and it is the same invariant `edit` already enforces when it refuses a file that moved under it.
Refusing is per file rather than all-or-nothing. Seven files changed and one edited by hand should still give six back, and saying which one did not go is more useful than refusing everything on account of it.
func (*State) UndoCycle ¶ added in v0.16.0
UndoCycle puts back what changed since the last BeginCycle.
Only what this cycle wrote. A path the turn touched earlier and this cycle left alone is not restored, because there is nothing about it to undo.
Without a BeginCycle it undoes nothing and says so by returning nothing. A cycle boundary nobody marked is not a boundary, and guessing one would undo the whole turn under a name that promises less.
func (*State) WriteSeq ¶
WriteSeq is how many writes this session has made, ever-increasing.
The set above answers "what changed"; this answers "did anything change since a moment I recorded". They are different questions, and the set cannot stand in for the counter: rewriting a file already in it leaves it identical, which is the ordinary shape of fixing what a failing check just reported.
A count rather than a clock, deliberately. The verification seal is compared between turns and shown to a person, and a timestamp there would vary per run for a fact that is purely ordinal.
type Symbol ¶
type Symbol struct{}
Symbol finds where an identifier is declared and where it is used.
The eighth tool, and what justifies it is NOT the word boundary — `\bParse\b` is a regular expression the model could already write, since grep takes Go regexps. What it could not write without knowing every language is the distinction between a definition and a use: `func Parse(` in Go, `def parse` in Python, `fn parse` in Rust, `function` or `const … =` in TypeScript.
That knowledge is data this package carries, rather than something the model reconstructs per language on every call and gets wrong in half of them.
func (Symbol) Description ¶
func (Symbol) Schema ¶
func (Symbol) Schema() json.RawMessage
type SymbolInput ¶
type SymbolInput struct {
// Name is the symbol. NOT a regular expression: it is escaped before it
// becomes a pattern. Accepting a regexp here would reintroduce the problem
// through the back door and make symbol into grep with another name.
Name string `json:"name"`
Kind string `json:"kind,omitempty"` // "def" | "ref" | "any"; default "any"
Path string `json:"path,omitempty"`
Glob string `json:"glob,omitempty"`
}
SymbolInput is the argument shape.
type Tool ¶
type Tool interface {
Name() string
Description() string
Schema() json.RawMessage
// Declare reports what this call would touch, with no side effect at all.
// Splitting it from Execute is what makes "policy decides first" a
// structural property rather than a rule someone has to remember.
Declare(input json.RawMessage) (policy.Request, error)
Execute(ctx context.Context, input json.RawMessage, s *State) (Result, error)
}
Tool is one capability.
type ToolError ¶
ToolError is a failure the model is expected to recover from.
Reason says what failed; Hint suggests a way forward without prescribing one. A generic message forces the model to guess, and guessing is how an agent corrupts a file.
type Write ¶
type Write struct{}
Write creates a file or replaces one wholesale.
func (Write) Description ¶
func (Write) Schema ¶
func (Write) Schema() json.RawMessage
type WriteInput ¶
WriteInput is the argument shape.