tools

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package tools defines zkit's canonical tool interface, registry, and result types for AI agent tool execution.

Tools expose typed names, JSON-schema-like parameters, effects metadata, and structured error kinds. Consumers compose Tool, Iterable, Executor, and Source rather than depending on a product-specific registry implementation.

First-party tools should prefer typed argument/result structs via NewTyped, or SchemaFor[Args] plus DecodeArgs[Args] when they need custom validation. Direct ToolParameters access is the escape hatch for genuinely dynamic JSON shapes, not the default implementation style.

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidToolName = errors.New("tools: invalid tool name")

ErrInvalidToolName is returned by ValidateToolSpec and registry registration when a ToolSpec has an empty or whitespace-only Name.

View Source
var ErrParseKind = errors.New("invalid input provided to parse to Kind")
View Source
var ErrParseWorkspaceAccess = errors.New("invalid input provided to parse to WorkspaceAccess")
View Source
var ErrWorkspaceConflict = errors.New("workspace access conflict")

ErrWorkspaceConflict reports that workspace access is held incompatibly by another owner. Callers can recover by completing later or choosing work that does not require the workspace.

View Source
var Kinds = kindsContainer{
	UNKNOWN: Kind{
		// contains filtered or unexported fields
	},
	VALIDATION: Kind{
		// contains filtered or unexported fields
	},
	NOTFOUND: Kind{
		// contains filtered or unexported fields
	},
	PERMISSION: Kind{
		// contains filtered or unexported fields
	},
	TRANSIENT: Kind{
		// contains filtered or unexported fields
	},
	BUDGET: Kind{
		// contains filtered or unexported fields
	},
	FATAL: Kind{
		// contains filtered or unexported fields
	},
	STALE: Kind{
		// contains filtered or unexported fields
	},
}

Kinds is a main entry point using the Kind type. It it a container for all enum values and provides a convenient way to access all enum values and perform operations, with convenience methods for common use cases.

View Source
var WorkspaceAccesses = workspaceAccessesContainer{
	INVALID: WorkspaceAccess{
		// contains filtered or unexported fields
	},
	NONE: WorkspaceAccess{
		// contains filtered or unexported fields
	},
	READ: WorkspaceAccess{
		// contains filtered or unexported fields
	},
	WRITE: WorkspaceAccess{
		// contains filtered or unexported fields
	},
}

WorkspaceAccesses is a main entry point using the WorkspaceAccess type. It it a container for all enum values and provides a convenient way to access all enum values and perform operations, with convenience methods for common use cases.

Functions

func CallSignature

func CallSignature(call ToolCall) string

CallSignature returns a stable, fixed-size key derived from a tool call's name and canonicalized arguments. Wrappers and guardrails that bucket per-call state (memoization, failure counting, repeat-call caps) all share this helper so semantically identical calls produce identical keys regardless of argument map ordering.

The key is a hex-encoded SHA-256 over (toolname, NUL, canonical JSON of args). Stdlib json.Marshal already sorts map[string]any keys; we recurse through nested values via canonicalize so the "same content, different ordering" guarantee holds at every depth.

func ContextWithNestedToolObserver added in v0.4.0

func ContextWithNestedToolObserver(ctx context.Context, obs NestedToolObserver) context.Context

ContextWithNestedToolObserver returns a child context carrying obs.

func DataAs added in v0.3.1

func DataAs[T any](r *ToolResult) (T, bool)

DataAs returns r.Data as T when it already has that dynamic type.

func DecodeArgs

func DecodeArgs[T any](params ToolParameters) (T, error)

DecodeArgs decodes a ToolParameters map into T via a JSON round-trip through repair.Unmarshal, so small-model quirks (literal newlines, trailing commas, missing closers) get repaired at the decode boundary. Returns a *Error of Kinds.VALIDATION on failure; callers pass it straight to Failure.

func ExhaustiveKinds

func ExhaustiveKinds(f func(Kind))

ExhaustiveKinds iterates over all enum values and calls the provided function for each value. This function is useful for performing operations on all valid enum values in a loop.

func ExhaustiveWorkspaceAccesses added in v0.11.0

func ExhaustiveWorkspaceAccesses(f func(WorkspaceAccess))

ExhaustiveWorkspaceAccesses iterates over all enum values and calls the provided function for each value. This function is useful for performing operations on all valid enum values in a loop.

func RedactSecrets

func RedactSecrets(s string) string

RedactSecrets removes common credential shapes before tool/process output is inserted into model-visible context or logs. It is a best-effort safety net, not a substitute for environment scrubbing.

func SchemaFor

func SchemaFor[T any]() llm.Schema

SchemaFor reflects over T and returns the tool's parameter schema as a typed llm.Schema (the shape tools.ToolSpec.Parameters expects) so a tool author never has to hand-write a schema tree. Conventions are the standard json-tag set plus two ergonomic additions:

`json:"name"`            field name (drop with `json:"-"`)
`json:",omitempty"`      field is optional
pointer / interface type field is optional
`doc:"..."`              human-readable description shown to the LLM
`description:"..."`      alias for doc
`enum:"a,b,c"`           restrict the value to a fixed set

Required fields are everything that isn't a pointer and doesn't carry omitempty. Order is preserved from the struct declaration. Unsupported types (channels, funcs, complex numbers, etc.) produce the zero Schema.

SchemaFor is the lazy escape hatch. Tool authors who need finer control (oneOf, allOf, format constraints, custom $ref) build the llm.Schema by hand, putting those keys in its Extra field.

func ValidateToolSpec added in v0.4.0

func ValidateToolSpec(spec ToolSpec) error

