subagent

package
v1.8.0 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package subagent runs specialized agents with isolated contexts. Runner is the typed host-facing API. Tool adapts a Runner for model-driven delegation, including parallel, chain, background, and team execution.

Index

Constants

View Source
const (
	ModeSingle     = "single"
	ModeParallel   = "parallel"
	ModeChain      = "chain"
	ModeBackground = "background"
)

Run modes — the authoritative set of values for RunMeta.Mode. Observers should compare against these rather than string literals.

Variables

View Source
var ErrUnknownAgent = errors.New("unknown agent")

ErrUnknownAgent is the sentinel for "agent name not registered with this Runner". Match with errors.Is; use errors.As with *UnknownAgentError to read the requested name and the available set. A host scheduler branches on this to classify the failure as deterministic (retrying the same run cannot succeed) without matching error strings.

Functions

This section is empty.

Types

type Config

type Config struct {
	Name        string
	Description string
	// Model is resolved when each sub-agent run starts. Wrappers that swap the
	// underlying model at runtime (e.g. agentcore.SwappableModel) take effect
	// on the next sub-agent run.
	Model        agentcore.ChatModel
	SystemPrompt string
	// SystemPromptMode is a host-interpreted hint controlling how
	// SystemPrompt composes with the host's base prompt. agentcore does
	// NOT consume this field — the team spawner / executor on the host
	// side reads it to assemble AgentContext.SystemBlocks. Kept as a
	// plain string at the boundary so agentcore stays agnostic to enum
	// values that only matter inside one host; empty / unrecognized
	// values fall back to the host's default mode.
	SystemPromptMode string
	Tools            []agentcore.Tool
	MaxTurns         int

	// ToolGate, when non-nil, is called once per tool call in this
	// sub-agent's runs — same contract as agentcore.WithToolGate for
	// top-level agents (nil decision = allow, UpdatedArgs rewrites the
	// executed arguments). Without it a sub-agent's tools run ungated, so
	// a harness with a permission system should thread its gate here.
	ToolGate agentcore.ToolGate

	// Middlewares wrap each tool execution in this sub-agent's runs
	// (outermost first). Mirrors agentcore.WithMiddlewares.
	Middlewares []agentcore.ToolMiddleware

	// ThinkingLevel sets the reasoning depth for this sub-agent's runs.
	// Empty ("") leaves it unspecified (model/provider default). Mirrors
	// agentcore.WithThinkingLevel for top-level agents. A runtime override
	// installed via Runner.SetThinkingLevel takes precedence over this baseline.
	ThinkingLevel agentcore.ThinkingLevel

	// MaxRetries caps the LLM call retry count for retryable errors within
	// this sub-agent's loop. 0 (default) disables retry entirely.
	MaxRetries int

	// StopAfterTools lists tool names that trigger early loop exit after
	// successful execution.
	StopAfterTools []string

	// StopAfterToolResult is the result-aware variant of StopAfterTools.
	StopAfterToolResult func(toolName string, result json.RawMessage) bool

	// OnMessage, if non-nil, is called after each message is appended to
	// context. The agentName and task are provided for session routing.
	OnMessage func(agentName, task string, msg agentcore.AgentMessage)

	// Optional context lifecycle hooks for long-running sub-agents.
	ContextManager        agentcore.ContextManager
	ContextManagerFactory func(model agentcore.ChatModel) agentcore.ContextManager
	ConvertToLLM          func(msgs []agentcore.AgentMessage) []agentcore.Message

	// CacheLastMessage, when non-empty, tags the last non-system message of
	// every LLM request in this sub-agent's loop with the given cache_control
	// value ("ephemeral", or "ephemeral:1h" for extended TTL). Mirrors
	// agentcore.WithCacheLastMessage for top-level agents; see that option
	// for placement semantics.
	CacheLastMessage string

	// PromptCacheKey is the base prompt-cache routing identity for this
	// sub-agent's LLM requests. Each spawn appends "#<seq>" so every run gets
	// its own cache lineage — one conversation, one key — which providers
	// with key-routed prefix caching (OpenAI prompt_cache_key) use to keep a
	// session's requests on the same cache shard. Empty sends no hint.
	PromptCacheKey string

	// StopGuardFactory, if non-nil, creates a fresh StopGuard for each run.
	StopGuardFactory func(agentName, task string) agentcore.StopGuard
}

Config defines a sub-agent's identity and capabilities.

type RunMeta added in v1.7.0

