tools

package
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 49 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultMaxLines       = 150       // No truncation threshold
	DefaultMaxBytes       = 24 * 1024 // 24KB
	DefaultTruncatedLines = 75        // Lines per side when truncated
	DefaultTruncatedBytes = 12 * 1024 // 12KB total when truncated
)
View Source
const DefaultGlobMaxResults = 100

DefaultGlobMaxResults caps one Glob call. Past it the result carries a truncation flag instead of more paths, the way Search degrades to compact output: a flat list of thousands of files is never what the next Read wants.

View Source
const EditPendingNextStep = "" /* 249-byte string literal not displayed */

EditPendingNextStep is the message shown when an edit is pending confirmation

View Source
const LargeFileThreshold = 1024 * 1024 // 1MB

LargeFileThreshold is the file size above which we require line hints for editing Files larger than this won't be loaded entirely into memory

View Source
const PendingEditAutoResolveThreshold = 5

PendingEditAutoResolveThreshold is the number of retries before auto-cancelling pending edit

View Source
const PendingEditEscalateThreshold = 3

PendingEditEscalateThreshold is the number of retries before escalating error message

View Source
const PendingEditMaxIgnoreCount = 5

PendingEditMaxIgnoreCount is the maximum number of ignored responses before auto-cancel

View Source
const PostEditContextLines = 3

PostEditContextLines is the number of surrounding lines to show in post-edit context

View Source
const StreamingEditBufferSize = 64 * 1024 // 64KB

StreamingEditBufferSize is the buffer size used for streaming file operations

View Source
const StructuredOutputToolName = "structured_output"

StructuredOutputToolName is the session-local tool a schema-carrying child must call with its final result.

Variables

View Source
var CategoryHeaders = map[string]string{
	"filesystem": "## File Tools Reference",
	"shell":      "## Shell Tool",
	"plan":       "## Plan Management Tools",
	"web":        "## Web Tools",
	"mcp":        "## MCP Tools (external servers)",
	"report":     "## Ending the Turn",
}

CategoryHeaders defines the section headers for each category

Functions

func ApplyLineEdit

func ApplyLineEdit(content string, startLine, endLine int, newText string) (string, int, int, error)

ApplyLineEdit applies a line-based edit to content. If endLine is 0, inserts newText at startLine (existing content shifts down). Otherwise replaces lines [startLine, endLine] (1-based, inclusive) with newText. Returns new content, edit start line, edit end line, and error.

func ApplyPatchChunks

func ApplyPatchChunks(content string, chunks []PatchChunk) (string, int, int, error)

ApplyPatchChunks applies patch chunks to file content. Returns new content, edit start line, edit end line, and error.

func BuildEditPreviewResult

func BuildEditPreviewResult(path, diff, newContent string, editStartLine, editEndLine int, isNewFile bool) map[string]any

BuildEditPreviewResult builds the standard preview result for an edit operation. This is the single place where the edit preview response format is defined.

func BuildEditSuccessResult

func BuildEditSuccessResult(path, diff, newContent string, editStartLine, editEndLine int, isNewFile bool) map[string]any

BuildEditSuccessResult builds the standard success result for an edit operation. This is the single place where the edit success response format is defined.

func BuildSubagentSystemPrompt added in v0.5.0

func BuildSubagentSystemPrompt(workspaceRoot, subagentType string) string

BuildSubagentSystemPrompt assembles the child system prompt: role line, environment, output-discipline block and batch-first guidance. What the child must NOT get: the parent's role/tasks/guidelines sections, project-instructions after blocks, or anything that tells it it can ask the user. The canonical templates live in internal/prompt/prompts/subagents/; this builder mirrors them without importing internal/prompt (which imports tools).

func CalculateEditLineRange

func CalculateEditLineRange(newContent string, replaceStart int, replaceText string) (startLine, endLine int)

CalculateEditLineRange calculates the line range affected by an edit given the new content and the byte position where the replacement was inserted. Returns 1-based start and end line numbers in the new content.

func Changes added in v0.2.0

func Changes(t Tool, args json.RawMessage) bool

Changes reports whether this particular call can change something: the tool Mutates, and it cannot vouch for these arguments as read-only.

func ClearPendingEditForPath

func ClearPendingEditForPath(toolCtx *ToolContext, path string)

ClearPendingEditForPath clears any pending edit for a specific path using the provided ToolContext.

func CommonEditCheck

func CommonEditCheck(ctx context.Context, args json.RawMessage, base *BaseEditTool) error

CommonEditCheck performs common validation for all edit tool modes

func CountMatches

func CountMatches(content, search string) int

CountMatches counts how many times search appears in content. When either side carries CRLF it also counts the CRLF-normalized form, so a LF search in a CRLF file reports its true multiplicity for the multiple-match check.

func CreateNewFileContent

func CreateNewFileContent(newText string) string

CreateNewFileContent creates content for a new file. For new files, ignores line numbers and just uses the newText as content.

func EnvironmentBlock added in v0.5.0

func EnvironmentBlock(workspaceRoot string) string

EnvironmentBlock describes where the child runs, mirroring internal/prompt.Environment without importing it (which would cycle tools->prompt->tools). Keep the working directory, platform and date; git and directory listing stay parent-only.

func FinalizeEdit

func FinalizeEdit(b *BaseEditTool, path, fullPath, oldContent, newContent, diff string,
	editStartLine, editEndLine int, isNewFile bool) (any, error)

FinalizeEdit handles preview mode or applies the edit with consistent result format. This is the single source of truth for the final edit handling logic.

func FindMatchPosition

func FindMatchPosition(content, search string) (start, end int, found bool)

FindMatchPosition finds the position of search in content Returns start index, end index, and whether found

func FindMatchWithSearch

func FindMatchWithSearch(ctx context.Context, cfg *config.Config, fullPath string, search string) (matchLine int, matchCount int, err error)

FindMatchWithSearch uses the Search tool (ripgrep/grep) to find matches in a file Returns: line number of first match, total match count, error This is robust and handles large files, long lines, binary files, etc.

func FindMostSimilarLine

func FindMostSimilarLine(content, search string) (lineNum int, line string, ratio float64)

FindMostSimilarLine finds the most similar line in content to the search string Useful for error messages suggesting what the user might have meant

func FindSimilarChunk

func FindSimilarChunk(content, search string, contextLines int) (startLine int, chunk string, ratio float64)

FindSimilarChunk finds a chunk of lines similar to the search text Returns the start line number, the chunk, and similarity ratio

func FloorChildConfig added in v0.5.0

func FloorChildConfig(cfg *config.Config, maxIters int) *config.Config

FloorChildConfig copies cfg for a subagent child loop. The iteration budget becomes the cap; read-before-edit is floored to cover the whole budget (a general child must never edit a file it has not read in its own loop); the preview handshake is forced off (Edit.confirm/Write.confirm are structurally absent from the child handle, so a pending_confirmation would wedge the child). SafetyConfirmations are copied, never shared. The agent package reuses this so the child tools and the child loop see the same config.

func FormatError

func FormatError(err error) string

FormatError checks if an error implements JSONError and returns JSON, otherwise returns plain text

func FormatToolError

func FormatToolError(err *ToolError) string

FormatToolError returns a formatted string representation of a ToolError If the error has details, returns JSON; otherwise returns plain message

func GeneralChildScope added in v0.5.0

func GeneralChildScope(parent *Registry, parentCtx *ToolContext, cfg *config.Config, maxIters int) (*Registry, *ToolContext)

GeneralChildScope is the test hook for the general allowlist.

func GeneratePostEditContext

func GeneratePostEditContext(newContent string, editStartLine, editEndLine int) string

GeneratePostEditContext generates a formatted view of the file content after edit showing the edited region with surrounding context lines. editStartLine and editEndLine are 1-based line numbers in the new content.

func GenerateStreamingDiff

func GenerateStreamingDiff(fullPath, path string, startLine, endLine int, oldLines, newText string) (string, error)

GenerateStreamingDiff generates a diff by reading only the affected line range This avoids loading the entire file for diff generation

func GetContextAroundPosition

func GetContextAroundPosition(content string, position int, contextLines int) string

GetContextAroundPosition returns a few lines of context around a byte position

func GetLineNumber

func GetLineNumber(content string, byteOffset int) int

GetLineNumber returns the 1-based line number for a byte offset in content

func GetMaxIgnoreCount

func GetMaxIgnoreCount(cfg *config.Config) int

GetMaxIgnoreCount returns the maximum ignore count from config or default

func HandleMultipleMatches

func HandleMultipleMatches(content, search, path string, count int) map[string]any

HandleMultipleMatches returns an error result with context for disambiguation

func HandleNoMatch

func HandleNoMatch(content, search, path string) map[string]any

HandleNoMatch returns a helpful error result when search text is not found

func IsBacktrackable

func IsBacktrackable(err error) bool

IsBacktrackable checks if an error should trigger backtracking Returns true only for semantic errors where the LLM should have known better

func IsLargeFile

func IsLargeFile(fullPath string) (bool, int64, error)

IsLargeFile checks if a file exceeds the large file threshold

func IsRipgrepAvailable

func IsRipgrepAvailable() bool

IsRipgrepAvailable returns true if ripgrep (rg) is available on the system

func LevenshteinDistance

func LevenshteinDistance(s1, s2 string) int

LevenshteinDistance calculates the edit distance between two strings

func LineEditDescription

func LineEditDescription(explicitDelete bool) string

LineEditDescription returns the description for line edit mode. When explicitDelete is true, it tells the model that removal is done via DeleteLines and that empty new_text blanks the line(s) rather than deleting them.

func LineEditJSONSchema

func LineEditJSONSchema(explicitDelete bool) map[string]any

LineEditJSONSchema returns the JSON schema for line edit mode. When explicitDelete is true, empty new_text blanks the addressed line(s) (deletes go through the DeleteLines tool) instead of removing them.

func LineEditPromptSection

func LineEditPromptSection(previewMode, explicitDelete bool) string

LineEditPromptSection returns the prompt section for line edit mode. When explicitDelete is true, it documents DeleteLines for removal and empty new_text as "blank the line" rather than delete.

func MatchWithNormalization

func MatchWithNormalization(content, search string, fuzzyThreshold float64, exactOnly bool) (start, end int, level int, found bool)

MatchWithNormalization tries to match search text with progressive normalization levels Returns position and the normalization level that succeeded: 0 = exact, 1 = rstrip, 2 = full strip, 3 = fuzzy

When exactOnly is true, only Level 0 (byte-exact) is attempted: a miss returns found=false rather than re-anchoring the match to a whitespace-normalized (line-start) position. This keeps the splice character-precise and turns the model's uncertainty into a clean no-match instead of a silent miscorrection.

func Mutates

func Mutates(t Tool) bool

Mutates reports whether running this tool can change something outside the conversation: the workspace or a background process. It is the same question isParallelSafe asks for Batch, so the two answers cannot drift — a tool safe to run beside another is one that only reads.

Report is excluded. It records what a turn did rather than doing anything, and counting it would make every reported turn look like a mutating one.

func NormalizeAndValidatePath

func NormalizeAndValidatePath(workspaceRoot, inputPath string) (string, bool, error)

NormalizeAndValidatePath normalizes a path and checks if it's outside workspace Returns: (normalizedPath, isOutside, error)

func NormalizeToolCallArguments

func NormalizeToolCallArguments(tool Tool, args json.RawMessage) (json.RawMessage, error)

NormalizeToolCallArguments is a middleware that normalizes tool arguments by converting string numbers to actual numbers when the schema expects numeric types

func NormalizeToolCallTypes

func NormalizeToolCallTypes(msg *llm.Message)

NormalizeToolCallTypes ensures all tool calls have type: "function" This handles cases where LLMs return tool calls with empty or missing type fields, which causes validation errors when replaying messages to stricter APIs like Mistral.

func NormalizeWhitespace

func NormalizeWhitespace(s string) string

NormalizeWhitespace strips leading and trailing whitespace from each line

func NormalizeWhitespaceRstrip

func NormalizeWhitespaceRstrip(s string) string

NormalizeWhitespaceRstrip strips only trailing whitespace from each line

func PatchEditDescription

func PatchEditDescription() string

PatchEditDescription returns the description for patch edit mode

func PatchEditJSONSchema

func PatchEditJSONSchema() map[string]any

PatchEditJSONSchema returns the JSON schema for patch edit mode

func PatchEditPromptSection

func PatchEditPromptSection(previewMode bool) string

PatchEditPromptSection returns the prompt section for patch edit mode

func ProcessEvents

func ProcessEvents(registry *procs.Registry, box *inbox.Inbox)

ProcessEvents turns what the registry has to report into inbox messages, so a process ending reaches the model the same way steering does.

func ReadLineRange

func ReadLineRange(fullPath string, startLine, endLine int) (content string, totalLines int, err error)

ReadLineRange reads a specific range of lines from a file without loading the entire file Returns the content of lines [startLine, endLine] (1-based, inclusive) and total line count

func ResearchChildScope added in v0.5.0

func ResearchChildScope(parent *Registry, parentCtx *ToolContext) (*Registry, *ToolContext)

ResearchChildScope is the test hook for the research allowlist: it builds the same filtered view Call uses, without running a child.

func SearchReplaceEditDescription

func SearchReplaceEditDescription() string

SearchReplaceEditDescription returns the description for search-replace edit mode

func SearchReplaceEditJSONSchema

func SearchReplaceEditJSONSchema() map[string]any

SearchReplaceEditJSONSchema returns the JSON schema for search-replace edit mode

func SearchReplaceEditPromptSection

func SearchReplaceEditPromptSection(previewMode bool) string

SearchReplaceEditPromptSection returns the prompt section for search-replace edit mode

