tools

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package tools defines a named-action interface and a registry that resolves a name to a Tool and runs it. An agent calls a tool by name without knowing its concrete type. A tool never sees the agent. See ../docs/plans/tools.md for the rationale.

Map: tool.go = Tool, InOut, Out; registry.go = Registry, New, Add, Get, Remove, Run, RunScoped, and the sentinel errors ErrNilTool, ErrBlankName, ErrDuplicateName, ErrUnknownName, ErrScopeDenied; execution_profile.go = ExecutionClass, ExecutionProfile, ProfiledTool, ResultBudgetTool, PrivilegedTool, ExecutionProfileOf, ResultBudgetOf, IsPrivileged; scope.go = ScopeOptions, Scope, NewScope. Contribution rules: ../AGENTS.md.

Index

Constants

View Source
const DefaultRunTimeout time.Duration = 10 * time.Minute

DefaultRunTimeout bounds a run when the tool declares no profile Timeout. Protection is the floor; no caller opts in.

View Source
const TimeoutNone time.Duration = -1

TimeoutNone is the canonical negative duration meaning "never cap this run". The resolver treats any negative value the same way.

Variables

View Source
var (
	// ErrNilTool is Add's error for a nil Tool interface value, the
	// t == nil case. Add checks t == nil before it calls any method
	// on t. A typed nil pointer that implements Tool is not nil as an
	// interface value; Add cannot detect it without reflection, which
	// this module forbids in packages. Passing one is caller error.
	ErrNilTool = errors.New("tools: tool must not be nil")
	// ErrBlankName is Add's error when t.Name() is empty after
	// strings.TrimSpace. A tool needs a real name to register under
	// and to look up later.
	ErrBlankName = errors.New("tools: tool name must not be blank")
	// ErrDuplicateName is Add's error for a name already registered.
	ErrDuplicateName = errors.New("tools: tool name already registered")
	// ErrUnknownName is Run's error when Get reports false for name.
	ErrUnknownName = errors.New("tools: unknown tool name")
	// ErrScopeDenied is RunScoped's error when scope.Allowed returns
	// false for the resolved tool.
	ErrScopeDenied = errors.New("tools: tool denied by scope")
	// ErrToolDeclined is RunScoped's error when scope.Approve returns
	// (false, nil). Test with errors.Is. Phase 36 addition.
	ErrToolDeclined = errors.New("tools: tool declined by approval")
)

Sentinel errors for Registry operations; test with errors.Is.

View Source
var ErrInvalidExecutionClass = errors.New("tools: invalid execution class")

ErrInvalidExecutionClass is Validate's error for a value outside the declared ExecutionClass set. Test with errors.Is.

View Source
var ErrRunTimeout = errors.New("tools: tool run exceeded its timeout")

ErrRunTimeout is Run's and RunScoped's error when a tool exceeds its effective bound. It returns wrapped with the tool name and the bound; test with errors.Is, never by matching the naked sentinel.

View Source
var ErrUnknownApprovalThreshold = errors.New("tools: unknown ApprovalThreshold")

ErrUnknownApprovalThreshold is ScopeOptions.Validate's error when ApprovalThreshold is not one of the four declared ExecutionClass values. Test with errors.Is.

Functions

func IsPrivileged

func IsPrivileged(t Tool) bool

IsPrivileged returns t.Privileged() when t implements PrivilegedTool; else it returns false.

func ResultBudgetOf

func ResultBudgetOf(t Tool) (int, bool)

ResultBudgetOf returns t.MaxResultBytes() and true when t implements ResultBudgetTool; else it returns 0, false.

func SchemaOf

func SchemaOf(t Tool) ([]byte, bool)

SchemaOf returns t.ParameterSchema() and true when t implements SchemaTool and publishes non-nil schema bytes. It fails closed: a tool whose ParameterSchema() returns nil gets nil, false, whether or not it implements SchemaTool, so a nil schema never reads as published. It follows ExecutionProfileOf's precedent: an optional marker, checked through a type assertion, with a paired accessor.

