spawn

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package spawn registers the spawn / supervise tools the root agent uses to manage workers. See .agents/WORKER_RUNTIME.md for the canonical schemas; the implementations here delegate to the workers.Manager so the LLM never reaches into manager internals.

Phase 5 scope: static workers only. Dynamic-worker tools land in Phase 6 alongside the validator that ensures spec.MCPs/Skills are a subset of the universe baifo knows.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func OpaqueToolNames

func OpaqueToolNames() []string

OpaqueToolNames returns the names of every tool this package registers whose arguments must NOT be walked by the secrets expander. They are the spawn tools, and the reason is structural: their args carry the child agent's full spec — prompt, initial message, allowed_secrets list, sandbox path, etc. If the expander rewrote a ${secret:NAME} placeholder appearing inside that spec, the raw value would land in the child agent's prompt at construction time, bypassing the child's own allowlist (which is usually narrower than the parent's, often empty).

Keeping the placeholders intact lets them propagate verbatim into the child's prompt. The child's own BeforeToolCallback then runs the expander at the child's own tool boundary, with the child's own allower in effect. That is the right gate for the allowed-secrets subset rule documented in DECISIONS.md #10 to be load-bearing rather than merely advisory.

The list is kept here (not in internal/secrets or internal/agent) because this package is the source of truth for which spawn tools exist. Adding a new spawn-style tool means updating this list in the same commit; the agent.Builder reads it through Builder.OpaqueTools without knowing what "spawn" means.

Returns a fresh slice on every call so callers cannot mutate the internal table by accident. Order matches the registration order in ADKTools().

Types

type BatchSpawnArgs

type BatchSpawnArgs struct {
	Agents []DynamicSpawnArgs `json:"agents"`
}

BatchSpawnArgs is the input of spawn_dynamic_agents — pure convenience over calling spawn_dynamic_agent N times in one turn.

type BatchSpawnResult

type BatchSpawnResult struct {
	WorkerIDs []string `json:"worker_ids"`
}

BatchSpawnResult mirrors SpawnResult but for the batch tool.

type CollectArgs

type CollectArgs struct {
	WorkerID  string `json:"worker_id"`
	TimeoutMs int    `json:"timeout_ms,omitempty"`
}

CollectArgs is the input of collect_agent.

type CollectResult

type CollectResult struct {
	Output string `json:"output"`
	Status string `json:"status"`
	Err    string `json:"error,omitempty"`
}

CollectResult is the response shape: output text + final status.

type DynamicLLM

type DynamicLLM struct {
	Provider string `json:"provider,omitempty"`
	Model    string `json:"model,omitempty"`

	// Reasoning optionally sets how hard the worker thinks for models
	// that support it: one of "minimal" / "low" / "medium" / "high"
	// (empty = the model's default). Call list_models to see which
	// models accept reasoning and at which levels.
	Reasoning string `json:"reasoning,omitempty"`
}

DynamicLLM is the per-spawn LLM override. When unset the worker inherits the root agent's provider+model (resolved at build time).

type DynamicSpawnArgs

type DynamicSpawnArgs struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Prompt      string `json:"prompt"`

	LLM            DynamicLLM `json:"llm,omitempty"`
	Skills         []string   `json:"skills,omitempty"`
	MCPs           []string   `json:"mcps,omitempty"`
	AllowedSecrets []string   `json:"allowed_secrets,omitempty"`
	InitialMessage string     `json:"initial_message,omitempty"`
}

DynamicSpawnArgs is the input of spawn_dynamic_agent. Mirrors the schema in WORKER_RUNTIME.md. ContextGuard is omitted in v1; the builder ignores the field until contextguard is wired in.

type InspectArgs

type InspectArgs struct {
	WorkerID   string `json:"worker_id"`
	SinceEvent int    `json:"since_event,omitempty"`
}

InspectArgs is the input of inspect_agent. SinceEvent is reserved for the day the manager keeps a per-worker ring buffer of events.

type KillArgs

type KillArgs struct {
	WorkerID string `json:"worker_id"`

	// Reason is an optional explanation surfaced on the subsequent
	// collect_agent call as CollectResult.Err. The LLM can use it to
	// pass context ("superseded by newer plan", "worker stuck on
	// rate-limit", ...). Empty defaults to "killed by agent".
	Reason string `json:"reason,omitempty"`
}

KillArgs is the input of kill_agent.

type KillResult

type KillResult struct {
	OK bool `json:"ok"`
}

KillResult mirrors QueryResult: a simple acknowledgment.

type ListResult

type ListResult struct {
	Workers []WorkerSummary `json:"workers"`
}

ListResult is the response shape of list_running_agents.

type NamedDescription

type NamedDescription struct {
	Name        string
	Description string
}

NamedDescription is the shape Universe.SkillDetails / SecretDetails return. Kept in this package (not borrowed from internal/app) so the spawn tools stay free of import cycles and tests can build trivial fakes.

type QueryArgs

type QueryArgs struct {
	WorkerID string `json:"worker_id"`
	Message  string `json:"message"`
}

QueryArgs is the input of query_agent.

type QueryResult

type QueryResult struct {
	OK bool `json:"ok"`
}

QueryResult is what the root sees after enqueueing a follow-up.

type RootDefaults

type RootDefaults struct {
	Provider string
	Model    string
}

