tool

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: AGPL-3.0, Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package tool defines the CoreTool contract and the built-in file/shell tools that give the agent its hands. Every tool is self-describing: a name and description for the model, a JSON-Schema input, behavioral flags used by the scheduler (read-only / concurrency-safe), a permission self-check, and a Call method. New tools are built through Build, which applies fail-closed defaults.

Index

Constants

View Source
const (
	SearchExtraToolsName = "SearchExtraTools"
	ExecuteExtraToolName = "ExecuteExtraTool"
)

Fixed names of the two always-loaded access tools.

Variables

This section is empty.

Functions

func BackgroundTasksDisabled

func BackgroundTasksDisabled() bool

BackgroundTasksDisabled reports whether background execution is turned off via the AGENT_CORE_DISABLE_BACKGROUND_TASKS environment variable.

func Capture

func Capture(tc *ToolContext, s string) string

Capture bounds a tool's textual output for the model. When the output exceeds the limit (ToolContext.MaxOutputChars, default 30000) and the host configured an OutputDir, the FULL output is written to a file there and a tight head + a pointer to that file is returned — so the model can read slices on demand (grep/sed) instead of losing the overflow. Without an OutputDir it falls back to a head+tail truncation (the overflow is discarded). Tools that produce potentially large output should return Capture(tc, output) rather than the raw string.

func CaptureOnce

func CaptureOnce(tc *ToolContext, s string) string

CaptureOnce is Capture with an idempotency guard: output a tool already ran through Capture (so it carries a spill pointer or truncation notice) is returned unchanged. This lets a single post-tool net cap every tool's output without re-truncating or re-spilling what built-in / MCP / custom tools already handled themselves. Output within the limit is a no-op either way, since Capture returns it unchanged.

func EnsureRipgrep

func EnsureRipgrep() string

EnsureRipgrep warms ripgrep resolution so the cost (including a possible npm install) is paid at startup rather than on the first Grep call. Hosts may call it once at boot. Returns the resolved path ("" = Go fallback).

func InteractiveShellDisabled

func InteractiveShellDisabled() bool

InteractiveShellDisabled reports whether interactive shell sessions are turned off via AGENT_CORE_DISABLE_INTERACTIVE_SHELL (mirrors BackgroundTasksDisabled).

func RenderDeferredToolsBlock

func RenderDeferredToolsBlock(names []string) string

RenderDeferredToolsBlock renders the <available-deferred-tools> block: the given tool names (one per line, sorted) plus the fixed usage instruction. Returns "" if names is empty. Hosts place this in the CACHED system-prompt prefix (a SystemPrompt segment before DynamicBoundary) — see design doc §2.1 / C1.

func RenderTodoReminder

func RenderTodoReminder(todos []Todo) string

RenderTodoReminder renders the todo list for the system-reminder body, one numbered item per line ("N. [status] content"). Returns "" for an empty list.

func RipgrepPath

func RipgrepPath() string

RipgrepPath returns the resolved ripgrep binary path, or "" when the pure-Go grep should be used. The heavy resolution (detect + optional npm install) runs at most once per process.

func ValidateInput

func ValidateInput(schema map[string]any, raw json.RawMessage) error

ValidateInput checks a raw JSON input against a minimal subset of JSON Schema sufficient for tool inputs: object type, required properties, and per-property primitive type checks (string/number/integer/boolean/array/object). It never panics; a non-nil error means the input is invalid (FR-04.5, NFR-07).

Types

type Answer

type Answer struct {
	// Selected holds the chosen option label(s). For free-form input the host
	// may return the typed text here.
	Selected []string
}

Answer is the user's response to a Question.

type AskUserFunc

type AskUserFunc func(ctx context.Context, q Question) Answer

AskUserFunc is the host's question callback (a CLI prompt, a GUI, a web form, or an automated policy). Like CanUseTool it is synchronous: the tool blocks until it returns. The host decides the UI; the SDK only defines the contract.

type CoreTool

