toolset

package
v1.2.0-alpha.2 Latest Latest
Warning

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

Go to latest
Published: May 25, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package toolset is the catalog of every tool the agent can construct.

Responsibilities split cleanly across the project:

  • Each internal/tools/<family> package knows how to BUILD its tools and reports the names it owns via Names(). It does NOT know whether those tools are eagerly or lazily loaded — that policy lives one layer up.

  • toolset.Build is the single name → instance resolver. One switch lists every tool the agent supports; auditing the surface = reading this file.

  • toolset.ToolState holds per-agent shared state (e.g. *todo.TodoStore) so stateful tool families can be constructed with the right backing data. The agent constructs one ToolState per agent instance, so two agents built from the same profile get isolated state.

  • The agent (internal/agent) decides WHICH tools to build eagerly (ActiveTools — exposed every turn) vs which to mark as lazy-loadable (DeferredTools — materialized on demand when first invoked).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Build

func Build(names []tools.ToolName, s *ToolState) ([]tools.Tool, error)

Build resolves each name to a tool instance via the public default Registry. Stateful tools pull their backing state from s; stateless tools are package-level singletons.

Unknown names return an error — there is no silent fallback.

External hosts that need to register additional tools should call pkg/toolset.DefaultRegistry().Register at startup before agent construction.

func Describe

func Describe(name tools.ToolName) (tools.Descriptor, error)

Describe returns the metadata (tools.Descriptor) for a tool name without registering the tool with any agent. Internally this constructs a throwaway instance just long enough to read its static metadata fields — for stateful tools the short-lived backing state is immediately garbage-collected.

Use Build when you actually need to call Execute.

Types

type SignalSender

type SignalSender struct {
	// NotifyDaemon wakes the agent loop after any daemon Emits a signal
	// (lifecycle transition or stream event). No-arg by design: the
	// signal queue in DaemonState is the durable backstop, so the wake-up
	// only needs to fire the CAS+runLoop entry. Installed by agent.New;
	// nil-safe in tests.
	NotifyDaemon func()
	RootCtx      func() context.Context
	AgentID      func() string
}

SignalSender is the narrow callback bundle the agent installs on the ToolState in agent.New so background-task and monitor tools can deliver event-driven results without importing internal/agent (which would create an import cycle, since internal/agent imports internal/toolset). Each callback is set by SetSignalSender; the DaemonHost methods below invoke them.

type ToolState

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

ToolState carries the shared backing state for stateful tool families. Accessors are lazy — state is allocated only when the first tool that needs it is built. Pass the same *ToolState to every Build call for an agent so siblings within a family share state.

Cross-cutting consumers (TUI, session persistence) hold this through the agent (via agent.ToolState()) and read state through the typed accessors rather than peeking into tool internals.

func NewToolState

func NewToolState() *ToolState

func (*ToolState) AgentID

func (s *ToolState) AgentID() string

AgentID returns the spawning agent's id, used to label snapshot rows. Empty when no sender is installed.

func (*ToolState) Config

func (s *ToolState) Config() *config.Config

Config returns the runtime configuration this ToolState was bootstrapped with, or nil if the agent never installed one (tests, narrow harnesses). Tools that need settings (TavilyAPIKey, FetchMaxBytes, AppHome, etc.) read through this accessor instead of importing pkg/config directly.

func (*ToolState) DaemonState

func (s *ToolState) DaemonState() *daemon.DaemonState

DaemonState returns the agent's daemon catalog, allocating on first use. Registers with the unified change stream so TUI strips + the agent's KindStoreUpdate bridge see lifecycle transitions.

The notify closure indirects through s.signalSender at call time rather than snapshotting NotifyDaemon at construction. Daemon tools are built during toolset.Build (which lazy-allocates the catalog here) before agent.New gets to SetSignalSender — a direct snapshot would lock in a nil notify and silently swallow every wake-up. Reading dynamically matches the pattern used by RootCtx / AgentID below and tolerates any init order. Tests that build a ToolState without a sender still get a nil notify (wake-up no-ops, drain still works).

func (*ToolState) DeferredLookup

func (s *ToolState) DeferredLookup() meta.DeferredLookup

DeferredLookup returns the currently-installed lookup for TOOL_SEARCH, or nil if none. Used as the late-bound lookup passed into meta.NewToolSearch.

func (*ToolState) HasDaemonState

func (s *ToolState) HasDaemonState() bool

HasDaemonState reports whether a daemon catalog has been allocated. The agent loop uses this to short-circuit the drain when no daemon has ever been registered (avoids forcing the lazy allocation just to peek at an empty queue).