RootDefaults is the fallback LLM the dynamic-spawn tool applies when args.LLM is unset. The App fills this from cfg.Root.LLM at startup so workers spawned without an explicit provider inherit the root's.

type SpawnResult

type SpawnResult struct {
	WorkerID string `json:"worker_id"`
}

SpawnResult is the result returned by both spawn tools.

type SpawnStaticArgs

type SpawnStaticArgs struct {
	Name           string   `json:"name"`
	InitialMessage string   `json:"initial_message"`
	AllowedSecrets []string `json:"allowed_secrets,omitempty"`
}

SpawnStaticArgs is the input of spawn_static_agent.

type TemplateResolver

type TemplateResolver interface {
	Resolve(name string) (config.AgentTemplate, bool)

	// ListTemplates returns every static-agent template currently
	// declared, in a stable order. Used by list_agents and by the
	// spawn_static_agent description so the LLM can see what is
	// actually available without trial-and-error.
	ListTemplates() []config.AgentTemplate
}

TemplateResolver looks up a static-agent template by name in the loaded agents.yaml. The Manager wiring builds the closure; we keep the dependency narrow so this package does not import internal/app.

type TemplateSummary

type TemplateSummary struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Provider    string `json:"provider,omitempty"`
	Model       string `json:"model,omitempty"`
}

TemplateSummary is the per-template row returned by list_agents. Description is the one-line note the user wrote in agents.yaml; the LLM uses it to pick the right template for the task at hand.

type TemplatesResult

type TemplatesResult struct {
	Templates []TemplateSummary `json:"templates"`
}

TemplatesResult is the response shape of list_agents.

type Tools

type Tools struct {
	Manager   *workers.Manager
	Templates TemplateResolver

	// The next three fields are needed only when dynamic spawning is
	// enabled. Static-only deployments can leave them zero.
	Universe      Universe
	RootDefaults  RootDefaults
	EnableDynamic bool

	// ParentAllowedSecrets is the universe of secrets the spawning
	// agent itself can dereference. It bounds what its spawn calls
	// (static and dynamic) may grant to children — the secrets
	// pipeline enforces least-privilege from there.
	//
	// Two encodings:
	//
	//   - nil           → parent is "sovereign": no per-agent
	//                     restriction. Used for the root, which is
	//                     the user-facing coordinator and the
	//                     frontier of trust. Sub-agent allowlists
	//                     are validated against the global universe
	//                     of declared secrets in this mode.
	//   - non-nil slice → parent's explicit allowlist (possibly
	//                     empty). Sub-agent allowlists must be a
	//                     subset of this list, and an empty list
	//                     means the parent cannot delegate any
	//                     secret.
	//
	// Today only the root holds spawn tools, so this field is
	// effectively always nil in production. The shape is preserved
	// for the day a static template gets its own spawn tools.
	ParentAllowedSecrets []string
}

Tools bundles the per-spawn dependencies needed to build every tool. Construct one in the App's wiring, then call ADKTools().

func (*Tools) ADKTools

func (t *Tools) ADKTools() ([]tool.Tool, error)

ADKTools returns the static-spawn / supervise toolset for the root agent, in the order they appear in WORKER_RUNTIME.md. The dynamic spawn tools are appended when t.EnableDynamic is true.

type Universe

type Universe interface {
	ListSkills() []string
	ListMCPs() []string
	ListProviders() []string
	ListSecretNames() []string

	// MCPTools returns the names of the tools exposed by the named
	// MCP. Used by the spawn_dynamic_agent description so the LLM
	// understands what a worker actually inherits when it lists an
	// MCP in its spec — otherwise the model assumes from the MCP
	// name alone (e.g. it sees "filesystem" and concludes the MCP
	// is read/write only, missing that "exec" lives there too).
	// Returns nil for an unknown name; callers should not error.
	MCPTools(name string) []string

	// MCPExternal reports whether the named MCP uses an external
	// transport (http / stdio). Those connect lazily, so their tool
	// list is unknown at description-compose time and MCPTools
	// returns nil for them — not because the server is empty, but
	// because we haven't connected yet. writeMCPs uses this to phrase
	// the difference accurately ("tools resolve at call time") instead
	// of implying an external MCP advertises nothing.
	MCPExternal(name string) bool

	// SpawnSkillDetails and SecretDetails enrich the spawn
	// description with the metadata the LLM needs to choose well:
	// a skill's purpose (from frontmatter) and a secret's intent
	// (from the store's description field). Returning empty slices
	// is fine — composeDynamicDescription falls back to "(none)".
	// Skill variant is namespaced SpawnSkillDetails so it does not
	// collide with App.SkillDetails (which already exists for the
	// TUI's Settings overlay and returns a different shape).
	SpawnSkillDetails() []NamedDescription
	SecretDetails() []NamedDescription
}

Universe enumerates the building blocks baifo knows about, used both to validate dynamic spawn specs (per decision #7) and to inject the list into the spawn-tool descriptions so the LLM composes specs from the right vocabulary.

type WorkerSummary

type WorkerSummary struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Kind      string `json:"kind"`
	Status    string `json:"status"`
	Elapsed   string `json:"elapsed"`
	LastEvent string `json:"last_event,omitempty"`
}

WorkerSummary is the per-worker row returned by list_running_agents and inspect_agent. Time-formatted so the LLM can reason about elapsed without re-parsing.

Jump to

Keyboard shortcuts

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