type CoreTool interface {
	Name() string
	Description() string
	Prompt() string
	InputSchema() map[string]any
	IsReadOnly(input json.RawMessage) bool
	IsConcurrencySafe(input json.RawMessage) bool
	CheckPermissions(ctx context.Context, input json.RawMessage, pc permission.Context) permission.Decision
	Call(ctx context.Context, input json.RawMessage, tc *ToolContext) (Result, error)
}

CoreTool is a capability the agent can invoke (FR-04.1).

func Build

func Build(s Spec) CoreTool

Build constructs a CoreTool from a Spec, applying fail-closed defaults.

func DefaultTools

func DefaultTools() []CoreTool

DefaultTools returns the core toolset every agent gets: the file/shell tools (Read, Write, Edit, MultiEdit, LS, Glob, Grep, Bash) plus Sleep — a small context-cancellable wait that pairs with background execution. Network and stateful tools (WebFetch, TodoWrite) remain opt-in via their own constructors.

func NewAskUserQuestion

func NewAskUserQuestion(ask AskUserFunc) CoreTool

NewAskUserQuestion builds the AskUserQuestion tool bound to a host callback. It lets the agent pause a long task to ask the user a structured question (with options) and continue with the answer — without ending the turn.

func NewBash

func NewBash() CoreTool

NewBash builds the Bash tool: run a shell command with a timeout, optionally in the background.

func NewEdit

func NewEdit() CoreTool

NewEdit builds the Edit tool: exact string replacement with uniqueness check.

func NewExecuteExtraTool

func NewExecuteExtraTool(reg *Registry, unlock *UnlockSet) CoreTool

NewExecuteExtraTool builds the always-loaded invoker for deferred tools. It runs the named tool from reg, but only if it is currently in the unlock set. Schema is validated before the call. unlock may be nil (then every registry tool is callable — no gating).

func NewGlob

func NewGlob() CoreTool

NewGlob builds the Glob tool: find files by pattern, newest first.

func NewGrep

func NewGrep() CoreTool

func NewLS

func NewLS() CoreTool

NewLS builds the LS tool: lists the entries of a directory.

func NewMonitor

func NewMonitor() CoreTool

NewMonitor builds the Monitor tool: start a long-running streaming process in the background (tail -f, watch, a dev server, a polling loop) and return immediately. Inspect it with Read/TaskOutput and end it with TaskStop.

func NewMultiEdit

func NewMultiEdit() CoreTool

NewMultiEdit builds the MultiEdit tool: applies a sequence of exact-string replacements to one file atomically. Edits apply in order to the in-memory content; if any fails to match, nothing is written.

func NewRead

func NewRead() CoreTool

NewRead builds the Read tool: reads a file with cat -n style line numbers and optional offset/limit.

func NewSearchExtraTools

func NewSearchExtraTools(reg *Registry, deferred []string) CoreTool

NewSearchExtraTools builds the always-loaded discovery tool. deferred is the set of tool names whose schemas are withheld; the tool searches those names in reg and returns each match's name + description + params schema.

func NewShellClose

func NewShellClose() CoreTool

NewShellClose 关闭会话。

func NewShellList

func NewShellList() CoreTool

NewShellList 列出会话。

func NewShellOpen

func NewShellOpen() CoreTool

NewShellOpen 开一个交互会话。

func NewShellRead

func NewShellRead() CoreTool

NewShellRead 读输出:增量流 或 整屏快照。

func NewShellSend

func NewShellSend() CoreTool

NewShellSend 向会话发送输入/按键。

func NewSleep

func NewSleep() CoreTool

NewSleep returns a tool that waits N seconds, interruptible via context. It pairs with background task execution: after spawning long-running work (e.g. a Bash command with run_in_background), an agent can sleep, then read partial output with TaskOutput. Part of DefaultTools — every agent gets it.

The wait is cancelled the moment ctx is done (pause/stop/timeout), returning promptly rather than blocking out the deadline.