type RunMeta struct {
	Agent      string
	InstanceID string
	Mode       string
}

RunMeta identifies one sub-agent run for an external event observer. It lets a harness route a run's raw AgentLoop events to a per-run sink (e.g. a live-preview transcript) without the subagent tool knowing anything about that sink.

  • Agent: the agent definition/type name (e.g. "explore"). Not unique when the same type runs more than once concurrently (parallel mode).
  • InstanceID: unique per run invocation within this Runner's lifetime. Use this — not Agent — as the routing key.
  • Mode: one of the Mode* constants below.

type RunResult added in v1.7.10

type RunResult struct {
	// Agent is the registered agent definition that ran.
	Agent string

	// Output is the final assistant text. Unlike the LLM tool-call surface,
	// it is NOT concatenated with TerminalResult and has no "(no output)"
	// placeholder — an agent that only called tools yields "".
	Output string

	// TerminalResult is the successful result of the tool that triggered a
	// StopAfterTools / StopAfterToolResult exit, nil when the run ended some
	// other way.
	TerminalResult json.RawMessage

	// Usage carries aggregated counters. Populated on both success and
	// failure paths (a run that errors mid-way still consumed tokens);
	// zero-valued when the run never started (e.g. unknown agent).
	Usage Usage
}

RunResult is the typed outcome of one sub-agent run.

type Runner added in v1.7.13

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

Runner executes registered agents through AgentLoop. It owns only agent definitions and per-run behavior; model-facing JSON and background/team orchestration live in Tool.

func NewRunner added in v1.7.13

func NewRunner(agents ...Config) *Runner

NewRunner creates a Runner from the supplied agent definitions. It panics when an agent name is empty or duplicated because the registry is static program configuration and either condition is a programming error.

func (*Runner) AgentConfig added in v1.7.13

func (r *Runner) AgentConfig(name string) (Config, bool)

AgentConfig returns the registered sub-agent definition for name, or (zero, false) if none is registered. Exposed read-only so a harness can rebuild a TeamSpawnRequest when resuming a teammate by its agent type without re-deriving the config from scratch.

func (*Runner) AsTool added in v1.7.13

func (r *Runner) AsTool() *Tool

AsTool exposes model-driven delegation backed by this Runner.

func (*Runner) Run added in v1.7.13

func (r *Runner) Run(ctx context.Context, agent, task string) (RunResult, error)

Run executes one registered sub-agent programmatically. Inputs and outputs are typed, and failures are Go errors carrying the loop's full chain (errors.Is(err, ErrUnknownAgent) for lookup failures, agentcore.ErrStopGuard / ErrMaxTurns / provider sentinels for loop failures — see agentcore.ErrorKind for the stable taxonomy).

Everything configured on the agent's Config applies exactly as in the tool-call path: StopGuard, StopAfterTools, OnMessage, context management, prompt-cache keys. Progress reporting via agentcore.WithToolProgress on ctx works identically.

func (*Runner) SetEventObserver added in v1.7.13

func (r *Runner) SetEventObserver(fn func(meta RunMeta, ev agentcore.Event))

SetEventObserver installs a callback that receives every raw AgentLoop event produced by any sub-agent run (single/parallel/chain/background), tagged with a RunMeta carrying a unique per-run InstanceID. A harness uses this to drive a live preview of sub-agent work — symmetric to how a teammate executor fans its loop events out. nil (the default) disables observation with zero cost.

The callback MUST be non-blocking: it runs inline on the sub-agent's execution goroutine (and on parallel/background goroutines concurrently), so a slow observer stalls the run. Sinks that may block should buffer + drop.

func (*Runner) SetThinkingLevel added in v1.7.13

func (r *Runner) SetThinkingLevel(agentName string, level agentcore.ThinkingLevel)

SetThinkingLevel overrides a sub-agent's reasoning depth at runtime, keyed by agent name. It takes effect on the next run of that agent (mirroring how a SwappableModel swap takes effect on the next run) and overrides the agent's Config.ThinkingLevel baseline. Safe to call concurrently with running agents: the override lives in an isolated map and never mutates the immutable agents config map. Empty level ("") means model/provider default.

type TeamSpawnRequest added in v1.6.10

