Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrUnknownJob = errors.New("unknown job ID") ErrNotSync = errors.New("subagent is already async") ErrNotRunning = errors.New("subagent already finished") )
Sentinel errors returned by promote/Promote, distinguishable by callers (e.g. pkg/serve maps them to specific HTTP statuses).
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
DefaultModel core.Model
CurrentModel func() core.Model
CurrentThinkingLevel func() string
CurrentPermissionCheck func() func(ctx context.Context, name string, args map[string]any) *core.ToolCallDecision
ProviderFactory func(core.Model) (core.Provider, error)
AgentsMD string
PromptBuilder func(opts agentcontext.SystemPromptOptions) string
ParentTools *core.Registry
AppCtx context.Context
WorkspaceRoot string // CWD passed to system prompt builder
SkillsIndex string // pre-formatted skills index for system prompt
MemoryIndex string // pre-formatted memory index (one line per fact)
// PromptCacheKey is the PARENT's cache-routing key. Each child derives its
// own from it plus the job id: a child is a separate conversation with its
// own system prompt, tools and history, so it shares no reusable prefix
// with the parent and must not share its routing group. Empty = no key.
PromptCacheKey string
// BashState, when non-nil, is the per-agent persistent shell state. The
// subagent seeds an isolated copy for the child (subshell semantics) and
// drops it when the child finishes. nil = no shell-state isolation.
BashState *tool.BashState
// AttachmentScope is the PARENT session's attachment capability, handed to
// every child agent through its own AgentConfig. It cannot be inherited from
// the context: a child's context is derived from AppCtx, not from the tool
// call that spawned it, so anything the parent's run installed is simply not
// there. Children persist their sidecar transcript, so they do externalize —
// always owned by the PARENT session ID, never the job ID: the store's GC
// only knows main sessions, so a job-owned blob would be collected on
// restart. nil = children work inline.
AttachmentScope *attachment.Scope
// OnAsyncComplete is called when an async subagent finishes (completed, failed, or cancelled).
// truncated is true when resultTail is only the last N lines of the full output.
OnAsyncComplete func(jobID, task, status, resultTail string, truncated bool)
// OnAsyncJobChange is called when an async job starts or finishes.
// count is the current number of running jobs.
OnAsyncJobChange func(count int)
// OnChildStart is called right before a child agent (sync or async) begins
// running, with its jobID/task/model/thinking level, whether it's async, its start
// time (so live UIs can compute elapsed and reconcile it after a reconnect),
// and its stable per-session creation ordinal (accentIndex) for a
// deterministic accent color that survives reconnects.
OnChildStart func(jobID, task, model, thinking, originToolCallID string, async bool, startedAt time.Time, accentIndex int)
// OnChildEvent is called for each typed bus event produced by translating
// the child's core.AgentEvent stream (via bus.TranslateAgentEvent). inner
// is already a concrete bus.* type (e.g. bus.TextDelta), never a raw
// core.AgentEvent — pkg/subagent imports pkg/bus directly (no import
// cycle: pkg/bus does not import pkg/subagent), so translation happens
// here rather than at the call site.
OnChildEvent func(jobID string, inner any)
// OnChildUsage is called each time a child closes a message (its
// message_end), with the child's accumulated usage/cost so far (cost using
// the CHILD's model pricing) and how full its own context window is
// (0-100, or -1 when its model has no known window). It lets live UIs show
// running tokens/cost/context before the terminal OnChildEnd. Same
// aggregation as OnChildEnd, so the live value stays consistent with the
// final total.
OnChildUsage func(jobID string, usage *core.Usage, costUSD float64, contextPct int)
// OnChildEnd is called once when a child agent (sync or async) finishes.
// Result/Error are the terminal child outcome, not the one-time model
// delivery claim used by subagent_wait and async notifications.
OnChildEnd func(jobID, task string, async bool, status, result, resultErr string, finishedAt time.Time, usage *core.Usage, costUSD float64)
// Title generation is deliberately asynchronous: starting work must never
// wait on a convenience model call. Resumed children keep their saved title.
TitleModel core.Model
TitleEnabled bool
OnChildTitle func(jobID, title string)
// ChildMaxTurns caps the number of turns a child agent may take. 0 (or
// negative) falls back to defaultChildMaxTurns.
ChildMaxTurns int
// ChildMaxRunDuration caps how long a child agent may run. 0 (or
// negative) falls back to defaultChildMaxRunDuration.
ChildMaxRunDuration time.Duration
// MaxConcurrentAsync caps how many async subagent jobs may run at once.
// 0 (or negative) falls back to defaultMaxConcurrentAsync.
MaxConcurrentAsync int
// AllowedModels restricts, by model ID, which models a subagent may be
// launched with. Empty (the default) means no restriction: the allowlist
// is opt-in and must not change behaviour for anyone who never set one.
// It also filters what the tool advertises — a model the agent cannot use
// must not be named in the schema or in an error, or the agent will keep
// trying it.
AllowedModels []string
// LoadAllowedModels reads the current global delegation policy. It keeps
// already-open sessions subject to policy changes without recreating them.
LoadAllowedModels func() []string
// InheritedCompactAt is the soft compaction threshold (tokens) a child
// should run under: the PARENT session's own threshold when it set one,
// otherwise the global default. Resolved by the caller, which is the only
// side that can see both. nil or 0 = compact at the child's model window.
//
// A child is a separate agent with its own history, so nothing reaches it
// implicitly: without this a long delegated task would keep filling context
// to the brim while the parent that spawned it compacts at 60%.
InheritedCompactAt func() int
// TranscriptLoader loads a persisted subagent transcript by job ID,
// enabling the "resume" parameter to continue a finished subagent's
// conversation instead of starting fresh. nil = resume unsupported (the
// tool reports a clear error when resume is requested). The caller wires
// this to its session-scoped transcript store (see pkg/serve).
TranscriptLoader func(jobID string) (ResumedTranscript, error)
}
type JobInfo ¶
type JobInfo struct {
JobID string
OriginToolCallID string
Task string
Title string
Model string
Thinking string
Status string
Async bool
StartedAt time.Time
FinishedAt time.Time
// Usage/CostUSD carry the child's accumulated usage/cost so far (nil Usage
// until the child has closed at least one message), so a reconnect snapshot
// can restore live cost without resetting it.
Usage *core.Usage
CostUSD float64
// ContextPercent is how full the CHILD's own context window is (0-100),
// or -1 when unknown — see job.contextPct.
ContextPercent int
// AccentIndex is the job's stable creation ordinal (see job.accentIndex),
// used by clients to pick a deterministic accent color that survives
// reconnects.
AccentIndex int
}
JobInfo describes a live (or recently finished) subagent job.
type Jobs ¶
type Jobs struct {
// contains filtered or unexported fields
}
Jobs is a handle onto the subagent job store, returned by RegisterAll.
func RegisterAll ¶
RegisterAll registers the subagent tools on reg and returns a handle onto the job store (for external consumers: init snapshot, tray UI, cancellation).
func (*Jobs) Cancel ¶
Cancel requests cancellation of a running job. Returns false if no job with that ID is tracked (so callers can surface a 404); returns true if the job exists, whether or not it was still running (idempotent for finished jobs).
func (*Jobs) Messages ¶
func (j *Jobs) Messages(jobID string) []core.AgentMessage
Messages returns a defensive deep copy of the stored transcript for jobID.
func (*Jobs) Promote ¶
Promote flips a running sync subagent job to async, unblocking its parent's blocking tool call while the child keeps running in the background. Propagates ErrUnknownJob, ErrNotSync, ErrNotRunning from the underlying store so callers (e.g. pkg/serve) can map them to specific responses.
func (*Jobs) Snapshot ¶
Snapshot lists all jobs currently tracked (live and recently finished, subject to the store's TTL cleanup).
func (*Jobs) Steer ¶
Steer queues a message for inter-step delivery to the running child agent of jobID. Returns false if no job with that ID is tracked, if the job is no longer running (finished, cancelled or still initializing), or if the child itself refused the message. Non-blocking; safe to call concurrently.
type ResumedTranscript ¶ added in v0.28.0
type ResumedTranscript struct {
Messages []core.AgentMessage
Model string
Thinking string
}
ResumedTranscript is what a TranscriptLoader hands back: the persisted child conversation plus the identity it ran under. Model/Thinking let a resume continue with the SAME model and thinking level the subagent already used instead of silently adopting the parent's; both are optional (empty on transcripts written before they were recorded, which fall back to the parent's settings, i.e. the historical behaviour).