func NewTaskList

func NewTaskList() CoreTool

NewTaskList builds the TaskList tool: enumerate this session's background tasks.

func NewTaskOutput

func NewTaskOutput() CoreTool

NewTaskOutput builds the TaskOutput tool: read the captured output of a background task (from Bash run_in_background or Monitor), optionally blocking until it finishes.

func NewTaskStop

func NewTaskStop() CoreTool

NewTaskStop builds the TaskStop tool: kill a running background task (or all).

func NewWebFetch

func NewWebFetch(cfg WebFetchConfig) CoreTool

NewWebFetch builds the WebFetch tool: fetches a URL over HTTP(S) and returns its content as Markdown (HTML is parsed and rendered; non-HTML is returned as-is). With extract=true it also appends a RECON section that preserves the signal HTML→Markdown normally discards — comments, <script> sources, inline endpoints, forms/inputs (including hidden) and meta tags — useful for security reconnaissance. cfg optionally routes requests through a proxy and/or skips TLS verification (see WebFetchConfig). It is network-facing, so it is not part of DefaultTools and requires permission to run.

func NewWebSearch

func NewWebSearch(cfg WebSearchConfig) (CoreTool, error)

NewWebSearch builds the web_search tool for the configured backend. It returns an error when the config is unusable (unknown backend, or brave-free without a key) so a session fails fast at construction instead of at first call. The tool is network-facing, so it is not part of DefaultTools and requires permission.

func NewWrite

func NewWrite() CoreTool

NewWrite builds the Write tool: creates or overwrites a file.

func ShellSessionTools

func ShellSessionTools() []CoreTool

ShellSessionTools returns the five interactive-shell tools. Callers add them to Options.Tools only when interactive shell is enabled for the agent.

type Manager

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

Manager owns the background tasks of one Session.

func NewManager

func NewManager(sessionID string) (*Manager, error)

NewManager creates the per-session task directory and garbage-collects stale directories left by previous sessions.

func (*Manager) Background

func (m *Manager) Background(id string)

Background marks a task as backgrounded: its completion will be surfaced to the model. If the task already finished (a race against a fast command), the notification is enqueued immediately.

func (*Manager) Cleanup

func (m *Manager) Cleanup()

Cleanup kills all tasks, cancels the root context, and removes the session's task directory. Safe to call on a nil Manager.

func (*Manager) DrainNotifications

func (m *Manager) DrainNotifications() []Notification

DrainNotifications returns and clears the pending completion notifications.

func (*Manager) Forget

func (m *Manager) Forget(id string)

Forget removes a task from the registry and deletes its output file. Used for foreground commands once their output has been returned inline.

func (*Manager) Get

func (m *Manager) Get(id string) (TaskInfo, bool)

Get returns a snapshot of one task.

func (*Manager) Kill

func (m *Manager) Kill(id string) bool

Kill terminates a running task (process tree) and marks it killed.

func (*Manager) KillAll

func (m *Manager) KillAll() int

KillAll terminates every running task and returns how many were stopped.

func (*Manager) List

func (m *Manager) List() []TaskInfo

List returns snapshots of all tasks in creation order.

func (*Manager) Output

func (m *Manager) Output(id string, maxBytes int) (string, error)

Output returns the tail of a task's output file, up to maxBytes (default 30000).

func (*Manager) RunForeground

func (m *Manager) RunForeground(ctx context.Context, spec SpawnSpec, timeout time.Duration) (t *Task, finished bool, output string, err error)

RunForeground starts a task and waits up to timeout. finished is true if the process exited within the timeout (output is then returned); otherwise the task keeps running and the caller decides whether to background or kill it. A cancelled ctx kills the task and returns ctx.Err().

func (*Manager) Spawn

func (m *Manager) Spawn(spec SpawnSpec) (*Task, error)

Spawn launches a process detached from the per-turn context. Its output streams to a file; a watch goroutine records the exit status and (if backgrounded) a completion notification.

func (*Manager) Wait