Types

type ExecutionClass

type ExecutionClass string

ExecutionClass is a tool's execution-risk category. Validate enforces the declared set.

const (
	ExecutionClassUnclassified ExecutionClass = ""
	ExecutionClassRead         ExecutionClass = "read"
	ExecutionClassWrite        ExecutionClass = "write"
	ExecutionClassExternal     ExecutionClass = "external"
)

The declared ExecutionClass values. ExecutionClassUnclassified is the zero value: the default for a tool with no ExecutionProfile.

func (ExecutionClass) Validate

func (c ExecutionClass) Validate() error

Validate rejects any ExecutionClass value outside ExecutionClassUnclassified, ExecutionClassRead, ExecutionClassWrite, and ExecutionClassExternal.

type ExecutionProfile

type ExecutionProfile struct {
	Class       ExecutionClass
	ResourceKey string
	Timeout     time.Duration
}

ExecutionProfile is execution-risk metadata for one tool: its class, its per-turn dedup key, and its run-timeout declaration. The registry enforces Timeout on every dispatched run; ResourceKey stays published-only metadata. See docs/packages/tools.md, "Run timeout backstop" and "Published, not enforced".

func ExecutionProfileOf

func ExecutionProfileOf(t Tool) ExecutionProfile

ExecutionProfileOf returns t's published ExecutionProfile when t implements ProfiledTool; else it returns the zero ExecutionProfile, whose Class is ExecutionClassUnclassified. It never calls Validate; an out-of-enum Class passes through unchanged.

type InOut

type InOut struct {
	Value any
}

InOut is a tool's input payload. A tool reads its typed argument through Value and asserts the concrete type it expects.

type Out

type Out struct {
	Value any
}

Out is a tool's output payload. A tool writes its typed result through Value.

type PrivilegedTool

type PrivilegedTool interface {
	Privileged() bool
}

PrivilegedTool is an optional interface. A Tool implements it to mark itself as needing explicit allowlisting.

type ProfiledTool

type ProfiledTool interface {
	ExecutionProfile() ExecutionProfile
}

ProfiledTool is an optional interface. A Tool implements it to publish an ExecutionProfile.

type Registry

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

Registry holds tools by name. Built only through New. Safe for concurrent Add, Get, Remove, and Run; a sync.RWMutex guards the map.

func New

func New() *Registry

New creates an empty Registry. Every run is bounded by DefaultRunTimeout unless the tool's profile declares its own Timeout.

func (*Registry) Add

func (r *Registry) Add(t Tool) error

Add registers t under t.Name(). Rejects a nil t (t == nil) with ErrNilTool, before it calls t.Name(). A typed nil pointer that implements Tool is caller error; see ErrNilTool. Rejects a blank name (empty after strings.TrimSpace) with ErrBlankName. Rejects a duplicate name with ErrDuplicateName.

func (*Registry) Get

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

Get resolves name to a Tool. Returns false when name is absent.

func (*Registry) Remove

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

Remove removes name from the registry. Returns whether name was present. Removing an absent name is not a fault; it returns false and changes nothing.

func (*Registry) Run

func (r *Registry) Run(ctx context.Context, name string, in InOut) (Out, error)

Run resolves name through Get and calls the tool's Run under the effective run-timeout bound; see registry_timeout.go. Returns ErrUnknownName when Get reports false.

func (*Registry) RunScoped

func (r *Registry) RunScoped(ctx context.Context, name string, in InOut, scope *Scope) (Out, error)

RunScoped resolves name through Get, checks scope.Allowed when scope is non-nil, then calls the tool the same way Run does, under the effective run-timeout bound. Returns ErrUnknownName for an unresolved name and ErrScopeDenied for a name the scope excludes. A nil scope allows every resolved tool, matching Run's behavior. After scope.Allowed passes, when scope.approve is non-nil and the resolved tool's rank meets or exceeds scope.approvalThreshold's rank, RunScoped calls scope.approve before it calls Run. approve returning (true, nil) proceeds to Run. approve returning (false, nil) returns ErrToolDeclined. approve returning a non-nil error returns that error unchanged. The map lookup lock is released before approve runs, so a blocking approve never blocks other registry callers.