func (*ToolState) HasUserPromptQueue

func (s *ToolState) HasUserPromptQueue() bool

HasUserPromptQueue reports whether a UserPromptQueue has already been allocated. The agent loop uses this to skip the drain in runs that never had user input queued (avoids forcing the lazy allocation just to peek at an empty queue).

func (*ToolState) HasWakeupQueue

func (s *ToolState) HasWakeupQueue() bool

HasWakeupQueue reports whether a WakeupQueue has already been allocated. The agent loop uses this to skip the drain call in runs that never built the SCHEDULE_WAKEUP tool (avoids the lazy allocation just to peek at an empty queue).

func (*ToolState) LSPManager

func (s *ToolState) LSPManager() *lsp.Manager

LSPManager returns the LSP server manager, or nil if none is configured. The agent installs this during New by loading the LSP config file; when no config exists the manager remains nil and lsp_request returns a clean error at Execute time.

func (*ToolState) PlanController

func (s *ToolState) PlanController() mode.PlanModeController

PlanController returns the currently-installed plan-mode controller, or nil if none. The EnterPlanMode / ExitPlanMode tools read through this lookup at Execute time so the *Agent can register itself after agent.New returns without an init ordering hazard.

func (*ToolState) QuestionBroker

func (s *ToolState) QuestionBroker() question.Broker

QuestionBroker returns the question back-channel, or nil if not installed. The AskUserQuestion tool reads through this at Execute time.

func (*ToolState) ReadTracker

func (s *ToolState) ReadTracker() *fs.ReadTracker

ReadTracker returns the session read-tracker shared by all fs tools, allocating one on first use.

func (*ToolState) RegisterStore

func (s *ToolState) RegisterStore(store observable.Store)

RegisterStore plugs a TaskGroup into the unified change stream. ToolState subscribes to store and re-publishes every Change to its own observers. Lazy accessors below call this on first allocation, so callers that just use the typed accessors get registration for free.

func (*ToolState) RootCtx

func (s *ToolState) RootCtx() context.Context

RootCtx returns the agent-lifetime context the bg / monitor goroutines bind to. nil-safe when the agent hasn't installed a sender yet (tests).

func (*ToolState) SetConfig

func (s *ToolState) SetConfig(cfg *config.Config)

SetConfig installs the runtime configuration on this ToolState. Called by agent.New after the agent finishes constructing so tool factories don't have to thread cfg explicitly. Subagents inherit the parent's config by getting their own ToolState with the same pointer.

func (*ToolState) SetDeferredLookup

func (s *ToolState) SetDeferredLookup(d meta.DeferredLookup)

SetDeferredLookup installs the lookup TOOL_SEARCH will read from. The agent layer calls this exactly once (root agent only); the *Agent itself satisfies meta.DeferredLookup.

func (*ToolState) SetLSPManager

func (s *ToolState) SetLSPManager(m *lsp.Manager)

SetLSPManager installs the LSP Manager. Called once by the agent during construction after loading the LSP config.

func (*ToolState) SetPlanController

func (s *ToolState) SetPlanController(c mode.PlanModeController)

SetPlanController installs the controller EnterPlanMode / ExitPlanMode will mutate. The agent layer calls this exactly once after construction; only the root agent satisfies the interface (subagents inherit nil and the tools surface a clear error if invoked there).

func (*ToolState) SetQuestionBroker

func (s *ToolState) SetQuestionBroker(b question.Broker)

SetQuestionBroker installs the broker the AskUserQuestion tool will block on. The agent layer calls this once after construction via WithQuestionBroker.

func (*ToolState) SetSignalSender

func (s *ToolState) SetSignalSender(sender SignalSender)

SetSignalSender installs the signal-delivery callbacks. Called once per agent by agent.New; subagents get their own bundle (subagent bg results bubble up through the parent's sink, not the parent's signalCh — only root signals trigger idle-wake).

func (*ToolState) SetSkillRegistry

func (s *ToolState) SetSkillRegistry(r *skill.Registry)

SetSkillRegistry installs the skill catalog the SKILL tool will read from. The host (cmd/evva) calls this once at startup with the merged home+workdir registry. Subagents inherit the same pointer via agent.WithSkillRegistry.

func (*ToolState) SetSubagentSpawner

func (s *ToolState) SetSubagentSpawner(sp meta.SubagentSpawner)

SetSubagentSpawner installs the spawner the AGENT tool will delegate to. The agent layer calls this exactly once, after constructing its Agent struct, so the *Agent itself satisfies meta.SubagentSpawner.