ValidateToolSpec checks that a ToolSpec meets basic invariants:

  • Name is non-empty after trimming whitespace

Additional provider-specific constraints (name length, character set) should be enforced at the provider conversion boundary, not here.

Types

type DescriptionStore

type DescriptionStore interface {
	Description(name ToolName) (description string, ok bool)
}

DescriptionStore maps tool names to human-authored override descriptions. Lookup is hot-path (fires on every tool-spec build that goes to an LLM) so implementations MUST be in-memory; persistence is the caller's job — load entries at startup via Load, and update via Set/Delete.

The zero-value lookup semantics: (description "", ok=false) means "no override — use the tool's code-default description.".

type Effect

type Effect struct {
	Kind    EffectKind     `json:"kind"`
	File    *FileEffect    `json:"file,omitempty"`
	Process *ProcessEffect `json:"process,omitempty"`
}

Effect is one post-action fact produced by a tool. The Kind field selects which optional payload is populated. Pointer payloads are used because nil is meaningful: a file effect has no process payload, and a process effect has no file payload.

func NewFileEffect

func NewFileEffect(op FileOp, path string) Effect

NewFileEffect returns a file effect for op/path.

func NewProcessEffect

func NewProcessEffect(command string, exitCode int) Effect

NewProcessEffect returns a process effect for command/exitCode.

func (Effect) IsFile added in v0.3.1

func (e Effect) IsFile() bool

IsFile reports whether e is a valid file effect.

func (Effect) IsProcess added in v0.3.1

func (e Effect) IsProcess() bool

IsProcess reports whether e is a valid process effect.

func (Effect) Validate added in v0.3.1

func (e Effect) Validate() error

Validate reports whether e has exactly the payload selected by Kind.

type EffectKind

type EffectKind string

EffectKind classifies a post-action effect emitted by a tool. Effects are control-plane facts for guardrails, harnesses, audit views, and eval reporting; they are not automatically rendered into the LLM transcript.

const (
	// EffectFile records a filesystem effect inside the tool workspace.
	EffectFile EffectKind = "file"
	// EffectProcess records a process effect, usually from the bash tool.
	EffectProcess EffectKind = "process"
)

type Error

type Error struct {
	// Kind classifies the failure. Consumers switch on this.
	Kind Kind
	// Op is the tool name (or sub-operation) that failed. Empty is fine.
	Op string
	// Reason is a short human-readable explanation. Mirrors what the
	// LLM ultimately sees in the tool message.
	Reason string
	// Wrapped is the underlying cause, if any. Surfaces through Unwrap
	// so deeper errors.Is / errors.AsType chains keep working.
	Wrapped error
}

Error is the typed error every tool returns when classification matters. The Kind dictates which fields the constructor populates; callers should always build one via the per-Kind constructors below rather than the struct literal.

func Budget

func Budget(op, reason string) *Error

Budget reports a per-task budget exhaustion.

func Fatal

func Fatal(op string, wrapped error) *Error

Fatal reports a non-recoverable execution failure.

func NotFound

func NotFound(op, reason string) *Error

NotFound reports that a requested resource doesn't exist.

func Permission

func Permission(op, reason string) *Error

Permission reports that the operation isn't permitted.

func Stale

func Stale(op, reason string) *Error

Stale reports that the arguments were well-formed but missed their target because it changed since it was read. reason should name the stale anchor and tell the caller to re-read; the Kind steers guardrails toward "re-read the file" rather than "fix your input format".

func Transient

func Transient(op string, wrapped error) *Error

Transient reports a temporary failure. wrapped carries the inner cause (network error, lock contention) so retry policy can inspect it.

func Validation

func Validation(op, reason string) *Error

Validation reports a malformed argument. reason should name the offending field and what's wrong with it.

func (*Error) Error

func (e *Error) Error() string

Error formats as "[op:] kind[: reason][: wrapped]" — empty pieces are omitted so the message stays terse.

func (*Error) MarshalJSON

func (e *Error) MarshalJSON() ([]byte, error)

MarshalJSON renders an *Error as a stable, human-readable JSON object. Designed so a ToolResult containing an Err field can be snapshotted to wire or sqlite and recovered later with errors.Is and errors.AsType[*tools.Error] still working — the Kind / Op / Reason survive; the Wrapped chain flattens to its string form.

func (*Error) UnmarshalJSON

func (e *Error) UnmarshalJSON(data []byte) error

UnmarshalJSON reconstructs an *Error from the projected JSON shape. Wrapped becomes a sentinel-less errors.New(message); downstream errors.Is/AsType walks reach it but can't recover its original typed identity. Kind comes back via ParseKind so an unrecognised value fails loud rather than silently turning into Kinds.UNKNOWN; an empty/absent kind stays Kinds.UNKNOWN for legacy payloads that predate the field.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the inner cause for errors.Is / errors.AsType walks.

type Executor

type Executor interface {
	Execute(ctx context.Context, c ToolCall) (*ToolResult, error)
}

Executor dispatches a tool call. Implementations translate the call's name and arguments into a concrete tool invocation; transport details are theirs to own.

type FallbackCall

type FallbackCall struct {
	Name      ToolName
	Arguments ToolParameters
}

FallbackCall is a tool call extracted from text emitted by a model that didn't use the structured tool_calls field. Pair Name + Arguments with Registry.ParseCall (and a fresh call ID) to dispatch.

func ParseFromText

func ParseFromText(content string) ([]FallbackCall, string)

ParseFromText extracts tool calls a model emitted as plain text instead of in the native structured tool_calls field. Covers the common "almost-tool-call" shapes small models fall back to when chat-template wiring is imperfect:

  • <tool_call>{"name": ..., "arguments": ...}</tool_call>
  • Gemma-4: <|tool_call>call:NAME{key:val,...}<tool_call|>
  • ```json\n{"name": ..., "arguments": ...}\n```
  • a bare JSON object containing both "name" and "arguments" keys

