Documentation
¶
Index ¶
- Constants
- Variables
- func NewSpawnAgentTool(mgr *Manager) tool.Tool
- func NewSpawnRemoteAgentTool(spawner RemoteAgentSpawner, mgr *Manager) (tool.Tool, error)
- func NewSpawnTools(mgr *Manager) []tool.Tool
- func NewStopAgentTool(mgr *Manager) tool.Tool
- func SpawnToolNames() []string
- type Alert
- type Budgets
- type Handle
- type Manager
- func (m *Manager) Alerts() <-chan Alert
- func (m *Manager) AllowAdhoc() bool
- func (m *Manager) AttachParent(a *agent.Agent)
- func (m *Manager) Catalog() []attach.SubagentCatalogInfo
- func (m *Manager) Close() error
- func (m *Manager) Get(name string) (*Handle, bool)
- func (m *Manager) GrantableToolNames() []string
- func (m *Manager) List() []*Handle
- func (m *Manager) ListSubagentCatalog() []attach.SubagentCatalogInfo
- func (m *Manager) ListSubagents() []attach.AgentInfo
- func (m *Manager) OnAlert(h func(Alert))
- func (m *Manager) Parent() *agent.Agent
- func (m *Manager) Predefined(name string) (Spec, bool)
- func (m *Manager) PredefinedNames() []string
- func (m *Manager) PrependPendingAlerts(prompt string) string
- func (m *Manager) ReferenceNames() []string
- func (m *Manager) SetSubagentTemplates(ts []SubagentTemplate) error
- func (m *Manager) Spawn(ctx context.Context, parentBranch string, spec Spec) (*Handle, error)
- func (m *Manager) SpawnRef(ctx context.Context, parentBranch, name, goal string, ov RefOverrides, ...) (*Handle, error)
- func (m *Manager) SpawnSubagent(ctx context.Context, spec attach.SubagentSpec) (attach.SubagentSpawnResponse, error)
- func (m *Manager) SpawnTemplate(ctx context.Context, parentBranch, name string, ov RefOverrides, ...) (*Handle, error)
- func (m *Manager) Stop(name string) error
- func (m *Manager) StopSubagent(name string) (bool, error)
- func (m *Manager) TemplateNames() []string
- type ManagerOption
- func WithAlertBuffer(n int) ManagerOption
- func WithAllowAdhoc(allow bool) ManagerOption
- func WithCatalog(tools []tool.Tool) ManagerOption
- func WithDefaultBudgets(b Budgets) ManagerOption
- func WithDefaultScheduler(s coretools.Scheduler) ManagerOption
- func WithGate(g *permissions.Gate) ManagerOption
- func WithMaxConcurrent(n int) ManagerOption
- func WithMaxDepth(n int) ManagerOption
- func WithPredefinedSpecs(specs []Spec) ManagerOption
- func WithProvider(p models.Provider, modelID string) ManagerOption
- func WithSmallModelID(id string) ManagerOption
- func WithSubagentTemplates(ts []SubagentTemplate) ManagerOption
- func WithSyncWaitTimeout(d time.Duration) ManagerOption
- type Mode
- type RefOverrides
- type RemoteAgentEvent
- type RemoteAgentHandle
- type RemoteAgentSpawner
- type RemoteAgentSpec
- type RemoteAgentStatus
- type Spec
- type Status
- type StopClass
- type SubagentTemplate
Constants ¶
const ( SpawnAgentToolName = "spawn_agent" StopAgentToolName = "stop_agent" )
The model-facing names of the two delegation tools. Exported because the delegation surface is something a caller has to be able to reason about by name: the CLI carves these out of what a subagent inherits so a subagent doesn't get a delegation surface it never asked for (#748).
Variables ¶
var ErrAdhocDisabled = errors.New("background: ad-hoc subagents are disabled; reference a configured subagent by name")
ErrAdhocDisabled is returned when the model attempts an ad-hoc (inline-persona) spawn while allow_adhoc is off. The remedy is to reference a configured subagent by name.
var ErrDepthExceeded = errors.New("background: max subagent depth exceeded")
ErrDepthExceeded is returned by Spawn when the calling context is already at the max subagent depth.
var ErrManagerClosed = errors.New("background: closed")
ErrManagerClosed is returned by Spawn after Close has been called.
var ErrModelNotOverridable = errors.New("background: model override must be \"small\" or omitted")
ErrModelNotOverridable is returned when a spawn requests a model override other than "small" (or inherit). Per D2, a specific model requires its own predefined spec — a reference may only downshift to the small tier or inherit the spec's configured model.
var ErrNoParent = errors.New("background: parent agent not wired (use agent.WithBackgroundManager)")
ErrNoParent is returned by Spawn when the manager hasn't been attached to an agent yet (i.e. agent.New(... WithBackgroundManager ...) hasn't run).
var ErrNoSmallModel = errors.New("background: no small-tier model configured")
ErrNoSmallModel is returned when a spawn requests model "small" but the manager was constructed without a small-tier model id (WithSmallModelID).
var ErrNoSpawner = errors.New("background: NewSpawnRemoteAgentTool: spawner is required (use RefuseRemoteAgentSpawner for the headless / unattended case)")
ErrNoSpawner is returned by NewSpawnRemoteAgentTool when nil is passed for the spawner. Use RefuseRemoteAgentSpawner instead of nil when you want the tool registered but no-op.
var ErrSelfSpawn = errors.New("background: a subagent may not spawn itself")
ErrSelfSpawn is returned by Spawn when a subagent tries to spawn a subagent it is itself an instance of. Recursion at depth 1 sits inside any sensible depth cap, so the cap can't see it: what stops it is the declared-name lineage the spawn context carries (#732).
var ErrSubagentExists = errors.New("background: subagent with this name already exists")
ErrSubagentExists is returned by Spawn when a RUNNING subagent with the requested name is already registered. Names must be unique among live subagents within a manager; a handle in a terminal state (completed / failed / stopped / deferred) is evicted by the next Spawn of the same name, so names become reusable once their previous run has finished.
var ErrTooManyConcurrent = errors.New("background: max concurrent subagents reached")
ErrTooManyConcurrent is returned by Spawn when the manager already has MaxConcurrent running subagents.
var ErrToolNotGranted = errors.New("background: tool not granted by predefined spec")
ErrToolNotGranted is returned when a reference spawn's tools override lists a tool the predefined spec does not grant. Overrides may only narrow the spec's tool set, never widen it.
var ErrUnknownScheduler = errors.New("background: unknown scheduler choice")
ErrUnknownScheduler is wrapped and returned by Spawn when a spec.Scheduler value isn't one of the recognized choices.
var ErrUnknownSubagent = errors.New("background: unknown predefined subagent")
ErrUnknownSubagent is returned when a spawn_agent reference names a predefined spec the manager doesn't have registered.
var ErrUnknownTool = errors.New("background: unknown tool")
ErrUnknownTool is wrapped and returned by Spawn when a spec.Tools or spec.Extras entry isn't present in the catalog.
Functions ¶
func NewSpawnAgentTool ¶
NewSpawnAgentTool returns a tool the parent's model can call to launch a new in-process background subagent. The tool's name in the model's view is "spawn_agent"; the registered handler defers to mgr.Spawn after reading the calling tool.Context's branch so the new subagent's events land in the right hierarchical branch.
Spawn errors (invalid spec, depth/concurrency cap, unknown tool) are returned as the tool's result text rather than as Go errors, so the model sees them in conversation context and can adapt (e.g. by stopping a sibling first). Provider/model construction errors propagate normally since those are typically caller-fixable configuration problems.
func NewSpawnRemoteAgentTool ¶
func NewSpawnRemoteAgentTool(spawner RemoteAgentSpawner, mgr *Manager) (tool.Tool, error)
NewSpawnRemoteAgentTool returns a tool the parent's model can call to launch an out-of-process subagent via the consumer-supplied spawner. The handle's Events() channel is drained by a goroutine the manager starts inside Spawn; events of Kind="alert" land on the manager's alert channel under the subagent's name, and the terminal handle status is recorded for stop_agent + operator-surface uniformity alongside in-process subagents.
Pass nil for mgr to skip the alert + registry fan-in (alerts will be dropped); typically you want both wired, especially for the bundled CLI.
func NewSpawnTools ¶
NewSpawnTools is a convenience that returns both model-facing background-agent tools (spawn_agent + stop_agent) in one slice, ready to pass through agent.WithTools. The bundled CLI uses this to wire the suite atomically. Introspection (list/check) is intentionally NOT a model tool: completed subagents push their results back to the parent (the [Background reports] channel) and spawn_agent {wait:true} covers blocking needs, so a poll loop is redundant. Operators inspect live instances out-of-band via the attach hub / TUI.
func NewStopAgentTool ¶
NewStopAgentTool returns a tool the parent's model can call to cancel a running subagent. No-op if the subagent already terminal. Returns an error result (not a tool failure) when the name is unknown so the model can adapt.
func SpawnToolNames ¶ added in v2.9.0
func SpawnToolNames() []string
SpawnToolNames returns the names NewSpawnTools registers, for callers that need to filter a tool slice rather than build one — chiefly the CLI, which withholds the delegation surface from subagents that inherit the parent's registry wholesale (#748).
Types ¶
type Alert ¶
type Alert struct {
From string
Text string
Timestamp time.Time
Kind string // "alert" (default) | "completed" | "failed" | "stopped"
}
Alert is one report message a spawned subagent (or the manager itself on completion) emitted upward to the parent.
type Budgets ¶
type Budgets struct {
MaxTurns int
MaxCost float64
MaxWallclock time.Duration
PerTurnTimeout time.Duration
}
Budgets bounds a single spawned subagent's run. Zero values mean no cap for that dimension. The manager's WithDefaultBudgets supplies the defaults; per-spawn overrides come from the spawn_agent tool args.
type Handle ¶
type Handle struct {
Name string
Branch string
StartedAt time.Time
// contains filtered or unexported fields
}
Handle is the lifecycle record for one spawned subagent. Exposed read-only via Manager.List / Manager.Get so operator surfaces (attach hub, TUI) and the stop_agent tool can introspect status without reaching into internal state.
func (*Handle) Done ¶
func (h *Handle) Done() <-chan struct{}
Done returns a channel that closes when the subagent's goroutine exits. Use to wait for completion from the parent without polling.
func (*Handle) Err ¶
Err returns the terminal error if the subagent's RunAutonomous returned one. Nil while running or on clean completion.
func (*Handle) Result ¶
func (h *Handle) Result() *autonomous.RunResult
Result returns the terminal RunResult if the subagent has finished, or nil if it's still running.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns the lifecycle of in-process background subagents that the parent agent's model decides to spawn at runtime via the spawn_agent tool family (see background_tools.go).
One manager backs one parent agent. The manager:
constructs each spawned subagent against the parent's session service (with branch isolation), the parent's permissions gate (inherited wholesale), and a fresh model.LLM (one client per spawn, see docs/background-subagents-design.md);
runs each subagent in its own goroutine via RunAutonomous with per-subagent budgets;
multiplexes alert + completion messages from every running subagent onto a single channel the parent's run loop drains before each turn (see Agent.Run);
enforces a configurable max-concurrent cap on top of the subagent depth cap (the existing CurrentSubagentDepth check from subagent.go) so a runaway model can't spawn unboundedly.
Construction order is intentional: the manager is built first (without a parent reference), the spawn-related tools are built against the manager, the parent agent.New is called with those tools registered and the manager wired via WithBackgroundManager. agent.New stamps the parent back-reference onto the manager during construction so Spawn can read parent.SessionService / AppName / UserID / SessionID without the consumer plumbing them twice.
func ManagerOf ¶
ManagerOf recovers the concrete *Manager from an agent's SubagentManager seam, or nil when the agent has no manager wired (or a different implementation). Rich callers (the runner's REPL, the embedded TUI) use this to reach Manager methods beyond the agent.SubagentManager interface (List, Get, Stop, Alerts, OnAlert).
func NewManager ¶
func NewManager(opts ...ManagerOption) (*Manager, error)
NewManager builds a manager from the supplied options. Required: provider + modelID (WithProvider). The parent agent reference is established later by WithBackgroundManager when the parent is constructed via agent.New — until that wiring happens, Spawn returns ErrNoParent.
func (*Manager) Alerts ¶
Alerts returns the channel external consumers (the runner's REPL alert display goroutine, library consumers building their own UIs) drain to surface alerts as they arrive. The pre-turn drain inside Agent.Run uses PrependPendingAlerts instead — that path uses a non-blocking drain so it doesn't compete with this channel.
Note: a single alert lands on this channel exactly once. Consumers must agree on who drains it; today the runner.WriteEvents alert- display goroutine drains for REPL display, and Agent.Run drains for pre-turn injection. They're separated by which path is active (REPL vs headless vs autonomous).
func (*Manager) AllowAdhoc ¶ added in v2.9.0
AllowAdhoc reports whether ad-hoc (inline-persona) spawns are permitted. False (the daemon default) means the model may only spawn by reference to a predefined spec.
func (*Manager) AttachParent ¶
AttachParent records the parent agent on the manager. Called by agent.New when WithBackgroundManager is set (via the agent.SubagentManager seam). Safe to call once; subsequent calls overwrite (last-writer-wins so re-construction in tests works cleanly).
func (*Manager) Catalog ¶ added in v2.9.0
func (m *Manager) Catalog() []attach.SubagentCatalogInfo
Catalog returns the configured-subagent roster (#627) — declarative templates first, then predefined catalog specs (async-only), each sorted by name. This is what the daemon LOADED, as opposed to Manager.List / ListSubagents (live spawned instances). Backs the operator-facing surfaces: GET .../subagents (via the SubagentManager interface's ListSubagentCatalog), the /subagent listing, and the boot dump. Never nil.
Modes records how each subagent can actually be invoked HERE, on this manager's parent. Every declarative template is "async" (spawn_agent {agent}); it is additionally "sync" only when the attached parent exposes it as a tool, which is a property of that parent's WithSubagents set, not of the template. Predefined catalog specs are always "async" only (spawn-by-reference, no synchronous tool).
func (*Manager) Close ¶
Close stops every running subagent and prevents new spawns. Blocks until each goroutine has exited or closeDrainTimeout elapses — stragglers are abandoned (their contexts stay cancelled) and reported in the returned error so the caller can log them. Idempotent.
func (*Manager) Get ¶
Get returns the handle for the named subagent. ok=false when the name isn't registered.
func (*Manager) GrantableToolNames ¶ added in v2.9.0
GrantableToolNames returns the sorted names an ad-hoc subagent's `tools` list may draw from — exactly the set resolveTools accepts, minus the auto-wired names it silently drops.
It exists so spawn_agent's own parameter description can name the real catalog instead of a hard-coded example. The shipped example listed `bash`, which on a distroless build is a name the model can pass and resolveTools will then reject with ErrUnknownTool — a wasted spawn to learn something the description could have said.
func (*Manager) List ¶
List returns all currently-tracked handles, sorted by start time. Terminal handles remain in the list until Close (so operator surfaces can still report final status). Defensive copy of slice.
func (*Manager) ListSubagentCatalog ¶ added in v2.9.0
func (m *Manager) ListSubagentCatalog() []attach.SubagentCatalogInfo
ListSubagentCatalog implements the agent.SubagentManager seam's operator-catalog method (#627): the configured roster in attach types, so the adapter can serve GET .../subagents without importing this package. Identical data to Catalog (which callers holding the concrete *Manager may use directly).
func (*Manager) ListSubagents ¶
ListSubagents implements agent.SubagentManager. Returns attach-facing metadata for the manager's live subagents; backs Agent.AttachAgents.
func (*Manager) OnAlert ¶
OnAlert installs a synchronous hook called from pushAlert before the channel send. Useful for surfacing alerts to side channels (e.g. the REPL's inline display) without competing with the model- context drain on Alerts() / PrependPendingAlerts. Pass nil to clear.
The hook runs in whichever goroutine triggered the alert (typically a subagent's goroutine for report_alert, the Spawn goroutine for completion). Hooks should not block.
func (*Manager) Parent ¶
Parent returns the agent the manager is attached to, or nil if no agent.New has wired it yet. Exposed for tests + diagnostics.
func (*Manager) Predefined ¶ added in v2.9.0
Predefined returns the registered spec for name (a copy) and whether it exists.
func (*Manager) PredefinedNames ¶ added in v2.9.0
PredefinedNames returns the registered predefined-spec names, sorted. Backs the operator catalog surfaces (#627) and diagnostics.
func (*Manager) PrependPendingAlerts ¶
PrependPendingAlerts drains every pending alert from the manager's channel (non-blocking) and, when non-empty, returns prompt with a "[Background reports]" header prepended. Empty channel returns prompt unchanged.
Called by Agent.Run before each turn so the parent's model sees what its subagents have reported since the last turn.
func (*Manager) ReferenceNames ¶ added in v2.9.0
ReferenceNames returns every subagent name spawnable by reference — declarative templates plus catalog predefined specs — sorted. Used by operator surfaces (the /subagent command) to list what can be spawned.
func (*Manager) SetSubagentTemplates ¶ added in v2.9.0
func (m *Manager) SetSubagentTemplates(ts []SubagentTemplate) error
SetSubagentTemplates installs (or replaces) the declarative-subagent roster after construction. Call it once, before the parent agent starts running — the declarative builder produces the templates only after the manager (and its spawn tools) exist, so they can't be passed to NewManager. Names must be non-empty, branch-safe, carry a non-nil ModelFactory, be unique among themselves, and not collide with a predefined (catalog) spec name. Returns an error without mutating state when any check fails.
Each template's Tools are rebound to THIS manager (see rebindSpawnTools) as they are installed, so one shared roster can be registered on many managers — which is exactly what a multi-session daemon does.
func (*Manager) Spawn ¶
Spawn launches a new background subagent under spec. parentBranch is the branch the calling tool's context carries (typically empty for the top-level parent, "bg.<name>" when nested); the subagent's own branch becomes "<parentBranch>.bg.<spec.Name>" via composeBranch so the eventlog audit trail remains hierarchical.
Returns the handle immediately; the subagent's goroutine runs autonomous.Run against spec.Goal until budgets fire, the model signals done via report_completed, the parent calls Stop, or the goroutine's context is cancelled.
Returned errors are pre-flight: invalid spec, depth or concurrency cap exceeded, unknown tool name, or manager not yet attached to a parent. Once the goroutine is running, terminal errors land on the handle (h.Err()) and a corresponding Alert is pushed.
func (*Manager) SpawnRef ¶ added in v2.9.0
func (m *Manager) SpawnRef(ctx context.Context, parentBranch, name, goal string, ov RefOverrides, explicitName string) (*Handle, error)
SpawnRef spawns a configured subagent by name — routing a declarative template to SpawnTemplate and a catalog predefined spec to Spawn — with the given goal and narrowing-only overrides. It is the operator-facing twin of the model's spawn_agent reference path (the /subagent TUI command uses it to reach the same roster the model can), and never opens the ad-hoc inline-persona path. Unknown names return ErrUnknownSubagent.
func (*Manager) SpawnSubagent ¶
func (m *Manager) SpawnSubagent(ctx context.Context, spec attach.SubagentSpec) (attach.SubagentSpawnResponse, error)
SpawnSubagent implements agent.SubagentManager. Translates an attach spec into a background Spec and delegates to Spawn; backs attachadapter.AttachSpawnSubagent.
func (*Manager) SpawnTemplate ¶ added in v2.9.0
func (m *Manager) SpawnTemplate(ctx context.Context, parentBranch, name string, ov RefOverrides, explicitName string) (*Handle, error)
SpawnTemplate launches a registered declarative subagent asynchronously (spawn_agent {agent: name}, #626). The template's persona, tools, toolsets, and model are pre-resolved; the caller supplies the goal via ov and may narrow the model (to "small") and budgets. explicitName, when non-empty, names the instance; otherwise the runtime auto-derives "<name>-<n>".
Tool narrowing is intentionally NOT supported for templates: a rooted subagent's grant spans built-ins, MCP toolset tools, and skills, so a name-subset can't be applied coherently here — configure a dedicated, narrower subagent instead. Model overrides other than inherit/"small" are rejected (D2), matching the catalog reference path.
func (*Manager) Stop ¶
Stop cancels the named subagent's context. The goroutine exits at the next ctx-aware checkpoint inside RunAutonomous. Returns nil even when the subagent is already terminal; surfaces "not found" when the name isn't registered.
func (*Manager) StopSubagent ¶ added in v2.9.0
StopSubagent implements agent.SubagentManager. Thin wrapper over Stop that separates "no such subagent" (false, nil — a 404 for the operator, who aimed at a name that isn't running) from a real failure (an error). Stopping an already-stopped subagent is a no-op that still reports true: the handle is still registered, so the operator's intent was satisfied.
func (*Manager) TemplateNames ¶ added in v2.9.0
TemplateNames returns the registered declarative-subagent template names, sorted. Backs the operator catalog surfaces (#627).
type ManagerOption ¶
type ManagerOption func(*bgMgrConfig)
ManagerOption configures NewManager.
func WithAlertBuffer ¶
func WithAlertBuffer(n int) ManagerOption
WithAlertBuffer sets the alert channel buffer. When full, the oldest pending alert is dropped to make room (with a warning logged). Default 256.
func WithAllowAdhoc ¶ added in v2.9.0
func WithAllowAdhoc(allow bool) ManagerOption
WithAllowAdhoc permits inline-persona (ad-hoc) spawns — the parent's model authoring a fresh system_prompt at spawn time rather than referencing a predefined spec. Off by default (the daemon posture): an unattended daemon should only spawn operator-vetted specs. Turn it on for interactive/dev sessions where a human is steering.
func WithCatalog ¶
func WithCatalog(tools []tool.Tool) ManagerOption
WithCatalog registers the tool instances spawn_agent arguments can refer to by name. Pass the parent's already-gated tool list (typically tools.Default() plus any MCP/skill tools flattened to a single slice); the manager looks up each requested tool by Tool.Name(). Tools not listed here can't be requested.
func WithDefaultBudgets ¶
func WithDefaultBudgets(b Budgets) ManagerOption
WithDefaultBudgets sets the budgets a spawn request inherits when its own per-call args don't override. Default: 50 turns / $1.00 / 10 minutes, no per-turn timeout.
func WithDefaultScheduler ¶
func WithDefaultScheduler(s coretools.Scheduler) ManagerOption
WithDefaultScheduler sets the tools.Scheduler that spawned subagents inherit when the per-spawn Spec.Scheduler is empty or "default". Pass tools.SleepScheduler() for the canonical in-process supervisor topology where the parent runs as a long-lived daemon and children sleep between scans. Pass tools.ExitOnDeferScheduler() for orchestrator-managed deployments. Pass nil (or leave unset) to run subagents without between-turn pacing — the schedule_next_turn tool is then unavailable to those subagents.
Per-spawn overrides via Spec.Scheduler win when supplied; see Spawn / NewSpawnAgentTool.
func WithGate ¶
func WithGate(g *permissions.Gate) ManagerOption
WithGate wires the permissions gate that spawned subagents inherit (by reference; same instance). Required when running in ask/allow mode; the manager rejects spawn requests when the gate is in ask-mode without a prompter (same deadlock guard as RunAutonomous).
func WithMaxConcurrent ¶
func WithMaxConcurrent(n int) ManagerOption
WithMaxConcurrent caps how many subagents can be Running at once. Spawn calls that would exceed this return a clean tool- result error the model can adapt to. Default 8.
func WithMaxDepth ¶
func WithMaxDepth(n int) ManagerOption
WithMaxDepth caps how deep the subagent tree can go. A spawn from a context already at depth>=N returns an error result instead of nesting further. Default 2.
func WithPredefinedSpecs ¶ added in v2.9.0
func WithPredefinedSpecs(specs []Spec) ManagerOption
WithPredefinedSpecs registers the operator-curated subagent roster the parent's model can spawn by reference (spawn_agent {agent: "<name>"}, #626). Each spec is a template: its SystemPrompt, tool grant, model (Spec.ModelID), and budgets are what a reference spawn inherits and may only narrow. Names must be unique and non-empty; each spec needs a SystemPrompt (its persona). Goal may be empty — the parent supplies the task per spawn. Duplicate or invalid specs make NewManager return an error.
func WithProvider ¶
func WithProvider(p models.Provider, modelID string) ManagerOption
WithProvider wires the model provider + model ID used to build a fresh LLM client per spawn. Required.
func WithSmallModelID ¶ added in v2.9.0
func WithSmallModelID(id string) ManagerOption
WithSmallModelID sets the model id the "small" per-spawn model override resolves to (D2). Without it, spawns requesting model: "small" are rejected with ErrNoSmallModel.
func WithSubagentTemplates ¶ added in v2.9.0
func WithSubagentTemplates(ts []SubagentTemplate) ManagerOption
WithSubagentTemplates registers the declarative-subagent roster at construction time. Most callers instead use SetSubagentTemplates, because the templates are built (cmd/core-agent/subagents.go) after the manager is constructed but the spawn tools reference the manager — a construction-ordering cycle the post-construction setter breaks.
func WithSyncWaitTimeout ¶ added in v2.9.0
func WithSyncWaitTimeout(d time.Duration) ManagerOption
WithSyncWaitTimeout bounds how long a synchronous spawn (spawn_agent {wait: true}, #626) holds the parent turn open before returning a partial/timeout result. The subagent keeps running in the background past the timeout (its result later pushed via [Background reports]); only the parent's blocking wait is capped. Zero (the default) waits until the subagent finishes on its own budget or the parent context is canceled.
type Mode ¶ added in v2.9.0
type Mode string
Mode distinguishes the two things "background subagent" has always meant, which want opposite termination rules (#730).
A BOUNDED delegation is handed one task and is done when it stops working: the first turn that ends without the model asking for another tool ends the run, and its last message is the deliverable. The return tool is registered too and ranked above that (#745), so a model that calls it hands back a curated result instead — but a model that forgets still ends, which is the property that matters.
A STANDING worker is a loop that watches something. A turn that produces only text is a status report, so the driver feeds it the continuation prompt and keeps going until a budget fires, the scheduler defers it, or it calls the return tool. This is the pre-#730 behavior, unchanged.
const ( // ModeAuto derives the mode: standing when a scheduler is // installed (an agent that asks to be re-run later is by // definition not finished when it stops talking), bounded // otherwise. This is the zero value, and the default for every // spawn that doesn't say. ModeAuto Mode = "" // ModeBounded forces the one-task delegation contract. ModeBounded Mode = "bounded" // ModeStanding forces the watch-loop contract. ModeStanding Mode = "standing" )
type RefOverrides ¶ added in v2.9.0
RefOverrides are the narrowing-only adjustments a caller may layer on top of a referenced predefined spec (#626). Every field is optional; the zero value means "take the spec's value unchanged".
Narrowing semantics (D2/D5):
- Goal replaces the spec's goal outright (a template spec typically carries no goal; the parent supplies the task per spawn).
- Model may only be "" / "inherit" (keep the spec's model) or "small" (downshift to the manager's small tier). A specific model is rejected.
- Tools, when non-empty, must be a SUBSET of the spec's granted tools+extras — it can only drop, never add.
- Budgets tighten only: a smaller positive cap wins; a larger or zero override is ignored.
type RemoteAgentEvent ¶
type RemoteAgentEvent struct {
Kind string // "alert" | "log" | "completed" | "failed" | "stopped"
Text string
Timestamp time.Time
}
RemoteAgentEvent is what the consumer's transport delivers back from the remote subagent. The Kind field is the manager's hook for classifying — "alert" gets fanned into the parent's alert channel as a normal Alert; terminal kinds ("completed" / "failed" / "stopped") trigger the terminal Alert + handle close.
type RemoteAgentHandle ¶
type RemoteAgentHandle interface {
// ID returns the spawner-assigned identifier for diagnostics.
// Stable across the handle's lifetime.
ID() string
// Status returns the current lifecycle state of the remote
// subagent. Implementations are expected to be cheap (cached
// status from the last received event is fine).
Status(ctx context.Context) (RemoteAgentStatus, error)
// Stop signals the remote subagent to terminate. The
// implementation decides whether this is best-effort (e.g.
// async cancel of a K8s Job) or synchronous.
Stop(ctx context.Context) error
// Events returns a channel of events streamed from the remote
// subagent. The consumer's implementation closes the channel
// once the remote has terminated; the manager's fan-in
// goroutine exits on close.
Events() <-chan RemoteAgentEvent
}
RemoteAgentHandle is the contract for a running remote subagent. Implementations live in the consumer's package (the substrate adapter); the manager treats it opaquely apart from draining Events() for the alert pipeline.
type RemoteAgentSpawner ¶
type RemoteAgentSpawner interface {
// Spawn launches a remote subagent under spec and returns a
// handle the manager uses for status checks, stop, and event
// fan-in. Errors at this point propagate as Go errors (caller
// is wiring the spawner directly); errors that surface later
// flow through RemoteAgentHandle.Events() as Kind="failed".
Spawn(ctx context.Context, spec RemoteAgentSpec) (RemoteAgentHandle, error)
}
RemoteAgentSpawner is implemented by consumers who want the parent agent's model to be able to spawn out-of-process subagents — gRPC to a remote agent server, K8s Jobs, Cloud Run, NATS-dispatched workers, anything that runs an agent somewhere other than this process. core-agent stays agnostic about transport: the consumer's Spawn implementation is responsible for whatever IPC and lifecycle the substrate requires.
Mirrors the consumer-pluggability shape of tools.NewAskUserTool + tools.Prompter: a small interface the host implements, wired into a tool the model can call uniformly.
func RefuseRemoteAgentSpawner ¶
func RefuseRemoteAgentSpawner(reason string) RemoteAgentSpawner
RefuseRemoteAgentSpawner returns a spawner whose Spawn always errors with reason. Use it as the default spawner when running headless / unattended so the model sees a clean tool result it can adapt to, rather than the bundled CLI crashing on a nil dereference. Analog of tools.RefusePrompter.
type RemoteAgentSpec ¶
type RemoteAgentSpec struct {
ID string
Name string
SystemPrompt string
Goal string
Tools []string
Extras []string
Budgets Budgets
}
RemoteAgentSpec is what the manager hands to the consumer's spawner — the same shape as the in-process Spec, plus a stable opaque ID the consumer can use as a primary key in their substrate (e.g. the K8s Job name). Spec.Name is the human-facing identifier the parent's model chose; Spec.ID is the manager's invariant under the manager's registry. They're usually the same string but kept distinct so consumers don't have to guess.
type RemoteAgentStatus ¶
type RemoteAgentStatus int
RemoteAgentStatus mirrors Status but lives in the remote space. Implementations map their native states (K8s Job phase, HTTP response, etc.) onto these.
const ( RemoteStatusPending RemoteAgentStatus = iota RemoteStatusRunning RemoteStatusCompleted RemoteStatusFailed RemoteStatusStopped )
func (RemoteAgentStatus) String ¶
func (s RemoteAgentStatus) String() string
type Spec ¶
type Spec struct {
Name string
// Description is a one-line summary of what this subagent is for.
// Surfaced to the operator catalog (#627) and — for a predefined
// spec, which the parent can reference by name — into the
// spawn_agent schema the model routes from (#640). Optional, but a
// spec without one is harder for the parent to route to.
Description string
// SystemPrompt is the subagent's task-specific instruction.
// Since #459 it COMPOSES: the built agent gets the layered
// baseline (agent.CoreInstruction + provider quirks +
// agent.AutonomousOverlay) with this text appended as a layer-5
// block — so spawned subagents keep the compaction contract and
// edit-safety rules they previously lost to the full replace.
// Set ReplaceSystemPrompt for the old bare-prompt behavior.
SystemPrompt string
// ReplaceSystemPrompt, when true, restores the pre-#459
// semantics: SystemPrompt fully replaces the layered baseline
// (agent.WithInstruction). The subagent then carries NO harness
// contract — compaction summaries arrive unexplained — so use
// only when you are supplying your own complete prompt.
ReplaceSystemPrompt bool
Goal string
Tools []string
Extras []string
Budgets Budgets
// ModelID selects the model this subagent runs on. Empty means
// "inherit the manager's model" (m.modelID — typically the parent's).
// A predefined spec carries its operator-configured model here; a
// per-spawn "small" override rewrites it to the manager's small-tier
// model id (see resolvePredefinedSpec / WithSmallModelID, #626).
ModelID string
// Scheduler selects the between-turn scheduler the subagent's
// RunAutonomous loop honors. Valid values: "" or "default" (use
// the manager's WithDefaultScheduler — may itself be
// nil), "sleep" (in-process goroutine sleep), "exit_on_defer"
// (orchestrator-managed exit), "none" (no scheduler — the
// schedule_next_turn tool won't be registered for this subagent).
Scheduler string
// Mode selects how the subagent's loop terminates. Empty derives
// it from Scheduler; see Mode.
Mode Mode
// Ref is the predefined-spec name this spec was resolved from, set
// by the reference path (resolvePredefinedSpec) and empty for an
// ad-hoc, inline-authored one. Name is rewritten to a per-instance
// name before the spawn ("triage-2"), so Ref is what survives to
// say WHICH configured subagent is running — the self-spawn guard
// matches on it (#732).
Ref string
}
Spec is the request shape a single Spawn call expects. Built from the spawn_agent tool args by the tool handler.
type Status ¶
type Status int
Status is the lifecycle state of a background subagent.
const ( // StatusRunning — goroutine alive, RunAutonomous loop active. StatusRunning Status = iota // StatusCompleted — RunAutonomous returned with Reason==Completed. StatusCompleted // StatusFailed — RunAutonomous returned with a non-Completed // terminal reason (MaxTurns, MaxCost, error, etc.). StatusFailed // StatusStopped — explicit Stop() canceled the run. StatusStopped // StatusDeferred — RunAutonomous cleanly deferred or hit a budget cap. StatusDeferred )
type StopClass ¶ added in v2.9.0
type StopClass string
StopClass is the machine-readable answer to the one question a delegating agent has to answer about a returned result: is this finished, or is it a partial (#730)?
Removing the "continue" re-drive from a bounded delegation means a subagent that runs out of room hands back what it has. That is the right contract — the parent holds the goal and can re-ask with specifics, which a blind "continue" injected inside the subagent cannot — but only if the parent can tell the two apart. Prose in a text blob is not sufficient.
const ( // StopNatural: the subagent finished — it stopped asking for tools // (bounded) or signalled completion (standing). The output is the // deliverable. StopNatural StopClass = "natural" // StopMaxSteps: the turn cap fired. A partial; re-ask with what is // still missing, or raise MaxTurns. StopMaxSteps StopClass = "max_steps" // StopBudget: a cost, token, or wall-clock bound fired. A partial. StopBudget StopClass = "budget" // StopDeferred: the subagent scheduled its own next turn and will // resume. Not a partial to re-ask — it isn't done with the loop. StopDeferred StopClass = "deferred" // StopStopped: the parent (or operator) stopped it. Whatever text // exists was cut mid-thought. StopStopped StopClass = "stopped" // StopError: the run failed, was cancelled, or exhausted its retry // policy. StopError StopClass = "error" )
type SubagentTemplate ¶ added in v2.9.0
type SubagentTemplate struct {
// Name is the reference key (spawn_agent {agent: Name}) and seeds
// auto-derived instance names ("cluster-1"). Must be branch-safe.
Name string
// Description is the operator-facing summary (backs the #627 catalog).
Description string
// Root is the subagent's content root (its own AGENTS.md + mcp.json +
// skills/ tree), relative to the recipe as authored in config. Empty
// for an inline (non-rooted) declarative subagent. Display-only —
// surfaced in the #627 operator catalog so operators can see which
// subagents carry their own content bundle.
Root string
// ModelFactory builds a fresh LLM for each spawn — session isolation,
// same as the catalog path's provider.Model call. Required.
ModelFactory func(context.Context) (adkmodel.LLM, error)
// ModelID labels the template's model for /usage pricing attribution.
ModelID string
// Instruction is the fully-resolved persona, installed as layer 4
// (user memory) exactly like the synchronous declarative path.
Instruction string
// Tools are the built-in tools (already resolved to instances) the
// subagent runs with. Shared, stateless — safe across instances.
Tools []tool.Tool
// Toolsets are the MCP + skills groups. Process-long-lived, stateless
// handles — shared across concurrent instances of the template.
Toolsets []tool.Toolset
// MaxDepth caps the subagent's OWN nesting (0 = substrate default).
MaxDepth int
// Budgets bound each async run; per-spawn overrides may only tighten.
Budgets Budgets
// Scheduler is the between-turn scheduler choice ("" = manager
// default); see resolveScheduler for the accepted values.
Scheduler string
// Mode selects how the run terminates. Empty derives it from the
// resolved Scheduler; see Mode.
Mode Mode
}
SubagentTemplate is a predefined declarative subagent, pre-resolved for async-by-reference spawning. Unlike a catalog Spec (persona + tool NAMES resolved against the manager's catalog at spawn time), a template already carries built tool instances, MCP + skills toolsets, and a model factory — because a declarative subagent may be rooted (its own content root, mcp.json, and skills/ tree) and can't be reconstructed from the parent's catalog.