Documentation
¶
Overview ¶
Package meta hosts agent-meta tools: Agent (spawn sub-agent), ToolSearch (load deferred-tool schemas), and ScheduleWakeup (self-pace /loop iterations). Each needs an agent-side hook supplied via constructor injection from the toolset Builders.
SKILL lives in its own package (pkg/skill) because it owns a registry of user-installed skill files; co-locating it there keeps the loader and the tool adjacent. The package is public so downstream SDK consumers can register programmatic skills via skill.NewRegistry + Add.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrSubagentForbidden = errors.New("meta: subagents cannot spawn subagents")
ErrSubagentForbidden is returned by Spawn when the calling agent is itself a subagent — only the root agent may spawn subagents. The AGENT tool surfaces this as a recoverable Result.IsError so the model can adjust its plan instead of aborting the loop.
Functions ¶
Types ¶
type AgentTool ¶
type AgentTool struct {
// contains filtered or unexported fields
}
AgentTool is the LLM-facing handle for spawning subagents. The actual work is delegated to a SubagentSpawner installed by the agent layer. The subagent's lifecycle (init, phase updates, terminal report) is surfaced through the parent's DaemonState — see internal/agent/agent_daemon.go.
func NewAgent ¶
func NewAgent(lookup SpawnerLookup) *AgentTool
NewAgent constructs an AgentTool that reads its spawner via lookup at Execute time. lookup may be nil (yields a clear runtime error if the model invokes the tool); it may also return nil (same outcome).
func (*AgentTool) Description ¶
func (*AgentTool) Schema ¶
func (t *AgentTool) Schema() json.RawMessage
type DeferredLookup ¶
type DeferredLookup interface {
// DeferredNames returns the tool names the profile allows the model to
// lazy-load. Order is implementation-defined and may vary between calls
// (e.g. a map iteration); TOOL_SEARCH sorts what it returns to the model.
DeferredNames() []tools.ToolName
// Describe returns the LLM-facing metadata for a deferred tool name.
// Returns an error if the name is unknown to the underlying catalog.
Describe(name tools.ToolName) (tools.Descriptor, error)
}
DeferredLookup is the agent-layer dependency the TOOL_SEARCH tool reads to enumerate and describe the deferred tools the current profile permits.
Like SubagentSpawner, this interface lives in meta so the agent layer can implement it without causing the cycle that would arise from meta importing agent. ToolSearchTool resolves the lookup at Execute time (late binding) so the agent can install itself after construction.
type DeferredLookupFn ¶
type DeferredLookupFn func() DeferredLookup
DeferredLookupFn is the late-binding shape NewToolSearch accepts; pass a method value bound to whatever owns the lookup (typically toolset.ToolState).
type SpawnRequest ¶
type SpawnRequest struct {
Name string
// Kind selects a preset profile: "explore" or "general-purpose".
// Empty/unknown values are the spawner's responsibility (return an error).
Kind string
// 3~5 words desc
Desc string
// Prompt is the task the subagent should accomplish. Must be non-empty.
Prompt string
// Level selects the model tier within the parent's provider:
// 1 = small (smaller model — Sonnet, DeepSeek-Flash, ...).
// 2 = medium (larger model — Opus, DeepSeek-Pro, ...).
// 3 = Large (larger model — Opus + hard effort, DeepSeek-Pro + hard effort, ...).
// Zero defaults to 1; out-of-range values clamp via
// constant.LLMProvider.ModelForLevel.
Level int
AsyncMode bool // default = false
// Isolation selects a filesystem-isolation strategy for the spawned
// subagent. Empty (the default) inherits the parent's workdir.
// "worktree" provisions a git worktree under
// `<repo>/.evva/worktrees/<slug>/` on a fresh branch and configures
// the child to run there — its filesystem mutations stay off the
// host workdir. The post-spawn cleanup auto-removes the worktree
// when the child made no changes; otherwise the path and branch
// are surfaced back to the parent so the user can inspect.
Isolation string
}
SpawnRequest is the parsed AGENT-tool input the spawner needs to actually run a subagent. Passed as a struct so future fields (model overrides, timeout, isolation mode) don't churn the SubagentSpawner interface.
type SpawnerLookup ¶
type SpawnerLookup func() SubagentSpawner
SpawnerLookup is the function shape a ToolState method (or any closure) satisfies to provide late-bound access to a SubagentSpawner. AgentTool keeps the lookup, not the spawner, so the order in which the agent and the tool are constructed doesn't matter — the spawner can be installed after the tool already exists.
type SubagentSpawner ¶
type SubagentSpawner interface {
// Spawn creates a subagent per the request, runs it, and returns the
// child's final assistant text on success.
Spawn(ctx context.Context, req SpawnRequest) (string, error)
// SubagentTypes returns the sorted list of agent names that may appear
// as the AGENT tool's subagent_type value. The agent layer typically
// returns AgentRegistry.ListSubagent().Name. Empty / nil falls back to
// the built-in pair so degenerate setups (no registry, tests with a
// stub spawner) still produce a valid schema.
SubagentTypes() []string
}
SubagentSpawner is the agent-layer dependency that the AGENT tool calls to construct and run a child agent.
The interface lives in meta (not in the agent package) so the agent package can implement it without causing the import cycle that would otherwise arise from meta importing agent. AgentTool reads the spawner through a late-binding lookup (see NewAgent) so the agent can install itself as the spawner after its own struct exists.
type ToolSearchTool ¶
type ToolSearchTool struct {
// contains filtered or unexported fields
}
ToolSearchTool exposes deferred-tool metadata to the model.
Deferred tools appear by name in the system prompt; their full JSON Schemas are pre-injected into the same prompt at session start (see sysprompt.mainDeferredToolsSection). TOOL_SEARCH itself returns a compact JSON envelope — the model has already seen the schemas and uses TOOL_SEARCH for discovery / "is this one available", not schema fetching.
This output shape mirrors ref/src/tools/ToolSearchTool/ToolSearchTool.ts. The divergence from ref: ref pre-injects only names and uses Anthropic tool_reference blocks to expand schemas on the wire; evva pre-injects full schemas because not every provider supports tool_reference expansion.
func NewToolSearch ¶
func NewToolSearch(lookup DeferredLookupFn) *ToolSearchTool
NewToolSearch constructs a ToolSearchTool that reads its lookup at Execute time. lookup may be nil (yields a clear runtime error); it may also return nil (same outcome).
func (*ToolSearchTool) Description ¶
func (t *ToolSearchTool) Description() string
func (*ToolSearchTool) Execute ¶
func (t *ToolSearchTool) Execute(_ context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)
func (*ToolSearchTool) Name ¶
func (t *ToolSearchTool) Name() string
func (*ToolSearchTool) Schema ¶
func (t *ToolSearchTool) Schema() json.RawMessage
type WakeupQueue ¶
type WakeupQueue struct {
// contains filtered or unexported fields
}
WakeupQueue is the side-channel between WakeupTool and the agent loop.
The tool sleeps for the requested delay, then Enqueue's its prompt; the loop calls Drain at the top of each iteration and appends every drained entry as a fresh RoleUser message before the next LLM call. Same pattern as drainAsyncSubagents — the model sees the prompt as if the user just typed it.
func NewWakeupQueue ¶
func NewWakeupQueue() *WakeupQueue
NewWakeupQueue returns a fresh, empty queue.
func (*WakeupQueue) Drain ¶
func (q *WakeupQueue) 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 (*WakeupQueue) Enqueue ¶
func (q *WakeupQueue) Enqueue(prompt string)
Enqueue appends a prompt to be delivered on the next loop iteration. Empty prompts are silently dropped — the tool validates non-empty at the public boundary, so a zero-arg push here would be a bug.
type WakeupTool ¶
type WakeupTool struct {
// contains filtered or unexported fields
}
WakeupTool implements SCHEDULE_WAKEUP. Execute blocks for delaySeconds (cancellable via ctx) and then enqueues prompt for delivery as a fresh user message on the next loop iteration.
The tool exists so the model can self-pace polling loops — e.g. "spawn async subagents, wakeup in 60s, then check on them". When the wakeup fires the queued prompt re-enters the conversation as if the user just typed it, giving the model a clean re-entry point.
func NewWakeup ¶
func NewWakeup(queue *WakeupQueue) *WakeupTool
NewWakeup constructs a WakeupTool bound to the given queue. The queue must be the same instance the agent loop drains from — typically obtained via ToolState.WakeupQueue().
func (*WakeupTool) Description ¶
func (t *WakeupTool) Description() string
func (*WakeupTool) Execute ¶
func (t *WakeupTool) Execute(ctx context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)
func (*WakeupTool) Name ¶
func (t *WakeupTool) Name() string
func (*WakeupTool) Schema ¶
func (t *WakeupTool) Schema() json.RawMessage