func (m *Manager) Wait(ctx context.Context, id string, timeout time.Duration) (TaskInfo, bool)

Wait blocks until the task exits or the timeout/ctx fires. ok is true only if the task reached a terminal state.

type Notification

type Notification struct {
	TaskID     string
	Kind       TaskKind
	Status     TaskStatus
	OutputPath string
	Summary    string
}

Notification is surfaced to the model when a backgrounded task exits.

func (Notification) String

func (n Notification) String() string

String renders the notification as the <task-notification> block injected into the conversation. It deliberately carries only the path, not the output.

type Option

type Option struct {
	Label       string
	Description string
}

Option is one selectable choice for an AskUserQuestion.

func (*Option) UnmarshalJSON

func (o *Option) UnmarshalJSON(data []byte) error

UnmarshalJSON lets Option accept the lowercase JSON the model emits.

type ProgressInfo

type ProgressInfo struct {
	Message string
}

ProgressInfo is an incremental progress update emitted during a long tool run.

type Question

type Question struct {
	Header      string // short label/category, e.g. "Auth method"
	Question    string // the full question text
	Options     []Option
	MultiSelect bool
}

Question is a single structured question the agent poses mid-task.

type Registry

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

Registry is an ordered, name-indexed collection of tools (FR-04.3).

func NewRegistry

func NewRegistry(tools ...CoreTool) *Registry

NewRegistry builds a registry from tools, preserving order.

func (*Registry) Add

func (r *Registry) Add(t CoreTool)

Add registers a tool, replacing any existing one with the same name.

func (*Registry) Get

func (r *Registry) Get(name string) (CoreTool, bool)

Get returns the named tool.

func (*Registry) List

func (r *Registry) List() []CoreTool

List returns all tools in registration order.

func (*Registry) Schemas

func (r *Registry) Schemas() []llm.ToolSchema

Schemas renders the registry into provider-facing tool definitions, merging each tool's extended Prompt into its description.

type Result

type Result struct {
	Content []llm.ContentBlock
	IsError bool
	// Extra holds additional messages the tool injects into the conversation
	// right after its tool_result (e.g. the Skill tool emits the invoked skill's
	// instructions as an independent user message rather than burying them in the
	// tool_result). Appended in order; nil for ordinary tools.
	Extra []llm.Message
}

Result is the outcome of a tool call. Content is the tool_result payload fed back to the model.

func Errorf

func Errorf(s string) Result

Errorf builds an error result.

func Text

func Text(s string) Result

Text builds a successful text result.

func (Result) Flatten

func (r Result) Flatten() string

Flatten concatenates the text of a result's content blocks.

type SearchResult

type SearchResult struct {
	Title       string `json:"title"`
	URL         string `json:"url"`
	Description string `json:"description"`
	Position    int    `json:"position"`
}

SearchResult is one web-search hit, normalized across backends.

func WebSearchProbe

func WebSearchProbe(ctx context.Context, cfg WebSearchConfig, query string, limit int) ([]SearchResult, error)

WebSearchProbe runs a one-off search with cfg and returns the results, bypassing the tool/permission layer. It's the entry point for a connectivity/config check (e.g. a "test" button): same backend selection + proxy/TLS handling as the web_search tool, so a success means the live tool would reach the backend too.

type SpawnSpec

type SpawnSpec struct {
	Command     string
	Description string
	Kind        TaskKind
	WorkingDir  string
	// Env holds extra "KEY=VALUE" entries layered onto the process environment for
	// this command (see ToolContext.Env). Empty = inherit unchanged.
	Env []string
}

SpawnSpec describes a process to launch in the background.

type Spec