type TeamSpawnRequest struct {
	// Config is the resolved sub-agent definition the teammate runs as.
	// Spawner reads SystemPrompt, Tools, Model, MaxTurns etc. from here.
	Config Config

	// Name is the teammate's identifier inside the team (routing key for
	// send_message). May equal Config.Name when the LLM did not specify one.
	Name string

	// TeamName is the active team's name; spawner validates against registry.
	TeamName string

	// InitialPrompt is the leader's first message to the teammate.
	InitialPrompt string

	// Description is an optional one-line summary for transcripts/UI.
	Description string

	// Color is an optional UI color assigned to this teammate.
	Color string

	// Model is non-nil when the LLM requested an override; nil means the
	// spawner should fall back to Config.Model.
	Model agentcore.ChatModel

	// History, if non-empty, seeds the teammate's conversation before its
	// first turn — the spawner forwards it to team.SpawnConfig.History. The
	// LLM never sets this; a harness populates it when resuming a teammate
	// with its prior transcript after a restart. nil ⇒ fresh teammate.
	History []agentcore.AgentMessage
}

TeamSpawnRequest is the contract between the subagent tool and the codebot-side team spawner. The subagent tool builds this from its params after validating the requested agent definition exists; the spawner is responsible for the actual goroutine launch, tool augmentation (e.g. injecting send_message), and team registry bookkeeping.

type TeamSpawnResult added in v1.6.10

type TeamSpawnResult struct {
	TaskID  string
	AgentID string // "name@team"
}

TeamSpawnResult is what the spawner returns synchronously. The teammate itself runs in the background; callers terminate it via task.Runtime.Stop (by TaskID) or by the team's shutdown protocol.

type TeamSpawner added in v1.6.10

type TeamSpawner func(ctx context.Context, req TeamSpawnRequest) (*TeamSpawnResult, error)

TeamSpawner is the function shape codebot installs via SetTeamSpawner. Kept as a function rather than an interface because the subagent tool only needs one method and call sites are simpler with a closure.

type Tool

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

Tool implements agentcore.Tool as an adapter over Runner.

func (*Tool) Description

func (t *Tool) Description() string

func (*Tool) Execute

func (t *Tool) Execute(ctx context.Context, args json.RawMessage) (json.RawMessage, error)

func (*Tool) Label

func (t *Tool) Label() string

func (*Tool) Name

func (t *Tool) Name() string

func (*Tool) Schema

func (t *Tool) Schema() map[string]any

func (*Tool) SetBgOutputFactory

func (t *Tool) SetBgOutputFactory(fn func(taskID, agentName string) (io.WriteCloser, string, error))

SetBgOutputFactory sets the factory that creates output writers for background tasks. The factory receives the task ID and agent name and returns a writer, file path, and error.

func (*Tool) SetCreateModel

func (t *Tool) SetCreateModel(fn func(name string) (agentcore.ChatModel, error))

SetCreateModel sets the factory for resolving model names to ChatModel instances at runtime. Enables LLM to override the default model per call.

func (*Tool) SetNotifyFn

func (t *Tool) SetNotifyFn(fn func(agentcore.AgentMessage))

SetNotifyFn sets the callback invoked when a background task completes. Typically bound to Agent.FollowUp so the main agent receives the result as a follow-up message.

func (*Tool) SetTaskRuntime

func (t *Tool) SetTaskRuntime(rt *task.Runtime)

SetTaskRuntime sets the shared task runtime for background task registration. Required for background mode.

func (*Tool) SetTeamSpawner added in v1.6.10

func (t *Tool) SetTeamSpawner(fn TeamSpawner)

SetTeamSpawner installs the closure that handles team-spawn mode. Without it, calls that set name or team_name are rejected with a clear error so the LLM learns the feature is unavailable rather than silently downgrading to a regular subagent run.

type UnknownAgentError added in v1.7.10

type UnknownAgentError struct {
	Agent     string
	Available []string
}

UnknownAgentError reports a lookup failure against the Runner's registry. errors.Is matches ErrUnknownAgent.

func (*UnknownAgentError) Error added in v1.7.10

func (e *UnknownAgentError) Error() string

func (*UnknownAgentError) Is added in v1.7.10

func (e *UnknownAgentError) Is(target error) bool

type Usage added in v1.7.10

type Usage struct {
	Input      int     `json:"input"`
	Output     int     `json:"output"`
	CacheRead  int     `json:"cache_read"`
	CacheWrite int     `json:"cache_write"`
	Cost       float64 `json:"cost"`
	Turns      int     `json:"turns"`
	Tools      int     `json:"tools"`
}

Usage aggregates token consumption and loop counters for one sub-agent run.

Jump to

Keyboard shortcuts

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