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 ¶
- Variables
- func CallSignature(call ToolCall) string
- func ContextWithNestedToolObserver(ctx context.Context, obs NestedToolObserver) context.Context
- func DataAs[T any](r *ToolResult) (T, bool)
- func DecodeArgs[T any](params ToolParameters) (T, error)
- func ExhaustiveKinds(f func(Kind))
- func ExhaustiveWorkspaceAccesses(f func(WorkspaceAccess))
- func RedactSecrets(s string) string
- func SchemaFor[T any]() llm.Schema
- func ValidateToolSpec(spec ToolSpec) error
- type DescriptionStore
- type Effect
- type EffectKind
- type Error
- type Executor
- type FallbackCall
- type FileEffect
- type FileOp
- type InvalidationBumper
- type Iterable
- type Kind
- func (k Kind) IsValid() bool
- func (k Kind) MarshalBinary() ([]byte, error)
- func (k Kind) MarshalJSON() ([]byte, error)
- func (k Kind) MarshalText() ([]byte, error)
- func (k Kind) MarshalYAML() ([]byte, error)
- func (k *Kind) Scan(value any) error
- func (k Kind) String() string
- func (k *Kind) UnmarshalBinary(by []byte) error
- func (k *Kind) UnmarshalJSON(by []byte) error
- func (k *Kind) UnmarshalText(by []byte) error
- func (k *Kind) UnmarshalYAML(by []byte) error
- func (k Kind) Value() (driver.Value, error)
- type MemoryDescriptionStore
- func (s *MemoryDescriptionStore) AddBumper(b InvalidationBumper)
- func (s *MemoryDescriptionStore) Delete(name ToolName)
- func (s *MemoryDescriptionStore) Description(name ToolName) (string, bool)
- func (s *MemoryDescriptionStore) Load(entries map[ToolName]string)
- func (s *MemoryDescriptionStore) Revision() int64
- func (s *MemoryDescriptionStore) Set(name ToolName, description string)
- type NestedToolCall
- type NestedToolObserver
- type NestedToolResult
- type OutputFormat
- type ProcessEffect
- type Registry
- func (r *Registry) BumpVersion()
- func (r *Registry) Execute(ctx context.Context, call ToolCall) (*ToolResult, error)
- func (r *Registry) Len() int
- func (r *Registry) Names() []ToolName
- func (r *Registry) ParseCall(name ToolName, callID ToolCallID, arguments map[string]any) (ToolCall, error)
- func (r *Registry) ProviderFor(name ToolName) string
- func (r *Registry) Register(tool Tool) error
- func (r *Registry) RegisterWithProvider(tool Tool, provider string) error
- func (r *Registry) SetDescriptionStore(store DescriptionStore)
- func (r *Registry) Tool(name ToolName) (Tool, bool)
- func (r *Registry) ToolCountForProvider(provider string) int
- func (r *Registry) ToolSpecs() []ToolSpec
- func (r *Registry) Tools(ctx context.Context) iter.Seq[Tool]
- func (r *Registry) ToolsByProvider(provider string) []Tool
- func (r *Registry) Unregister(name ToolName)
- func (r *Registry) UnregisterProvider(provider string)
- func (r *Registry) Version() int
- type RemoteTool
- type Source
- type Tool
- func NewTyped[Args any, Result any](spec ToolSpec, handler TypedHandler[Args, Result], opts ...TypedOption[Result]) Tool
- func UnwrapDescriptionOverride(t Tool) Tool
- func WrapDescriptionOverrides(tools []Tool, store DescriptionStore) []Tool
- func WrapMCPTools(client *mcp.Client, defs []mcp.ToolDef) []Tool
- type ToolCall
- type ToolCallID
- type ToolCallStatus
- type ToolMetadata
- type ToolName
- type ToolParameters
- func (tp ToolParameters) Bool(key string, defaultValue bool) bool
- func (tp ToolParameters) Float(key string, defaultValue float64) float64
- func (tp ToolParameters) Int(key string, defaultValue int) int
- func (tp ToolParameters) Map(key string) map[string]string
- func (tp ToolParameters) Slice(key string) []string
- func (tp ToolParameters) String(key, defaultValue string) string
- type ToolPreference
- type ToolResult
- type ToolSpec
- type TypedHandler
- type TypedOption
- type WorkspaceAccess
- func (w WorkspaceAccess) IsValid() bool
- func (w WorkspaceAccess) MarshalBinary() ([]byte, error)
- func (w WorkspaceAccess) MarshalJSON() ([]byte, error)
- func (w WorkspaceAccess) MarshalText() ([]byte, error)
- func (w WorkspaceAccess) MarshalYAML() ([]byte, error)
- func (w *WorkspaceAccess) Scan(value any) error
- func (w WorkspaceAccess) String() string
- func (w *WorkspaceAccess) UnmarshalBinary(by []byte) error
- func (w *WorkspaceAccess) UnmarshalJSON(by []byte) error
- func (w *WorkspaceAccess) UnmarshalText(by []byte) error
- func (w *WorkspaceAccess) UnmarshalYAML(by []byte) error
- func (w WorkspaceAccess) Value() (driver.Value, error)
- type WorkspaceCoordinator
- type WorkspaceLease
- type WorkspaceOwner
Constants ¶
This section is empty.
Variables ¶
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.
var ErrParseKind = errors.New("invalid input provided to parse to Kind")
var ErrParseWorkspaceAccess = errors.New("invalid input provided to parse to WorkspaceAccess")
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.
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.
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 ¶
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 ¶
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 ¶
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
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 ¶
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 ¶
NewFileEffect returns a file effect for op/path.
func NewProcessEffect ¶
NewProcessEffect returns a process effect for command/exitCode.
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 Permission ¶
Permission reports that the operation isn't permitted.
func Stale ¶
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 ¶
Transient reports a temporary failure. wrapped carries the inner cause (network error, lock contention) so retry policy can inspect it.
func Validation ¶
Validation reports a malformed argument. reason should name the offending field and what's wrong with it.
func (*Error) Error ¶
Error formats as "[op:] kind[: reason][: wrapped]" — empty pieces are omitted so the message stays terse.
func (*Error) MarshalJSON ¶
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 ¶
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.
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.
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
MarshalBinary implements the encoding.BinaryMarshaler interface for Kind. It returns the binary representation of the enum value as a byte slice.
func (Kind) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Kind. It returns the JSON representation of the enum value as a byte slice.
func (Kind) MarshalText ¶
MarshalText implements the encoding.TextMarshaler interface for Kind. It returns the string representation of the enum value as a byte slice
func (Kind) MarshalYAML ¶
MarshalYAML implements the yaml.Marshaler interface for Kind. It returns the string representation of the enum value.
func (*Kind) Scan ¶
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 ¶
String implements the Stringer interface. It returns the canonical absolute name of the enum value.
func (*Kind) UnmarshalBinary ¶
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 ¶
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 ¶
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 ¶
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.
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 ¶
func (s *MemoryDescriptionStore) AddBumper(b InvalidationBumper)
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 ¶
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 ¶
Execute dispatches a call to the registered tool. Returns an error if the tool isn't registered.
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 ¶
ProviderFor returns the provider tag a tool was registered under, or "" if the tool is unregistered or has no provider.
func (*Registry) Register ¶
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 ¶
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) ToolCountForProvider ¶
ToolCountForProvider returns the number of tools tagged with the given provider.
func (*Registry) ToolSpecs ¶
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 ¶
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 ¶
ToolsByProvider returns every tool tagged with the given provider.
func (*Registry) Unregister ¶
Unregister removes a tool by name.
func (*Registry) UnregisterProvider ¶
UnregisterProvider removes every tool tagged with the given provider. No-op if no tools match.
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 ¶
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 ¶
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 ¶
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 ¶
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.
Common tool name constants used by default registries and examples.
type ToolParameters ¶
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 ¶
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
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
ReadsWorkspace reports whether s requires workspace read or write access.
func (ToolSpec) WritesWorkspace ¶ added in v0.11.0
WritesWorkspace reports whether s requires exclusive workspace write access.
type TypedHandler ¶ added in v0.2.1
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.
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
func (c *WorkspaceCoordinator) Acquire(owner WorkspaceOwner, access WorkspaceAccess) (WorkspaceLease, error)
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.
Source Files
¶
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. |