The returned remaining string is the content with any matched tool-call fragments removed and whitespace trimmed — safe to surface to the user as the turn's textual reply when no native tool_calls were produced.

type FileEffect

type FileEffect struct {
	Path       string `json:"path,omitempty"`
	FromPath   string `json:"from_path,omitempty"`
	Op         FileOp `json:"op,omitempty"`
	BytesAfter int64  `json:"bytes_after,omitempty"`
}

FileEffect describes a filesystem action. Paths are workspace-relative.

type FileOp

type FileOp string

FileOp is the concrete filesystem operation a file effect represents.

const (
	FileRead   FileOp = "read"
	FileCreate FileOp = "create"
	FileModify FileOp = "modify"
	FileAppend FileOp = "append"
	FileDelete FileOp = "delete"
	FileRename FileOp = "rename"
)

The recognised file operations. Rename is the only one that populates FileEffect.FromPath alongside Path.

type InvalidationBumper

type InvalidationBumper interface {
	BumpVersion()
}

InvalidationBumper is whatever signals downstream caches (e.g. a Registry's spec cache) that descriptions have changed and their derived state is stale.

type Iterable

type Iterable interface {
	Tools(ctx context.Context) iter.Seq[Tool]
}

Iterable is the read side of a tool source: a cheap, read-only snapshot of the tools currently visible to a single runner iteration. Implementations must not perform I/O, block on work, mutate state, or hold a lock that an in-flight Execute call could need. The ctx carries request-scoped values such as task depth; use it only to decide visibility, not to trigger work.

type Kind

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

Kind is a type that represents a single enum value. It combines the core information about the enum constant and it's defined fields.

func KindOf

func KindOf(err error) Kind

KindOf walks err's chain and returns the first *Error's Kind, or Kinds.UNKNOWN when no *tools.Error is present. Use this when only the classification matters and the full struct doesn't.

func ParseKind

func ParseKind(input any) (Kind, error)

ParseKind parses the input value into an enum value. It returns the parsed enum value or an error if the input is invalid. It is a convenience function that can be used to parse enum values from various input types, such as strings, byte slices, or other enum types.

func (Kind) IsValid

func (k Kind) IsValid() bool

IsValid checks whether the Kinds value is valid. A valid value is one that is defined in the original enum and not marked as invalid.

func (Kind) MarshalBinary

func (k Kind) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface for Kind. It returns the binary representation of the enum value as a byte slice.

func (Kind) MarshalJSON

func (k Kind) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for Kind. It returns the JSON representation of the enum value as a byte slice.

func (Kind) MarshalText

func (k Kind) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface for Kind. It returns the string representation of the enum value as a byte slice

func (Kind) MarshalYAML

func (k Kind) MarshalYAML() ([]byte, error)

MarshalYAML implements the yaml.Marshaler interface for Kind. It returns the string representation of the enum value.

func (*Kind) Scan

func (k *Kind) Scan(value any) error

Scan implements the database/sql.Scanner interface for Kind. It parses the string representation of the enum value from the database row. It returns an error if the row does not contain a valid enum value.

func (Kind) String

func (k Kind) String() string

String implements the Stringer interface. It returns the canonical absolute name of the enum value.

func (*Kind) UnmarshalBinary

func (k *Kind) UnmarshalBinary(by []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface for Kind. It parses the binary representation of the enum value from the byte slice. It returns an error if the byte slice does not contain a valid enum value.

func (*Kind) UnmarshalJSON

func (k *Kind) UnmarshalJSON(by []byte) error

UnmarshalJSON implements the json.Unmarshaler interface for Kind. It parses the JSON representation of the enum value from the byte slice. It returns an error if the input is not a valid JSON representation.

func (*Kind) UnmarshalText

func (k *Kind) UnmarshalText(by []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface for Kind. It parses the string representation of the enum value from the byte slice. It returns an error if the byte slice does not contain a valid enum value.

func (*Kind) UnmarshalYAML

func (k *Kind) UnmarshalYAML(by []byte) error

UnmarshalYAML implements the yaml.Unmarshaler interface for Planet. It parses the byte slice representation of the enum value and returns an error if the YAML byte slice does not contain a valid enum value.

func (Kind) Value

func (k Kind) Value() (driver.Value, error)

Value implements the database/sql/driver.Valuer interface for Kind. It returns the string representation of the enum value.

type MemoryDescriptionStore

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

MemoryDescriptionStore is a thread-safe map of name→description overrides. Populated at startup via Load; mutated in place by admin writes which also trigger registered InvalidationBumpers so caches regenerate against the new text.

func NewMemoryDescriptionStore

func NewMemoryDescriptionStore() *MemoryDescriptionStore

NewMemoryDescriptionStore creates an empty store. Seed with Load.

func (*MemoryDescriptionStore) AddBumper

AddBumper registers a cache that must be invalidated on any change. Safe to call multiple times; bump order matches add order.

func (*MemoryDescriptionStore) Delete

func (s *MemoryDescriptionStore) Delete(name ToolName)

Delete removes an override; subsequent Description lookups return ok=false and the caller falls back to the code default. No-op (no bump) when the name has no entry to remove.

func (*MemoryDescriptionStore) Description

func (s *MemoryDescriptionStore) Description(name ToolName) (string, bool)

Description satisfies DescriptionStore.

func (*MemoryDescriptionStore) Load

func (s *MemoryDescriptionStore) Load(entries map[ToolName]string)

Load replaces the store contents with the given set — used at startup after a full repository read. Triggers one bump regardless of entry count (cheaper than one per row).

func (*MemoryDescriptionStore) Revision

func (s *MemoryDescriptionStore) Revision() int64

Revision returns a monotonically-increasing counter that bumps on any mutation. Useful for tests or observers detecting "did anything change since I last looked?" without holding the mutex or rescanning the map.

func (*MemoryDescriptionStore) Set

func (s *MemoryDescriptionStore) Set(name ToolName, description string)

Set records or replaces an override and notifies invalidation hooks.

type NestedToolCall added in v0.4.0

type NestedToolCall struct {
	ParentID ToolCallID
	ChildID  ToolCallID
	Sequence int
	Call     ToolCall
	Started  time.Time
}

NestedToolCall describes a child tool invocation performed inside a composite tool such as program. The child call is already being executed by the parent; observers use this only for progress/UI reporting.

type NestedToolObserver added in v0.4.0

type NestedToolObserver interface {
	OnNestedToolStarted(context.Context, NestedToolCall)
	OnNestedToolFinished(context.Context, NestedToolResult)
}

NestedToolObserver observes child tool calls made by composite tools. Methods must be non-blocking or quick; they run on the composite tool's execution path.

func NestedToolObserverFromContext added in v0.4.0

func NestedToolObserverFromContext(ctx context.Context) NestedToolObserver

NestedToolObserverFromContext returns the observer installed on ctx, if any.

type NestedToolResult added in v0.4.0

type NestedToolResult struct {
	NestedToolCall
	Result   *ToolResult
	Err      error
	Kind     Kind
	Error    string
	Duration time.Duration
}

NestedToolResult describes the terminal state of a child tool invocation performed inside a composite tool.

type OutputFormat

type OutputFormat string

OutputFormat selects how a tool result renders for the model: labelled plaintext (the default) or JSON. Both views come from the SAME structured data the tool already collected; only the rendering — the result's String() — differs. The labelled form is what small models read best and what the production registry defaults to; a caller opts into JSON per-call via the tool's `output` argument. Shared across tool packages so every tool's `output` argument means the same thing.

const (
	// OutputLabeled is the default: labelled plaintext (header line + indented
	// rows). It matches the shape ripgrep / IDE search / directory listings
	// use, which is what the model has the strongest training prior on, and is
	// cheaper in tokens than the JSON equivalent.
	OutputLabeled OutputFormat = "labeled"
	// OutputJSON renders the structured data as JSON — the machine-readable
	// shape a caller can parse without re-deriving fields from text.
	OutputJSON OutputFormat = "json"
)

func (OutputFormat) Resolve

func (f OutputFormat) Resolve() OutputFormat

Resolve returns the format with the empty/zero value defaulted to labelled.

type ProcessEffect

type ProcessEffect struct {
	Command          string `json:"command,omitempty"`
	ExitCode         int    `json:"exit_code,omitempty"`
	Background       bool   `json:"background,omitempty"`
	ProcessID        string `json:"process_id,omitempty"`
	PID              int    `json:"pid,omitempty"`
	TimedOut         bool   `json:"timed_out,omitempty"`
	OutputTruncated  bool   `json:"output_truncated,omitempty"`
	AutoBackgrounded bool   `json:"auto_backgrounded,omitempty"`
}

ProcessEffect describes a process launched by a tool. File effects caused by that process are intentionally out of scope for the first pass; callers can derive those separately with snapshot/diff wrappers when needed.

type Registry

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

Registry manages tool registration and lookup. Tools are addressed by ToolName. The Version field bumps on every mutation so consumers (e.g. spec caches) can invalidate their state lazily.

A Registry can also apply DescriptionStore overrides to its ToolSpecs output — operators edit tool descriptions live without code changes. The store is set via SetDescriptionStore; for cache invalidation, register the Registry as a bumper on the store via store.AddBumper(reg).

Tools may also be tagged with a "provider" — a string identifying where they came from (e.g. "obsidian" for tools discovered from an MCP server, "homeassistant" for tools synthesized from HA entities). Provider grouping lets profile systems whitelist all tools from a source without enumerating tool names individually.

func NewRegistry

func NewRegistry(tools ...Tool) *Registry

NewRegistry creates a registry. Pass tools to register them inline:

reg := tools.NewRegistry(&myTool{}, &otherTool{})

func (*Registry) BumpVersion

func (r *Registry) BumpVersion()

BumpVersion increments the version counter and invalidates the spec memo. Satisfies InvalidationBumper so a DescriptionStore can notify the Registry when overrides change.

func (*Registry) Execute

func (r *Registry) Execute(ctx context.Context, call ToolCall) (*ToolResult, error)

Execute dispatches a call to the registered tool. Returns an error if the tool isn't registered.

func (*Registry) Len

func (r *Registry) Len() int

Len returns the number of registered tools.

func (*Registry) Names

func (r *Registry) Names() []ToolName

Names returns every registered tool name.

func (*Registry) ParseCall

func (r *Registry) ParseCall(name ToolName, callID ToolCallID, arguments map[string]any) (ToolCall, error)

ParseCall builds a ToolCall envelope for the named tool. The arguments are not interpreted — concrete tools validate their own argument shape during Execute. Returns an error if the named tool isn't registered.

func (*Registry) ProviderFor

func (r *Registry) ProviderFor(name ToolName) string

ProviderFor returns the provider tag a tool was registered under, or "" if the tool is unregistered or has no provider.

func (*Registry) Register

func (r *Registry) Register(tool Tool) error

Register adds a tool to the registry. Tools are addressed by their Definition().Name; subsequent registrations under the same name replace. The tool has no provider tag.

func (*Registry) RegisterWithProvider

func (r *Registry) RegisterWithProvider(tool Tool, provider string) error

RegisterWithProvider adds a tool tagged with a provider name. Useful for tools discovered from a third-party source (MCP server, HA entity sync, etc.) so profiles can whitelist by provider rather than enumerating individual tool names.

func (*Registry) SetDescriptionStore

func (r *Registry) SetDescriptionStore(store DescriptionStore)

SetDescriptionStore installs a DescriptionStore the Registry consults when building ToolSpecs. Each spec's Description is overridden by the store's entry for that tool name when one exists.

func (*Registry) Tool

func (r *Registry) Tool(name ToolName) (Tool, bool)

Tool returns a tool by name, or false if not registered.

func (*Registry) ToolCountForProvider

func (r *Registry) ToolCountForProvider(provider string) int

ToolCountForProvider returns the number of tools tagged with the given provider.

func (*Registry) ToolSpecs

func (r *Registry) ToolSpecs() []ToolSpec

ToolSpecs returns the LLM specs for every registered tool, sorted by name, with DescriptionStore overrides applied — derived through the same path as Tools() so the two views cannot drift. The result is memoized until the next registration or description change.

func (*Registry) Tools

func (r *Registry) Tools(ctx context.Context) iter.Seq[Tool]

Tools returns every registered tool as an iter.Seq. The snapshot is taken under the read lock so callers can range over it without holding any registry locks themselves. The snapshot is sorted by tool name so the yield order is deterministic across calls and process restarts — the runner serialises this order straight into the request's tool list, and a stable order keeps the request's byte prefix identical turn-to-turn (DeepSeek / llama.cpp prefix caching only fires on an exact byte-prefix match; map iteration order would reshuffle the specs and miss the cache every turn).

func (*Registry) ToolsByProvider

func (r *Registry) ToolsByProvider(provider string) []Tool

ToolsByProvider returns every tool tagged with the given provider.

func (*Registry) Unregister

func (r *Registry) Unregister(name ToolName)

Unregister removes a tool by name.

func (*Registry) UnregisterProvider

func (r *Registry) UnregisterProvider(provider string)

UnregisterProvider removes every tool tagged with the given provider. No-op if no tools match.

func (*Registry) Version

func (r *Registry) Version() int

Version returns the monotonically-increasing version, bumped on mutation.

type RemoteTool

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

RemoteTool wraps an MCP-discovered tool as a Tool. The bridge keeps tools.Tool's interface clean — mcp.Client and mcp.ToolDef stay confined to this file; consumers register a *RemoteTool with a Registry and never see the protocol types.

Definition is set once at construction (from the MCP discover result) and Execute dispatches each call to the MCP server via the underlying client.

func NewRemoteTool

func NewRemoteTool(client *mcp.Client, def mcp.ToolDef) *RemoteTool

NewRemoteTool creates a Tool that dispatches to an MCP server. The MCP-discovered InputSchema is stored verbatim as ToolSpec.Parameters so MCP servers' rich schemas (anyOf, array items, minimum/maximum) reach the LLM without lossy round-tripping.

func (*RemoteTool) Definition

func (r *RemoteTool) Definition() ToolSpec

Definition returns the LLM-facing spec assembled from the MCP discover.

func (*RemoteTool) Execute

func (r *RemoteTool) Execute(ctx context.Context, call ToolCall) (*ToolResult, error)

Execute dispatches the call to the MCP server and returns the full multimodal content. ToolResult.Data carries `[]mcp.Content` — each element is one of TextContent, ImageContent, AudioContent, ResourceContent — so consumers preserve images, audio, and resource references rather than collapsing to first-text.

Errors are surfaced two ways depending on origin:

  • Transport / RPC failures: error return + Success=false.
  • Tool-reported failures (MCP IsError flag): error return + Success=false, with the error message extracted from the result's text content.

Both paths populate ToolResult.Error for consumers that ignore the error return.

type Source

type Source interface {
	Iterable
	Executor
}

Source is the canonical live tool source contract: enumerate visible tools and execute calls against them.

type Tool

type Tool interface {
	Definition() ToolSpec
	Execute(ctx context.Context, call ToolCall) (*ToolResult, error)
}

Tool is the canonical interface every tool implements. Two methods only:

  • Definition: how the tool describes itself to an LLM (name, params schema)
  • Execute: runs the tool against a Call and returns a Result

ParseLLMResponse / CreateToolCalls / per-tool plumbing methods that used to live on tool implementations have been moved to the Registry — concrete tools are pure execution logic.

func NewTyped added in v0.2.1

func NewTyped[Args any, Result any](spec ToolSpec, handler TypedHandler[Args, Result], opts ...TypedOption[Result]) Tool

NewTyped adapts typed tool business logic to the existing Tool interface. The adapter is intentionally a boundary: it decodes the raw ToolParameters map once, runs typed code, and returns a typed result payload. Existing registries, runners, guardrails, and providers continue to see a normal Tool.

func UnwrapDescriptionOverride

func UnwrapDescriptionOverride(t Tool) Tool

UnwrapDescriptionOverride returns the original tool if t is an override wrapper, else t itself. Admin surfaces use this to read a tool's code-default description without going through the override.

func WrapDescriptionOverrides

func WrapDescriptionOverrides(tools []Tool, store DescriptionStore) []Tool

WrapDescriptionOverrides returns a new slice where each tool is wrapped with a live-looking-up override wrapper. Use this for tool collections that aren't held by a Registry (e.g. a per-call slice assembled by a caller that doesn't own a Registry) — Registry has its own SetDescriptionStore for the in-registry case.

func WrapMCPTools

func WrapMCPTools(client *mcp.Client, defs []mcp.ToolDef) []Tool

WrapMCPTools wraps every MCP tool definition as a Tool ready for registration. Convenience for after a Client.Discover() call:

defs, err := client.Discover(ctx)
if err != nil { return err }
for _, t := range tools.WrapMCPTools(client, defs) {
    registry.Register(t)
}

type ToolCall

type ToolCall struct {
	ID        ToolCallID     `json:"id"`
	ToolName  ToolName       `json:"tool_name"`
	Arguments ToolParameters `json:"arguments"`
	Status    ToolCallStatus `json:"status"`
	CreatedAt time.Time      `json:"created_at"`
}

ToolCall is a structured tool invocation.

type ToolCallID added in v0.3.1

type ToolCallID string

ToolCallID identifies one model-requested tool invocation.

func (ToolCallID) String added in v0.3.1

func (id ToolCallID) String() string

String returns the string representation.

type ToolCallStatus

type ToolCallStatus string

ToolCallStatus is the lifecycle state of a tool call.

const (
	ToolCallStatusPending   ToolCallStatus = "pending"
	ToolCallStatusExecuting ToolCallStatus = "executing"
	ToolCallStatusCompleted ToolCallStatus = "completed"
	ToolCallStatusFailed    ToolCallStatus = "failed"
)

The tool-call lifecycle states, in order: a call is created Pending, marked Executing at dispatch, and ends Completed or Failed.

func (ToolCallStatus) String

func (s ToolCallStatus) String() string

String returns the string representation.

type ToolMetadata

type ToolMetadata map[string]any

ToolMetadata carries result metadata (timing, error info, cache state).

func NewToolMetadata

func NewToolMetadata(toolName ToolName) ToolMetadata

NewToolMetadata creates ToolMetadata pre-populated with the tool name.

func (ToolMetadata) SetCacheHit

func (tm ToolMetadata) SetCacheHit(hit bool) ToolMetadata

SetCacheHit records cache hit status.

func (ToolMetadata) SetError

func (tm ToolMetadata) SetError(err error) ToolMetadata

SetError records an error in the metadata.

func (ToolMetadata) SetExecutionTime

func (tm ToolMetadata) SetExecutionTime(duration time.Duration) ToolMetadata

SetExecutionTime records execution duration in the metadata.

func (ToolMetadata) SetToolInfo

func (tm ToolMetadata) SetToolInfo(toolName ToolName, expression string) ToolMetadata

SetToolInfo records the tool name and any expression context.

type ToolName

type ToolName string

ToolName is a typed identifier for a tool.

const (
	ToolNameWebSearch ToolName = "web_search"
	ToolNameWebFetch  ToolName = "web_fetch"
)

Common tool name constants used by default registries and examples.

func (ToolName) String

func (n ToolName) String() string

String returns the string representation.

type ToolParameters

type ToolParameters map[string]any

ToolParameters are raw model-provided arguments at the tool dispatch boundary. Prefer DecodeArgs or NewTyped in tool implementations so business logic receives a typed argument struct.

func (ToolParameters) Bool

func (tp ToolParameters) Bool(key string, defaultValue bool) bool

Bool returns the value at key as a bool, or defaultValue if missing.

func (ToolParameters) Float

func (tp ToolParameters) Float(key string, defaultValue float64) float64

Float returns the value at key as a float64, or defaultValue if missing or unconvertible. JSON numbers always decode as float64 through encoding/json so the type assertion catches the common case.

func (ToolParameters) Int

func (tp ToolParameters) Int(key string, defaultValue int) int

Int returns the value at key as an int, or defaultValue if missing or unconvertible.

func (ToolParameters) Map

func (tp ToolParameters) Map(key string) map[string]string

Map returns the value at key as a map[string]string. Values are stringified — strings pass through, anything else uses fmt.Sprint. Missing or wrong-shape entries return nil.

func (ToolParameters) Slice

func (tp ToolParameters) Slice(key string) []string

Slice returns the value at key as a []string. Each element is stringified — strings pass through, anything else uses fmt.Sprint. Missing or wrong-shape entries return nil so callers can treat them the same as the zero value.

func (ToolParameters) String

func (tp ToolParameters) String(key, defaultValue string) string

String returns the value at key as a string, or defaultValue if missing.

type ToolPreference

type ToolPreference struct {
	Tool       ToolName       `json:"tool"`
	Enabled    bool           `json:"enabled"`
	Weight     float64        `json:"weight"`
	Parameters ToolParameters `json:"parameters"`
	Reason     string         `json:"reason"`
}

ToolPreference captures hints about a tool — enabled, weight for selection, optional parameter overrides — used by upstream selectors.

type ToolResult

type ToolResult struct {
	ToolCallID ToolCallID `json:"tool_call_id,omitempty"`
	Success    bool       `json:"success"`
	Data       any        `json:"data,omitempty"`
	Error      string     `json:"error,omitempty"`
	// Err is the typed failure carrying Op / Reason / Wrapped, populated
	// by the failure helpers (failure in zkit/ai/tools/code,
	// failedFromError in zkit/agent/runner). Guardrails should switch on
	// Err when it's non-nil rather than substring-matching Error —
	// errors.AsType and errors.Is both work natively against this field.
	Err        *Error       `json:"err,omitempty"`
	Metadata   ToolMetadata `json:"metadata,omitempty"`
	Effects    []Effect     `json:"effects,omitempty"`
	ExecutedAt time.Time    `json:"executed_at"`
}

ToolResult is the outcome of executing a tool.

func Failure

func Failure(callID ToolCallID, err error) *ToolResult

Failure packages an error as a failed ToolResult. When err is (or wraps) a *Error, Kind and the typed Err field are populated structurally; a bare error leaves both at their zero values. The projection mirrors what the runner expects after dispatch, so every tool returns the same shape regardless of which error type the body produced.

func Success

func Success(callID ToolCallID, data any, effects ...Effect) *ToolResult

Success packages data as a successful ToolResult. It mirrors Failure so tools construct result envelopes consistently instead of open-coding timestamps and effect slices at each call site.

func (*ToolResult) AddEffect

func (r *ToolResult) AddEffect(e Effect)

AddEffect appends e to r. A nil receiver is ignored so callers can use it defensively around optional tool results.

func (*ToolResult) FileEffects

func (r *ToolResult) FileEffects() []FileEffect

FileEffects returns every file payload on r, preserving effect order. The returned slice is a copy and may be mutated by the caller.

func (*ToolResult) ProcessEffects

func (r *ToolResult) ProcessEffects() []ProcessEffect

ProcessEffects returns every process payload on r, preserving effect order. The returned slice is a copy and may be mutated by the caller.

type ToolSpec

type ToolSpec struct {
	Name        ToolName `json:"name"`
	Description string   `json:"description"`
	// Parameters is the tool's input JSON Schema. Typed tools derive it
	// from their Args struct via SchemaFor; tools whose schema comes from
	// an external source (e.g. an MCP server) ingest it via llm.SchemaFromMap,
	// whose Extra field preserves rich features (anyOf, array items,
	// minimum/maximum) so they reach the LLM without a lossy round-trip.
	// The zero Schema means the tool takes no arguments.
	Parameters llm.Schema `json:"parameters"`
	// Mutates declares that a successful call produces a durable FILE edit —
	// write / edit / write_append / apply_patch, or a meta-tool that rewrites
	// the registry. This is the narrow "would show up in a diff / counts as
	// work" signal: the completion gate's empty-patch guard and spawn's verify
	// mode gate on it. A shell tool like bash leaves this false — running a
	// build or test is not a file edit — and declares AffectsWorkspace instead.
	Mutates bool `json:"mutates,omitempty"`
	// WorkspaceAccess is the workspace capability required while the tool runs.
	// NONE means the tool does not access workspace state, READ permits concurrent
	// readers, and WRITE requires exclusive access. Legacy specs derive this
	// conservatively from ChangesWorkspace: changing tools require WRITE; others
	// default to NONE.
	WorkspaceAccess WorkspaceAccess `json:"workspace_access,omitempty"`
	// AffectsWorkspace declares that executing the tool can change durable
	// state by some means OTHER than a tracked file edit — the canonical case
	// is bash, whose command may write files, mutate git state, or touch the
	// environment. It is the broad "treat conservatively" signal: cache
	// invalidation, plan-first gating, and read-only explore blocking gate on
	// ChangesWorkspace (Mutates OR this), so a file edit need only set Mutates
	// and is still caught. Pure-read tools leave both false.
	AffectsWorkspace bool `json:"affects_workspace,omitempty"`
}

ToolSpec is the LLM-facing description of a tool. The Definition() method on Tool returns one.

func (ToolSpec) Access added in v0.11.0

func (s ToolSpec) Access() WorkspaceAccess

Access reports the workspace capability required by s. An explicit valid WorkspaceAccess takes precedence. Legacy specs retain conservative behavior: workspace-changing tools require WRITE, and tools without a workspace effect require NONE.

func (ToolSpec) ChangesWorkspace

func (s ToolSpec) ChangesWorkspace() bool

ChangesWorkspace reports whether a successful call could alter durable state by any means — a tracked file edit (Mutates) or a side effect like a shell command (AffectsWorkspace).

func (ToolSpec) MarshalJSON added in v0.11.0

func (s ToolSpec) MarshalJSON() ([]byte, error)

MarshalJSON omits an unspecified WorkspaceAccess while preserving explicit NONE. WorkspaceAccess is a generated struct enum, so omitempty cannot detect its invalid zero value without this transport boundary.

func (ToolSpec) ReadsWorkspace added in v0.11.0

func (s ToolSpec) ReadsWorkspace() bool

ReadsWorkspace reports whether s requires workspace read or write access.

func (ToolSpec) WritesWorkspace added in v0.11.0

func (s ToolSpec) WritesWorkspace() bool

WritesWorkspace reports whether s requires exclusive workspace write access.

type TypedHandler added in v0.2.1

type TypedHandler[Args any, Result any] func(context.Context, Args) (Result, error)

TypedHandler is the business logic for a typed tool. Args is decoded from the model-provided tool-call arguments, and Result is stored directly in the ToolResult data field. Use exported fields with json tags on Args and Result so the LLM-facing schema and the runtime decoder agree.

type TypedOption added in v0.2.1

type TypedOption[Result any] func(*typedOptions[Result])

TypedOption customizes a typed tool adapter for a specific Result type.

func WithTypedEffects added in v0.2.1

func WithTypedEffects[Result any](fn func(Result) []Effect) TypedOption[Result]

WithTypedEffects derives result effects from a typed result. This keeps the main handler typed while still letting file/process tools emit structured side-effect facts for guardrails and audit views.

type WorkspaceAccess added in v0.11.0

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

WorkspaceAccess is a type that represents a single enum value. It combines the core information about the enum constant and it's defined fields.

func ParseWorkspaceAccess added in v0.11.0

func ParseWorkspaceAccess(input any) (WorkspaceAccess, error)

ParseWorkspaceAccess parses the input value into an enum value. It returns the parsed enum value or an error if the input is invalid. It is a convenience function that can be used to parse enum values from various input types, such as strings, byte slices, or other enum types.

func (WorkspaceAccess) IsValid added in v0.11.0

func (w WorkspaceAccess) IsValid() bool

IsValid checks whether the WorkspaceAccesses value is valid. A valid value is one that is defined in the original enum and not marked as invalid.

func (WorkspaceAccess) MarshalBinary added in v0.11.0

func (w WorkspaceAccess) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface for WorkspaceAccess. It returns the binary representation of the enum value as a byte slice.

func (WorkspaceAccess) MarshalJSON added in v0.11.0

func (w WorkspaceAccess) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for WorkspaceAccess. It returns the JSON representation of the enum value as a byte slice.

func (WorkspaceAccess) MarshalText added in v0.11.0

func (w WorkspaceAccess) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface for WorkspaceAccess. It returns the string representation of the enum value as a byte slice

func (WorkspaceAccess) MarshalYAML added in v0.11.0

func (w WorkspaceAccess) MarshalYAML() ([]byte, error)

MarshalYAML implements the yaml.Marshaler interface for WorkspaceAccess. It returns the string representation of the enum value.

func (*WorkspaceAccess) Scan added in v0.11.0

func (w *WorkspaceAccess) Scan(value any) error

Scan implements the database/sql.Scanner interface for WorkspaceAccess. It parses the string representation of the enum value from the database row. It returns an error if the row does not contain a valid enum value.

func (WorkspaceAccess) String added in v0.11.0

func (w WorkspaceAccess) String() string

String implements the Stringer interface. It returns the canonical absolute name of the enum value.

func (*WorkspaceAccess) UnmarshalBinary added in v0.11.0

func (w *WorkspaceAccess) UnmarshalBinary(by []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface for WorkspaceAccess. It parses the binary representation of the enum value from the byte slice. It returns an error if the byte slice does not contain a valid enum value.

func (*WorkspaceAccess) UnmarshalJSON added in v0.11.0

func (w *WorkspaceAccess) UnmarshalJSON(by []byte) error

UnmarshalJSON implements the json.Unmarshaler interface for WorkspaceAccess. It parses the JSON representation of the enum value from the byte slice. It returns an error if the input is not a valid JSON representation.

func (*WorkspaceAccess) UnmarshalText added in v0.11.0

func (w *WorkspaceAccess) UnmarshalText(by []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface for WorkspaceAccess. It parses the string representation of the enum value from the byte slice. It returns an error if the byte slice does not contain a valid enum value.

func (*WorkspaceAccess) UnmarshalYAML added in v0.11.0

func (w *WorkspaceAccess) UnmarshalYAML(by []byte) error

UnmarshalYAML implements the yaml.Unmarshaler interface for Planet. It parses the byte slice representation of the enum value and returns an error if the YAML byte slice does not contain a valid enum value.

func (WorkspaceAccess) Value added in v0.11.0

func (w WorkspaceAccess) Value() (driver.Value, error)

Value implements the database/sql/driver.Valuer interface for WorkspaceAccess. It returns the string representation of the enum value.

type WorkspaceCoordinator added in v0.11.0

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

WorkspaceCoordinator coordinates access to one workspace. It never blocks: conflicting requests return ErrWorkspaceConflict immediately. The same owner may reenter any compatible access; a writer owner may also read while it holds the write lease.

func NewWorkspaceCoordinator added in v0.11.0

func NewWorkspaceCoordinator() *WorkspaceCoordinator

NewWorkspaceCoordinator creates an empty coordinator for one workspace.

func (*WorkspaceCoordinator) Acquire added in v0.11.0

Acquire tries to acquire access for owner without waiting. NONE produces a no-op lease. An empty owner is invalid because ownership is required for conflict detection and reentry.

type WorkspaceLease added in v0.11.0

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

WorkspaceLease represents workspace access held by one owner. Release is idempotent so deferred cleanup remains safe after nested control flow.

func (WorkspaceLease) Access added in v0.11.0

func (l WorkspaceLease) Access() WorkspaceAccess

Access returns the access capability held by l.

func (WorkspaceLease) Release added in v0.11.0

func (l WorkspaceLease) Release()

Release relinquishes one acquisition represented by l. It is safe to call more than once. A zero lease does nothing.

type WorkspaceOwner added in v0.11.0

type WorkspaceOwner string

WorkspaceOwner identifies the task that owns workspace access. It is an opaque value so coordination does not depend on runner or spawn packages.

Directories

Path Synopsis
Package code provides workspace-scoped tools for reading, writing, patching, searching, and executing commands.
Package code provides workspace-scoped tools for reading, writing, patching, searching, and executing commands.
Package computer exposes typed AI tools for the zkit computer-use model.
Package computer exposes typed AI tools for the zkit computer-use model.
Package dynamic provides the runtime substrate for self-extending agents: a BinaryTool that wraps a compiled CLI as a tools.Tool, a Catalog that persists registrations across restarts (via a pluggable Store — sqlite in production, JSON file in tests), and a Registrar that ties them to a tools.Registry.
Package dynamic provides the runtime substrate for self-extending agents: a BinaryTool that wraps a compiled CLI as a tools.Tool, a Catalog that persists registrations across restarts (via a pluggable Store — sqlite in production, JSON file in tests), and a Registrar that ties them to a tools.Registry.
Package fetch provides a web_fetch tool the agent can call to retrieve page content.
Package fetch provides a web_fetch tool the agent can call to retrieve page content.
Package search provides search-engine tools the agent can call.
Package search provides search-engine tools the agent can call.
Package toolkit makes authoring a dynamic tool a few lines of Go.
Package toolkit makes authoring a dynamic tool a few lines of Go.

Jump to

Keyboard shortcuts

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