type Spec struct {
	Name        string
	Description string
	Prompt      string
	Schema      map[string]any
	ReadOnly    func(json.RawMessage) bool
	Concurrent  func(json.RawMessage) bool
	Permissions func(ctx context.Context, input json.RawMessage, pc permission.Context) permission.Decision
	Run         func(ctx context.Context, input json.RawMessage, tc *ToolContext) (Result, error)

	// RawInput hands Run whatever the model produced, skipping the schema check
	// the harness otherwise applies first.
	//
	// Schema stays required and is still advertised to the model — the two are
	// separate jobs. Schema tells the model what to send; validation decides what
	// gets through. A tool that repairs malformed arguments itself needs the
	// first and is defeated by the second: a truncated or fence-wrapped call is
	// not valid JSON, so it would be rejected before Run ever saw it, and the
	// repair could never run.
	//
	// Only set this on a tool that treats its input as untrusted bytes and
	// answers every shape with a Result rather than an error.
	RawInput bool
}

Spec declares a tool. Build fills unset behavioral fields with fail-closed defaults (FR-04.2): not read-only, not concurrency-safe, requires approval.

type Task

type Task struct {
	ID          string
	Kind        TaskKind
	Command     string
	Description string
	Status      TaskStatus
	ExitCode    int
	StartTime   time.Time
	EndTime     time.Time
	OutputPath  string
	// contains filtered or unexported fields
}

Task is a single background process tracked by a Manager. Its mutable fields are guarded by Manager.mu; read snapshots through Manager methods (which copy into TaskInfo).

type TaskInfo

type TaskInfo struct {
	ID           string
	Kind         TaskKind
	Command      string
	Description  string
	Status       TaskStatus
	ExitCode     int
	StartTime    time.Time
	EndTime      time.Time
	OutputPath   string
	Backgrounded bool
}

TaskInfo is an immutable snapshot of a task for safe reads outside the lock.

type TaskKind

type TaskKind string

TaskKind distinguishes a one-shot background command from a long-lived monitor.

const (
	KindBash    TaskKind = "bash"
	KindMonitor TaskKind = "monitor"
)

type TaskStatus

type TaskStatus string

TaskStatus is the lifecycle state of a background task.

const (
	TaskRunning   TaskStatus = "running"
	TaskCompleted TaskStatus = "completed"
	TaskFailed    TaskStatus = "failed"
	TaskKilled    TaskStatus = "killed"
)

func (TaskStatus) Terminal

func (s TaskStatus) Terminal() bool

Terminal reports whether the status is final (the process has exited).

type Todo

type Todo struct {
	Content string     `json:"content"`
	Status  TodoStatus `json:"status"`
}

Todo is one task in the agent's working plan.

type TodoStatus

type TodoStatus string

TodoStatus is the state of a todo item.

const (
	TodoPending    TodoStatus = "pending"
	TodoInProgress TodoStatus = "in_progress"
	TodoCompleted  TodoStatus = "completed"
)

type TodoStore

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

TodoStore holds the current todo list across tool calls. It is safe for concurrent reads; the host can inspect progress via List.

func NewTodoStore

func NewTodoStore() *TodoStore

NewTodoStore returns an empty store.

func (*TodoStore) List

func (s *TodoStore) List() []Todo

List returns a snapshot of the current todos.

func (*TodoStore) Tool

func (s *TodoStore) Tool() CoreTool

Tool returns the TodoWrite tool bound to this store. The agent uses it to plan and track multi-step work; each call replaces the whole list.

type ToolContext

type ToolContext struct {
	WorkingDir string
	AgentID    string
	// ToolUseID is the id of the tool_use block this call answers. Tools that
	// need to refer back to their own call in the conversation — to anchor
	// something to it, or to recognise it later — read it here.
	ToolUseID      string
	MaxOutputChars int
	// OutputDir, when set, makes oversized tool output spill to a file there
	// (full content preserved) instead of being discarded by truncation; the
	// tool returns a head + a pointer to the file. Empty disables spilling.
	OutputDir string
	// Emit reports progress; may be nil. Safe to call from the tool goroutine.
	Emit func(ProgressInfo)
	// Tasks is the background-task manager for this session; may be nil (background
	// execution disabled). Tools use it to spawn, read, and stop background work.
	Tasks *Manager
	// Env holds extra "KEY=VALUE" entries injected into the environment of Bash
	// subprocesses (both foreground and background). Applied on top of the parent
	// process environment, so these override inherited values. Empty = inherit
	// unchanged. Used e.g. to route child-command HTTP through a proxy + trust a
	// CA, without affecting the agent's own process.
	Env []string
}