func (*ToolState) SetWorktreeController

func (s *ToolState) SetWorktreeController(c mode.WorktreeController)

SetWorktreeController installs the controller EnterWorktree / ExitWorktree will read through. Called once after construction; only the root agent satisfies the interface (subagents leave the slot nil and the tools surface a clear error if invoked there).

func (*ToolState) SkillRegistry

func (s *ToolState) SkillRegistry() *skill.Registry

SkillRegistry returns the currently-installed skill catalog, or nil if none. The SKILL tool reads through this lookup at Execute time so the host can SetSkillRegistry any time before the model invokes it.

func (*ToolState) SubagentSpawner

func (s *ToolState) SubagentSpawner() meta.SubagentSpawner

SubagentSpawner returns the currently-installed spawner, or nil if none. Used as the lookup closure passed into meta.NewAgent at build time so the AGENT tool sees a late-bound spawner without an init ordering hazard.

func (*ToolState) Subscribe

func (s *ToolState) Subscribe(fn observable.Observer)

Subscribe registers an observer that receives every Change from every registered TaskGroup. The agent uses this to bridge into its event sink.

func (*ToolState) TodoStore

func (s *ToolState) TodoStore() *todo.TodoStore

TodoStore returns the todo subsystem's backing store, allocating one on first use. The todo_write tool constructed against the same ToolState shares it. First-use also registers the store on the change stream so the agent's event bridge picks up every todo mutation without per-store wiring.

func (*ToolState) UserPromptQueue

func (s *ToolState) UserPromptQueue() *UserPromptQueue

UserPromptQueue returns the side-channel for prompts the user typed while a Run was in flight, allocating one on first use. The UI's submit handler Enqueue's; the agent loop's drainUserPrompts pulls every entry and folds them into the session as fresh RoleUser messages between iterations.

func (*ToolState) WakeupQueue

func (s *ToolState) WakeupQueue() *meta.WakeupQueue

WakeupQueue returns the SCHEDULE_WAKEUP side-channel, allocating one on first use. WakeupTool Enqueue's the prompt here when its sleep finishes; the agent loop Drain's the queue at the top of every iteration and appends each entry as a RoleUser message.

func (*ToolState) Workdir

func (s *ToolState) Workdir() string

Workdir returns the configured workdir, or "" if no Config is installed. Together with Config() this satisfies the pkg/tools.State interface so downstream tool factories can read config + workdir without reaching for internal accessors.

func (*ToolState) WorktreeController

func (s *ToolState) WorktreeController() mode.WorktreeController

WorktreeController returns the currently-installed worktree controller, or nil if none. The EnterWorktree / ExitWorktree tools read through this lookup at Execute time so the *Agent can install itself after agent.New returns without an init ordering hazard.

type UserPromptQueue

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

UserPromptQueue is the bridge that lets the UI hand the agent a fresh user message WITHOUT starting a new Run. The agent loop drains the queue at the top of every iteration and folds each entry into the session as a RoleUser message — same pattern as drainAsyncSubagents / drainWakeupPrompts.

Why a side-channel and not just another Run: while a Run is in flight the previous assistant turn's tool_calls may not yet be answered. A second Run that appended RoleUser there would orphan the tool_calls and every provider would 400 (the bug we fixed earlier in this branch). The queue defers the append to a safe point — between iterations, after the previous turn's RoleTool message has landed — so the conversation stays well-formed.

Subagents do not drain this queue; the user has no view into the subagent's loop, so enqueuing there would be invisible. Each agent has its own ToolState (and therefore its own queue), so subagent queues simply stay empty.

func NewUserPromptQueue

func NewUserPromptQueue() *UserPromptQueue

NewUserPromptQueue returns a fresh, empty queue.

func (*UserPromptQueue) Drain

func (q *UserPromptQueue) Drain() []string

Drain returns every queued prompt and clears the queue. Returns nil (not an empty slice) when nothing is queued so callers can short-circuit with a single nil-check.

func (*UserPromptQueue) Enqueue

func (q *UserPromptQueue) Enqueue(prompt string)

Enqueue appends a prompt to be delivered on the next loop iteration. Empty / whitespace-only prompts are silently dropped — they'd produce a useless empty RoleUser turn.

func (*UserPromptQueue) Len

func (q *UserPromptQueue) Len() int

Len reports the number of pending prompts without draining. UIs use this to badge a "+N queued" indicator on the status bar without consuming the queue.

Jump to

Keyboard shortcuts

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