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
- Variables
- func IsPrivileged(t Tool) bool
- func ResultBudgetOf(t Tool) (int, bool)
- func SchemaOf(t Tool) ([]byte, bool)
- type ExecutionClass
- type ExecutionProfile
- type InOut
- type Out
- type PrivilegedTool
- type ProfiledTool
- type Registry
- func (r *Registry) Add(t Tool) error
- func (r *Registry) Get(name string) (Tool, bool)
- func (r *Registry) Remove(name string) bool
- func (r *Registry) Run(ctx context.Context, name string, in InOut) (Out, error)
- func (r *Registry) RunScoped(ctx context.Context, name string, in InOut, scope *Scope) (Out, error)
- func (r *Registry) Tools() []Tool
- type ResultBudgetTool
- type SchemaTool
- type Scope
- type ScopeOptions
- type Tool
- type ToolCall
Constants ¶
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.
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 ¶
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.
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.
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.
Functions ¶
func IsPrivileged ¶
IsPrivileged returns t.Privileged() when t implements PrivilegedTool; else it returns false.
func ResultBudgetOf ¶
ResultBudgetOf returns t.MaxResultBytes() and true when t implements ResultBudgetTool; else it returns 0, false.
func SchemaOf ¶
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 ¶
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) Remove ¶
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 ¶
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 ¶
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.
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.
func (*Scope) Allowed ¶
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.
type Tool ¶
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.