func SequenceMatcherRatio

func SequenceMatcherRatio(s1, s2 string) float64

SequenceMatcherRatio implements a ratio similar to Python's difflib.SequenceMatcher.ratio() Uses the Ratcliff/Obershelp algorithm: 2 * matching_chars / total_chars This is faster than Levenshtein for large strings and better suited for code comparison

func SimilarityRatio

func SimilarityRatio(s1, s2 string) float64

SimilarityRatio calculates the similarity ratio between two strings (0.0 to 1.0) Based on the formula: 1 - (distance / max(len(s1), len(s2)))

func StorePendingEdit

func StorePendingEdit(toolCtx *ToolContext, path, fullPath, oldContent, newContent, diff string, isNewFile bool, editStartLine, editEndLine int)

StorePendingEdit stores a computed edit for preview mode using the provided ToolContext. editStartLine and editEndLine are 1-based line numbers in the new content.

func StreamingSearchInRange

func StreamingSearchInRange(fullPath string, search string, startLine, endLine int) (matchStartLine, matchEndLine int, found bool, err error)

StreamingSearchInRange searches for text within a specific line range Returns the matched content's start/end line numbers within the range

func TestChildScope added in v0.5.0

func TestChildScope(parent *Registry, parentCtx *ToolContext, cfg *config.Config, maxIters int, subagentType string) (*Registry, *ToolContext)

TestChildScope builds the same filtered view Call uses for the given type, without running a child.

Types

type BaseEditTool

type BaseEditTool struct {
	Config        *config.Config
	WorkspaceRoot string
	ToolCtx       *ToolContext
}

BaseEditTool provides common functionality for all edit tool implementations

func (*BaseEditTool) CheckReadBeforeEdit

func (b *BaseEditTool) CheckReadBeforeEdit(path string) error

CheckReadBeforeEdit validates that the file was read recently if configured

func (*BaseEditTool) CheckStaleOnly added in v0.5.0

func (b *BaseEditTool) CheckStaleOnly(fullPath string) error

CheckStaleOnly fails closed when another loop (a subagent child) edited this absolute path since this context last read it. Patch-mode edits, which skip the read-recency check by design (their context is anchored in the patch text), still go through this at Call time via applyFilePatch.

func (*BaseEditTool) GetConfig

func (b *BaseEditTool) GetConfig() *config.Config

GetConfig returns the tool's config

func (*BaseEditTool) ReadFileForEdit

func (b *BaseEditTool) ReadFileForEdit(fullPath string) (content string, isNewFile bool, err error)

ReadFileForEdit reads a file for editing, handling new file creation Returns content, isNewFile, and error

func (*BaseEditTool) StreamingLineReplace

func (b *BaseEditTool) StreamingLineReplace(fullPath string, startLine, endLine int, newText string) error

StreamingLineReplace performs a streaming line-based replacement Replaces lines [startLine, endLine] with newText without loading entire file into memory

func (*BaseEditTool) ValidateAndResolvePath

func (b *BaseEditTool) ValidateAndResolvePath(path string) (fullPath string, outside bool, err error)

ValidateAndResolvePath validates and resolves a path for editing Returns the full path, whether it's outside workspace, and any error

func (*BaseEditTool) WriteFileAtomic

func (b *BaseEditTool) WriteFileAtomic(fullPath, content string, isNewFile bool) error

WriteFileAtomic writes content to a file atomically using temp file + rename

type BatchResult

type BatchResult struct {
	Index  int    `json:"index"`
	Tool   string `json:"tool"`
	OK     bool   `json:"ok"`
	Result any    `json:"result,omitempty"`
	Error  string `json:"error,omitempty"`
	// Images carries attachments from an image-bearing call (ReadImage).
	// Only metadata is serialized (bytes are json:"-"); the runner attaches
	// the pixels to a follower user message, preserving which call read
	// which image.
	Images []llm.ImagePart `json:"images,omitempty"`
}

BatchResult is what one call in a batch produced. Every call gets one, whether it worked or not, so the model can tell which of the four reads failed rather than being told the batch failed.

type BatchTool

type BatchTool struct {
	// contains filtered or unexported fields
}

BatchTool runs several independent tool calls in one request.

A model that wants to read four files pays for four round trips: four requests carrying the whole conversation, and four rounds of thinking. Doing them in one call costs one of each. Calls that only read are run at the same time, so a batch of four reads takes about as long as one.

func NewBatchTool

func NewBatchTool(cfg *config.Config) *BatchTool

NewBatchTool builds the tool. The registry it dispatches through is set afterwards with SetRegistry, because the registry is what holds this tool.

func (*BatchTool) Call

func (t *BatchTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*BatchTool) Check

func (t *BatchTool) Check(ctx context.Context, args json.RawMessage) error

func (*BatchTool) Description

func (t *BatchTool) Description() string

func (*BatchTool) JSONSchema

func (t *BatchTool) JSONSchema() map[string]any

func (*BatchTool) Name

func (t *BatchTool) Name() string

func (*BatchTool) PromptCategory

func (t *BatchTool) PromptCategory() string

func (*BatchTool) PromptOrder

func (t *BatchTool) PromptOrder() int

func (*BatchTool) PromptSection

func (t *BatchTool) PromptSection() string

func (*BatchTool) PromptTemplateName

func (t *BatchTool) PromptTemplateName() string

func (*BatchTool) SelfTimeout