func (*Registry) Tools

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

Tools returns a snapshot of every registered Tool, sorted by name. The result is a fresh slice; mutating it does not affect the Registry. The result is empty and non-nil for an empty Registry.

type ResultBudgetTool

type ResultBudgetTool interface {
	MaxResultBytes() int
}

ResultBudgetTool is an optional interface. A Tool implements it to bound its output size.

type SchemaTool

type SchemaTool interface {
	// ParameterSchema returns the tool's parameter schema as raw
	// bytes, in a format the caller's model provider understands.
	ParameterSchema() []byte
	// DecodeArguments turns raw model-supplied argument bytes into
	// the tool's own InOut input value.
	DecodeArguments(raw []byte) (InOut, error)
}

SchemaTool is an optional interface. A Tool implements it to publish its parameter schema and decode raw argument bytes.

type Scope

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

Scope is a narrowing filter over tool names, plus an optional approval gate. Built only through NewScope. Narrows only: ExtraDenylist always wins over Allowlist, and no operation on a built Scope can re-add a name ExtraDenylist removed.

func NewScope

func NewScope(opts ScopeOptions) *Scope

NewScope builds a Scope from opts. An empty Allowlist means every non-denied, non-privileged tool is allowed. ExtraDenylist always removes a name from the allowed set, even when Allowlist also names it. Approve and ApprovalThreshold carry through unchanged for RunScoped's approval check. NewScope does not reject an unknown ApprovalThreshold; it ranks as External there, the highest class, so approve fires only for External-class tools. Call NewScopeChecked, or opts.Validate directly, for construction-time rejection of an unknown ApprovalThreshold.

func NewScopeChecked added in v0.4.0

func NewScopeChecked(opts ScopeOptions) (*Scope, error)

NewScopeChecked builds a Scope like NewScope, but first calls opts.Validate and returns its error instead of silently accepting an unknown ApprovalThreshold. Prefer this constructor when construction-time enforcement matters more than NewScope's zero-config convenience.

func (*Scope) Allowed

func (s *Scope) Allowed(name string, t Tool) bool

Allowed reports whether name passes the denylist, the privileged check, and the allowlist. A name in ExtraDenylist is denied regardless of Allowlist. A privileged tool (t implements PrivilegedTool and reports true) is denied unless name appears in Allowlist. When Allowlist is empty, every non-denied, non-privileged tool is allowed; otherwise only names in Allowlist are allowed.

type ScopeOptions

type ScopeOptions struct {
	Allowlist         []string
	ExtraDenylist     []string
	Approve           func(ctx context.Context, call ToolCall) (bool, error)
	ApprovalThreshold ExecutionClass
}

ScopeOptions holds the inputs to NewScope: an allowlist, an extra denylist, and an optional approval gate. Approve and ApprovalThreshold are phase 36 additions; both are optional. A ScopeOptions with neither set behaves exactly as phase 31 shipped it, with no approval check.

func (ScopeOptions) Validate added in v0.4.0

func (o ScopeOptions) Validate() error

Validate checks opts: ApprovalThreshold must be one of the four declared ExecutionClass values. An unknown class has no rank, so RunScoped would silently treat it as "never approve"; Validate rejects it instead, for callers that want construction-time enforcement.

type Tool

type Tool interface {
	Name() string
	Run(ctx context.Context, in InOut) (Out, error)
}

Tool is a named action a Registry can resolve and run. Name returns the registration key. Run performs the action and returns its result or an error.

type ToolCall

type ToolCall struct {
	Name    string
	In      InOut
	Profile ExecutionProfile
}

ToolCall describes one call RunScoped is about to make, passed to a Scope's Approve function. Name is the resolved tool's registration name. In is the caller's input payload, unchanged from the RunScoped call. Profile is ExecutionProfileOf(t) for the resolved tool.

Jump to

Keyboard shortcuts

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