ToolContext carries per-call execution state into a tool.

type UnlockSet

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

UnlockSet is a session-scoped, concurrency-safe set of deferred-tool names that are currently callable via ExecuteExtraTool. Globally-available deferred tools are seeded at construction; skill-gated ones are added at runtime when their skill loads. Withholding the schema (deferred) is separate from gating the call (unlock): a name may be listed yet locked until its skill is invoked.

func NewUnlockSet

func NewUnlockSet(initial ...string) *UnlockSet

NewUnlockSet builds an unlock set seeded with the given tool names.

func (*UnlockSet) Add

func (s *UnlockSet) Add(names ...string)

Add unlocks the given tool names (idempotent).

func (*UnlockSet) Has

func (s *UnlockSet) Has(name string) bool

Has reports whether name is currently unlocked (callable).

func (*UnlockSet) List

func (s *UnlockSet) List() []string

List returns the unlocked names, sorted.

type WebFetchConfig

type WebFetchConfig struct {
	// Proxy, when set, routes requests through this http(s) proxy URL. Empty = direct.
	Proxy string
	// CACert, when set, is a path to a PEM CA certificate added to the system
	// trust pool. This lets WebFetch *verify* HTTPS fetched through a MITM/recording
	// proxy that re-signs certificates with its own CA — the proper alternative to
	// InsecureTLS, keeping verification on for every other connection. Preferred
	// over InsecureTLS; ignored when InsecureTLS is set.
	CACert string
	// InsecureTLS skips TLS certificate verification entirely (equivalent to
	// curl -k). A blunt fallback when no CA cert is available; prefer CACert.
	InsecureTLS bool
}

WebFetchConfig tunes the network behavior of a WebFetch tool.

type WebSearchConfig

type WebSearchConfig struct {
	// Backend chooses the provider: "ddgs" (DuckDuckGo scrape, no key),
	// "brave-free" (Brave Search API, needs BraveAPIKey), "tavily" (Tavily
	// Search API, needs TavilyAPIKey), or "deepseek" (DeepSeek's server-side
	// web search, needs DeepSeek*). Empty defaults to "ddgs".
	Backend string
	// BraveAPIKey authenticates the "brave-free" backend. Required (and only
	// used) when Backend == "brave-free".
	BraveAPIKey string
	// TavilyAPIKey authenticates the "tavily" backend. Required (and only
	// used) when Backend == "tavily".
	TavilyAPIKey string
	// DeepSeekBaseURL / DeepSeekAPIKey / DeepSeekModel configure the "deepseek"
	// backend. That backend does NOT call a search API — DeepSeek has none.
	// Search there only exists inside its Anthropic-compatible messages endpoint
	// as the web_search_20250305 server tool, so this provider spends one model
	// call per search. BaseURL must be the Anthropic-format root (the provider
	// appends /v1/messages); the OpenAI-format endpoint rejects server tools
	// outright ("unknown variant, expected `function`").
	DeepSeekBaseURL string
	DeepSeekAPIKey  string
	DeepSeekModel   string
	// Proxy, when set, routes search requests through this http(s) proxy URL —
	// useful when the search endpoint is only reachable via a proxy. Empty = direct.
	Proxy string
	// CACert / InsecureTLS mirror WebFetchConfig: trust a proxy's CA in addition
	// to the system roots (CACert), or skip TLS verification entirely (InsecureTLS).
	// Prefer CACert. See WebFetchConfig for the full semantics.
	CACert      string
	InsecureTLS bool
}

WebSearchConfig selects and configures the active search backend.

Jump to

Keyboard shortcuts

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