func (t *BatchTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout: a batch is as slow as the calls in it, and each of those has its own limits.

func (*BatchTool) SetRegistry

func (t *BatchTool) SetRegistry(registry *Registry)

SetRegistry gives the tool the registry it looks calls up in.

func (*BatchTool) SetToolContext

func (t *BatchTool) SetToolContext(tc *ToolContext)

SetToolContext gives the tool the shared state its inner calls report into. A batch is one call to the loop, so without this a batch that edited a file would look like a turn that only read.

func (*BatchTool) ShortPromptSection

func (t *BatchTool) ShortPromptSection() string

ShortPromptSection keeps only Batch's limits; when to reach for it is said once, in the prompt's account of how the session works.

type CancelEditTool

type CancelEditTool struct {
	// contains filtered or unexported fields
}

CancelEditTool cancels the pending edit from edit (only used in preview mode)

func NewCancelEditTool

func NewCancelEditTool(cfg *config.Config, toolCtx *ToolContext) *CancelEditTool

func (*CancelEditTool) Call

func (t *CancelEditTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*CancelEditTool) Check

func (t *CancelEditTool) Check(ctx context.Context, args json.RawMessage) error

func (*CancelEditTool) Description

func (t *CancelEditTool) Description() string

func (*CancelEditTool) JSONSchema

func (t *CancelEditTool) JSONSchema() map[string]any

func (*CancelEditTool) Name

func (t *CancelEditTool) Name() string

func (*CancelEditTool) PromptCategory

func (t *CancelEditTool) PromptCategory() string

func (*CancelEditTool) PromptOrder

func (t *CancelEditTool) PromptOrder() int

func (*CancelEditTool) PromptSection

func (t *CancelEditTool) PromptSection() string

func (*CancelEditTool) PromptTemplateName

func (t *CancelEditTool) PromptTemplateName() string

type CancelWriteTool

type CancelWriteTool struct {
	// contains filtered or unexported fields
}

CancelWriteTool cancels a pending write operation

func NewCancelWriteTool

func NewCancelWriteTool(cfg *config.Config, toolCtx *ToolContext) *CancelWriteTool

func (*CancelWriteTool) Call

func (t *CancelWriteTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*CancelWriteTool) Check

func (t *CancelWriteTool) Check(ctx context.Context, args json.RawMessage) error

func (*CancelWriteTool) Description

func (t *CancelWriteTool) Description() string

func (*CancelWriteTool) JSONSchema

func (t *CancelWriteTool) JSONSchema() map[string]any

func (*CancelWriteTool) Name

func (t *CancelWriteTool) Name() string

func (*CancelWriteTool) PromptCategory

func (t *CancelWriteTool) PromptCategory() string

func (*CancelWriteTool) PromptOrder

func (t *CancelWriteTool) PromptOrder() int

func (*CancelWriteTool) PromptSection

func (t *CancelWriteTool) PromptSection() string

func (*CancelWriteTool) PromptTemplateName

func (t *CancelWriteTool) PromptTemplateName() string

type ConfirmEditTool

type ConfirmEditTool struct {
	// contains filtered or unexported fields
}

ConfirmEditTool confirms and applies the last previewed edit from edit (only used in preview mode)

func NewConfirmEditTool

func NewConfirmEditTool(cfg *config.Config, toolCtx *ToolContext) *ConfirmEditTool

func (*ConfirmEditTool) Call

func (t *ConfirmEditTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ConfirmEditTool) Check

func (t *ConfirmEditTool) Check(ctx context.Context, args json.RawMessage) error

func (*ConfirmEditTool) Description

func (t *ConfirmEditTool) Description() string

func (*ConfirmEditTool) JSONSchema

func (t *ConfirmEditTool) JSONSchema() map[string]any

func (*ConfirmEditTool) Name

func (t *ConfirmEditTool) Name() string

func (*ConfirmEditTool) PromptCategory

func (t *ConfirmEditTool) PromptCategory() string

func (*ConfirmEditTool) PromptOrder

func (t *ConfirmEditTool) PromptOrder() int

func (*ConfirmEditTool) PromptSection

func (t *ConfirmEditTool) PromptSection() string

func (*ConfirmEditTool) PromptTemplateName

func (t *ConfirmEditTool) PromptTemplateName() string

type ConfirmWriteTool

type ConfirmWriteTool struct {
	// contains filtered or unexported fields
}

ConfirmWriteTool confirms and applies a pending write operation

func NewConfirmWriteTool

func NewConfirmWriteTool(cfg *config.Config, toolCtx *ToolContext) *ConfirmWriteTool

func (*ConfirmWriteTool) Call

func (t *ConfirmWriteTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ConfirmWriteTool) Check

func (t *ConfirmWriteTool) Check(ctx context.Context, args json.RawMessage) error

func (*ConfirmWriteTool) Description

func (t *ConfirmWriteTool) Description() string

func (*ConfirmWriteTool) JSONSchema

func (t *ConfirmWriteTool) JSONSchema() map[string]any

func (*ConfirmWriteTool) Name

func (t *ConfirmWriteTool) Name() string

func (*ConfirmWriteTool) PromptCategory

func (t *ConfirmWriteTool) PromptCategory() string

func (*ConfirmWriteTool) PromptOrder

func (t *ConfirmWriteTool) PromptOrder() int

func (*ConfirmWriteTool) PromptSection

func (t *ConfirmWriteTool) PromptSection() string

func (*ConfirmWriteTool) PromptTemplateName

func (t *ConfirmWriteTool) PromptTemplateName() string

type DebugLogger

type DebugLogger interface {
	Debug(msg string)
}

DebugLogger is an interface for debug logging to avoid import cycles

type DeleteLinesTool

type DeleteLinesTool struct {
	BaseEditTool
}

DeleteLinesTool removes a range of lines from a file. It is a first-class, explicitly named delete affordance (Improvement 3), gated behind tools.edit.explicit_delete. It is edit-mode independent (purely line-range based) and routes through the pending-edit machinery, so it is previewed/applied by Edit.confirm and discarded by Edit.cancel.

func NewDeleteLinesTool

func NewDeleteLinesTool(cfg *config.Config, toolCtx *ToolContext) *DeleteLinesTool

NewDeleteLinesTool creates a new DeleteLinesTool.

func (*DeleteLinesTool) Call

func (t *DeleteLinesTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*DeleteLinesTool) Check

func (t *DeleteLinesTool) Check(ctx context.Context, args json.RawMessage) error

func (*DeleteLinesTool) Description

func (t *DeleteLinesTool) Description() string

func (*DeleteLinesTool) JSONSchema

func (t *DeleteLinesTool) JSONSchema() map[string]any

func (*DeleteLinesTool) Name

func (t *DeleteLinesTool) Name() string

func (*DeleteLinesTool) PromptCategory

func (t *DeleteLinesTool) PromptCategory() string

func (*DeleteLinesTool) PromptOrder

func (t *DeleteLinesTool) PromptOrder() int

func (*DeleteLinesTool) PromptSection

func (t *DeleteLinesTool) PromptSection() string

func (*DeleteLinesTool) PromptTemplateName

func (t *DeleteLinesTool) PromptTemplateName() string

type EditPreviewResult

type EditPreviewResult struct {
	Status    string `json:"status"` // "pending_confirmation"
	NextStep  string `json:"next_step"`
	Diff      string `json:"diff"`
	AfterEdit string `json:"after_edit,omitempty"` // Shows how file looks after edit
	Path      string `json:"path"`
	IsNewFile bool   `json:"is_new_file,omitempty"`
	Message   string `json:"message,omitempty"`
}

EditPreviewResult is returned when preview mode is enabled

type EditResult

type EditResult struct {
	Success bool   `json:"success"`
	Path    string `json:"path"`
	Diff    string `json:"diff,omitempty"`
	Created bool   `json:"created,omitempty"`
	Message string `json:"message,omitempty"`
}

EditResult contains the result of an edit operation

type EditTool

type EditTool interface {
	Tool
	// GetConfig returns the tool's config for shared functionality
	GetConfig() *config.Config
}

EditTool is the interface that all edit tool implementations must satisfy

type FilePatch

type FilePatch struct {
	Action PatchAction
	Path   string
	Chunks []PatchChunk
}

FilePatch represents a patch for a single file

func ParsePatch

func ParsePatch(patch string) ([]FilePatch, error)

ParsePatch parses a V4A-format patch into structured FilePatch objects

type FileReadTracker

type FileReadTracker struct {
	// contains filtered or unexported fields
}

FileReadTracker tracks which files have been read recently for read-before-edit enforcement

func (*FileReadTracker) CurrentMessageID

func (t *FileReadTracker) CurrentMessageID() int

CurrentMessageID returns the current message ID

func (*FileReadTracker) NextMessage

func (t *FileReadTracker) NextMessage() int

NextMessage increments and returns the new message ID (call for each agent loop iteration)

func (*FileReadTracker) RecordRead

func (t *FileReadTracker) RecordRead(path string, messageID int)

RecordRead records that a file was read

func (*FileReadTracker) StatChangedSinceRead added in v0.5.0

func (t *FileReadTracker) StatChangedSinceRead(path string) (changed, found bool)

StatChangedSinceRead reports whether the file's current size or modTime differs from its most recent read entry. found is false when no entry exists for the path or the entry was recorded without a stat — the caller skips the mtime check then (fail open on stat, fail closed on prompt).

func (*FileReadTracker) WasReadRecently

func (t *FileReadTracker) WasReadRecently(path string, currentMessageID, withinMessages int) bool

WasReadRecently checks if a file was read within the last N messages

type FormAsker added in v0.2.0

type FormAsker func(ctx context.Context, questions []FormQuestion) (FormReply, error)

FormAsker puts questions to the program driving this process and waits for the answer, with no time limit of its own.

type FormOption added in v0.2.0

type FormOption struct {
	Label       string
	Description string
}

FormOption is one choice of a question.

type FormQuestion added in v0.2.0

type FormQuestion struct {
	Question string
	Header   string
	Options  []FormOption
	Multi    bool
}

FormQuestion is one question of a form.

type FormReply added in v0.2.0

type FormReply struct {
	Action string
	Chosen [][]string
	Typed  []string
}

FormReply is what the program answered. Action is "accept", "decline" or "cancel"; for an accepted form, Chosen holds each question's chosen labels and Typed its answer in the person's own words, in the questions' order.

type FuzzyMatcher

type FuzzyMatcher struct {
	Threshold float64 // Similarity threshold (0.0 to 1.0)
}

FuzzyMatcher provides fuzzy string matching capabilities

func NewFuzzyMatcher

func NewFuzzyMatcher(threshold float64) *FuzzyMatcher

NewFuzzyMatcher creates a new FuzzyMatcher with the given threshold

func (*FuzzyMatcher) FindBestMatch

func (fm *FuzzyMatcher) FindBestMatch(content, search string) (start, end int, ratio float64, matched string, found bool)

FindBestMatch finds the best matching substring in content for the search string Uses LINE-BASED matching like Aider for performance (O(lines²) instead of O(chars²)) Returns the start position, end position, similarity ratio, and the matched text

type GlobTool added in v0.4.0

type GlobTool struct {
	// contains filtered or unexported fields
}

GlobTool finds files by glob pattern and lists directory contents. It owns every "which files exist" question: Read reads bytes, Search reads inside files, Glob names them.

func NewGlobTool added in v0.4.0

func NewGlobTool(cfg *config.Config) *GlobTool

func (*GlobTool) Call added in v0.4.0

func (t *GlobTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*GlobTool) Check added in v0.4.0

func (t *GlobTool) Check(ctx context.Context, args json.RawMessage) error

func (*GlobTool) Description added in v0.4.0

func (t *GlobTool) Description() string

func (*GlobTool) JSONSchema added in v0.4.0

func (t *GlobTool) JSONSchema() map[string]any

func (*GlobTool) Name added in v0.4.0

func (t *GlobTool) Name() string

func (*GlobTool) ParallelSafe added in v0.4.0

func (t *GlobTool) ParallelSafe() bool

ParallelSafe says Glob can run alongside other reads in a Batch: it changes nothing, so nothing else in the batch can be affected by when it runs.

func (*GlobTool) PromptCategory added in v0.4.0

func (t *GlobTool) PromptCategory() string

func (*GlobTool) PromptOrder added in v0.4.0

func (t *GlobTool) PromptOrder() int

func (*GlobTool) PromptSection added in v0.4.0

func (t *GlobTool) PromptSection() string

func (*GlobTool) PromptTemplateName added in v0.4.0

func (t *GlobTool) PromptTemplateName() string

func (*GlobTool) SetToolContext added in v0.5.0

func (t *GlobTool) SetToolContext(toolCtx *ToolContext)

SetToolContext gives the tool the context it needs to deny prompts inside a subagent child.

func (*GlobTool) ShortPromptSection added in v0.4.0

func (t *GlobTool) ShortPromptSection() string

ShortPromptSection says what Glob returns and where it stops.

type GroupMemberSource

type GroupMemberSource func(ctx context.Context) ([]Tool, []GroupNote, error)

GroupMemberSource resolves a group's members, and any notes that come with them. It is a function so a group can take its members from somewhere internal/tools must not import: cmd/kvit-coder builds one over the MCP manager, which is also what keeps the members' dial lazy. Nil means the group has only the members handed to it directly.

type GroupNote added in v0.6.1

type GroupNote struct {
	Source string // what sent it, as the model should see it: "MCP server h3"
	Text   string
}

GroupNote is guidance that arrives with some of a group's members rather than from the configuration: the instructions an MCP server sent for using its tools. The group shows each one, under Source, after its own instructions.

type GroupTool

type GroupTool struct {
	// contains filtered or unexported fields
}

GroupTool is the single registered tool that stands in for a group.

func NewGroupTool

func NewGroupTool(name, description, instructions, category string, order int, source GroupMemberSource) *GroupTool

NewGroupTool builds the tool that stands in for one group. Members from source are resolved the first time something needs them, once per process.

func (*GroupTool) AddLocalMembers

func (t *GroupTool) AddLocalMembers(members ...Tool)

AddLocalMembers hands the group tools that are already built -- built-in tools the configuration moved into this group. SetupRegistry calls it instead of registering them.

func (*GroupTool) Call

func (t *GroupTool) Call(ctx context.Context, args json.RawMessage) (any, error)

Call either returns the group's reference (no tool named) or runs one member.

func (*GroupTool) Check

func (t *GroupTool) Check(ctx context.Context, args json.RawMessage) error

Check validates the shape of the call only. Whether the named tool exists is settled in Call, because answering it means resolving the members and that can mean reaching a server -- work that belongs to the call itself.

func (*GroupTool) Description

func (t *GroupTool) Description() string

func (*GroupTool) JSONSchema

func (t *GroupTool) JSONSchema() map[string]any

func (*GroupTool) Name

func (t *GroupTool) Name() string

func (*GroupTool) PromptCategory

func (t *GroupTool) PromptCategory() string

func (*GroupTool) PromptOrder

func (t *GroupTool) PromptOrder() int

func (*GroupTool) PromptSection

func (t *GroupTool) PromptSection() string

func (*GroupTool) PromptTemplateName

func (t *GroupTool) PromptTemplateName() string

func (*GroupTool) SelfTimeout

func (t *GroupTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket per-tool timeout: a group call is as slow as the member it dispatches to, and members set their own deadlines.

func (*GroupTool) ShortPromptSection

func (t *GroupTool) ShortPromptSection() string

ShortPromptSection says the one thing the schema cannot: that the tools exist but are undescribed until asked for.

type ImageCarrier

type ImageCarrier interface {
	ToolImages() []llm.ImagePart
}

ImageCarrier is an optional interface for a tool result that carries image attachments alongside its JSON summary. The runner attaches them to a follower user message, so tool results stay text while the model still sees the pixels. BatchResult carries them per call the same way.

type ImageReadResult

type ImageReadResult struct {
	Success   bool   `json:"success"`
	Path      string `json:"path"`
	MediaType string `json:"media_type,omitempty"`
	Width     int    `json:"width,omitempty"`
	Height    int    `json:"height,omitempty"`
	Size      int64  `json:"size,omitempty"`

	// Page and TotalPages are filled when the source was a PDF, so the model
	// knows which page it is looking at and what else is there.
	Page       int    `json:"page,omitempty"`
	TotalPages int    `json:"total_pages,omitempty"`
	Hint       string `json:"hint,omitempty"`
	// contains filtered or unexported fields
}

ImageReadResult is what ReadImage returns: a small JSON summary for the conversation plus the attachments, which the runner places on a follower user message. Only the summary is serialized; pixels travel in memory and on disk, never in JSON.

func (*ImageReadResult) ToolImages

func (r *ImageReadResult) ToolImages() []llm.ImagePart

ToolImages hands the attachments to the runner.

type JSONError

type JSONError interface {
	error
	ToJSON() map[string]any
}

JSONError is an interface for errors that can provide structured JSON output

type ObserveAddTool

type ObserveAddTool struct {
	// contains filtered or unexported fields
}

ObserveAddTool records a command to run again on a schedule.

func NewObserveAddTool

func NewObserveAddTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ObserveAddTool

NewObserveAddTool builds the tool.

func (*ObserveAddTool) Call

func (t *ObserveAddTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ObserveAddTool) Check

func (t *ObserveAddTool) Check(ctx context.Context, args json.RawMessage) error

func (*ObserveAddTool) Description

func (t *ObserveAddTool) Description() string

func (*ObserveAddTool) JSONSchema

func (t *ObserveAddTool) JSONSchema() map[string]any

func (*ObserveAddTool) Name

func (t *ObserveAddTool) Name() string

func (*ObserveAddTool) PromptCategory

func (t *ObserveAddTool) PromptCategory() string

func (*ObserveAddTool) PromptOrder

func (t *ObserveAddTool) PromptOrder() int

func (*ObserveAddTool) PromptSection

func (t *ObserveAddTool) PromptSection() string

func (*ObserveAddTool) PromptTemplateName

func (t *ObserveAddTool) PromptTemplateName() string

type ObserveWaitTool

type ObserveWaitTool struct {
	// contains filtered or unexported fields
}

ObserveWaitTool blocks until a background process does something worth coming back for.

func NewObserveWaitTool

func NewObserveWaitTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ObserveWaitTool

NewObserveWaitTool builds the tool.

func (*ObserveWaitTool) Call

func (t *ObserveWaitTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ObserveWaitTool) Check

func (t *ObserveWaitTool) Check(ctx context.Context, args json.RawMessage) error

func (*ObserveWaitTool) Description

func (t *ObserveWaitTool) Description() string

func (*ObserveWaitTool) JSONSchema

func (t *ObserveWaitTool) JSONSchema() map[string]any

func (*ObserveWaitTool) Name

func (t *ObserveWaitTool) Name() string

func (*ObserveWaitTool) PromptCategory

func (t *ObserveWaitTool) PromptCategory() string

func (*ObserveWaitTool) PromptOrder

func (t *ObserveWaitTool) PromptOrder() int

func (*ObserveWaitTool) PromptSection

func (t *ObserveWaitTool) PromptSection() string

func (*ObserveWaitTool) PromptTemplateName

func (t *ObserveWaitTool) PromptTemplateName() string

func (*ObserveWaitTool) SelfTimeout

func (t *ObserveWaitTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout: waiting is what this tool is for. It honours its own max_wait instead.

func (*ObserveWaitTool) ShortPromptSection

func (t *ObserveWaitTool) ShortPromptSection() string

ShortPromptSection says what Observe.wait returns and why.

type OutputBuffer

type OutputBuffer struct {
	// contains filtered or unexported fields
}

OutputBuffer manages shell command output with memory/file buffering and truncation

func NewOutputBuffer

func NewOutputBuffer(tempFileMgr *TempFileManager) *OutputBuffer

NewOutputBuffer creates a new output buffer

func (*OutputBuffer) Close

func (o *OutputBuffer) Close() error

Close cleans up resources (but doesn't delete temp file - that's session cleanup's job)

func (*OutputBuffer) FormatForLLM

func (o *OutputBuffer) FormatForLLM() (string, error)

FormatForLLM returns the output formatted for LLM consumption - If output is small (< 150 lines and < 24KB), return all of it - Otherwise, save to temp file and return truncated output with file path Uses shared truncation utility for consistent behavior across all tools

func (*OutputBuffer) Write

func (o *OutputBuffer) Write(p []byte) (n int, err error)

Write implements io.Writer, buffering output in memory up to 20MB, then spilling to temp file

type ParallelSafeTool

type ParallelSafeTool interface {
	ParallelSafe() bool
}

ParallelSafeTool is an optional interface a Tool implements to say it can run at the same time as other calls in a Batch. Only a tool that reads is safe: Read, Search and the process-status tools return true, while Edit, Write and Shell do not, because they change the workspace or the pending-edit state that the call after them reads. A tool that says nothing is treated as unsafe, which is the answer that cannot be wrong.

type ParentBudgetSetter added in v0.5.0

type ParentBudgetSetter interface {
	SetParentRemaining(int)
}

ParentBudgetSetter lets the parent loop tell the tool how many iterations it has left, so child_max = min(cfg max, parent remaining). The runner sets it before each call; Batch passes the context through, so delegation through a Batch sees the same budget.

type PatchAction

type PatchAction string

PatchAction represents the type of file operation

const (
	PatchAdd    PatchAction = "Add"
	PatchUpdate PatchAction = "Update"
	PatchDelete PatchAction = "Delete"
)

type PatchChunk

type PatchChunk struct {
	Scope       string   // Optional @@ scope marker
	LineHint    int      // Optional line number hint for large files (from ":line N" in scope)
	Context     []string // Context lines (space prefix) - before the change
	Deletions   []string // Lines to remove (- prefix)
	Additions   []string // Lines to add (+ prefix)
	PostContext []string // Context lines after the change
}

PatchChunk represents a single change within a file

type PatchEditTool

type PatchEditTool struct {
	BaseEditTool
}

PatchEditTool implements V4A-style patch editing

func NewPatchEditTool

func NewPatchEditTool(cfg *config.Config, toolCtx *ToolContext) *PatchEditTool

NewPatchEditTool creates a new PatchEditTool

func (*PatchEditTool) Call

func (t *PatchEditTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*PatchEditTool) Check

func (t *PatchEditTool) Check(ctx context.Context, args json.RawMessage) error

func (*PatchEditTool) Description

func (t *PatchEditTool) Description() string

func (*PatchEditTool) JSONSchema

func (t *PatchEditTool) JSONSchema() map[string]any

func (*PatchEditTool) Name

func (t *PatchEditTool) Name() string

func (*PatchEditTool) PromptCategory

func (t *PatchEditTool) PromptCategory() string

func (*PatchEditTool) PromptOrder

func (t *PatchEditTool) PromptOrder() int

func (*PatchEditTool) PromptSection

func (t *PatchEditTool) PromptSection() string

func (*PatchEditTool) PromptTemplateName

func (t *PatchEditTool) PromptTemplateName() string

type PendingEditState

type PendingEditState struct {
	HasPending             bool   // Whether there's an unresolved pending edit
	PendingPath            string // Path of the pending edit (empty if none)
	LastPendingIdx         int    // Index of the last pending_confirmation message
	BlockCountSincePending int    // Number of BLOCKED messages since last pending_confirmation
}

PendingEditState represents the state of pending edits derived from message history

func AnalyzePendingEditState

func AnalyzePendingEditState(messageRoles []string, messageContents []string, toolNames []string) PendingEditState

AnalyzePendingEditState scans message history to determine pending edit state. This is the source of truth - RAM state may be out of sync after history manipulation. messageRoles, messageContents, and toolNames should be parallel arrays from the message history. toolNames contains the tool name for tool messages (empty for non-tool messages).

type PermissionAnswer added in v0.2.0

type PermissionAnswer int

PermissionAnswer is what the program answered.

const (
	PermissionRefused PermissionAnswer = iota
	PermissionAllowOnce
	// PermissionAllowSession allows it for the rest of this session.
	PermissionAllowSession
)

type PermissionRequest added in v0.2.0

type PermissionRequest struct {
	// Title says what is asked, in one line.
	Title string
	// Pattern is what an allowance covers, as a grant records it ("curl *").
	Pattern string
}

PermissionRequest is a question about one tool call, put to the program driving this process rather than to a person at a terminal.

type QuestionAnswer

type QuestionAnswer struct {
	Question string `json:"question"`
	// Answers holds the labels of the options that were chosen.
	Answers []string `json:"answers,omitempty"`
	// FreeText is what was typed when it was not an option number.
	FreeText string `json:"free_text,omitempty"`
	// Dismissed is set when the person ended the turn instead of answering.
	Dismissed bool `json:"dismissed,omitempty"`
	// Unanswered is set when nobody answered within the time allowed.
	Unanswered bool `json:"unanswered,omitempty"`
	// Declined is set when the person chose not to answer.
	Declined bool `json:"declined,omitempty"`
}

QuestionAnswer is one question and what came back.

type QuestionTool

type QuestionTool struct {
	// contains filtered or unexported fields
}

QuestionTool lets the model ask a person something and get the answer as the tool's result, so no new input path is needed: the answer arrives through the inbox like everything else that reaches a running turn.

It blocks. That costs nothing, because a background process is not in the loop — a build started earlier keeps building while you decide — and the only thing that stops is the model's turn, which had stopped anyway: it asked because it could not proceed.

func NewQuestionTool

func NewQuestionTool(cfg *config.Config, toolCtx *ToolContext) *QuestionTool

NewQuestionTool builds the tool. The inbox it reads comes from the tool context, which the agent shares with the loop.

func (*QuestionTool) Call

func (t *QuestionTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*QuestionTool) Check

func (t *QuestionTool) Check(ctx context.Context, args json.RawMessage) error

func (*QuestionTool) Description

func (t *QuestionTool) Description() string

func (*QuestionTool) JSONSchema

func (t *QuestionTool) JSONSchema() map[string]any

func (*QuestionTool) Name

func (t *QuestionTool) Name() string

func (*QuestionTool) PromptCategory

func (t *QuestionTool) PromptCategory() string

func (*QuestionTool) PromptOrder

func (t *QuestionTool) PromptOrder() int

func (*QuestionTool) PromptSection

func (t *QuestionTool) PromptSection() string

func (*QuestionTool) PromptTemplateName

func (t *QuestionTool) PromptTemplateName() string

func (*QuestionTool) SelfTimeout

func (t *QuestionTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout: waiting for a person is the whole point of this tool.

func (*QuestionTool) ShortPromptSection

func (t *QuestionTool) ShortPromptSection() string

ShortPromptSection keeps only what happens when a question is put; when to ask is said once, in the prompt's account of how the session works.

type ReadFileTool

type ReadFileTool struct {
	// contains filtered or unexported fields
}

ReadFileTool reads file contents with enhanced capabilities

func NewReadFileTool

func NewReadFileTool(cfg *config.Config, toolCtx *ToolContext) *ReadFileTool

func (*ReadFileTool) Call

func (t *ReadFileTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ReadFileTool) Check

func (t *ReadFileTool) Check(ctx context.Context, args json.RawMessage) error

func (*ReadFileTool) Description

func (t *ReadFileTool) Description() string

func (*ReadFileTool) JSONSchema

func (t *ReadFileTool) JSONSchema() map[string]any

func (*ReadFileTool) Name

func (t *ReadFileTool) Name() string

func (*ReadFileTool) ParallelSafe

func (t *ReadFileTool) ParallelSafe() bool

ParallelSafe says Read can run alongside other reads in a Batch: it changes nothing, so nothing else in the batch can be affected by when it runs.

func (*ReadFileTool) PromptCategory

func (t *ReadFileTool) PromptCategory() string

func (*ReadFileTool) PromptOrder

func (t *ReadFileTool) PromptOrder() int

func (*ReadFileTool) PromptSection

func (t *ReadFileTool) PromptSection() string

func (*ReadFileTool) PromptTemplateName

func (t *ReadFileTool) PromptTemplateName() string

func (*ReadFileTool) ShortPromptSection

func (t *ReadFileTool) ShortPromptSection() string

ShortPromptSection gives Read's limits, which the schema cannot express.

type ReadImageTool

type ReadImageTool struct {
	// contains filtered or unexported fields
}

ReadImageTool reads an image file so the model can see it. Text stays with Read; this tool sniffs the content, normalizes a copy into the session's tmp/, and returns the reference. An image read never satisfies the read-before-edit gate, which is about text the model will quote back.

func NewReadImageTool

func NewReadImageTool(cfg *config.Config, toolCtx *ToolContext) *ReadImageTool

func (*ReadImageTool) Call

func (t *ReadImageTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ReadImageTool) Check

func (t *ReadImageTool) Check(ctx context.Context, args json.RawMessage) error

func (*ReadImageTool) Description

func (t *ReadImageTool) Description() string

func (*ReadImageTool) JSONSchema

func (t *ReadImageTool) JSONSchema() map[string]any

func (*ReadImageTool) Name

func (t *ReadImageTool) Name() string

func (*ReadImageTool) ParallelSafe

func (t *ReadImageTool) ParallelSafe() bool

ParallelSafe says ReadImage can run alongside other reads in a Batch: it writes only its own normalized copy under a unique name.

func (*ReadImageTool) PromptCategory

func (t *ReadImageTool) PromptCategory() string

func (*ReadImageTool) PromptOrder

func (t *ReadImageTool) PromptOrder() int

func (*ReadImageTool) PromptSection

func (t *ReadImageTool) PromptSection() string

func (*ReadImageTool) PromptTemplateName

func (t *ReadImageTool) PromptTemplateName() string

type ReadOnlyCaller added in v0.2.0

type ReadOnlyCaller interface {
	ReadOnlyCall(args json.RawMessage) bool
}

ReadOnlyCaller is a tool that can change things in general but can tell from one call's arguments that this call only reads. Shell is the case: ls and rm are both Shell calls, and only the command tells them apart.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry manages enabled tools

func NewRegistry

func NewRegistry() *Registry

func SetupRegistry

func SetupRegistry(sc SetupConfig) *Registry

SetupRegistry creates and configures the tool registry based on config. It enables all tools according to the configuration and returns the populated registry.

func (*Registry) All

func (r *Registry) All() []Tool

All returns all registered tools

func (*Registry) Disable

func (r *Registry) Disable(name string)

Disable removes a tool from the registry

func (*Registry) Enable

func (r *Registry) Enable(t Tool)

Enable adds a tool to the registry (makes it available for use)

func (*Registry) EnabledCategories

func (r *Registry) EnabledCategories() []string

EnabledCategories returns the list of categories that have enabled tools

func (*Registry) ExtractToolCallsFromText

func (r *Registry) ExtractToolCallsFromText(content string) []llm.ToolCall

ExtractToolCallsFromText attempts to parse tool calls from text content This handles cases where LLM describes tool calls in XML-like format or JSON format

func (*Registry) GenerateToolPrompt

func (r *Registry) GenerateToolPrompt() string

GenerateToolPrompt returns complete tool documentation for system prompt

func (*Registry) Get

func (r *Registry) Get(name string) Tool

Get retrieves a tool by name

func (*Registry) InternalName

func (r *Registry) InternalName(name string) string

InternalName returns the registered internal name for a provider-facing alias.

func (*Registry) IsEnabled

func (r *Registry) IsEnabled(name string) bool

IsEnabled returns true if a tool with the given name is enabled

func (*Registry) ListTools

func (r *Registry) ListTools() []string

ListTools returns a sorted list of all enabled tool names

func (*Registry) LooksLikeMalformedToolCall

func (r *Registry) LooksLikeMalformedToolCall(content string) bool

LooksLikeMalformedToolCall checks if content appears to be a malformed tool call (e.g., when LLM outputs "read{\"path\": \"...\"}" as text instead of a proper tool call)

func (*Registry) ModelName

func (r *Registry) ModelName(name string) string

ModelName returns the provider-facing alias for an internal tool name. OpenAI-compatible function names must match ^[A-Za-z0-9_-]+$, while several kvit-coder internal names use dots for grouping (Shell.advanced).

func (*Registry) NormalizeToolCallName

func (r *Registry) NormalizeToolCallName(name string) string

NormalizeToolCallName converts any known internal or provider-facing tool name to the provider-facing spelling that can safely be sent back to the LLM API.

func (*Registry) PromptSections

func (r *Registry) PromptSections() map[string][]toolDoc

PromptSections returns documentation for all registered tools, grouped by category

func (*Registry) RewriteToolNamesForPrompt

func (r *Registry) RewriteToolNamesForPrompt(text string) string

RewriteToolNamesForPrompt rewrites internal dotted tool names in prompt text to their provider-facing aliases so examples match the advertised tool specs.

func (*Registry) Specs

func (r *Registry) Specs() []llm.ToolSpec

Specs returns OpenAI-compatible tool specs for all registered tools

func (*Registry) ToolsInCategory

func (r *Registry) ToolsInCategory(category string) []Tool

ToolsInCategory returns tools in a given category, sorted by PromptOrder

type ReportTool

type ReportTool struct {
	// contains filtered or unexported fields
}

ReportTool ends a turn with a structured report instead of free prose.

The model calls it with JSON in the shape internal/report describes. The tool normalizes what it gets, checks it, and either accepts it — which ends the turn — or returns every problem it found so the model can fix the whole report in one more call. Nothing else about the turn changes: the report is an ordinary tool call, so it reaches the session as one, and the front end reads it back from there between turns.

docs/structured-reports.md is the design.

func NewReportTool

func NewReportTool(cfg *config.Config, toolCtx *ToolContext) *ReportTool

NewReportTool builds the tool. The accepted report and the repair count live on the tool context, because the agent loop is what reads them.

func (*ReportTool) Call

func (t *ReportTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ReportTool) Check

func (t *ReportTool) Check(ctx context.Context, args json.RawMessage) error

Check does nothing. Everything a report can get wrong is checked in Call, because a Check error is treated as a misuse the loop may backtrack over, which would discard the report the model just wrote instead of handing back the list of things to fix.

func (*ReportTool) Description

func (t *ReportTool) Description() string

func (*ReportTool) JSONSchema

func (t *ReportTool) JSONSchema() map[string]any

func (*ReportTool) Name

func (t *ReportTool) Name() string

func (*ReportTool) PromptCategory

func (t *ReportTool) PromptCategory() string

func (*ReportTool) PromptOrder

func (t *ReportTool) PromptOrder() int

func (*ReportTool) PromptSection

func (t *ReportTool) PromptSection() string

PromptSection is the long form, used when the profile is not "strong". It is the short form plus a worked example, because a model that needs the long form needs to see the shape rather than read about it.

func (*ReportTool) PromptTemplateName

func (t *ReportTool) PromptTemplateName() string

func (*ReportTool) ShortPromptSection

func (t *ReportTool) ShortPromptSection() string

ShortPromptSection is what the strong profile gets: the schema says what the fields are, so this says only what the schema cannot — when to call it, that it ends the turn, how the user reads the card, and the rules a model reliably gets wrong.

type SearchReplaceEditTool

type SearchReplaceEditTool struct {
	BaseEditTool
}

SearchReplaceEditTool implements content-based search and replace editing

func NewSearchReplaceEditTool

func NewSearchReplaceEditTool(cfg *config.Config, toolCtx *ToolContext) *SearchReplaceEditTool

NewSearchReplaceEditTool creates a new SearchReplaceEditTool

func (*SearchReplaceEditTool) Call

func (*SearchReplaceEditTool) Check

func (*SearchReplaceEditTool) Description

func (t *SearchReplaceEditTool) Description() string

func (*SearchReplaceEditTool) JSONSchema

func (t *SearchReplaceEditTool) JSONSchema() map[string]any

func (*SearchReplaceEditTool) Name

func (t *SearchReplaceEditTool) Name() string

func (*SearchReplaceEditTool) PromptCategory

func (t *SearchReplaceEditTool) PromptCategory() string

func (*SearchReplaceEditTool) PromptOrder

func (t *SearchReplaceEditTool) PromptOrder() int

func (*SearchReplaceEditTool) PromptSection

func (t *SearchReplaceEditTool) PromptSection() string

func (*SearchReplaceEditTool) PromptTemplateName

func (t *SearchReplaceEditTool) PromptTemplateName() string

func (*SearchReplaceEditTool) ShortPromptSection

func (t *SearchReplaceEditTool) ShortPromptSection() string

ShortPromptSection gives the searchreplace Edit's one hard rule.

type SearchTool

type SearchTool struct {
	// contains filtered or unexported fields
}

SearchTool searches for code patterns and returns snippets with line numbers

func NewSearchTool

func NewSearchTool(cfg *config.Config, tempFileMgr *TempFileManager) *SearchTool

func (*SearchTool) Call

func (t *SearchTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*SearchTool) Check

func (t *SearchTool) Check(ctx context.Context, args json.RawMessage) error

func (*SearchTool) Description

func (t *SearchTool) Description() string

func (*SearchTool) JSONSchema

func (t *SearchTool) JSONSchema() map[string]any

func (*SearchTool) Name

func (t *SearchTool) Name() string

func (*SearchTool) ParallelSafe

func (t *SearchTool) ParallelSafe() bool

ParallelSafe says Search can run alongside other reads in a Batch.

func (*SearchTool) PromptCategory

func (t *SearchTool) PromptCategory() string

func (*SearchTool) PromptOrder

func (t *SearchTool) PromptOrder() int

func (*SearchTool) PromptSection

func (t *SearchTool) PromptSection() string

func (*SearchTool) PromptTemplateName

func (t *SearchTool) PromptTemplateName() string

func (*SearchTool) SetToolContext added in v0.5.0

func (t *SearchTool) SetToolContext(toolCtx *ToolContext)

SetToolContext gives the tool the context it needs to deny prompts inside a subagent child.

func (*SearchTool) ShortPromptSection

func (t *SearchTool) ShortPromptSection() string

ShortPromptSection says what Search does when there is too much to show.

type SelfTimeoutTool

type SelfTimeoutTool interface {
	SelfTimeout() bool
}

SelfTimeoutTool is an optional interface a Tool can implement to opt out of the agent loop's blanket per-tool timeout (15s) and manage its own execution deadline instead. MCP tools implement it: their calls (a web fetch, a database query, a browser action) routinely exceed 15s, and each applies its own configurable per-call deadline inside Call.

type SetupConfig

type SetupConfig struct {
	Cfg         *config.Config
	Logger      DebugLogger // Optional debug logger (can be nil)
	TempFileMgr *TempFileManager
	// ProcRegistry owns the processes that outlive a turn. Nil disables the
	// Shell.start / Observe.* tools.
	ProcRegistry *procs.Registry
	ToolCtx      *ToolContext // Shared mutable state for tools (created if nil)

	// MCPTools are adapters for tools discovered on configured MCP servers,
	// already built by the MCP manager. Passing built []Tool (rather than the
	// manager) keeps internal/tools free of any dependency on internal/mcp,
	// avoiding an import cycle. Nil/empty when MCP is disabled.
	MCPTools []Tool

	// ToolGroups are the groups from cfg.ToolGroups, already built with their
	// member sources bound (see group.go). Each is registered as one tool, and
	// any built-in tool the configuration moved into a group is handed to it
	// here instead of being registered on its own.
	ToolGroups []*GroupTool

	// SubRunner drives the Subagent child loop. It is the parent Runner,
	// injected after both exist (turn setup rebinds it), breaking the
	// tools->agent import cycle the same way MCPTools does. Nil disables
	// child runs: Subagent validates args but fails at Call time.
	SubRunner SubRunner
}

SetupConfig contains all dependencies needed to set up the tool registry

type ShellAdvancedTool

type ShellAdvancedTool struct {
	// contains filtered or unexported fields
}

ShellAdvancedTool - full implementation with working_dir and timeout options

func NewShellAdvancedTool

func NewShellAdvancedTool(cfg *config.Config, timeout time.Duration, tempFileMgr *TempFileManager) *ShellAdvancedTool

func (*ShellAdvancedTool) Call

func (t *ShellAdvancedTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ShellAdvancedTool) Check

func (t *ShellAdvancedTool) Check(ctx context.Context, args json.RawMessage) error

Check performs validation for ShellAdvancedTool

func (*ShellAdvancedTool) Description

func (t *ShellAdvancedTool) Description() string

func (*ShellAdvancedTool) JSONSchema

func (t *ShellAdvancedTool) JSONSchema() map[string]any

func (*ShellAdvancedTool) Name

func (t *ShellAdvancedTool) Name() string

func (*ShellAdvancedTool) PromptCategory

func (t *ShellAdvancedTool) PromptCategory() string

func (*ShellAdvancedTool) PromptOrder

func (t *ShellAdvancedTool) PromptOrder() int

func (*ShellAdvancedTool) PromptSection

func (t *ShellAdvancedTool) PromptSection() string

func (*ShellAdvancedTool) PromptTemplateName

func (t *ShellAdvancedTool) PromptTemplateName() string

func (*ShellAdvancedTool) ReadOnlyCall added in v0.2.0

func (t *ShellAdvancedTool) ReadOnlyCall(args json.RawMessage) bool

ReadOnlyCall reports whether the command only reads. The working directory and timeout do not change the answer.

func (*ShellAdvancedTool) SetToolContext

func (t *ShellAdvancedTool) SetToolContext(toolCtx *ToolContext)

SetToolContext gives the tool the grants and the inbox it needs to ask about a command that needs permission.

func (*ShellAdvancedTool) ShortPromptSection

func (t *ShellAdvancedTool) ShortPromptSection() string

ShortPromptSection folds Shell.advanced into a line, since it differs from Shell only in taking two more arguments the schema already describes.

type ShellKillTool

type ShellKillTool struct {
	// contains filtered or unexported fields
}

ShellKillTool stops a background process.

func NewShellKillTool

func NewShellKillTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ShellKillTool

NewShellKillTool builds the tool.

func (*ShellKillTool) Call

func (t *ShellKillTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ShellKillTool) Check

func (t *ShellKillTool) Check(ctx context.Context, args json.RawMessage) error

func (*ShellKillTool) Description

func (t *ShellKillTool) Description() string

func (*ShellKillTool) JSONSchema

func (t *ShellKillTool) JSONSchema() map[string]any

func (*ShellKillTool) Name

func (t *ShellKillTool) Name() string

func (*ShellKillTool) PromptCategory

func (t *ShellKillTool) PromptCategory() string

func (*ShellKillTool) PromptOrder

func (t *ShellKillTool) PromptOrder() int

func (*ShellKillTool) PromptSection

func (t *ShellKillTool) PromptSection() string

func (*ShellKillTool) PromptTemplateName

func (t *ShellKillTool) PromptTemplateName() string

type ShellListTool

type ShellListTool struct {
	// contains filtered or unexported fields
}

ShellListTool lists everything this session has started.

func NewShellListTool

func NewShellListTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ShellListTool

NewShellListTool builds the tool.

func (*ShellListTool) Call

func (t *ShellListTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ShellListTool) Check

func (t *ShellListTool) Check(ctx context.Context, args json.RawMessage) error

func (*ShellListTool) Description

func (t *ShellListTool) Description() string

func (*ShellListTool) JSONSchema

func (t *ShellListTool) JSONSchema() map[string]any

func (*ShellListTool) Name

func (t *ShellListTool) Name() string

func (*ShellListTool) ParallelSafe

func (t *ShellListTool) ParallelSafe() bool

ParallelSafe says listing processes changes nothing.

func (*ShellListTool) PromptCategory

func (t *ShellListTool) PromptCategory() string

func (*ShellListTool) PromptOrder

func (t *ShellListTool) PromptOrder() int

func (*ShellListTool) PromptSection

func (t *ShellListTool) PromptSection() string

func (*ShellListTool) PromptTemplateName

func (t *ShellListTool) PromptTemplateName() string

type ShellOutputTool

type ShellOutputTool struct {
	// contains filtered or unexported fields
}

ShellOutputTool reads what a background process has written.

func NewShellOutputTool

func NewShellOutputTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ShellOutputTool

NewShellOutputTool builds the tool.

func (*ShellOutputTool) Call

func (t *ShellOutputTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ShellOutputTool) Check

func (t *ShellOutputTool) Check(ctx context.Context, args json.RawMessage) error

func (*ShellOutputTool) Description

func (t *ShellOutputTool) Description() string

func (*ShellOutputTool) JSONSchema

func (t *ShellOutputTool) JSONSchema() map[string]any

func (*ShellOutputTool) Name

func (t *ShellOutputTool) Name() string

func (*ShellOutputTool) ParallelSafe

func (t *ShellOutputTool) ParallelSafe() bool

ParallelSafe says reading a process's output changes nothing.

func (*ShellOutputTool) PromptCategory

func (t *ShellOutputTool) PromptCategory() string

func (*ShellOutputTool) PromptOrder

func (t *ShellOutputTool) PromptOrder() int

func (*ShellOutputTool) PromptSection

func (t *ShellOutputTool) PromptSection() string

func (*ShellOutputTool) PromptTemplateName

func (t *ShellOutputTool) PromptTemplateName() string

type ShellStartTool

type ShellStartTool struct {
	// contains filtered or unexported fields
}

ShellStartTool starts a command and returns immediately.

func NewShellStartTool

func NewShellStartTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ShellStartTool

NewShellStartTool builds the tool.

func (*ShellStartTool) Call

func (t *ShellStartTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ShellStartTool) Check

func (t *ShellStartTool) Check(ctx context.Context, args json.RawMessage) error

func (*ShellStartTool) Description

func (t *ShellStartTool) Description() string

func (*ShellStartTool) JSONSchema

func (t *ShellStartTool) JSONSchema() map[string]any

func (*ShellStartTool) Name

func (t *ShellStartTool) Name() string

func (*ShellStartTool) PromptCategory

func (t *ShellStartTool) PromptCategory() string

func (*ShellStartTool) PromptOrder

func (t *ShellStartTool) PromptOrder() int

func (*ShellStartTool) PromptSection

func (t *ShellStartTool) PromptSection() string

func (*ShellStartTool) PromptTemplateName

func (t *ShellStartTool) PromptTemplateName() string

func (*ShellStartTool) ShortPromptSection

func (t *ShellStartTool) ShortPromptSection() string

ShortPromptSection says the one thing that separates Shell.start from Shell.

type ShellStatusTool

type ShellStatusTool struct {
	// contains filtered or unexported fields
}

ShellStatusTool reports whether a background process is still running.

func NewShellStatusTool

func NewShellStatusTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ShellStatusTool

NewShellStatusTool builds the tool.

func (*ShellStatusTool) Call

func (t *ShellStatusTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ShellStatusTool) Check

func (t *ShellStatusTool) Check(ctx context.Context, args json.RawMessage) error

func (*ShellStatusTool) Description

func (t *ShellStatusTool) Description() string

func (*ShellStatusTool) JSONSchema

func (t *ShellStatusTool) JSONSchema() map[string]any

func (*ShellStatusTool) Name

func (t *ShellStatusTool) Name() string

func (*ShellStatusTool) ParallelSafe

func (t *ShellStatusTool) ParallelSafe() bool

ParallelSafe says asking a process's status changes nothing.

func (*ShellStatusTool) PromptCategory

func (t *ShellStatusTool) PromptCategory() string

func (*ShellStatusTool) PromptOrder

func (t *ShellStatusTool) PromptOrder() int

func (*ShellStatusTool) PromptSection

func (t *ShellStatusTool) PromptSection() string

func (*ShellStatusTool) PromptTemplateName

func (t *ShellStatusTool) PromptTemplateName() string

type ShellTool

type ShellTool struct {
	// contains filtered or unexported fields
}

ShellTool - simple string-only interface, translates to Shell.advanced internally

func NewShellTool

func NewShellTool(cfg *config.Config, timeout time.Duration, tempFileMgr *TempFileManager) *ShellTool

func (*ShellTool) Call

func (t *ShellTool) Call(ctx context.Context, args json.RawMessage) (any, error)

Call executes command - delegates to Shell.advanced (working_dir/timeout behave the same)

func (*ShellTool) Check

func (t *ShellTool) Check(ctx context.Context, args json.RawMessage) error

Check performs validation - delegates to Shell.advanced

func (*ShellTool) Description

func (t *ShellTool) Description() string

func (*ShellTool) JSONSchema

func (t *ShellTool) JSONSchema() map[string]any

func (*ShellTool) Name

func (t *ShellTool) Name() string

func (*ShellTool) PromptCategory

func (t *ShellTool) PromptCategory() string

func (*ShellTool) PromptOrder

func (t *ShellTool) PromptOrder() int

func (*ShellTool) PromptSection

func (t *ShellTool) PromptSection() string

func (*ShellTool) PromptTemplateName

func (t *ShellTool) PromptTemplateName() string

func (*ShellTool) ReadOnlyCall added in v0.2.0

func (t *ShellTool) ReadOnlyCall(args json.RawMessage) bool

ReadOnlyCall reports whether the command only reads, so a turn that ran nothing but ls, grep or git diff is not treated as one that changed files.

func (*ShellTool) SetToolContext

func (t *ShellTool) SetToolContext(toolCtx *ToolContext)

SetToolContext passes the context through to the tool that does the work.

func (*ShellTool) ShortPromptSection

func (t *ShellTool) ShortPromptSection() string

ShortPromptSection says the two things about Shell that are not in its schema.

type ShellTuneTool

type ShellTuneTool struct {
	// contains filtered or unexported fields
}

ShellTuneTool adjusts when the model is reminded about a process that is already running, and whether it survives the turn: the same report/until/remind_every/persistent policy Shell.start takes up front. It is how the model changes its mind after a reminder without restarting the command.

func NewShellTuneTool

func NewShellTuneTool(cfg *config.Config, registry *procs.Registry, toolCtx *ToolContext) *ShellTuneTool

NewShellTuneTool builds the tool.

func (*ShellTuneTool) Call

func (t *ShellTuneTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*ShellTuneTool) Check

func (t *ShellTuneTool) Check(ctx context.Context, args json.RawMessage) error

func (*ShellTuneTool) Description

func (t *ShellTuneTool) Description() string

func (*ShellTuneTool) JSONSchema

func (t *ShellTuneTool) JSONSchema() map[string]any

func (*ShellTuneTool) Name

func (t *ShellTuneTool) Name() string

func (*ShellTuneTool) PromptCategory

func (t *ShellTuneTool) PromptCategory() string

func (*ShellTuneTool) PromptOrder

func (t *ShellTuneTool) PromptOrder() int

func (*ShellTuneTool) PromptSection

func (t *ShellTuneTool) PromptSection() string

func (*ShellTuneTool) PromptTemplateName

func (t *ShellTuneTool) PromptTemplateName() string

type ShortPromptTool

type ShortPromptTool interface {
	ShortPromptSection() string
}

ShortPromptTool is an optional interface a Tool implements to give a briefer version of its documentation, used under the strong agent profile. The short form says only what the schema cannot: the failure modes and the invariants — Read's truncation limits, Edit's byte-exact match, the shell's statelessness. A tool that does not implement it keeps its full section under both profiles.

type StatsCarrier added in v0.5.0

type StatsCarrier interface {
	ChildStats() *stats.AgentStats
}

StatsCarrier is implemented by the Subagent result so the parent loop can fold the child's tokens/cost into its own totals.

type StructuredOutputCapture added in v0.5.0

type StructuredOutputCapture struct {
	// contains filtered or unexported fields
}

StructuredOutputCapture is the per-Call closure a structured_output tool records into: whether the child filed a valid result, its canonical value, and the most recent validation failures (for nudge prompts).

func (*StructuredOutputCapture) Snapshot added in v0.5.0

func (c *StructuredOutputCapture) Snapshot() (called bool, value json.RawMessage, lastErrors []string)

Snapshot returns the capture's current state.

type StructuredOutputTool added in v0.5.0

type StructuredOutputTool struct {
	// contains filtered or unexported fields
}

StructuredOutputTool is the session-local tool a schema-carrying child calls with its final result. It exists only in the child's registry for one Subagent.Call (and the child's Batch, which dispatches through the same handle — no smuggling, it is born there).

func (*StructuredOutputTool) Call added in v0.5.0

func (*StructuredOutputTool) Check added in v0.5.0

func (*StructuredOutputTool) Description added in v0.5.0

func (t *StructuredOutputTool) Description() string

func (*StructuredOutputTool) JSONSchema added in v0.5.0

func (t *StructuredOutputTool) JSONSchema() map[string]any

func (*StructuredOutputTool) Name added in v0.5.0

func (t *StructuredOutputTool) Name() string

func (*StructuredOutputTool) PromptCategory added in v0.5.0

func (t *StructuredOutputTool) PromptCategory() string

func (*StructuredOutputTool) PromptOrder added in v0.5.0

func (t *StructuredOutputTool) PromptOrder() int

func (*StructuredOutputTool) PromptSection added in v0.5.0

func (t *StructuredOutputTool) PromptSection() string

func (*StructuredOutputTool) PromptTemplateName added in v0.5.0

func (t *StructuredOutputTool) PromptTemplateName() string

type SubRunner added in v0.5.0

type SubRunner interface {
	RunSubagent(ctx context.Context, params SubagentRunParams) (SubagentRunResult, error)
	// StartSubagent runs the child loop to completion and returns a
	// session handle for follow-ups alongside the result.
	StartSubagent(ctx context.Context, params SubagentRunParams) (*SubagentSession, SubagentRunResult, error)
	// ContinueSubagent re-enters the session's loop with one more user
	// prompt (a structured_output or summary nudge) and runs it to
	// completion again.
	ContinueSubagent(ctx context.Context, sess *SubagentSession, prompt string) (SubagentRunResult, error)
}

SubRunner drives a child loop synchronously inside the turn's process. It is implemented in internal/agent (which owns the Runner) and injected here via SetupConfig, mirroring how SetupConfig injects ProcRegistry. The interface lives in tools so tools never imports agent.

type SubagentCallResult added in v0.5.0

type SubagentCallResult struct {
	Result           string `json:"result"`
	Truncated        bool   `json:"truncated,omitempty"`
	OverflowPath     string `json:"overflow_path,omitempty"`
	BudgetExhausted  bool   `json:"budget_exhausted,omitempty"`
	TimedOut         bool   `json:"timed_out,omitempty"`
	Killed           bool   `json:"killed,omitempty"`
	Iterations       int    `json:"iterations,omitempty"`
	Model            string `json:"model,omitempty"`
	PromptTokens     int    `json:"prompt_tokens,omitempty"`
	CompletionTokens int    `json:"completion_tokens,omitempty"`
	// contains filtered or unexported fields
}

SubagentCallResult is the JSON-serializable result of one Subagent call. It carries the truncated head text inline; the full text spills to tmp/.

func (*SubagentCallResult) ChildStats added in v0.5.0

func (r *SubagentCallResult) ChildStats() *stats.AgentStats

ChildStats folds the child's ledger into the parent totals on return, so tokens/cost are attributed, not lost.

type SubagentKillTool added in v0.5.0

type SubagentKillTool struct {
	// contains filtered or unexported fields
}

SubagentKillTool stops one running spawned subagent without touching its siblings. Killing a finished handle reports its state and changes nothing.

func NewSubagentKillTool added in v0.5.0

func NewSubagentKillTool(shared *SubagentTool) *SubagentKillTool

NewSubagentKillTool builds the tool over the shared Subagent instance.

func (*SubagentKillTool) Call added in v0.5.0

func (t *SubagentKillTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*SubagentKillTool) Check added in v0.5.0

func (t *SubagentKillTool) Check(ctx context.Context, args json.RawMessage) error

func (*SubagentKillTool) Description added in v0.5.0

func (t *SubagentKillTool) Description() string

func (*SubagentKillTool) JSONSchema added in v0.5.0

func (t *SubagentKillTool) JSONSchema() map[string]any

func (*SubagentKillTool) Name added in v0.5.0

func (t *SubagentKillTool) Name() string

func (*SubagentKillTool) ParallelSafe added in v0.5.0

func (t *SubagentKillTool) ParallelSafe() bool

ParallelSafe keeps kills in Batch's sequential phase: no carve-out.

func (*SubagentKillTool) PromptCategory added in v0.5.0

func (t *SubagentKillTool) PromptCategory() string

func (*SubagentKillTool) PromptOrder added in v0.5.0

func (t *SubagentKillTool) PromptOrder() int

func (*SubagentKillTool) PromptSection added in v0.5.0

func (t *SubagentKillTool) PromptSection() string

func (*SubagentKillTool) PromptTemplateName added in v0.5.0

func (t *SubagentKillTool) PromptTemplateName() string

type SubagentOutputTool added in v0.5.0

type SubagentOutputTool struct {
	// contains filtered or unexported fields
}

SubagentOutputTool collects a finished subagent's result. Stats fold into the turn totals and change reporting lands here, on collect — never on spawn, never twice.

func NewSubagentOutputTool added in v0.5.0

func NewSubagentOutputTool(shared *SubagentTool) *SubagentOutputTool

NewSubagentOutputTool builds the tool over the shared Subagent instance.

func (*SubagentOutputTool) Call added in v0.5.0

func (t *SubagentOutputTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*SubagentOutputTool) Check added in v0.5.0

func (t *SubagentOutputTool) Check(ctx context.Context, args json.RawMessage) error

func (*SubagentOutputTool) Description added in v0.5.0

func (t *SubagentOutputTool) Description() string

func (*SubagentOutputTool) JSONSchema added in v0.5.0

func (t *SubagentOutputTool) JSONSchema() map[string]any

func (*SubagentOutputTool) Name added in v0.5.0

func (t *SubagentOutputTool) Name() string

func (*SubagentOutputTool) ParallelSafe added in v0.5.0

func (t *SubagentOutputTool) ParallelSafe() bool

ParallelSafe keeps collects in Batch's sequential phase: no carve-out.

func (*SubagentOutputTool) PromptCategory added in v0.5.0

func (t *SubagentOutputTool) PromptCategory() string

func (*SubagentOutputTool) PromptOrder added in v0.5.0

func (t *SubagentOutputTool) PromptOrder() int

func (*SubagentOutputTool) PromptSection added in v0.5.0

func (t *SubagentOutputTool) PromptSection() string

func (*SubagentOutputTool) PromptTemplateName added in v0.5.0

func (t *SubagentOutputTool) PromptTemplateName() string

func (*SubagentOutputTool) SelfTimeout added in v0.5.0

func (t *SubagentOutputTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout: with wait set, collecting is allowed to take as long as the timeout says.

type SubagentRunParams added in v0.5.0

type SubagentRunParams struct {
	System      string
	Prompt      string
	Description string
	MaxIters    int
	Registry    *Registry
	ToolCtx     *ToolContext
}

SubagentRunParams is what the tool hands the child loop.

type SubagentRunResult added in v0.5.0

type SubagentRunResult struct {
	Text             string
	BudgetExhausted  bool
	TimedOut         bool
	Killed           bool
	Iterations       int
	PromptTokens     int
	CompletionTokens int
	Cost             float64
	Model            string
}

SubagentRunResult is what the child loop hands back.

type SubagentSession added in v0.5.0

type SubagentSession struct {
	// contains filtered or unexported fields
}

SubagentSession is an opaque, in-process handle to a child loop whose history persists across ContinueSubagent calls. It carries the messages, registry and tool context one Subagent.Call needs for its follow-ups. It never reaches the session directory, history.jsonl, or the parent registry: it is built per Call and dies with the turn.

func NewSubagentSession added in v0.5.0

func NewSubagentSession(impl any) *SubagentSession

NewSubagentSession wraps the driver's private session state. The agent package fills impl; the tool package only carries it back to Continue.

func (*SubagentSession) Unwrap added in v0.5.0

func (s *SubagentSession) Unwrap() any

Unwrap returns the driver's private session state.

type SubagentStartTool added in v0.5.0

type SubagentStartTool struct {
	// contains filtered or unexported fields
}

SubagentStartTool spawns a child that runs while the turn continues and returns a handle immediately. Same arguments as Subagent; collect the handle later with Subagent.output.

func NewSubagentStartTool added in v0.5.0

func NewSubagentStartTool(shared *SubagentTool) *SubagentStartTool

NewSubagentStartTool builds the tool over the shared Subagent instance, so budgets, the concurrency cap and handles are one pool.

func (*SubagentStartTool) Call added in v0.5.0

func (t *SubagentStartTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*SubagentStartTool) Check added in v0.5.0

func (t *SubagentStartTool) Check(ctx context.Context, args json.RawMessage) error

func (*SubagentStartTool) Description added in v0.5.0

func (t *SubagentStartTool) Description() string

func (*SubagentStartTool) JSONSchema added in v0.5.0

func (t *SubagentStartTool) JSONSchema() map[string]any

func (*SubagentStartTool) Name added in v0.5.0

func (t *SubagentStartTool) Name() string

func (*SubagentStartTool) ParallelSafe added in v0.5.0

func (t *SubagentStartTool) ParallelSafe() bool

ParallelSafe keeps spawns in Batch's sequential phase: three cheap sequential spawns still overlap, so no carve-out is needed.

func (*SubagentStartTool) PromptCategory added in v0.5.0

func (t *SubagentStartTool) PromptCategory() string

func (*SubagentStartTool) PromptOrder added in v0.5.0

func (t *SubagentStartTool) PromptOrder() int

func (*SubagentStartTool) PromptSection added in v0.5.0

func (t *SubagentStartTool) PromptSection() string

func (*SubagentStartTool) PromptTemplateName added in v0.5.0

func (t *SubagentStartTool) PromptTemplateName() string

type SubagentStatusTool added in v0.5.0

type SubagentStatusTool struct {
	// contains filtered or unexported fields
}

SubagentStatusTool reports whether a spawned subagent is still running.

func NewSubagentStatusTool added in v0.5.0

func NewSubagentStatusTool(shared *SubagentTool) *SubagentStatusTool

NewSubagentStatusTool builds the tool over the shared Subagent instance.

func (*SubagentStatusTool) Call added in v0.5.0

func (t *SubagentStatusTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*SubagentStatusTool) Check added in v0.5.0

func (t *SubagentStatusTool) Check(ctx context.Context, args json.RawMessage) error

func (*SubagentStatusTool) Description added in v0.5.0

func (t *SubagentStatusTool) Description() string

func (*SubagentStatusTool) JSONSchema added in v0.5.0

func (t *SubagentStatusTool) JSONSchema() map[string]any

func (*SubagentStatusTool) Name added in v0.5.0

func (t *SubagentStatusTool) Name() string

func (*SubagentStatusTool) ParallelSafe added in v0.5.0

func (t *SubagentStatusTool) ParallelSafe() bool

ParallelSafe keeps collects in Batch's sequential phase: no carve-out.

func (*SubagentStatusTool) PromptCategory added in v0.5.0

func (t *SubagentStatusTool) PromptCategory() string

func (*SubagentStatusTool) PromptOrder added in v0.5.0

func (t *SubagentStatusTool) PromptOrder() int

func (*SubagentStatusTool) PromptSection added in v0.5.0

func (t *SubagentStatusTool) PromptSection() string

func (*SubagentStatusTool) PromptTemplateName added in v0.5.0

func (t *SubagentStatusTool) PromptTemplateName() string

type SubagentTool added in v0.5.0

type SubagentTool struct {
	// contains filtered or unexported fields
}

SubagentTool runs a child agent loop in-process with its own history and a restricted tool set, returning only the final summary text to the parent.

It also owns the background-child pool for phase-3 fan-out: Subagent.start spawns into it, Subagent.status/output/wait/kill collect from it. The five collect tools hold this same instance, so budgets, the concurrency cap and handles are shared.

func NewSubagentTool added in v0.5.0

func NewSubagentTool(cfg *config.Config, toolCtx *ToolContext, tempMgr *TempFileManager) *SubagentTool

NewSubagentTool builds the tool. The parent registry it filters at Call time is set afterwards with SetRegistry, because the registry is what holds this tool. The child-loop driver is set with SetRunner after the agent Runner exists (turn setup), breaking the tools->agent import cycle.

func (*SubagentTool) Call added in v0.5.0

func (t *SubagentTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*SubagentTool) Check added in v0.5.0

func (t *SubagentTool) Check(ctx context.Context, args json.RawMessage) error

func (*SubagentTool) Description added in v0.5.0

func (t *SubagentTool) Description() string

func (*SubagentTool) JSONSchema added in v0.5.0

func (t *SubagentTool) JSONSchema() map[string]any

func (*SubagentTool) Name added in v0.5.0

func (t *SubagentTool) Name() string

func (*SubagentTool) ParallelSafe added in v0.5.0

func (t *SubagentTool) ParallelSafe() bool

ParallelSafe keeps Subagent in Batch's sequential phase: even research children burn minutes of wall-clock, and parallel delegation is the phase-3 spawn/collect split (Subagent.start), never a Batch carve-out.

func (*SubagentTool) PromptCategory added in v0.5.0

func (t *SubagentTool) PromptCategory() string

func (*SubagentTool) PromptOrder added in v0.5.0

func (t *SubagentTool) PromptOrder() int

func (*SubagentTool) PromptSection added in v0.5.0

func (t *SubagentTool) PromptSection() string

func (*SubagentTool) PromptTemplateName added in v0.5.0

func (t *SubagentTool) PromptTemplateName() string

func (*SubagentTool) SelfTimeout added in v0.5.0

func (t *SubagentTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout: a child loop burns minutes of wall-clock, bounded instead by tools.subagent.timeout_s via ctx.WithTimeout.

func (*SubagentTool) SetParentRemaining added in v0.5.0

func (t *SubagentTool) SetParentRemaining(n int)

SetParentRemaining records the parent loop's remaining iteration budget.

func (*SubagentTool) SetRegistry added in v0.5.0

func (t *SubagentTool) SetRegistry(r *Registry)

SetRegistry gives the tool the parent registry it filters at Call time.

func (*SubagentTool) SetRunner added in v0.5.0

func (t *SubagentTool) SetRunner(r SubRunner)

SetRunner injects the child-loop driver (the parent Runner).

type SubagentWaitTool added in v0.5.0

type SubagentWaitTool struct {
	// contains filtered or unexported fields
}

SubagentWaitTool blocks until a spawned subagent finishes, a timeout passes, or the call is cancelled — one tool call however long it takes, where polling Subagent.status in a loop costs a full round of thinking each time.

func NewSubagentWaitTool added in v0.5.0

func NewSubagentWaitTool(shared *SubagentTool) *SubagentWaitTool

NewSubagentWaitTool builds the tool over the shared Subagent instance.

func (*SubagentWaitTool) Call added in v0.5.0

func (t *SubagentWaitTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*SubagentWaitTool) Check added in v0.5.0

func (t *SubagentWaitTool) Check(ctx context.Context, args json.RawMessage) error

func (*SubagentWaitTool) Description added in v0.5.0

func (t *SubagentWaitTool) Description() string

func (*SubagentWaitTool) JSONSchema added in v0.5.0

func (t *SubagentWaitTool) JSONSchema() map[string]any

func (*SubagentWaitTool) Name added in v0.5.0

func (t *SubagentWaitTool) Name() string

func (*SubagentWaitTool) ParallelSafe added in v0.5.0

func (t *SubagentWaitTool) ParallelSafe() bool

ParallelSafe keeps waits in Batch's sequential phase: no carve-out.

func (*SubagentWaitTool) PromptCategory added in v0.5.0

func (t *SubagentWaitTool) PromptCategory() string

func (*SubagentWaitTool) PromptOrder added in v0.5.0

func (t *SubagentWaitTool) PromptOrder() int

func (*SubagentWaitTool) PromptSection added in v0.5.0

func (t *SubagentWaitTool) PromptSection() string

func (*SubagentWaitTool) PromptTemplateName added in v0.5.0

func (t *SubagentWaitTool) PromptTemplateName() string

func (*SubagentWaitTool) SelfTimeout added in v0.5.0

func (t *SubagentWaitTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout: waiting is what this tool is for. It honours its own timeout instead.

type TempFileManager

type TempFileManager struct {
	// contains filtered or unexported fields
}

TempFileManager tracks and manages temporary files created during the session

func NewTempFileManager

func NewTempFileManager(tempDir string) *TempFileManager

NewTempFileManager creates a manager that spills large tool output into the given directory. The directory is used as given: it is the session's tmp/, which already exists for the purpose. It used to append ".kvit-coder/tmp" to whatever it was handed, which after the move into the session produced paths like <session>/tmp/.kvit-coder/tmp/shell-123.

func (*TempFileManager) CleanupAll

func (m *TempFileManager) CleanupAll()

CleanupAll removes all tracked temp files (called at session end)

func (*TempFileManager) CreateTempFile

func (m *TempFileManager) CreateTempFile() (*os.File, error)

CreateTempFile creates a new temporary file and tracks it for cleanup

func (*TempFileManager) TempDir

func (m *TempFileManager) TempDir() string

TempDir returns the directory spilled output is written to, which is the session's tmp/. A tool that names its own files rather than asking for an anonymous one -- Web.fetch keys a page on its URL so a second fetch is free -- needs the directory rather than a handle.

type Tool

type Tool interface {
	// Name returns the tool identifier (e.g., "shell", "read")
	Name() string

	// Description returns a human-readable description for the LLM
	Description() string

	// JSONSchema returns the OpenAI-compatible function schema
	JSONSchema() map[string]any

	// Check performs validation and user confirmations before execution
	// Returns error if the tool should not be executed
	Check(ctx context.Context, args json.RawMessage) error

	// Call executes the tool with the given arguments
	// Check should be called before Call
	Call(ctx context.Context, args json.RawMessage) (any, error)

	// PromptSection returns detailed usage documentation for the system prompt.
	// Returns empty string if no additional documentation is needed.
	// Deprecated: Use PromptTemplateName() with the template system instead.
	PromptSection() string

	// PromptCategory returns the category for grouping in the system prompt.
	// Valid categories: "filesystem", "shell", "plan", "web", "mcp", "report"
	PromptCategory() string

	// PromptOrder returns the sort order within the category (lower numbers first).
	// This ensures deterministic ordering for prompt caching.
	PromptOrder() int

	// PromptTemplateName returns the name of the template file for this tool's
	// system prompt documentation. Returns empty string to use PromptSection() instead.
	// Template files are located in prompts/tools/<name>.tmpl
	PromptTemplateName() string
}

Tool is the interface all agent tools must implement

func NewStructuredOutputTool added in v0.5.0

func NewStructuredOutputTool(schemaJSON json.RawMessage, capture *StructuredOutputCapture) (Tool, error)

NewStructuredOutputTool builds the session-local tool for one call. The schema must already have passed checkOutputSchemaShape; a schema that fails to compile is the model's fault.

type ToolContext

type ToolContext struct {
	ReadTracker *FileReadTracker
	// contains filtered or unexported fields
}

ToolContext holds shared mutable state for all tools in a session. This replaces the global variables (globalReadTracker, globalPendingEdit, globalPendingWrite) and enables proper testing and concurrent session isolation.

func NewToolContext

func NewToolContext() *ToolContext

NewToolContext creates a new ToolContext with initialized state.

func NewToolContextWithReadWindow added in v0.5.0

func NewToolContextWithReadWindow(n int) *ToolContext

NewToolContextWithReadWindow creates a ToolContext whose read tracker keeps a window of n messages (trimmed at n*5 entries). Subagent children use max(10, MaxIters) so the floored read-before-edit window (section 1 of spec/subagents-phase2.md) can never be trimmed away inside budget.

func (*ToolContext) AcceptReport

func (tc *ToolContext) AcceptReport(r *report.Report)

AcceptReport records the report that ends this turn. The loop reads it after each round of tool calls: an accepted report is what stops the turn, in place of the model choosing to stop.

func (*ToolContext) AcceptedReport

func (tc *ToolContext) AcceptedReport() *report.Report

AcceptedReport returns the report this turn submitted, or nil.

func (*ToolContext) AddPromptWait

func (tc *ToolContext) AddPromptWait(d time.Duration)

AddPromptWait records time spent waiting for a person to answer.

func (*ToolContext) ChangedThisTurn added in v0.2.0

func (tc *ToolContext) ChangedThisTurn() bool

ChangedThisTurn reports whether any call this turn changed something. It is what decides a completed report has to say how the work was checked.

func (*ToolContext) ClearPendingEdit

func (tc *ToolContext) ClearPendingEdit()

ClearPendingEdit clears any pending edit.

func (*ToolContext) ClearPendingEditIfPath

func (tc *ToolContext) ClearPendingEditIfPath(path string)

ClearPendingEditIfPath clears pending edit only if it matches the given path.

func (*ToolContext) ClearStalePath added in v0.5.0

func (tc *ToolContext) ClearStalePath(path string)

ClearStalePath clears the re-read requirement, called by Read on success.

func (*ToolContext) EditedPaths added in v0.5.0

func (tc *ToolContext) EditedPaths() []string

EditedPaths returns the absolute paths this context mutated, sorted for deterministic results. It is what Subagent.Call hands to the parent.

func (*ToolContext) FormAsker added in v0.2.0

func (tc *ToolContext) FormAsker() FormAsker

FormAsker returns where the Question tool's questions go, or nil for the terminal.

func (*ToolContext) GetAndClearPendingEdit

func (tc *ToolContext) GetAndClearPendingEdit() *pendingEdit

GetAndClearPendingEdit returns and clears the pending edit.

func (*ToolContext) GetAndClearPendingWrite

func (tc *ToolContext) GetAndClearPendingWrite() *pendingWrite

GetAndClearPendingWrite returns and clears the pending write.

func (*ToolContext) GetPendingEdit

func (tc *ToolContext) GetPendingEdit() *pendingEdit

GetPendingEdit returns the current pending edit without clearing it.

func (*ToolContext) GetPendingEditDiff

func (tc *ToolContext) GetPendingEditDiff() string

GetPendingEditDiff returns the diff of the pending edit, or empty if none.

func (*ToolContext) GetPendingEditPath

func (tc *ToolContext) GetPendingEditPath() string

GetPendingEditPath returns the path of the pending edit, or empty if none.

func (*ToolContext) GetPendingWritePath

func (tc *ToolContext) GetPendingWritePath() string

func (*ToolContext) Grantor

func (tc *ToolContext) Grantor() *permissions.Grantor

Grantor returns the permission grants that apply to this run, or nil.

func (*ToolContext) HasPendingEdit

func (tc *ToolContext) HasPendingEdit() bool

HasPendingEdit returns true if there's a pending edit.

func (*ToolContext) HasPendingWrite

func (tc *ToolContext) HasPendingWrite() bool

GetPendingWritePath returns the path of the pending write, or empty if none. HasPendingWrite reports whether this process is holding a staged overwrite.

func (*ToolContext) Inbox

func (tc *ToolContext) Inbox() *inbox.Inbox

Inbox returns the turn's inbox, or nil when there is none.

func (*ToolContext) Interactive

func (tc *ToolContext) Interactive() bool

Interactive reports whether a tool that waits for a person can expect one.

func (*ToolContext) IsStalePath added in v0.5.0

func (tc *ToolContext) IsStalePath(path string) bool

IsStalePath reports whether the path needs a fresh Read before editing.

func (*ToolContext) IsSubagentChild added in v0.5.0

func (tc *ToolContext) IsSubagentChild() bool

IsSubagentChild reports whether this context belongs to a Subagent child.

func (*ToolContext) MutatedThisTurn

func (tc *ToolContext) MutatedThisTurn() bool

MutatedThisTurn reports whether any tool that changes something has run.

func (*ToolContext) NoteChange added in v0.2.0

func (tc *ToolContext) NoteChange()

NoteChange records that a call this turn changed something, as opposed to running a tool that could have and only read this time, such as Shell running ls. It implies NoteMutatingTool.

func (*ToolContext) NoteMutatingTool

func (tc *ToolContext) NoteMutatingTool()

NoteMutatingTool records that this turn ran a tool that changes something outside the conversation. Report mode "mutating" asks for a report only from a turn that did.

func (*ToolContext) PermissionAsker added in v0.2.0

func (tc *ToolContext) PermissionAsker() func(PermissionRequest) PermissionAnswer

PermissionAsker returns where permission questions go, or nil for the terminal.

func (*ToolContext) PromptWait

func (tc *ToolContext) PromptWait() time.Duration

PromptWait is the total time spent waiting for a person so far.

func (*ToolContext) RecordDismissedQuestion

func (tc *ToolContext) RecordDismissedQuestion(question string)

RecordDismissedQuestion notes that a question was put and not answered.

func (*ToolContext) RecordEditedPath added in v0.5.0

func (tc *ToolContext) RecordEditedPath(absPath string)

RecordEditedPath notes that this context successfully mutated absPath (already absolute from the caller). It refreshes this context's own tracker entry to the post-write stat — so a second edit in the same loop is not flagged by its own first edit — and appends to this context's edited set, the handoff payload RequireReRead consumes.

func (*ToolContext) RecordReportRepair

func (tc *ToolContext) RecordReportRepair() int

RecordReportRepair counts one rejected report and returns how many this turn has had. Past the configured budget the model is told to stop repairing, so a model that cannot satisfy the schema ends its turn rather than spending it resubmitting.

func (*ToolContext) ReportRepairs

func (tc *ToolContext) ReportRepairs() int

ReportRepairs is how many reports this turn has had rejected.

func (*ToolContext) RequireReRead added in v0.5.0

func (tc *ToolContext) RequireReRead(paths []string)

RequireReRead marks paths as requiring a fresh Read before this context may edit them. Only Subagent.Call adds to it, with files a child edited this turn — so the stale set never contains anything else, and no global parent behavior changes.

func (*ToolContext) SessionTmp

func (tc *ToolContext) SessionTmp() string

SessionTmp returns the session's tmp/ directory, or "" when there is none.

func (*ToolContext) SetFormAsker added in v0.2.0

func (tc *ToolContext) SetFormAsker(fn FormAsker)

SetFormAsker sends the Question tool's questions to fn rather than to the terminal. Passing nil puts them back on the terminal.

func (*ToolContext) SetGrantor

func (tc *ToolContext) SetGrantor(g *permissions.Grantor)

SetGrantor gives tools the permission grants that apply to this run.

func (*ToolContext) SetInbox

func (tc *ToolContext) SetInbox(box *inbox.Inbox)

SetInbox gives tools access to the turn's inbox.

func (*ToolContext) SetInteractive

func (tc *ToolContext) SetInteractive(interactive bool)

SetInteractive records whether someone is at the terminal to answer a tool that asks. It is set by whatever started reading stdin.

func (*ToolContext) SetPendingEdit

func (tc *ToolContext) SetPendingEdit(p *pendingEdit)

SetPendingEdit stores a pending edit operation.

func (*ToolContext) SetPendingWrite

func (tc *ToolContext) SetPendingWrite(p *pendingWrite)

SetPendingWrite stores a pending write operation.

func (*ToolContext) SetPermissionAsker added in v0.2.0

func (tc *ToolContext) SetPermissionAsker(fn func(PermissionRequest) PermissionAnswer)

SetPermissionAsker sends permission questions to fn rather than to the terminal. Passing nil puts them back on the terminal.

func (*ToolContext) SetSessionTmp

func (tc *ToolContext) SetSessionTmp(dir string)

SetSessionTmp records the session's tmp/ directory for this turn.

func (*ToolContext) SetSubagentChild added in v0.5.0

func (tc *ToolContext) SetSubagentChild(child bool)

SetSubagentChild marks this context as belonging to a Subagent child loop. A child has no UI: every interactive path resolves to deny.

func (*ToolContext) WasQuestionDismissed

func (tc *ToolContext) WasQuestionDismissed(question string) bool

WasQuestionDismissed reports whether this exact question was already put and dismissed.

type ToolError

type ToolError struct {
	Type    ToolErrorType
	Message string
	Details map[string]any // Optional structured data for LLM
}

ToolError is an error type that classifies errors as runtime or semantic

func CheckPendingEditBlockWithConfig

func CheckPendingEditBlockWithConfig(toolName string, args json.RawMessage, blockCount int, cfg *config.Config, toolCtx *ToolContext) *ToolError

CheckPendingEditBlockWithConfig is the legacy version that uses RAM state. Deprecated: Use CheckPendingEditBlockWithState with history-derived state instead.

func CheckPendingEditBlockWithState

func CheckPendingEditBlockWithState(toolName string, state PendingEditState, cfg *config.Config, toolCtx *ToolContext) *ToolError

CheckPendingEditBlockWithState checks if a tool call should be blocked due to pending edit. Uses history-derived state as the source of truth for whether a pending edit exists. The toolCtx is used for the diff content in error messages.

func RuntimeError

func RuntimeError(msg string) *ToolError

RuntimeError creates a runtime error (not backtrackable) Use for: file system errors, network errors, external failures

func RuntimeErrorWithDetails

func RuntimeErrorWithDetails(msg string, details map[string]any) *ToolError

RuntimeErrorWithDetails creates a runtime error with structured details

func RuntimeErrorf

func RuntimeErrorf(format string, args ...any) *ToolError

RuntimeErrorf creates a formatted runtime error

func SemanticError

func SemanticError(msg string) *ToolError

SemanticError creates a semantic error (backtrackable) Use for: LLM misuse, wrong tool sequence, invalid state, unknown tools

func SemanticErrorWithDetails

func SemanticErrorWithDetails(msg string, details map[string]any) *ToolError

SemanticErrorWithDetails creates a semantic error with structured details

func SemanticErrorf

func SemanticErrorf(format string, args ...any) *ToolError

SemanticErrorf creates a formatted semantic error

func WrapAsRuntime

func WrapAsRuntime(err error) *ToolError

WrapAsRuntime wraps any error as a runtime error

func WrapAsSemantic

func WrapAsSemantic(err error) *ToolError

WrapAsSemantic wraps any error as a semantic error

func (*ToolError) Error

func (e *ToolError) Error() string

Error implements the error interface

func (*ToolError) ToJSON

func (e *ToolError) ToJSON() map[string]any

ToJSON implements JSONError interface for structured output

type ToolErrorType

type ToolErrorType int

ToolErrorType classifies tool errors for backtracking decisions

const (
	// ToolErrorRuntime - Tool executed but failed (file not found, network error, etc.)
	// NOT backtrackable - error goes to history, LLM should see and handle it
	ToolErrorRuntime ToolErrorType = iota

	// ToolErrorSemantic - LLM misused the tool (wrong sequence, invalid state, etc.)
	// Backtrackable - discard and retry, LLM should have known better from prompt
	ToolErrorSemantic
)

type TruncationResult

type TruncationResult struct {
	Content      string // Full content if not truncated, formatted if truncated
	WasTruncated bool   // Whether content was truncated
	TotalLines   int    // Total lines in original
	TotalBytes   int    // Total bytes in original
}

TruncationResult contains the truncation outcome

func TruncateContent

func TruncateContent(content []byte, maxLines, maxBytes, truncatedLines, truncatedBytes int) TruncationResult

TruncateContent truncates content to show first and last portions if it exceeds limits If content is within limits, returns it as-is Otherwise, shows first N and last N lines/bytes with truncation marker

type UnifiedEditTool

type UnifiedEditTool struct {
	BaseEditTool
}

UnifiedEditTool implements all edit modes with a shared flow

func NewUnifiedEditTool

func NewUnifiedEditTool(cfg *config.Config, toolCtx *ToolContext) *UnifiedEditTool

NewUnifiedEditTool creates a new UnifiedEditTool

func (*UnifiedEditTool) Call

func (t *UnifiedEditTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*UnifiedEditTool) Check

func (t *UnifiedEditTool) Check(ctx context.Context, args json.RawMessage) error

func (*UnifiedEditTool) Description

func (t *UnifiedEditTool) Description() string

func (*UnifiedEditTool) JSONSchema

func (t *UnifiedEditTool) JSONSchema() map[string]any

func (*UnifiedEditTool) Name

func (t *UnifiedEditTool) Name() string

func (*UnifiedEditTool) PromptCategory

func (t *UnifiedEditTool) PromptCategory() string

func (*UnifiedEditTool) PromptOrder

func (t *UnifiedEditTool) PromptOrder() int

func (*UnifiedEditTool) PromptSection

func (t *UnifiedEditTool) PromptSection() string

func (*UnifiedEditTool) PromptTemplateName

func (t *UnifiedEditTool) PromptTemplateName() string

func (*UnifiedEditTool) ShortPromptSection

func (t *UnifiedEditTool) ShortPromptSection() string

ShortPromptSection gives the line-mode Edit's one hard rule.

type WebFetchTool

type WebFetchTool struct {
	// contains filtered or unexported fields
}

WebFetchTool retrieves one URL and converts it to markdown.

func NewWebFetchTool

func NewWebFetchTool(cfg *config.Config, tempFileMgr *TempFileManager) *WebFetchTool

func (*WebFetchTool) Call

func (t *WebFetchTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*WebFetchTool) Check

func (t *WebFetchTool) Check(ctx context.Context, args json.RawMessage) error

func (*WebFetchTool) Description

func (t *WebFetchTool) Description() string

func (*WebFetchTool) JSONSchema

func (t *WebFetchTool) JSONSchema() map[string]any

func (*WebFetchTool) Name

func (t *WebFetchTool) Name() string

func (*WebFetchTool) ParallelSafe

func (t *WebFetchTool) ParallelSafe() bool

ParallelSafe says several fetches can run at once in a Batch: the tool only reads, and the network is the thing being waited on.

func (*WebFetchTool) PromptCategory

func (t *WebFetchTool) PromptCategory() string

func (*WebFetchTool) PromptOrder

func (t *WebFetchTool) PromptOrder() int

func (*WebFetchTool) PromptSection

func (t *WebFetchTool) PromptSection() string

func (*WebFetchTool) PromptTemplateName

func (t *WebFetchTool) PromptTemplateName() string

func (*WebFetchTool) SelfTimeout

func (t *WebFetchTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout. A page can be slow in a way an API is not, and the client applies its own deadline.

func (*WebFetchTool) ShortPromptSection

func (t *WebFetchTool) ShortPromptSection() string

ShortPromptSection says what Web.fetch cannot express in its schema: the two result shapes, and the one failure that looks like success.

type WebSearchTool

type WebSearchTool struct {
	// contains filtered or unexported fields
}

WebSearchTool queries the Brave Search API.

It keeps no count of the monthly quota. The key is normally shared with other programs, often on other machines, so nothing here can see what they spent -- but every response carries the account-wide state in its x-ratelimit-* headers, which already includes them. Reading what the server reports is both simpler than local accounting and correct where local accounting would not be.

func NewWebSearchTool

func NewWebSearchTool(cfg *config.Config) *WebSearchTool

func (*WebSearchTool) Call

func (t *WebSearchTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*WebSearchTool) Check

func (t *WebSearchTool) Check(ctx context.Context, args json.RawMessage) error

func (*WebSearchTool) Description

func (t *WebSearchTool) Description() string

func (*WebSearchTool) JSONSchema

func (t *WebSearchTool) JSONSchema() map[string]any

func (*WebSearchTool) Name

func (t *WebSearchTool) Name() string

func (*WebSearchTool) ParallelSafe

func (t *WebSearchTool) ParallelSafe() bool

ParallelSafe says several searches can run at once in a Batch. The provider's one-per-second limit will serialize them anyway, which costs wall clock rather than a model round trip.

func (*WebSearchTool) PromptCategory

func (t *WebSearchTool) PromptCategory() string

func (*WebSearchTool) PromptOrder

func (t *WebSearchTool) PromptOrder() int

func (*WebSearchTool) PromptSection

func (t *WebSearchTool) PromptSection() string

func (*WebSearchTool) PromptTemplateName

func (t *WebSearchTool) PromptTemplateName() string

func (*WebSearchTool) SelfTimeout

func (t *WebSearchTool) SelfTimeout() bool

SelfTimeout opts out of the loop's blanket 15-second tool timeout: with retries against a one-per-second limit a call can legitimately take longer.

func (*WebSearchTool) ShortPromptSection

func (t *WebSearchTool) ShortPromptSection() string

ShortPromptSection says what Web.search cannot express in its schema: what a result is for, and that the two refusals mean different things.

type WriteFileTool

type WriteFileTool struct {
	// contains filtered or unexported fields
}

WriteFileTool writes entire content to a file (creates or overwrites)

func NewWriteFileTool

func NewWriteFileTool(cfg *config.Config, toolCtx *ToolContext) *WriteFileTool

func (*WriteFileTool) Call

func (t *WriteFileTool) Call(ctx context.Context, args json.RawMessage) (any, error)

func (*WriteFileTool) Check

func (t *WriteFileTool) Check(ctx context.Context, args json.RawMessage) error

func (*WriteFileTool) Description

func (t *WriteFileTool) Description() string

func (*WriteFileTool) JSONSchema

func (t *WriteFileTool) JSONSchema() map[string]any

func (*WriteFileTool) Name

func (t *WriteFileTool) Name() string

func (*WriteFileTool) PromptCategory

func (t *WriteFileTool) PromptCategory() string

func (*WriteFileTool) PromptOrder

func (t *WriteFileTool) PromptOrder() int

func (*WriteFileTool) PromptSection

func (t *WriteFileTool) PromptSection() string

func (*WriteFileTool) PromptTemplateName

func (t *WriteFileTool) PromptTemplateName() string

func (*WriteFileTool) ShortPromptSection

func (t *WriteFileTool) ShortPromptSection() string

ShortPromptSection says the one thing about Write that is not in its schema.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL