Documentation
¶
Overview ¶
Package agent wraps the Google ADK runner with sensible defaults (streaming mode, in-memory session service, app name) so consumers hit the same shape regardless of whether they're driving the agent from a one-shot CLI, a REPL, or an HTTP handler.
Multi-turn conversation history is preserved automatically when Run() is called repeatedly with the same userID + sessionID — by default ADK's session.InMemoryService accumulates events. Pass WithSessionService to plug in a durable backend (e.g. an eventlog-backed Service for SQLite/Postgres persistence + audit log + crash-resume).
Index ¶
- Constants
- Variables
- func CurrentSubagentDepth(ctx context.Context) int
- func EventlogMetadataExtractor() eventlog.MetadataExtractor
- func FormatAutoContinueInbox(messages []string) string
- func IsCostCeilingExceeded(err error) bool
- func NewBackgroundSpawnTools(mgr *BackgroundAgentManager) []tool.Tool
- func NewCheckAgentTool(mgr *BackgroundAgentManager) tool.Tool
- func NewListAgentsTool(mgr *BackgroundAgentManager) tool.Tool
- func NewMarkTaskDoneTool(getter func() *Agent) tool.Tool
- func NewSpawnAgentTool(mgr *BackgroundAgentManager) tool.Tool
- func NewSpawnRemoteAgentTool(spawner RemoteAgentSpawner, mgr *BackgroundAgentManager) (tool.Tool, error)
- func NewStopAgentTool(mgr *BackgroundAgentManager) tool.Tool
- func NewSubagentTool(opts SubagentOptions) (tool.Tool, error)
- type Agent
- func (a *Agent) AgentName() string
- func (a *Agent) AppName() string
- func (a *Agent) AskSideQuestion(ctx context.Context, question string) (string, error)
- func (a *Agent) AttachAddAllow(patterns []string) error
- func (a *Agent) AttachAddDeny(patterns []string) error
- func (a *Agent) AttachAgents() []attach.AgentInfo
- func (a *Agent) AttachAskSideQuestion(ctx context.Context, question string) (string, error)
- func (a *Agent) AttachCheckpoint(ctx context.Context, note string) (attach.CheckpointResponse, error)
- func (a *Agent) AttachCompact(ctx context.Context, focus string) (attach.CompactResponse, error)
- func (a *Agent) AttachContext() attach.ContextInfo
- func (a *Agent) AttachInterrupt() bool
- func (a *Agent) AttachMCP() attach.MCPInfo
- func (a *Agent) AttachMemory() []attach.MemorySource
- func (a *Agent) AttachPerms() attach.PermsInfo
- func (a *Agent) AttachPricing() attach.PricingInfo
- func (a *Agent) AttachPromptBroker() *attach.PromptBroker
- func (a *Agent) AttachRefreshPricing(ctx context.Context) (attach.PricingRefreshResponse, error)
- func (a *Agent) AttachReload(ctx context.Context) attach.ReloadResponse
- func (a *Agent) AttachReplan(ctx context.Context, req attach.ReplanRequest) (attach.ReplanResponse, error)
- func (a *Agent) AttachSetManualPricing(req attach.PricingSetRequest) error
- func (a *Agent) AttachSkills() []attach.SkillInfo
- func (a *Agent) AttachSpawnSubagent(ctx context.Context, spec attach.SubagentSpec) (attach.SubagentSpawnResponse, error)
- func (a *Agent) AttachStatus() attach.StatusInfo
- func (a *Agent) AttachTools() []attach.ToolInfo
- func (a *Agent) AttachUsage() attach.UsageInfo
- func (a *Agent) BackgroundManager() *BackgroundAgentManager
- func (a *Agent) Checkpoint(ctx context.Context, taskNote string) (CheckpointResult, error)
- func (a *Agent) Compact(ctx context.Context, focus string) (CompactionResult, error)
- func (a *Agent) CompactIfNeeded(ctx context.Context, focus string) (CompactionResult, error)
- func (a *Agent) ContextStats() ContextStats
- func (a *Agent) CostCeilingTripped() (bool, string)
- func (a *Agent) Description() string
- func (a *Agent) DrainInbox() []string
- func (a *Agent) EventLog() *eventlog.Handle
- func (a *Agent) HasCheckpointer() bool
- func (a *Agent) HasCompactor() bool
- func (a *Agent) InboxArrived() <-chan struct{}
- func (a *Agent) Inject(message string) error
- func (a *Agent) InjectAs(message string, caller auth.Caller) error
- func (a *Agent) Interrupt() bool
- func (a *Agent) ModelName() string
- func (a *Agent) PendingInboxCount() int
- func (a *Agent) RequestWake()
- func (a *Agent) ResetCostCeiling()
- func (a *Agent) Run(ctx context.Context, prompt string) iter.Seq2[*session.Event, error]
- func (a *Agent) RunSubtask(ctx context.Context, spec SubtaskSpec) (SubtaskResult, error)
- func (a *Agent) RunWithContents(ctx context.Context, contents []*genai.Content) iter.Seq2[*session.Event, error]
- func (a *Agent) SessionID() string
- func (a *Agent) SessionService() session.Service
- func (a *Agent) SetAttachEmitter(f func(eventType string, payload any))
- func (a *Agent) Tools() []tool.Tool
- func (a *Agent) UserID() string
- func (a *Agent) WakeRequested() <-chan struct{}
- type Alert
- type AutonomousHandle
- func (h *AutonomousHandle) Done() <-chan struct{}
- func (h *AutonomousHandle) Inject(message string) error
- func (h *AutonomousHandle) InjectAs(message string, caller auth.Caller) error
- func (h *AutonomousHandle) Pause() error
- func (h *AutonomousHandle) Ready() <-chan struct{}
- func (h *AutonomousHandle) RequestWake()
- func (h *AutonomousHandle) Resume() error
- func (h *AutonomousHandle) Status() AutonomousStatus
- func (h *AutonomousHandle) Stop() error
- func (h *AutonomousHandle) Wait() (RunResult, error)
- type AutonomousOption
- func WithBeforeTurn(cb func(ctx context.Context, turnNo int) error) AutonomousOption
- func WithContinuationPrompt(s string) AutonomousOption
- func WithDoneToolDescription(desc string) AutonomousOption
- func WithDoneToolName(name string) AutonomousOption
- func WithMaxCost(usd float64) AutonomousOption
- func WithMaxDefer(d time.Duration) AutonomousOption
- func WithMaxTokens(input, output int) AutonomousOption
- func WithMaxTurns(n int) AutonomousOption
- func WithMaxWallclock(d time.Duration) AutonomousOption
- func WithPerTurnTimeout(d time.Duration) AutonomousOption
- func WithPermissionsGate(g *permissions.Gate) AutonomousOption
- func WithPricing(p usage.Pricing) AutonomousOption
- func WithProgress(cb func(turn int, ev *session.Event)) AutonomousOption
- func WithRetryPolicy(p RetryPolicy) AutonomousOption
- func WithScheduleToolDescription(desc string) AutonomousOption
- func WithScheduleToolMaxDefer(d time.Duration) AutonomousOption
- func WithScheduleToolName(name string) AutonomousOption
- func WithScheduler(s coretools.Scheduler) AutonomousOption
- func WithTracker(t *usage.Tracker, p usage.Pricing) AutonomousOption
- type AutonomousStatus
- type BackgroundAgentManager
- func (m *BackgroundAgentManager) Alerts() <-chan Alert
- func (m *BackgroundAgentManager) Close() error
- func (m *BackgroundAgentManager) Get(name string) (*BackgroundHandle, bool)
- func (m *BackgroundAgentManager) List() []*BackgroundHandle
- func (m *BackgroundAgentManager) OnAlert(h func(Alert))
- func (m *BackgroundAgentManager) Parent() *Agent
- func (m *BackgroundAgentManager) PrependPendingAlerts(prompt string) string
- func (m *BackgroundAgentManager) Spawn(ctx context.Context, parentBranch string, spec BackgroundSpec) (*BackgroundHandle, error)
- func (m *BackgroundAgentManager) Stop(name string) error
- type BackgroundBudgets
- type BackgroundHandle
- type BackgroundManagerOption
- func WithBackgroundAlertBuffer(n int) BackgroundManagerOption
- func WithBackgroundCatalog(tools []tool.Tool) BackgroundManagerOption
- func WithBackgroundDefaultBudgets(b BackgroundBudgets) BackgroundManagerOption
- func WithBackgroundDefaultScheduler(s coretools.Scheduler) BackgroundManagerOption
- func WithBackgroundGate(g *permissions.Gate) BackgroundManagerOption
- func WithBackgroundMaxConcurrent(n int) BackgroundManagerOption
- func WithBackgroundMaxDepth(n int) BackgroundManagerOption
- func WithBackgroundProvider(p models.Provider, modelID string) BackgroundManagerOption
- type BackgroundSpec
- type BackgroundStatus
- type BuildFunc
- type CheckpointResult
- type Checkpointer
- type CompactionResult
- type Compactor
- type ContextStats
- type CostCeiling
- type DefaultCheckpointer
- type DefaultCompactor
- type Option
- func WithAppName(s string) Option
- func WithAttachMCPProvider(fn func() attach.MCPInfo) Option
- func WithAttachMemoryProvider(fn func() []attach.MemorySource) Option
- func WithAttachPricingProvider(fn func() attach.PricingInfo) Option
- func WithAttachPricingSetter(fn func(req attach.PricingSetRequest) error) Option
- func WithAttachPromptBroker(b *attach.PromptBroker) Option
- func WithAttachRefreshPricer(fn func(ctx context.Context) (attach.PricingRefreshResponse, error)) Option
- func WithAttachReloader(fn func(ctx context.Context) attach.ReloadResponse) Option
- func WithAttachReplanner(...) Option
- func WithAttachSkillsProvider(fn func() []attach.SkillInfo) Option
- func WithBackgroundManager(mgr *BackgroundAgentManager) Option
- func WithCheckpointer(c Checkpointer) Option
- func WithCompactor(c Compactor) Option
- func WithCostCeiling(c CostCeiling) Option
- func WithDescription(s string) Option
- func WithEventHook(onEvent func(*session.Event), onTurnEnd func()) Option
- func WithEventLog(h *eventlog.Handle) Option
- func WithGate(g *permissions.Gate) Option
- func WithInstruction(s string) Option
- func WithName(s string) Option
- func WithPostConstruct(f func(*Agent)) Option
- func WithSession(userID, sessionID string) Option
- func WithSessionRegistry(r attachRegistrar) Option
- func WithSessionService(s session.Service) Option
- func WithStreaming(m adkagent.StreamingMode) Option
- func WithSubagents(agents []*Agent) Option
- func WithSystemInstructionPrefix(prefix string) Option
- func WithTools(ts []tool.Tool) Option
- func WithToolsets(ts []tool.Toolset) Option
- func WithUsageTracker(t *usage.Tracker) Option
- func WithWatchdog(w watchdog.Watchdog, onAlert func(watchdog.Alert)) Option
- type RemoteAgentEvent
- type RemoteAgentHandle
- type RemoteAgentSpawner
- type RemoteAgentSpec
- type RemoteAgentStatus
- type ResumeBuildFunc
- type RetryDecision
- type RetryPolicy
- type RunResult
- type SessionRef
- type StopReason
- type SubagentOptions
- type SubtaskBudgets
- type SubtaskResult
- type SubtaskSpec
Constants ¶
const ( SubtaskDefaultMaxTurns = 5 SubtaskDefaultMaxWallclock = 60 * time.Second )
Defaults for SubtaskBudgets. Aimed at "narrow tool wrapper" shape (AgenticReadFile etc.); broader-research wrappers can override.
const CheckpointEventTag = "checkpoint"
CheckpointEventTag is the value stored under session.Event.CustomMetadata["compaction"] for a task-boundary checkpoint event. Distinct from CompactionEventTag ("summary") so the audit log + telemetry can distinguish "we hit the token wall and summarized" from "the model said this task was done."
const CheckpointNoteKey = "checkpoint_note"
CheckpointNoteKey carries the operator/model-supplied task note (the `detail` arg of mark_task_done, or the operator's /done argument) on the checkpoint event's CustomMetadata. Parallel to CompactionFocusKey for compaction events.
const CompactionEventTag = "summary"
CompactionEventTag is the value stored under session.Event.CustomMetadata["compaction"] to mark an event as a compaction summary. The history-slicing path in Run scans for the most recent event carrying this marker and drops everything before it from the LLM request. See docs/context-management-design.md (Mechanism A) for the rationale.
const CompactionFocusKey = "compaction_focus"
CompactionFocusKey carries the operator-supplied focus hint (from `/compact <focus>` or Agent.Compact(ctx, focus)) on the summary event's CustomMetadata, so it survives in the audit log alongside the summary text.
const CompactionMetadataKey = "compaction"
CompactionMetadataKey is the key under which CompactionEventTag is stored on session.Event.CustomMetadata. Exported so consumers querying the audit log can find summaries deterministically.
const DefaultAppName = "core-agent"
DefaultAppName tags this process in the ADK runner. Telemetry and session stores key off this; override with WithAppName when embedding in a host that wants its own identity.
const DefaultCompactionThreshold = 0.85
DefaultCompactionThreshold is the fallback for DefaultCompactor.Threshold when no per-tier entry matches. 0.85 leaves headroom for one more full turn before hitting the actual context wall, and matches the historical universal default so frontier sessions see no behavior change.
const DefaultInstruction = `You are a helpful assistant. Be concise and accurate.
For non-trivial work — multi-file edits, architectural choices, or asks with multiple valid approaches — sketch your plan in 1-3 sentences before acting so the user can redirect cheaply. Skip the preamble for trivial asks (typo fixes, single-line changes, narrowly-scoped tasks with one obvious solution); just do them.
Tools execute in parallel by default. Execute multiple independent tool calls in parallel when feasible — searching, reading files, independent shell commands, or editing different files. When investigating code, if you need to read multiple files or grep multiple directories, issue all the tool calls in a single response; do not execute them one by one.
Do not issue multiple ` + "`edit_file`" + ` or ` + "`write_file`" + ` calls targeting the same path in one response — those must run sequentially across turns so each edit sees the prior result; parallel writes to the same file race and corrupt state. Efficiency is secondary to correctness: if you are unsure whether two operations are independent, run them sequentially.
Earlier conversation may have been summarized into context for you in one of two shapes: "[Conversation compacted…]" framing (we hit the context wall mid-task and the prior turns were condensed), or "[The prior task is complete…]" framing (the prior task closed cleanly and a handover record replaces its history). Both arrive wrapped at the start of your context, both are authoritative shared history. Read FROM them when the user references prior work — what was discussed, what files were touched, what was decided, recap, summary, status — rather than re-running tools to rediscover what's already recorded there. The conversation continues in both cases; treat the framing as picking up an in-progress session, not as a fresh start.`
DefaultInstruction is the system instruction applied to every agent that doesn't override it via WithInstruction. Comprises a baseline helpfulness/concision directive plus a parallelism mandate adapted from google-gemini/gemini-cli's prompt patterns (packages/core/src/prompts/snippets.ts).
The parallelism mandate is load-bearing for Gemini, which otherwise emits one tool call per assistant turn even when independent operations are obviously batchable. Direct measurement (dev/parallel-probe/) shows Gemini-3.1-pro-preview-customtools without this instruction never batched across 65 search turns; Claude is less affected but still benefits marginally.
Exported so consumers building on WithInstruction can reuse the baseline + mandate verbatim: `agent.WithInstruction(agent.DefaultInstruction + "\n\n" + extra)`.
const DefaultSchedulingInstruction = `` /* 1137-byte string literal not displayed */
DefaultSchedulingInstruction is the composable system-instruction constant for autonomous loops that have a tools.Scheduler installed (via RunAutonomous's WithScheduler option, or per-subagent via BackgroundAgentManager). It covers the cross-cutting cadence and state-persistence guidance that doesn't fit in the schedule_next_turn tool's per-call description.
Opt-in by composition — the autonomous driver does NOT inject this automatically. Recommended consumer usage:
agent.New(m,
agent.WithInstruction(
agent.DefaultInstruction + "\n\n" +
agent.DefaultSchedulingInstruction + "\n\n" +
myConsumerInstruction,
),
agent.WithTools(...),
)
See docs/scheduled-monitoring-design.md for the design rationale and the matching tool-description text (Layer 1 of the steering pattern).
Variables ¶
var ErrDepthExceeded = errors.New("agent: BackgroundAgentManager: max subagent depth exceeded")
ErrDepthExceeded is returned by Spawn when the calling context is already at the max subagent depth.
var ErrInboxClosed = errors.New("agent: inbox closed")
ErrInboxClosed is returned by Agent.Inject when the agent's inbox has been closed (typically because the agent has been shut down via its run context cancelling). Callers that publish messages on an unrelated lifecycle (e.g. a stdin scanner) should treat this as "stop publishing."
var ErrManagerClosed = errors.New("agent: BackgroundAgentManager: closed")
ErrManagerClosed is returned by Spawn after Close has been called.
var ErrNoCheckpointer = errors.New("agent: no checkpointer wired (pass WithCheckpointer at agent.New)")
ErrNoCheckpointer is returned by Agent.Checkpoint when the agent was constructed without WithCheckpointer. Callers should check for this sentinel before treating it as a hard failure.
var ErrNoCompactor = errors.New("agent: no compactor wired (pass WithCompactor at agent.New)")
ErrNoCompactor is returned by Agent.Compact when the agent was constructed without WithCompactor. Callers should check for this sentinel before treating it as a hard failure.
var ErrNoParent = errors.New("agent: BackgroundAgentManager: 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 ErrNoSpawner = errors.New("agent: 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 ErrSubagentExists = errors.New("agent: BackgroundAgentManager: subagent with this name already exists")
ErrSubagentExists is returned by Spawn when a subagent with the requested name is already registered (running or terminal). Names must be unique within a manager.
ErrSubagentSpawnerUnavailable is returned by AttachSpawnSubagent when the agent wasn't constructed with WithBackgroundManager. The attach handler maps this to HTTP 501 so the operator sees "subagent spawn not registered" instead of a 500.
var ErrSubtaskSpecInvalid = errors.New("agent: invalid SubtaskSpec")
ErrSubtaskSpecInvalid is returned when SubtaskSpec fails validation (empty Name / SystemPrompt / UserMessage). Pre- flight check so the caller gets a clear error rather than a confusing downstream ADK failure.
var ErrTooManyConcurrent = errors.New("agent: BackgroundAgentManager: max concurrent subagents reached")
ErrTooManyConcurrent is returned by Spawn when the manager already has MaxConcurrent running subagents.
var ErrUnknownScheduler = errors.New("agent: BackgroundAgentManager: unknown scheduler choice")
ErrUnknownScheduler is wrapped and returned by Spawn when a spec.Scheduler value isn't one of the recognized choices.
var ErrUnknownTool = errors.New("agent: BackgroundAgentManager: 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 CurrentSubagentDepth ¶
CurrentSubagentDepth returns the recursion depth of the current subagent invocation. Zero when we're not inside a subagent (i.e. the parent's top-level turn).
func EventlogMetadataExtractor ¶
func EventlogMetadataExtractor() eventlog.MetadataExtractor
EventlogMetadataExtractor returns an eventlog.MetadataExtractor that pulls the per-event auth.Caller identity (and proxy attribution, when present) from the request context onto the eventlog row's sidecar metadata.
Pass to eventlog.Open via eventlog.WithMetadataExtractor — typically at daemon startup in cmd/core-agent. The extractor is a pure function of the per-event context, so the eventlog package itself stays auth-agnostic; only the binary that wires both packages together depends on both.
Returns nil entries when no Caller is on context (legacy / single-user / out-of-band Run callers); the eventlog package then stores no sidecar JSON for that row.
func FormatAutoContinueInbox ¶
FormatAutoContinueInbox renders messages with the system-note framing used by the TUI's "auto-continue from queued input" flow (see docs/operator-input-design.md). The framing tells the model these notes arrived while it was working and gives it explicit branches for the cases that show up in practice — most importantly the dedup case (operators frequently re-phrase the same ask while waiting) and the post-completion case (the prior turn ended with mark_task_done, so there's no "current task" to adapt).
Iteration history:
- v1 (PR-α): "If any of these change the current task, adapt your next step. If they're separate requests, use the `todo` tool to capture them and continue what you were doing." Both branches assumed an active task to "continue" — false after mark_task_done. Bundle of similar asks got handled as N separate tasks (issue #144's read_file loop pattern).
- v2 (#144): explicit branches for (a) variants-of-the-same-ask dedup, (b) mid-task adjustments, (c) separate asks during an active task, (d) post-completion bundles. The post-completion branch tells the model to treat the bundle as the next operator request and respond once.
Returns "" when messages is empty (caller should not auto- continue in that case). Format intentionally differs from formatInboxForPrompt (which prepends to an operator-typed prompt) because auto-continue turns have no operator prompt to prepend to — the formatted block IS the user message for the new turn.
func IsCostCeilingExceeded ¶
IsCostCeilingExceeded returns true when err was returned by Run because a previous turn tripped a configured cost ceiling. Operators / hosts use this to distinguish "operator must reset the ceiling" from other Run errors that may warrant retry.
func NewBackgroundSpawnTools ¶
func NewBackgroundSpawnTools(mgr *BackgroundAgentManager) []tool.Tool
NewBackgroundSpawnTools is a convenience that returns all four model-facing background-agent tools in one slice, ready to pass through agent.WithTools. The bundled CLI uses this to wire the full suite atomically.
func NewCheckAgentTool ¶
func NewCheckAgentTool(mgr *BackgroundAgentManager) tool.Tool
NewCheckAgentTool returns a tool the parent's model can call to inspect one subagent's detailed status — including its terminal result (final text, stop reason, totals) once it's finished.
func NewListAgentsTool ¶
func NewListAgentsTool(mgr *BackgroundAgentManager) tool.Tool
NewListAgentsTool returns a tool the parent's model can call to see every subagent the manager has tracked (running + terminal). Empty list when none have been spawned.
func NewMarkTaskDoneTool ¶
NewMarkTaskDoneTool returns the model-facing tool that signals task completion. The handler doesn't fire the checkpoint inline — that would require synchronous LLM I/O from inside a tool call, which ADK's runner doesn't expect. Instead the handler stashes the detail on the agent and flips a pending flag; Agent.Run's post-turn hook picks it up and fires Checkpoint before the next turn.
Takes a getter rather than a *Agent directly because we register this tool BEFORE the agent struct is constructed (llmagent.New snapshots its tool list at construction time, so registration has to happen up front). The getter resolves lazily — agent.New sets the agent pointer after llmagent.New returns, and the getter walks the closure to find it. A nil return from the getter is treated as "registration race not yet completed" and the call is a silent no-op (defensive — shouldn't happen in practice because the model never sees the tool before agent.New returns).
Registered automatically in agent.New when a Checkpointer is wired via WithCheckpointer.
func NewSpawnAgentTool ¶
func NewSpawnAgentTool(mgr *BackgroundAgentManager) tool.Tool
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 *BackgroundAgentManager) (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 list/check/stop 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 NewStopAgentTool ¶
func NewStopAgentTool(mgr *BackgroundAgentManager) tool.Tool
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 NewSubagentTool ¶
func NewSubagentTool(opts SubagentOptions) (tool.Tool, error)
NewSubagentTool wraps an *agent.Agent as a tool the parent's model can call. The subagent runs through ADK's runner using the parent's session.Service (so its events stream live into the same audit log as the parent), with session.Event.Branch set to "<parent_branch>.<this>" so the audit log stays distinguishable and ADK's contents-processor branch filter keeps the subagent's events from leaking into the parent's next-turn LLM request.
The parent's session.Service is captured from Inner — Inner is expected to have been constructed with the same WithEventLog (or WithSessionService) the parent uses. The agent.WithSubagents convenience option handles this wiring automatically; consumers who construct subagent tools directly via NewSubagentTool need to share the session.Service themselves.
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent is the wrapper around an ADK llmagent + runner. One Agent represents one configured LLM-driven role.
func New ¶
New constructs an Agent backed by model. Returns a clear error if the underlying ADK constructors reject the configuration.
func (*Agent) AgentName ¶
AgentName returns the configured agent name (the WithName value) stored on construction. Used by NewSubagentTool to derive a default tool name.
func (*Agent) AppName ¶
AppName returns the AppName the agent was constructed with (the value passed to runner.Config). Used by callers that need to identify the session triple (app, user, session) for queries against the event log or session.Service.
func (*Agent) AskSideQuestion ¶
AskSideQuestion runs one tool-less LLM call that sees the agent's current conversation history plus the supplied question, and returns the model's answer as a single string. Intended for the TUI's /btw side-question flow (docs/operator-input-design.md layer C): the operator asks a quick context-grounded question ("what was that file again?") without polluting the main conversation. The call bypasses Agent.Run entirely — no inbox drain, no permission gating, no event-log writeback, no tools.
The question is appended to the existing history as a transient user turn that exists only for this one call; nothing about the agent's persisted session state changes. The model can therefore reference prior tool output, prior assistant turns, prior user messages — but cannot call any tool to do new work.
Errors:
- context cancellation: returns ctx.Err().
- no session.Service wired: returns a clear error (defensive; agent.New always installs one, but hand-constructed Agents used in tests don't).
- GenerateContent failures bubble up unchanged so callers can distinguish transport vs API vs model errors via errors.Is.
func (*Agent) AttachAddAllow ¶
AttachAddAllow implements attach.PermsController. Delegates to permissions.Gate.AddAllowPatterns. Returns nil if no gate was wired (no-op rather than error — operators shouldn't see an error for an absent gate). Surfaces validation errors from the gate so the operator sees malformed-pattern feedback.
func (*Agent) AttachAddDeny ¶
AttachAddDeny implements attach.PermsController. Delegates to permissions.Gate.AddDenyPatterns.
func (*Agent) AttachAgents ¶
AttachAgents implements attach.AgentsProvider. Returns the live background subagents from this agent's BackgroundAgentManager, or an empty slice when no manager was wired.
func (*Agent) AttachAskSideQuestion ¶
AttachAskSideQuestion implements attach.SideQueryProvider. Wraps Agent.AskSideQuestion (the /btw side-channel that doesn't persist to the event log).
func (*Agent) AttachCheckpoint ¶
func (a *Agent) AttachCheckpoint(ctx context.Context, note string) (attach.CheckpointResponse, error)
AttachCheckpoint implements attach.CheckpointSlashProvider. Wraps Agent.Checkpoint.
func (*Agent) AttachCompact ¶
AttachCompact implements attach.CompactSlashProvider. Wraps Agent.Compact and projects the result into the JSON wire format. Errors propagate; the attach handler turns them into 500s.
func (*Agent) AttachContext ¶
func (a *Agent) AttachContext() attach.ContextInfo
AttachContext implements attach.ContextProvider. Projects the agent's ContextStats (compaction / checkpoint / subtask shape) into the attach wire format. Same cost as ContextStats (one session.Service.Get() + O(events) scan) — operator-driven, infrequent.
func (*Agent) AttachInterrupt ¶
AttachInterrupt implements attach.InterruptProvider so the attach-mode POST /sessions/<sid>/interrupt handler can dispatch cancel intents from a remote operator without importing this package directly.
func (*Agent) AttachMemory ¶
func (a *Agent) AttachMemory() []attach.MemorySource
AttachMemory implements attach.MemoryProvider. Returns nil when no provider was wired — the handler emits 200 with an empty `{"sources": []}`.
func (*Agent) AttachPerms ¶
AttachPerms implements attach.PermsProvider. Returns the gate's current Snapshot (mode + allow + deny pattern lists) projected into the attach wire format, plus the per-session approval log so the remote TUI's /permissions slash can render what was approved this session. Returns zero PermsInfo if no gate was wired via WithGate.
func (*Agent) AttachPricing ¶
func (a *Agent) AttachPricing() attach.PricingInfo
AttachPricing implements attach.PricingProvider.
func (*Agent) AttachPromptBroker ¶
func (a *Agent) AttachPromptBroker() *attach.PromptBroker
AttachPromptBroker implements attach.PromptBrokerProvider.
func (*Agent) AttachRefreshPricing ¶
AttachRefreshPricing implements attach.PricingController. Returns attach.ErrCapabilityNotRegistered when no func was wired — the handler maps that to HTTP 501.
func (*Agent) AttachReload ¶
func (a *Agent) AttachReload(ctx context.Context) attach.ReloadResponse
AttachReload implements attach.Reloader. Returns a response with Errors populated by ErrCapabilityNotRegistered when no func was wired so the handler emits the same 501 the other unwired controllers do.
func (*Agent) AttachReplan ¶
func (a *Agent) AttachReplan(ctx context.Context, req attach.ReplanRequest) (attach.ReplanResponse, error)
AttachReplan implements attach.ReplanProvider. Routes to the closure wired by WithAttachReplanner; returns ErrCapabilityNotRegistered when no func was wired.
func (*Agent) AttachSetManualPricing ¶
func (a *Agent) AttachSetManualPricing(req attach.PricingSetRequest) error
AttachSetManualPricing implements attach.PricingController.
func (*Agent) AttachSkills ¶
AttachSkills implements attach.SkillsProvider.
func (*Agent) AttachSpawnSubagent ¶
func (a *Agent) AttachSpawnSubagent(ctx context.Context, spec attach.SubagentSpec) (attach.SubagentSpawnResponse, error)
AttachSpawnSubagent implements attach.SubagentSpawner. Delegates to the wired BackgroundAgentManager. Returns ErrSubagentSpawnerUnavailable when no manager is attached.
func (*Agent) AttachStatus ¶
func (a *Agent) AttachStatus() attach.StatusInfo
AttachStatus implements attach.StatusProvider. V1 returns the agent's model name + a coarse "idle" state — finer-grained state (running / deferred / paused) would require run-loop instrumentation that hasn't been wired yet; the design doc captures pause/resume + state mutation as v3 work.
func (*Agent) AttachTools ¶
AttachTools implements attach.ToolsProvider. Returns the agent's full tool catalog as ToolInfo entries with source classification (builtin vs other) and the gate's pre-flight state per tool when a gate was wired via WithGate. MCP / skill attribution is "other" in v1 — distinguishing them at the slice level needs an upstream metadata pass we haven't done yet.
func (*Agent) AttachUsage ¶
AttachUsage implements attach.UsageProvider. Returns the agent's usage tracker totals plus a per-model breakdown when more than one model has been used in this session (typical pattern: parent on a frontier model, subtasks on a cheap flash-tier model via --agentic-small-model), plus a per-turn array so operators can answer per-turn cost/cache questions without hand-scraping the eventlog (issue #222). Returns a zero UsageInfo if no usage tracker was wired (WithUsageTracker).
cost_usd_uncached_reference is computed per-turn using the resolved pricing for that turn's model — sessions that mix models (parent + subtask on a flash-tier via --agentic-small-model) get accurate reference numbers instead of averaging one model's rates over the other. Rolled up into Overall / PerModel by summing per-turn contributions.
func (*Agent) BackgroundManager ¶
func (a *Agent) BackgroundManager() *BackgroundAgentManager
BackgroundManager returns the BackgroundAgentManager the agent was constructed with via WithBackgroundManager, or nil when none was wired. Used by spawn tools + the runner's REPL alert display to reach the manager without keeping a separate reference.
func (*Agent) Checkpoint ¶
Checkpoint writes a task-boundary checkpoint event to the session and clears any pending checkpoint flag. Like Compact, the event becomes the slicing boundary for the next turn's model request.
taskNote is the operator/model-supplied detail (the mark_task_done `detail` arg, or /done's argument). Empty is fine — the prompt still produces a useful summary, just without the leading completion note.
Errors:
- ErrNoCheckpointer when no checkpointer was wired.
- Context cancellation: ctx.Err().
- Model errors propagate wrapped so callers can errors.Is on transport vs API failures.
func (*Agent) Compact ¶
Compact runs an out-of-band summarizer LLM call against the current session's history and writes the result as a "summary" marker event the history-slicing path in Run picks up on the next turn. Used both programmatically and by the TUI's /compact slash. See Agent.Checkpoint for the task-boundary variant that shares this machinery.
focus is an optional hint for the summarizer ("focus on the auth-rewrite thread"). Empty is fine — the default prompt instructs the model to produce a balanced handover.
Errors:
- ErrNoCompactor when no compactor was wired.
- Context cancellation: ctx.Err().
- Model errors propagate wrapped so callers can errors.Is on transport vs API failures.
func (*Agent) CompactIfNeeded ¶
CompactIfNeeded fires Compact when the wired Compactor's ShouldCompact returns true, otherwise reports Skipped=true. Useful for hosts that want to drive automatic compaction on a turn-end hook without re-implementing the threshold check.
func (*Agent) ContextStats ¶
func (a *Agent) ContextStats() ContextStats
ContextStats returns a snapshot of compaction/checkpoint/subtask activity for this session. Safe to call from any goroutine. Errors fetching the session (e.g. session.Service hiccup) are swallowed and result in zero boundary fields — the subtask fields are populated regardless since they're in-memory.
Cost: one session.Service.Get() call + O(events) scan. Designed for operator-driven /context slash usage, not hot-path telemetry — cache or sample if you need it per-turn.
func (*Agent) CostCeilingTripped ¶
CostCeilingTripped reports whether the agent is currently blocking new turns because a configured ceiling was exceeded. Exposed for /stats and similar UI surfaces; operators surface this alongside the totals so the "why is the agent refusing my prompts?" question has an obvious answer.
Returns (true, reason) when blocked; (false, "") otherwise.
func (*Agent) Description ¶
Description returns the one-line description set via WithDescription. Empty when unset. Satisfies attach.DescriptionProvider — the /.well-known/agent-card.json handler falls back to this when no explicit AgentCardConfig.Description override is supplied.
func (*Agent) DrainInbox ¶
DrainInbox pulls every currently-queued message off the inbox and returns them in arrival order. The queue is emptied as a side effect — repeat calls return nil until new messages arrive.
Exposed so harnesses that want to format inbox messages differently from the built-in `[Inbox]` block (e.g. the TUI's auto-continue flow uses FormatAutoContinueInbox to add system-note framing) can pull the messages themselves and pass the formatted result to Run as a regular prompt. Run's internal drain then finds an empty inbox and is a no-op, so there's no double-injection.
Returns nil when no agent or no inbox is wired — both are defensive cases for hand-constructed Agent structs in tests.
Emits an `inbox`/dequeued event for each drained message with the message's prompt_id, so SSE consumers can correlate the dequeue with the earlier queued event.
Callers that need the per-message originator identity (multi-session turn threading) should use drainInboxFull instead (internal). The public DrainInbox shape is preserved for external harnesses that only care about the text payloads.
func (*Agent) EventLog ¶
EventLog returns the *eventlog.Handle the agent was constructed with via WithEventLog, or nil when no event log was wired. Use to reach back to Stream.Since / Stream.Watch for replay or live tail without keeping a separate reference.
func (*Agent) HasCheckpointer ¶
HasCheckpointer reports whether a Checkpointer was wired via WithCheckpointer. Hosts use this to gate `/done` (and the `/checkpoint` alias) out of `/help` and the slash palette when --no-checkpoint was passed. Same shape as HasCompactor.
func (*Agent) HasCompactor ¶
HasCompactor reports whether a Compactor was wired via WithCompactor. Hosts use this to gate operator-facing surfaces: don't list `/compact` in `/help` when there's nothing to invoke. Same idea as nil-checking a.compactor directly, but exported so adapters living outside the agent package don't need a reflection trick.
func (*Agent) InboxArrived ¶
func (a *Agent) InboxArrived() <-chan struct{}
InboxArrived returns a channel that fires when a new message has been injected. The harness can use this to decide when to start the next turn instead of polling — typical pattern:
for {
select {
case <-ctx.Done():
return
case <-ag.InboxArrived():
runOneTurn("continue") // inbox prepended automatically
}
}
The channel has a 1-buffer signal-style semantics: multiple pushes between consumer wake-ups coalesce into one notification (the consumer drains the queue and sees them all).
func (*Agent) Inject ¶
Inject queues message on the agent's inbox. The next call to Agent.Run will drain pending messages, format them as an "[Inbox]" block, and prepend the block to the prompt the model sees.
Inject is safe to call concurrently from any goroutine — typical usage is a harness goroutine reading from stdin, an HTTP handler, or an orchestrator's gRPC stream. Drop-oldest backpressure kicks in when the queue exceeds the soft cap (256) so a stuck consumer can't deadlock the agent.
Emits an `inbox`/queued event on the attach event stream (if one is wired) with the assigned prompt_id, so SSE consumers can surface the queued state to operators. The same prompt_id flows out as the `inbox`/dequeued event when the inbox is drained inside Run.
Returns ErrInboxClosed once the agent has been shut down; callers publishing on an unrelated lifecycle should treat that as "stop."
Inject queues without an originator identity. Callers that have a per-request auth.Caller (typically attach handlers in a multi-session deployment) should use InjectAs instead — the caller is threaded into the turn context so the eventlog metadata sidecar and per-caller MCP path see who triggered the turn.
func (*Agent) InjectAs ¶
InjectAs is Inject with a per-message originator identity. The caller is stored on the queued message and used as the turn originator when the inbox drains: the agent loop wraps the turn context with auth.WithCaller(caller) so eventlog metadata, MCP outbound context, and other caller-aware substrates see the identity that triggered the turn.
Zero-value caller (Identity == "") is equivalent to Inject — no identity is threaded.
When multiple injects queue between turns, the LAST message's caller wins as the turn originator (per docs/multi-session-design.md "the turn answers the most recent ask"). Same prompt_id correlation, same SSE events as Inject.
func (*Agent) Interrupt ¶
Interrupt cancels the in-flight turn (if any) by invoking the stored cancel func. Returns true if there was something to cancel (a turn was in flight when called), false if the agent was idle (no-op). Safe for concurrent callers; the cancel is single-shot per turn — a second Interrupt during the same turn is a no-op.
Cancellation propagates through context.Canceled to the in-flight model call. The agent's tools (bash, fetch_url, etc.) cancel their I/O when they see the cancel; the model call returns immediately with a partial response; the run loop emits any already-accumulated content and exits. Sessions, the event log, background subagents, and the attach registry all survive untouched.
func (*Agent) ModelName ¶
ModelName returns the name of the LLM the agent was constructed with (sourced from model.Name() at New() time). Used by the attach-mode /status endpoint so the TUI usage panel can label the in/out/cost figures with the model in use.
func (*Agent) PendingInboxCount ¶
PendingInboxCount peeks at the inbox without draining it. Useful for UI surfaces (TUI queue panel) that need to render the current queue length without claiming the messages.
Returns 0 for nil agent / inbox.
func (*Agent) RequestWake ¶
func (a *Agent) RequestWake()
RequestWake fires the agent's wake signal. Used by:
- BackgroundAgentManager (via a driver-side goroutine) to wake a sleeping supervisor as soon as a child alert arrives, instead of waiting for the supervisor's next scheduled wake.
- The future attach-mode `POST /sessions/<id>/wake` endpoint, when an operator outside the process wants an immediate rescan.
- Operator input via Agent.Inject — Inject calls RequestWake internally so a typed command also pierces an active sleep.
No-op when the agent has no wake signal (defensive: hand-constructed Agent structs used in tests don't necessarily wire one up).
func (*Agent) ResetCostCeiling ¶
func (a *Agent) ResetCostCeiling()
ResetCostCeiling clears any tripped cost-ceiling flag, allowing the agent to accept new turns again. Typically wired to an operator slash command after the operator has reviewed why the ceiling tripped. Safe to call even if no ceiling is configured or no flag was set — no-op in that case.
func (*Agent) Run ¶
Run executes one turn of the agent against prompt and returns the event iterator straight from ADK's runner. Callers are expected to range over the returned iter.Seq2 and consume events as they arrive — partial text chunks, tool calls, and the final TurnComplete event.
Multi-turn use: call Run() repeatedly on the same Agent. The configured session ID is reused across calls, so the ADK accumulates conversation history automatically.
When a BackgroundAgentManager is wired via WithBackgroundManager, any alerts background subagents have emitted since the last turn are drained (non-blocking) and prepended to the prompt so the parent's model sees them before deciding what to do next.
Inbox messages queued via Agent.Inject from external callers (harness, orchestrator, HTTP handler) are also drained and prepended, sibling to the alerts block. Ordering: alerts go first (internal state changes); inbox goes second (external input, closer to the prompt logically); then the original prompt.
func (*Agent) RunSubtask ¶
func (a *Agent) RunSubtask(ctx context.Context, spec SubtaskSpec) (SubtaskResult, error)
RunSubtask runs a synchronous, fresh-context, single-purpose LLM call against the parent's session.Service (with a distinct branch so events don't leak into the parent's next Run request). Returns the model's digested answer.
Properties:
- Caller blocks until the subtask returns.
- The subtask runs in its own ADK session (deriveSubagentSessionID produces a parent-prefixed ID; the audit log can correlate).
- The subtask sees NO parent history; its model gets only the SystemPrompt + UserMessage from the spec.
- The subtask's tool set is the spec's Tools — nothing inherited from the parent.
- Cost rolls up to the parent's usage.Tracker (when one is wired via WithUsageTracker) so /stats reflects everything.
- On budget exhaustion: returns SubtaskResult{Truncated: true} with whatever partial Digest accumulated. NOT a Go error.
- On model failure or other unrecoverable error: returns the wrapped error; caller can errors.Is on transport vs API vs spec-validation failure.
Used by the agentic tool wrappers in core-agent/tools ( AgenticReadFile, AgenticFetchURL, AgenticGrep, AgenticResearch); also directly callable by host code that wants to spawn a one-shot research subagent without going through a tool.
func (*Agent) RunWithContents ¶
func (a *Agent) RunWithContents(ctx context.Context, contents []*genai.Content) iter.Seq2[*session.Event, error]
RunWithContents drives one agent turn from a pre-built conversation history (genai.Contents) instead of a single prompt string. The trailing message is treated as the new user input; everything before it is pre-populated into a fresh session as history events.
Each call uses a fresh sessionID so prior calls don't accumulate state — the caller-supplied history is authoritative. Use this when integrating with a runtime (the AX adapter is the motivating example) that supplies the full conversation history per turn rather than relying on a session-managed prompt.
The last content's Role must be genai.RoleUser; non-user trailing messages return an error. Empty contents return an error.
func (*Agent) SessionID ¶
SessionID returns the session identifier the agent was constructed with. Combined with AppName + UserID this is the key the event log uses to scope ForSession queries.
func (*Agent) SessionService ¶
SessionService returns the session.Service backing this agent. When no WithSessionService option was passed at construction this is the default in-memory service. Useful for callers that want to query session state directly (e.g. listing prior events) without keeping their own reference to the Service they passed in.
func (*Agent) SetAttachEmitter ¶
SetAttachEmitter installs (or clears, when f is nil) the callback the agent uses to push typed events onto the attach SSE event stream. The attach broadcaster calls this on first subscriber (wiring its Emit method) and again with nil when the last subscriber disconnects.
When a tracker is wired via WithUsageTracker, SetAttachEmitter also installs (or clears) a tracker.SetOnAppend callback that emits a usage-update event with cumulative + per-model totals after every Append. That's what carries the "running cost" the spec describes for the usage-update event type — the turn-complete event reports 0 for cost because the agent itself has no pricing reference (pricing lives in the harness).
Optional. When no callback is installed, all agent-side emit calls are no-ops — events are dropped to the floor since no consumer can see them. This matches the protocol's design intent: typed events are operator-visible signals, not audit log entries; if there's no operator, there's nothing to signal.
Safe to call concurrently with the agent's own emit path; the internal mutex serializes the swap and any in-flight emit reads.
func (*Agent) Tools ¶
Tools returns the resolved tool list the agent was constructed with — including any subagent tools materialized by WithSubagents. Useful for diagnostics ("does my parent know about the research subagent?") without introspecting ADK internals.
func (*Agent) WakeRequested ¶
func (a *Agent) WakeRequested() <-chan struct{}
WakeRequested returns a channel that fires whenever RequestWake (or Inject, which calls RequestWake internally) is invoked. The autonomous driver attaches this channel to the context it passes to Scheduler.BeforeNextTurn so SleepScheduler can select on it alongside its sleep timer and ctx.Done. Buffered(1) coalesced semantics: multiple wakes between consumer drains land as one notification.
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 AutonomousHandle ¶
type AutonomousHandle struct {
// contains filtered or unexported fields
}
AutonomousHandle is the programmatic-control surface returned by StartAutonomous. The autonomous loop runs in its own goroutine; methods on the handle are safe for concurrent callers.
Typical usage from a harness:
h, _ := agent.StartAutonomous(ctx, build, "monitor cluster X",
agent.WithMaxTurns(0), agent.WithMaxWallclock(time.Hour))
defer h.Stop()
// Inject new instructions as they arrive from outside:
h.Inject("priority changed: focus on Q4 review")
// Or pause briefly:
h.Pause(); ...; h.Resume()
// Block until terminal:
result, err := h.Wait()
func StartAutonomous ¶
func StartAutonomous(ctx context.Context, build BuildFunc, goal string, opts ...AutonomousOption) (*AutonomousHandle, error)
StartAutonomous launches a new autonomous run in a goroutine and returns a handle the caller uses to control / observe it. Otherwise identical surface to RunAutonomous — same BuildFunc, same options. Wait() returns the same RunResult shape.
The goroutine context is derived from ctx with our own cancel function so Stop() can cancel independently of the caller's ctx. If the caller's ctx fires, the run is still cancelled (the derived ctx inherits cancellation).
func (*AutonomousHandle) Done ¶
func (h *AutonomousHandle) Done() <-chan struct{}
Done returns the channel that closes when the autonomous goroutine exits. Useful when a caller wants to combine the wait with other selects (e.g. ctx + Done).
func (*AutonomousHandle) Inject ¶
func (h *AutonomousHandle) Inject(message string) error
Inject queues a message on the underlying agent's inbox. The next turn drains the inbox and prepends an "[Inbox]" block to the prompt the model sees. Returns an error when called before the goroutine has constructed the agent (typically a fraction of a second after StartAutonomous returns) or after the agent is inaccessible.
func (*AutonomousHandle) InjectAs ¶
func (h *AutonomousHandle) InjectAs(message string, caller auth.Caller) error
InjectAs is Inject with a per-message originator identity (see Agent.InjectAs). Same lifecycle rules as Inject.
func (*AutonomousHandle) Pause ¶
func (h *AutonomousHandle) Pause() error
Pause requests the loop to pause at the next per-turn checkpoint. The currently-running turn finishes normally; subsequent turns block until Resume fires or Stop / ctx cancellation tears the goroutine down.
Idempotent: calling Pause while already paused is a no-op. Returns an error only when called after the run has terminated.
Emits a synthetic "paused" event to the agent's eventlog (Author="<binary>/autonomous", CustomMetadata.kind="paused") for audit, when an eventlog is wired. No-op when not.
func (*AutonomousHandle) Ready ¶
func (h *AutonomousHandle) Ready() <-chan struct{}
Ready returns a channel that closes once the underlying agent has been constructed (i.e. the wrappedBuild closure inside the autonomous loop has run and captured the agent). Inject and RequestWake fail with "agent not yet constructed" when called before this fires; out-of-band consumers (stdin readers, alert watchers) should wait on Ready before issuing those calls. May already be closed by the time Ready returns — the select-on-Ready pattern handles both cases naturally.
func (*AutonomousHandle) RequestWake ¶
func (h *AutonomousHandle) RequestWake()
RequestWake fires the underlying agent's wake signal, interrupting any active scheduler sleep. Pairs with Inject for "operator nudged the loop, wake now" semantics; Inject already calls RequestWake internally, so this is for the alert-arrival case (or any other signal that doesn't carry a message). No-op when the agent hasn't been constructed yet.
func (*AutonomousHandle) Resume ¶
func (h *AutonomousHandle) Resume() error
Resume unblocks the autonomous loop's BeforeTurn hook so the next turn can start. Idempotent: calling Resume while not paused is a no-op. Returns an error only when called after the run has terminated.
Emits a synthetic "resumed" event to the agent's eventlog for audit, when an eventlog is wired.
func (*AutonomousHandle) Status ¶
func (h *AutonomousHandle) Status() AutonomousStatus
Status returns the current lifecycle state. Safe to call any time; the goroutine's terminal handoff is mutex-coordinated.
func (*AutonomousHandle) Stop ¶
func (h *AutonomousHandle) Stop() error
Stop cancels the run's context. The currently-running LLM call returns context.Canceled; the loop exits; the goroutine cleans up. Idempotent: subsequent Stop calls are no-ops.
If the loop is paused when Stop is called, the ctx cancellation unblocks the BeforeTurn hook (which selects on both pauseCh and ctx.Done) so the goroutine can exit.
func (*AutonomousHandle) Wait ¶
func (h *AutonomousHandle) Wait() (RunResult, error)
Wait blocks until the autonomous goroutine exits, then returns the same RunResult + error pair RunAutonomous returns. Safe to call from multiple goroutines; the result + err are set under the mutex once before the done channel closes.
type AutonomousOption ¶
type AutonomousOption func(*autoConfig)
AutonomousOption mutates RunAutonomous configuration. Use the With* helpers below.
func WithBeforeTurn ¶
func WithBeforeTurn(cb func(ctx context.Context, turnNo int) error) AutonomousOption
WithBeforeTurn installs a callback invoked at the top of each iteration of the autonomous loop, after budget checks and before the turn's runOneTurn call. The callback receives the upcoming turn number (1-based). Returning a non-nil error aborts the run with that error.
This is the seam AutonomousHandle uses to implement Pause: the callback blocks while paused, returning when Resume fires or the run context is cancelled. Library callers can wire arbitrary gating logic (rate limits, external approvals, etc.) on top.
func WithContinuationPrompt ¶
func WithContinuationPrompt(s string) AutonomousOption
WithContinuationPrompt overrides the prompt sent on every turn after the first. Default: "continue". Real consumers often pass something more specific to their loop ("what's your next step?").
func WithDoneToolDescription ¶
func WithDoneToolDescription(desc string) AutonomousOption
WithDoneToolDescription overrides the description shown to the model for the internal done tool. Override when the default prose doesn't fit your task — for example to instruct the model to call done only after writing a summary.
func WithDoneToolName ¶
func WithDoneToolName(name string) AutonomousOption
WithDoneToolName overrides the function name of the internal done tool. Useful when "report_done" collides with an existing tool the consumer has registered. Default: "report_done".
func WithMaxCost ¶
func WithMaxCost(usd float64) AutonomousOption
WithMaxCost caps the cumulative dollar cost of the run. Requires a non-zero pricing source — either WithTracker(tracker, pricing) or the recorded UsageMetadata being priced via the same Pricing.
func WithMaxDefer ¶
func WithMaxDefer(d time.Duration) AutonomousOption
WithMaxDefer is a driver-level ceiling on how far in the future the scheduler can wait. Zero means no cap, matching the existing WithMaxTurns / WithMaxWallclock convention. Acts as an operator safety net: if a turn emits a schedule intent past this ceiling, the driver clamps the wake-time and logs a warning, then proceeds with the clamped value. The model-facing cap is configured via WithScheduleToolMaxDefer.
func WithMaxTokens ¶
func WithMaxTokens(input, output int) AutonomousOption
WithMaxTokens caps the cumulative input + output token totals for the run. A zero value for either disables that side of the cap.
func WithMaxTurns ¶
func WithMaxTurns(n int) AutonomousOption
WithMaxTurns caps the number of turns the loop will execute. Zero disables the cap (use with caution; pair with another budget). The default is 50.
func WithMaxWallclock ¶
func WithMaxWallclock(d time.Duration) AutonomousOption
WithMaxWallclock caps the wall-clock duration of the run, measured from RunAutonomous entry. Checked between turns; a single rogue turn can still exceed this — pair with WithPerTurnTimeout to bound that.
func WithPerTurnTimeout ¶
func WithPerTurnTimeout(d time.Duration) AutonomousOption
WithPerTurnTimeout wraps each turn's context with a timeout so a single hung turn cannot stall the whole run. Distinct from WithMaxWallclock, which bounds total time.
func WithPermissionsGate ¶
func WithPermissionsGate(g *permissions.Gate) AutonomousOption
WithPermissionsGate hands the driver a reference to the permissions gate the consumer wired into their tools. The driver only uses this for one purpose: a startup check that rejects ask-mode + no-prompter configurations that would deadlock on the first tool call. The gate is otherwise enforced by the tools themselves; passing it here does not change runtime gating behavior.
Pass this when your build function constructs gated tools and your permission mode might be ask. Omit it for ModeYolo / ModeAllow runs where deadlock isn't a risk.
func WithPricing ¶
func WithPricing(p usage.Pricing) AutonomousOption
WithPricing sets the Pricing used for cost rollup when a usage.Tracker is not supplied. Useful for headless runs that just want a final dollar number on RunResult.
func WithProgress ¶
func WithProgress(cb func(turn int, ev *session.Event)) AutonomousOption
WithProgress invokes cb for every session.Event observed during the run. The turn index is the 1-based count of completed turns at the time the event is emitted (always at least 1 inside a turn).
func WithRetryPolicy ¶
func WithRetryPolicy(p RetryPolicy) AutonomousOption
WithRetryPolicy installs a callback consulted whenever a turn returns an error. The callback receives the error and the 1-indexed attempt count and returns one of AbortRun, RetryTurn, or SkipTurn. Without a policy, the driver aborts on the first error.
func WithScheduleToolDescription ¶
func WithScheduleToolDescription(desc string) AutonomousOption
WithScheduleToolDescription overrides the description shown to the model for the internal schedule tool. The default includes a cadence ladder, good-vs-bad next_prompt examples, and the state-persistence reminder; override when domain-specific guidance is needed (e.g. "always wake by the top of the hour"). Only takes effect when WithScheduler is also set.
func WithScheduleToolMaxDefer ¶
func WithScheduleToolMaxDefer(d time.Duration) AutonomousOption
WithScheduleToolMaxDefer sets the tool-level cap on how far the model may schedule a wake. Calls past the cap return a tool-result error to the model so it can adapt. Zero means no cap. Distinct from WithMaxDefer, which is the driver's silent safety net. Only takes effect when WithScheduler is also set.
func WithScheduleToolName ¶
func WithScheduleToolName(name string) AutonomousOption
WithScheduleToolName overrides the function name of the internal schedule tool. Useful when the default "schedule_next_turn" collides with a consumer-registered tool. Only takes effect when WithScheduler is also set.
func WithScheduler ¶
func WithScheduler(s coretools.Scheduler) AutonomousOption
WithScheduler installs a tools.Scheduler that's consulted between turns when the prior turn emitted a schedule intent via the schedule_next_turn tool. Loops without a scheduler don't get the tool registered at all, so the model can't emit intent the driver has no way to honor.
Bundled schedulers: tools.SleepScheduler() for long-lived daemons (sleeps the goroutine between turns), tools.ExitOnDeferScheduler() for orchestrator-managed deployments (exits with StopReasonDeferred + RunResult.NextWakeAt populated, ResumeAutonomous picks up at the wake-time). See docs/scheduled-monitoring-design.md.
func WithTracker ¶
func WithTracker(t *usage.Tracker, p usage.Pricing) AutonomousOption
WithTracker hands the driver an existing usage.Tracker plus the Pricing to use for per-turn cost accounting. Each turn appends to the tracker; RunResult also rolls up totals independently so callers can read them without touching the tracker.
When omitted, RunResult still tracks tokens — but cost is zero unless a non-zero Pricing is supplied via WithPricing.
type AutonomousStatus ¶
type AutonomousStatus int
AutonomousStatus describes the lifecycle state of a run started via StartAutonomous. Read via AutonomousHandle.Status; transitions are driven by Pause / Resume / Stop and by the run goroutine's terminal handoff.
const ( // AutonomousRunning — goroutine is alive and not paused. AutonomousRunning AutonomousStatus = iota // AutonomousPaused — Pause was called; loop is blocked at the // next pre-turn checkpoint until Resume fires. AutonomousPaused // AutonomousStopped — Stop was called; goroutine has unwound or // is about to (the ctx cancel propagates through the current // turn's LLM/tool calls). AutonomousStopped // AutonomousCompleted — RunAutonomous returned with // Reason==Completed. AutonomousCompleted // AutonomousFailed — RunAutonomous returned with a non-Completed // terminal reason (budget exceeded, retry aborted, etc.) or a // Go error from the loop machinery. AutonomousFailed )
func (AutonomousStatus) String ¶
func (s AutonomousStatus) String() string
String renders the status for diagnostics and tool results.
type BackgroundAgentManager ¶
type BackgroundAgentManager struct {
// contains filtered or unexported fields
}
BackgroundAgentManager 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 NewBackgroundAgentManager ¶
func NewBackgroundAgentManager(opts ...BackgroundManagerOption) (*BackgroundAgentManager, error)
NewBackgroundAgentManager builds a manager from the supplied options. Required: provider + modelID (WithBackgroundProvider). 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 (*BackgroundAgentManager) Alerts ¶
func (m *BackgroundAgentManager) Alerts() <-chan Alert
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 (*BackgroundAgentManager) Close ¶
func (m *BackgroundAgentManager) Close() error
Close stops every running subagent and prevents new spawns. Blocks until each goroutine has exited so callers don't race with shutdown. Idempotent.
func (*BackgroundAgentManager) Get ¶
func (m *BackgroundAgentManager) Get(name string) (*BackgroundHandle, bool)
Get returns the handle for the named subagent. ok=false when the name isn't registered.
func (*BackgroundAgentManager) List ¶
func (m *BackgroundAgentManager) List() []*BackgroundHandle
List returns all currently-tracked handles, sorted by start time. Terminal handles remain in the list until Close (so check_agent can return final status). Defensive copy of slice.
func (*BackgroundAgentManager) OnAlert ¶
func (m *BackgroundAgentManager) OnAlert(h func(Alert))
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 (*BackgroundAgentManager) Parent ¶
func (m *BackgroundAgentManager) Parent() *Agent
Parent returns the agent the manager is attached to, or nil if no agent.New has wired it yet. Exposed for tests + diagnostics.
func (*BackgroundAgentManager) PrependPendingAlerts ¶
func (m *BackgroundAgentManager) PrependPendingAlerts(prompt string) string
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 (*BackgroundAgentManager) Spawn ¶
func (m *BackgroundAgentManager) Spawn(ctx context.Context, parentBranch string, spec BackgroundSpec) (*BackgroundHandle, error)
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 RunAutonomous 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 (*BackgroundAgentManager) Stop ¶
func (m *BackgroundAgentManager) Stop(name string) error
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.
type BackgroundBudgets ¶
type BackgroundBudgets struct {
MaxTurns int
MaxCost float64
MaxWallclock time.Duration
PerTurnTimeout time.Duration
}
BackgroundBudgets bounds a single spawned subagent's run. Zero values mean no cap for that dimension. The manager's WithBackgroundDefaultBudgets supplies the defaults; per-spawn overrides come from the spawn_agent tool args.
type BackgroundHandle ¶
type BackgroundHandle struct {
Name string
Branch string
StartedAt time.Time
// contains filtered or unexported fields
}
BackgroundHandle is the lifecycle record for one spawned subagent. Exposed read-only via Manager.List / Manager.Get so the parent model's check_agent tool can introspect status without reaching into internal state.
func (*BackgroundHandle) Done ¶
func (h *BackgroundHandle) 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 (*BackgroundHandle) Err ¶
func (h *BackgroundHandle) Err() error
Err returns the terminal error if the subagent's RunAutonomous returned one. Nil while running or on clean completion.
func (*BackgroundHandle) Result ¶
func (h *BackgroundHandle) Result() *RunResult
Result returns the terminal RunResult if the subagent has finished, or nil if it's still running.
func (*BackgroundHandle) Status ¶
func (h *BackgroundHandle) Status() BackgroundStatus
Status returns the current status (safe for concurrent callers).
type BackgroundManagerOption ¶
type BackgroundManagerOption func(*bgMgrConfig)
BackgroundManagerOption configures NewBackgroundAgentManager.
func WithBackgroundAlertBuffer ¶
func WithBackgroundAlertBuffer(n int) BackgroundManagerOption
WithBackgroundAlertBuffer sets the alert channel buffer. When full, the oldest pending alert is dropped to make room (with a warning logged). Default 256.
func WithBackgroundCatalog ¶
func WithBackgroundCatalog(tools []tool.Tool) BackgroundManagerOption
WithBackgroundCatalog 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 WithBackgroundDefaultBudgets ¶
func WithBackgroundDefaultBudgets(b BackgroundBudgets) BackgroundManagerOption
WithBackgroundDefaultBudgets 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 WithBackgroundDefaultScheduler ¶
func WithBackgroundDefaultScheduler(s coretools.Scheduler) BackgroundManagerOption
WithBackgroundDefaultScheduler sets the tools.Scheduler that spawned subagents inherit when the per-spawn BackgroundSpec.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 BackgroundSpec.Scheduler win when supplied; see Spawn / NewSpawnAgentTool.
func WithBackgroundGate ¶
func WithBackgroundGate(g *permissions.Gate) BackgroundManagerOption
WithBackgroundGate 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 WithBackgroundMaxConcurrent ¶
func WithBackgroundMaxConcurrent(n int) BackgroundManagerOption
WithBackgroundMaxConcurrent 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 WithBackgroundMaxDepth ¶
func WithBackgroundMaxDepth(n int) BackgroundManagerOption
WithBackgroundMaxDepth 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 WithBackgroundProvider ¶
func WithBackgroundProvider(p models.Provider, modelID string) BackgroundManagerOption
WithBackgroundProvider wires the model provider + model ID used to build a fresh LLM client per spawn. Required.
type BackgroundSpec ¶
type BackgroundSpec struct {
Name string
SystemPrompt string
Goal string
Tools []string
Extras []string
Budgets BackgroundBudgets
// Scheduler selects the between-turn scheduler the subagent's
// RunAutonomous loop honors. Valid values: "" or "default" (use
// the manager's WithBackgroundDefaultScheduler — 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
}
BackgroundSpec is the request shape a single Spawn call expects. Built from the spawn_agent tool args by the tool handler.
type BackgroundStatus ¶
type BackgroundStatus int
BackgroundStatus is the lifecycle state of a background subagent.
const ( // StatusRunning — goroutine alive, RunAutonomous loop active. StatusRunning BackgroundStatus = 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 )
func (BackgroundStatus) String ¶
func (s BackgroundStatus) String() string
String renders the status for tool results and diagnostics.
type BuildFunc ¶
BuildFunc has the same shape RunAutonomous expects: the driver hands it the extra tools it injected (today: just the done tool) and the consumer returns a configured *Agent. The Agent's session.Service must be wired (durable or in-memory).
type CheckpointResult ¶
type CheckpointResult struct {
CheckpointEventID string
SummaryText string
TaskNote string
Duration time.Duration
Skipped bool
}
CheckpointResult reports what happened on a Checkpoint call. Same fields as CompactionResult plus TaskNote (the detail that triggered the checkpoint, surfaced so UI / telemetry can show it without re-reading the event metadata).
type Checkpointer ¶
type Checkpointer interface {
// ShouldCheckpoint is the heuristic gate fired post-turn when
// the model didn't explicitly call mark_task_done. Default
// implementation always returns false (heuristic off). Custom
// implementations could scan the just-completed assistant
// message for completion patterns.
ShouldCheckpoint(ctx context.Context, a *Agent) bool
// CheckpointInstruction returns the system instruction the
// summarizer LLM call uses. taskNote carries the operator/
// model-supplied completion detail (the `detail` arg of
// mark_task_done, or the operator's /done argument).
CheckpointInstruction(taskNote string) string
}
Checkpointer decides whether to auto-trigger task-boundary checkpoints and produces the summarizer prompt. The mark_task_done tool always triggers a checkpoint regardless of ShouldCheckpoint — the heuristic is for OPTIONAL post-turn auto-fire based on assistant-text patterns ("looks done") and is off by default. Consumers customize by implementing Checkpointer themselves.
func NewDefaultCheckpointer ¶
func NewDefaultCheckpointer() Checkpointer
NewDefaultCheckpointer returns a DefaultCheckpointer. Pass &DefaultCheckpointer{} directly if you want to assert the type; the constructor exists for symmetry with NewDefaultCompactor.
type CompactionResult ¶
type CompactionResult struct {
// SummaryEventID is the ID of the event the compactor wrote to
// the session. Empty when no compaction ran (compactor returned
// no-op, or the call errored before writing).
SummaryEventID string
// SummaryText is the full text the model produced. Useful for
// callers that want to surface the summary in the UI before the
// next turn runs.
SummaryText string
// Duration is wall-clock time spent in the summarizer LLM call.
Duration time.Duration
// Skipped is true when the compactor decided not to compact
// (e.g., ShouldCompact returned false from a programmatic
// Agent.CompactIfNeeded call). When Skipped is true, the rest of
// the fields are zero-valued.
Skipped bool
}
CompactionResult reports what happened on a Compact call.
type Compactor ¶
type Compactor interface {
// ShouldCompact returns true when the agent should compact before
// the next turn. Called from Agent.Run's post-turn hook with the
// agent's usage tracker so the implementation can read context-
// window state (Tracker.ContextWindowUsed / ContextWindowSize).
// Returning false is a no-op — the next turn proceeds normally.
ShouldCompact(ctx context.Context, a *Agent) bool
// SummarizerInstruction returns the system instruction the
// compactor LLM call uses. focus carries the operator's optional
// focus hint (empty when none). The instruction tells the model
// what kind of summary to produce; the conversation history is
// supplied as the LLMRequest.Contents.
SummarizerInstruction(focus string) string
}
Compactor decides when context-window compaction should fire and produces the summary prompt the agent sends to its model. Consumers plug a custom implementation via agent.WithCompactor; the package default (NewDefaultCompactor) covers the common case.
func NewDefaultCompactor ¶
func NewDefaultCompactor() Compactor
NewDefaultCompactor returns a DefaultCompactor with the package- default fallback threshold AND the default per-tier overrides from modeltier.DefaultCompactionThresholds — so small/mid/frontier each get a tier-appropriate trigger out of the box. Pass a &DefaultCompactor{...} literal directly to customize either knob.
type ContextStats ¶
type ContextStats struct {
// Compaction* report on Mechanism A boundary events.
CompactionCount int
LastCompactionFocus string // CompactionFocusKey from the last compaction event (empty when none)
LastCompactionTime time.Time // zero when none
// Checkpoint* report on Mechanism C boundary events.
CheckpointCount int
LastCheckpointNote string // CheckpointNoteKey from the last checkpoint event (empty when none)
LastCheckpointTime time.Time // zero when none
// TotalSummaryChars is the aggregate character count of all
// boundary summary text (compaction + checkpoint) written
// this session. Proxy for "how much history has been
// compressed" — useful as a sanity check that compaction is
// actually doing something.
TotalSummaryChars int
// Subtask* report on Mechanism B usage. Count + tokens + cost
// are accumulated in recordSubtaskUsage; usage.Tracker totals
// (/stats) include this same cost in their grand total.
SubtaskCount int
SubtaskInputTokens int
SubtaskOutputTokens int
SubtaskCostUSD float64
// ModelBreakdown surfaces /stats-style per-model totals when
// multiple models were used in the session (typically: parent
// on a frontier model, subtasks on a cheap flash/haiku-tier
// model via --agentic-small-model). Pulled from
// usage.Tracker.TotalsByModel; empty when no tracker is wired
// or only one model has been used (in which case the
// breakdown wouldn't tell the operator anything new beyond
// /stats' grand total).
ModelBreakdown map[string]usage.Totals
// DigestSavings surfaces the MCP digest wrap's cumulative
// effect on the parent's context (#223). Structural and agentic
// paths are broken out because their cost math differs — the
// renderer labels the block "savings vs. no-digest baseline"
// since these are hypothetical (what it would have cost without
// the wrap layer), not real spend reductions. Zero-value when
// the wrap layer is off or nothing has fired yet.
DigestSavings usage.DigestSavingsTotals
}
ContextStats is a snapshot view of the three context-management mechanisms wired on the Agent. All fields are zero-value-safe; the consumer renders "no compactions yet" / "no subtasks yet" based on the counts.
Boundary fields (Compaction*, Checkpoint*, TotalSummaryChars) are derived from the session event log on each call. Subtask fields come from in-memory counters bumped in RunSubtask.
type CostCeiling ¶
type CostCeiling struct {
// MaxTurnUSD is the cap on a single conversation turn's spend
// (cumulative cost of every model call between one operator
// inject and the next agent-done state). Tripped → next Run
// refuses with an ErrCostCeilingExceeded error.
MaxTurnUSD float64
// MaxSessionUSD is the cap on the session's cumulative spend
// across all turns (parent + subtask).
MaxSessionUSD float64
}
CostCeiling configures the per-turn / per-session spend caps the post-turn hook enforces. Zero or negative values disable that specific ceiling — both fields default to disabled when constructed via the zero value.
type DefaultCheckpointer ¶
type DefaultCheckpointer struct{}
DefaultCheckpointer is the package-default Checkpointer. Heuristic is off (ShouldCheckpoint always false) — mark_task_done + /done are the trigger paths. Prompt mirrors DefaultCompactor's five-section handover plus a "Completion record" preamble that names the just-finished task as the focal point of the summary.
func (*DefaultCheckpointer) CheckpointInstruction ¶
func (c *DefaultCheckpointer) CheckpointInstruction(taskNote string) string
CheckpointInstruction returns the five-section handover prompt with a "Completion record" preamble that names the just- finished task. The model's summary will frame the conversation from "this task is now done" angle rather than the "we're still mid-task" angle DefaultCompactor produces.
func (*DefaultCheckpointer) ShouldCheckpoint ¶
func (c *DefaultCheckpointer) ShouldCheckpoint(_ context.Context, _ *Agent) bool
ShouldCheckpoint returns false. Heuristic-based auto-checkpoint is intentionally off by default — false positives (declaring a task done when the operator is mid-thought) are costly. The mark_task_done tool gives the model an explicit signal it can invoke when it's confident; /done gives the operator the same.
type DefaultCompactor ¶
type DefaultCompactor struct {
// Threshold is the fallback context-window utilization at which
// compaction fires when no per-tier override applies (unknown
// model, or the tier isn't in ThresholdByTier). 0.85 means
// "compact once we've used 85% of the model's context window."
// Zero is treated as "use the package default."
Threshold float64
// ThresholdByTier overrides Threshold per modeltier classification.
// Keys are pkg/modeltier tier labels ("frontier", "mid", "small").
// When the current model classifies to a tier present here, that
// tier's threshold wins. Empty map (or zero value at a key) falls
// back to Threshold. Use this to keep frontier sessions at 0.85
// while compacting small-tier sessions much earlier.
ThresholdByTier map[string]float64
// TierClassifier is the function used to look up the current
// model's tier. Defaults to modeltier.Classify. Override in tests
// to inject deterministic tier resolutions without depending on
// the modeltier table's current state.
TierClassifier func(modelID string) string
}
DefaultCompactor is the package-default Compactor. Triggers on context-window utilization ≥ a per-model threshold (default 0.85 for frontier, 0.65 for mid, 0.35 for small; see pkg/modeltier.DefaultCompactionThresholds) and produces a five-section "teammate handover" summary (current state, files & changes, technical context, strategy & approach, exact next steps) per docs/context-management-design.md §Mechanism A.
Consumers needing a different prompt or trigger logic implement Compactor themselves; this type is a sensible default, not a required base class.
func (*DefaultCompactor) ShouldCompact ¶
func (c *DefaultCompactor) ShouldCompact(_ context.Context, a *Agent) bool
ShouldCompact returns true when the agent's usage tracker reports context-window utilization at or above the resolved threshold for the current model's tier. Returns false when the tracker doesn't yet know the window size (no turn has landed, or the model isn't in usage.ContextWindowSizeFor's table) so a session with an unknown model never triggers premature compaction.
func (*DefaultCompactor) SummarizerInstruction ¶
func (c *DefaultCompactor) SummarizerInstruction(focus string) string
SummarizerInstruction returns the five-section handover prompt. focus, when non-empty, appended as a "Compact focus: <text>" directive so the summarizer prioritizes that thread.
type Option ¶
type Option func(*options)
Option mutates Agent construction. Use the With* helpers below.
func WithAppName ¶
WithAppName overrides the AppName handed to the ADK runner. Useful when embedding so telemetry and session stores can distinguish multiple agents inside one binary.
func WithAttachMCPProvider ¶
WithAttachMCPProvider wires a snapshot func for /sessions/<sid>/mcp (backs /mcp).
func WithAttachMemoryProvider ¶
func WithAttachMemoryProvider(fn func() []attach.MemorySource) Option
WithAttachMemoryProvider wires a snapshot func that returns the agent's loaded instruction sources for the remote-attach /sessions/<sid>/memory endpoint (backs the remote TUI's /memory slash). The caller usually projects an `instruction.Loaded`'s Sources list into []attach.MemorySource; nil = endpoint returns empty.
func WithAttachPricingProvider ¶
func WithAttachPricingProvider(fn func() attach.PricingInfo) Option
WithAttachPricingProvider wires a snapshot func for /sessions/<sid>/pricing (backs the remote TUI's /pricing read).
func WithAttachPricingSetter ¶
func WithAttachPricingSetter(fn func(req attach.PricingSetRequest) error) Option
WithAttachPricingSetter wires a func that runs on POST /sessions/<sid>/pricing/set — writes a manual per-model rate and rebuilds the catalog.
func WithAttachPromptBroker ¶
func WithAttachPromptBroker(b *attach.PromptBroker) Option
WithAttachPromptBroker wires the broker that bridges the agent's permissions.Gate prompts to remote operators over GET /sessions/<sid>/perms/stream and POST /perms/respond. The caller is also responsible for wiring this broker into the gate (typically via Gate.SetPrompter(broker)) so prompts the gate generates actually flow through it. Without this option the /perms/stream + /perms/respond routes return 501.
func WithAttachRefreshPricer ¶
func WithAttachRefreshPricer(fn func(ctx context.Context) (attach.PricingRefreshResponse, error)) Option
WithAttachRefreshPricer wires a func that runs on POST /sessions/<sid>/pricing/refresh — typically calls into `internal/pricing.Refresh` and rebuilds the catalog. Returns the outcome the operator sees.
func WithAttachReloader ¶
func WithAttachReloader(fn func(ctx context.Context) attach.ReloadResponse) Option
WithAttachReloader wires a func that runs on POST /sessions/<sid>/reload. The closure is expected to re-walk project deps (instruction sources, skills bundles, MCP config) and return per-surface success in the response so the operator sees which parts succeeded and which failed. The agent doesn't inspect the response shape; what "reload" means is the host's concern. Without this option the operator sees 501 / capability not registered.
func WithAttachReplanner ¶
func WithAttachReplanner(fn func(ctx context.Context, req attach.ReplanRequest) (attach.ReplanResponse, error)) Option
WithAttachReplanner wires a func that runs on POST /sessions/<sid>/slash/replan and on the in-process TUI's /replan slash dispatch. The closure is expected to clear the gate's planRecorded flag and archive the latest plan artifact (typically `tools.RevokeLatestPlan(gate, agentsDir)`). Without this option the slash returns 501 / "capability not registered".
Wire only when plan-first gating is active (config permissions.require_plan_artifact: true). Wiring it under other configs is a no-op but harmless.
func WithAttachSkillsProvider ¶
WithAttachSkillsProvider wires a snapshot func for /sessions/<sid>/skills (backs /skills).
func WithBackgroundManager ¶
func WithBackgroundManager(mgr *BackgroundAgentManager) Option
WithBackgroundManager attaches a BackgroundAgentManager to the agent. The manager's parent back-reference is set during construction so its Spawn calls can read the agent's session triple + session.Service without the consumer plumbing them twice.
Each turn of Agent.Run drains pending alerts from the manager's channel (non-blocking) and prepends them to the prompt the underlying ADK runner sees, so the parent's model is aware of what its background subagents have reported since the last turn.
Pass nil to clear (e.g. for tests that re-construct an agent).
func WithCheckpointer ¶
func WithCheckpointer(c Checkpointer) Option
WithCheckpointer wires a Checkpointer implementation that drives task-boundary checkpoints (Mechanism C of docs/context-management-design.md). When wired, the agent automatically registers the mark_task_done built-in tool — the model can call it to signal task completion, and the post-turn hook in Run promotes that into a pending checkpoint the next Run drains by writing a richer handover record. The TUI's /done slash drives the same path manually.
Pass agent.NewDefaultCheckpointer() for the package default (heuristic off; mark_task_done + /done are the trigger paths; six-section completion-record prompt). Custom Checkpointer implementations let consumers swap in a different prompt or heuristic.
Optional. When nil, Agent.Checkpoint returns ErrNoCheckpointer and the mark_task_done tool is not registered.
func WithCompactor ¶
WithCompactor wires a Compactor implementation that drives context-window compaction (Mechanism A of docs/context-management-design.md). When wired, the post-turn hook in Run checks Compactor.ShouldCompact(); if true, the next Run call fires Compact() before its actual work, replacing the pre-summary history with a single summary event.
Pass agent.NewDefaultCompactor() for the package default (threshold 0.85, five-section handover prompt). Custom Compactor implementations let consumers swap in a different prompt or trigger logic.
Optional. When nil, Agent.Compact returns ErrNoCompactor and the post-turn hook is a no-op — compaction has to be wired in explicitly.
func WithCostCeiling ¶
func WithCostCeiling(c CostCeiling) Option
WithCostCeiling wires per-turn and per-session spend caps. Pass a zero-value CostCeiling{} (or 0 in either field) to disable the corresponding bound — at least one must be > 0 for enforcement to run at all. Mirrors the usual WithX option shape.
func WithDescription ¶
WithDescription overrides the agent's description.
func WithEventHook ¶
WithEventHook wires per-event and end-of-turn observer callbacks. onEvent is called once per session.Event as events stream from Agent.Run — from inside the same iterator tap the watchdog and usage tracker already sit in, so observation is synchronous and ordered relative to the event yield. onTurnEnd is called from the post-turn cleanup that runs after wrapWithCleanup drains the iterator, alongside the watchdog / compaction / checkpoint hooks.
Either callback may be nil to disable that half of the surface. Both nil is legal and turns the option into a no-op; useful when a consumer wraps WithEventHook in a builder that always sets it.
Consumer contract: callbacks must not panic and should return quickly. A slow callback stalls the agent's event stream — synchronous by design so the hook mechanism can rely on ordering (matches pkg/hooks.Dispatcher, which spawns subprocesses with per-command timeouts to bound its own latency).
Single-slot: calling WithEventHook twice replaces the previous binding. Multi-consumer fan-out is the caller's responsibility (wrap two callbacks in one). This matches WithWatchdog's shape.
func WithEventLog ¶
WithEventLog wires an eventlog.Handle into the agent — the Handle's Service becomes the agent's session.Service (so every event lands in the durable log), and the Handle is stored on the agent so callers can reach back to it for replay/watch via Agent.EventLog().
Equivalent to WithSessionService(h.Service) plus a stash of the Handle for later access; passing nil is a no-op.
func WithGate ¶
func WithGate(g *permissions.Gate) Option
WithGate wires the permissions gate that gates every tool call into the agent's metadata, so it can be surfaced over the attach-mode /tools endpoint (each tool gets a pre-flight `gate_state` field — "allowed" / "denied" / "prompted" / "denied-allow-mode" — without actually consulting the gate at request time). Optional; without it, the /tools endpoint reports an empty gate_state per tool and the TUI's auditing column is blank.
This is metadata-only — the gate that actually mediates tool calls is still the one wired into the tool constructors themselves. The agent does not call this gate; it just exposes a read-only view.
func WithInstruction ¶
WithInstruction overrides the system instruction.
func WithPostConstruct ¶
WithPostConstruct registers a callback invoked once the *Agent is fully built (right before New returns). Useful for late- binding patterns where the caller needs the agent pointer to wire something they registered earlier — e.g., an externally- constructed tool whose handler closure captured a *Agent placeholder. The hook fires on the happy path only; if New returns an error the hook is not called.
One hook per agent. Calling WithPostConstruct twice keeps the last one (Option-pattern overwrite semantics, same as other scalar With* options).
func WithSession ¶
WithSession overrides the user/session IDs handed to the ADK runner. Reuse the same pair across Run() calls to preserve conversation history.
func WithSessionRegistry ¶
func WithSessionRegistry(r attachRegistrar) Option
WithSessionRegistry opts the constructed agent into attach-mode by auto-registering it with the supplied registry. Once registered the agent is reachable over HTTP/SSE via attach.NewServer for observability (GET /sessions/<app>/<sid>/events) and control (POST /inject, /wake).
The registry's Register is called from agent.New; if it returns an error (typically attach.ErrSessionExists from a double-register), agent.New surfaces it. Pass nil to skip registration (default).
Lifetime: the agent stays registered until the operator calls registry.Unregister explicitly, or the listener that owns the registry shuts down. In typical deployments (one agent per process, long-lived) the agent IS the process and lives until shutdown.
The registrar argument is typed as an interface so this package doesn't import attach/ (avoids cycle). Pass attach.NewAgentRegistrarAdapter(reg) to wire a *attach.SessionRegistry.
func WithSessionService ¶
WithSessionService overrides the session.Service handed to the ADK runner. The default is session.InMemoryService(), which loses all state when the process exits. Pass a durable Service (typically the one returned by eventlog.Open(...).Service when wiring the audit log + crash-resume substrate) to persist sessions across runs.
The supplied Service is also exposed via Agent.SessionService() so callers can query session state directly without keeping their own reference. Passing nil restores the default.
func WithStreaming ¶
func WithStreaming(m adkagent.StreamingMode) Option
WithStreaming overrides the streaming mode. Default is StreamingModeSSE (required to receive Partial events).
func WithSubagents ¶
WithSubagents registers each agent as a callable tool the parent's model can invoke by name. The subagent runs through ADK's runner using the parent's session.Service (so its events stream live into the same audit log) with session.Event.Branch set to "<parent_branch>.<subagent_name>" — ADK's contents-processor branch filter then keeps the subagent's events from leaking back into the parent's next-turn LLM request, which preserves context isolation while keeping the audit log unified.
Each subagent's tool name comes from its own WithName value. Use NewSubagentTool directly for per-subagent overrides (custom name, description, depth cap, branch label).
Resolved at the end of New() so that the parent's session.Service and session triple — set by other With* options — are captured at the point the subagent tools are constructed.
func WithSystemInstructionPrefix ¶
WithSystemInstructionPrefix prepends prefix to the agent's default instruction. Used for memory loading: AGENTS.md / CLAUDE.md / GEMINI.md project memory becomes part of the system prompt rather than the user's first message.
func WithTools ¶
WithTools registers a set of tools the agent may call. Order is preserved but immaterial; ADK keys tools by Name.
func WithToolsets ¶
WithToolsets registers groups of tools (MCP servers, skills, etc.). Each Toolset implements google.golang.org/adk/tool.Toolset and is passed to llmagent.Config.Toolsets.
func WithUsageTracker ¶
WithUsageTracker wires a shared *usage.Tracker into the agent so agent-level code (the compactor's threshold check, future per-turn rollups) can read context-window state without the consumer reaching in. The same tracker can be shared with a TUI host that already keeps one for /stats — both populate via usage.Append and read the same totals.
Optional. Nil-safe: components that read the tracker check first and degrade gracefully ("don't trigger threshold-based compaction if we don't know how full the window is").
func WithWatchdog ¶
WithWatchdog wires a behavioral watchdog. The agent calls w.ObserveToolCall as tool calls stream by, and w.Check from the post-turn hook. Returned alerts are passed to onAlert if non-nil; when onAlert is nil the alerts are collected and discarded each turn (useful for tests, or for hosts that read the watchdog's own state via a side channel).
Composable with everything else: pass alongside WithCompactor / WithCheckpointer / WithCostCeiling / etc. The watchdog runs in the same post-turn hook the others use.
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 BackgroundBudgets
}
RemoteAgentSpec is what the manager hands to the consumer's spawner — the same shape as the in-process BackgroundSpec, 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 BackgroundStatus 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 ResumeBuildFunc ¶
ResumeBuildFunc is the agent constructor accepted by ResumeAutonomous. It mirrors RunAutonomous's BuildFunc signature but adds the sessionID the new agent must adopt — implementations pass it to agent.WithSession so the constructed agent reuses the session being resumed.
type RetryDecision ¶
type RetryDecision int
RetryDecision tells the driver what to do after a turn fails.
const ( // AbortRun stops the run immediately and propagates the error. AbortRun RetryDecision = iota // RetryTurn re-runs the same prompt for another attempt. RetryTurn // SkipTurn moves on to the continuation prompt as if the failed // turn had completed normally without a done signal. SkipTurn )
type RetryPolicy ¶
type RetryPolicy func(turnErr error, attempt int) RetryDecision
RetryPolicy decides what RunAutonomous does when a turn errors. The callback receives the error and the 1-indexed attempt count (the first failure is attempt=1, second is attempt=2, etc.).
type RunResult ¶
type RunResult struct {
// Reason explains why the loop stopped.
Reason StopReason
// FinalText is the accumulated streaming text from the last turn
// that produced any output.
FinalText string
// Turns is the number of turns the driver actually executed
// (including failed ones that were retried or skipped).
Turns int
// InputTokens / OutputTokens are summed from each turn's
// UsageMetadata. Zero when no usage info was returned.
InputTokens int
OutputTokens int
// CostUSD is the cumulative dollar cost computed via the
// configured Pricing. Zero when pricing is zero.
CostUSD float64
// Duration is the wall-clock time from RunAutonomous entry to
// loop exit.
Duration time.Duration
// DoneDetail is the detail string the model passed to the done
// tool when Reason==StopReasonCompleted.
DoneDetail string
// NextWakeAt is set when Reason==StopReasonDeferred — the
// scheduler returned ErrSchedulerDefer and the loop exited
// cleanly with a wake-time persisted to the eventlog. Whatever
// orchestrator wraps the process restarts at or after this time
// and ResumeAutonomous picks up the deferred checkpoint.
NextWakeAt time.Time
}
RunResult is the structured outcome of RunAutonomous.
func ResumeAutonomous ¶
func ResumeAutonomous(ctx context.Context, build ResumeBuildFunc, ref SessionRef, opts ...AutonomousOption) (RunResult, error)
ResumeAutonomous reads the most recent checkpoint event from the session's event log, reconstructs RunResult totals, and continues the run from the next turn. The build function receives the resumed sessionID so the constructed agent rejoins the same session via agent.WithSession.
Behavior:
- Acquires an exclusive SessionLock on (App, User, Session). A concurrent ResumeAutonomous on the same session returns ErrSessionLocked from eventlog.
- If the session has no checkpoint events at all, the run starts from turn 0 with whatever event history the session already holds — "make this existing session autonomous from here" is a valid use case.
- If the latest checkpoint has stop_reason set (terminal state), ResumeAutonomous returns that state immediately without constructing the agent or running any turns.
- Otherwise, the loop continues with prompt = checkpoint.ContinuationPrompt; budgets carry forward.
func RunAutonomous ¶
func RunAutonomous(ctx context.Context, build func(extraTools []tool.Tool) (*Agent, error), goal string, opts ...AutonomousOption) (RunResult, error)
RunAutonomous drives a multi-turn loop against an Agent built by build, sending goal as the first prompt and a continuation prompt thereafter, until one of the stop conditions fires. Returns a RunResult describing why it stopped and the totals it accumulated, plus any error.
The driver constructs the agent via build, passing in an extra "done" tool the model calls to signal completion. The tool name is "report_done" by default and can be overridden with WithDoneToolName. Consumers compose the done tool with their own tool registry inside build (see examples/autonomous for the pattern).
The constructor pattern keeps the driver from mutating a caller-supplied Agent (which would race with concurrent runs) and keeps agent.New's surface free of "extra tools" plumbing that only matters here.
type SessionRef ¶
SessionRef identifies the session ResumeAutonomous resumes from. Handle supplies both the eventlog.Stream (used to find the latest checkpoint) and the session.Service (used by the constructed agent for live event reads + writes).
type StopReason ¶
type StopReason string
StopReason explains why RunAutonomous returned.
const ( // StopReasonCompleted means the model called the done tool. StopReasonCompleted StopReason = "completed" // StopReasonMaxTurns means WithMaxTurns was hit. StopReasonMaxTurns StopReason = "max_turns_exceeded" // StopReasonMaxTokens means WithMaxTokens (input or output) was hit. StopReasonMaxTokens StopReason = "max_tokens_exceeded" //nolint:gosec // not a credential // StopReasonMaxCost means WithMaxCost was hit. StopReasonMaxCost StopReason = "max_cost_exceeded" // StopReasonWallclockExceeded means WithMaxWallclock was hit. StopReasonWallclockExceeded StopReason = "wallclock_exceeded" // StopReasonContextCancelled means the supplied context was // cancelled or its deadline expired. StopReasonContextCancelled StopReason = "context_cancelled" // StopReasonRetryAborted means the configured RetryPolicy // returned AbortRun for a turn error. StopReasonRetryAborted StopReason = "retry_policy_aborted" // StopReasonDeferred means the configured Scheduler returned // ErrSchedulerDefer in response to a schedule emission. The loop // exited cleanly with RunResult.NextWakeAt populated; whatever // orchestrator wraps the process restarts at or after the // wake-time and ResumeAutonomous picks up. StopReasonDeferred StopReason = "deferred" )
type SubagentOptions ¶
type SubagentOptions struct {
// Inner is the *agent.Agent to expose as a tool the parent's
// model can call. The tool's function name comes from
// Inner.AgentName() (set via agent.WithName), unless overridden
// via Name. The tool's description comes from Inner's
// llmagent.Description, unless overridden via Description.
Inner *Agent
// Name overrides the function name shown to the parent's model.
// Empty falls back to Inner.AgentName().
Name string
// Description overrides the function description shown to the
// parent's model. Empty falls back to Inner's Description (or a
// generic fallback when that's also empty).
Description string
// MaxDepth caps recursion depth. A subagent at depth >= MaxDepth
// that is invoked from another subagent gets an error result
// rather than being allowed to recurse. Default 2; pass a
// larger value if your agent topology genuinely needs deeper
// nesting.
MaxDepth int
// Branch overrides the branch label appended to the parent's
// branch on the subagent's events. Defaults to the tool name
// (which is Inner.AgentName() unless Name overrides it). The
// resulting branch is "<parent_branch>.<this>".
Branch string
// ParentService, when non-nil, overrides the session.Service
// the subagent's runner uses. The agent.WithSubagents
// convenience option fills this in automatically with the
// parent agent's service so subagent events land in the
// parent's audit log without any consumer plumbing.
//
// When nil, NewSubagentTool falls back to Inner.SessionService()
// — which is fine for callers who construct subagents
// pre-wired against the same Handle.
ParentService session.Service
// ParentAppName, ParentUserID, ParentSessionID identify the
// parent's session triple. When set, the subagent runs through
// the parent's session row (with branch isolation) so cross-
// session audit queries find both. Empty values fall back to
// Inner's own AppName/UserID/SessionID. Set automatically by
// agent.WithSubagents.
ParentAppName string
ParentUserID string
ParentSessionID string
}
SubagentOptions configures NewSubagentTool. Inner is required; everything else has sensible defaults.
type SubtaskBudgets ¶
type SubtaskBudgets struct {
// MaxTurns caps how many model turns the subtask may take.
// Default is SubtaskDefaultMaxTurns. Subtasks are meant to be
// single-purpose; >5 turns usually means the question is too
// open-ended for a subtask and belongs in the parent's loop.
MaxTurns int
// MaxWallclock caps wall-clock duration. Default is
// SubtaskDefaultMaxWallclock. Belt-and-suspenders against a
// flaky model that streams forever without producing a final
// turn.
MaxWallclock time.Duration
}
SubtaskBudgets caps subtask cost in three dimensions. Whichever hits first wins. Returns SubtaskResult{Truncated: true} on budget exhaustion (not an error — caller's model can choose to retry with a wider budget or fall back to running the raw tool).
type SubtaskResult ¶
type SubtaskResult struct {
// Name echoes SubtaskSpec.Name for trace correlation.
Name string
// Digest is the model's final text output across all turns
// (joined). Empty when Truncated is true and the model
// produced no final-turn text before the budget hit.
Digest string
// Truncated reports that a budget (MaxTurns / MaxWallclock)
// fired before the model emitted a final TurnComplete.
// Digest may still hold partial text from earlier turns.
Truncated bool
// InputTokens / OutputTokens accumulate across every turn of
// the subtask. Subtask cost rolls up to the parent's
// usage.Tracker if WithUsageTracker is wired, so /stats
// totals reflect everything.
InputTokens int
OutputTokens int
CostUSD float64
// Duration is wall-clock time spent in the subtask. Useful
// for the "fresh-context is fast" claim — typical subtask
// completes in <2s on a flash-tier model.
Duration time.Duration
// TurnsUsed is the number of model turns the subtask actually
// took. Useful for budget tuning ("am I hitting MaxTurns
// often?").
TurnsUsed int
}
SubtaskResult is what RunSubtask hands back. Digest is the distilled text the subtask's model produced; Truncated flags budget exhaustion so the caller can decide whether the partial answer is useful.
type SubtaskSpec ¶
type SubtaskSpec struct {
// Name attributes cost + traces. Required (non-empty) so the
// audit log can distinguish subtask events; the subtask's
// session-branch derives from this name.
Name string
// SystemPrompt is the subtask's role instruction. Replaces
// agent.DefaultInstruction for this subtask only; the parent's
// instruction is NOT inherited (the subtask runs in fresh
// context).
SystemPrompt string
// UserMessage is the question / instruction the subtask
// receives. Treated as the operator's first user message in
// the subtask's brand-new session.
UserMessage string
// Tools is the restricted set the subtask may call.
// Typically a small read-only subset (read_file, grep,
// fetch_url, etc.). Empty means tool-less.
Tools []tool.Tool
// Model overrides the parent's model for this subtask. Nil
// (the default) means "use the parent's model." Wrapper tools
// like AgenticReadFile typically point this at a smaller,
// faster model (haiku-tier / flash-tier) so a "summarize this
// file" subtask doesn't burn opus tokens.
Model adkmodel.LLM
// Budgets caps the subtask's resource use. Zero fields fall
// back to SubtaskBudgetDefaults.
Budgets SubtaskBudgets
}
SubtaskSpec configures one RunSubtask call.
Source Files
¶
- agent.go
- autonomous.go
- autonomous_handle.go
- background.go
- background_report.go
- background_spawn.go
- background_tools.go
- btw.go
- checkpoint.go
- checkpointer.go
- compactor.go
- context_stats.go
- cost_ceiling.go
- eventhook.go
- inbox.go
- internal_llm_usage.go
- metadata.go
- remote.go
- resume.go
- subagent.go
- subtask.go
- tool_savings_observer.go
- wake.go
- watchdog.go