Documentation
¶
Index ¶
- Constants
- func InheritedAgentMode() agentdomain.AgentMode
- type A2AClearer
- type A2AStateProvider
- type BackgroundJob
- type BackgroundShell
- type BackgroundShellService
- type BackgroundTaskRegistry
- type BackgroundTaskService
- type JobKind
- type JobLivenessReporter
- type JobMeta
- type JobNotifier
- type JobOutputProvider
- type JobSignal
- type JobStatus
- type JobStopper
- type JobSubmitter
- type OutputRingBuffer
- type PaneObservation
- type RunEvent
- type RunRecord
- type RunStatus
- type ScheduledJob
- type ShellInfo
- type ShellState
- type ShellTracker
- type SubagentApprovalFile
- type SubagentResultFile
- type SubagentState
- type SubagentStatus
- type SubagentTracker
- type TaskInfo
- type TaskRetainer
- type TaskRetentionService
- type TitleGenerator
- type TrackedJob
- type WindSignal
Constants ¶
const ( SubagentModeHeadless = "headless" SubagentModeInteractive = "interactive" )
SubagentMode selects how a subagent is surfaced while it runs.
const EnvSubagentAgentMode = "INFER_SUBAGENT_AGENT_MODE"
EnvSubagentAgentMode names the environment variable the Agent tool sets to carry the parent chat's coding mode (the AgentMode.AllowedlistKey form - "standard"/"plan"/"auto") to a spawned subagent, so it starts in the same mode as its parent. It is absent for top-level `infer chat`/`infer headless` runs, which therefore stay Standard-by-default.
const EnvSubagentApprovalFile = "INFER_SUBAGENT_APPROVAL_FILE"
EnvSubagentApprovalFile names the environment variable the Agent tool sets on an interactive subagent's `infer chat` so it writes a SubagentApprovalFile JSON to that path whenever it blocks on a tool-approval prompt (and removes it when the prompt resolves). The parent watches this file to surface "subagent is awaiting approval" to the user and relay the decision (ApproveSubagent). Unset for normal `infer chat`, which writes nothing.
const EnvSubagentHistoryName = "INFER_SUBAGENT_HISTORY_NAME"
EnvSubagentHistoryName names the environment variable the Agent tool sets on an interactive subagent's `infer chat` so it uses its own history file (<configDir>/history/history-<name>) instead of the main agent's history. When unset or empty, the subagent falls back to the main history file; the reserved value SubagentHistoryMemoryOnly selects in-memory-only history.
const EnvSubagentResultFile = "INFER_SUBAGENT_RESULT_FILE"
EnvSubagentResultFile names the environment variable the Agent tool sets on an interactive subagent's `infer chat` so it writes its last assistant message (as a SubagentResultFile JSON) to that path on each completed turn. The parent reads it to deliver the subagent's real answer - not the tmux pane's chrome - when the subagent finishes. Unset for normal `infer chat`, which writes nothing.
const SubagentHistoryMemoryOnly = ":memory:"
SubagentHistoryMemoryOnly is the reserved EnvSubagentHistoryName value telling an interactive subagent to keep its input history in memory only (no file). The Agent tool sets it for subagents without a usable label so they don't create a new single-use history file per spawn. sanitizeSlug never yields this value (it contains ':'), so it can't collide with a real slug.
Variables ¶
This section is empty.
Functions ¶
func InheritedAgentMode ¶ added in v0.178.1
func InheritedAgentMode() agentdomain.AgentMode
InheritedAgentMode returns the coding mode a subagent should start in, read from EnvSubagentAgentMode. Falls back to standard mode when unset or unparseable.
Types ¶
type A2AClearer ¶
type A2AClearer interface {
ClearAllAgents()
}
A2AClearer is the one-method projection of the A2A tracker used by conversation clear/switch to discard the A2A context/task graph. The concrete *utils.A2ATaskTrackerImpl (and the BackgroundTaskRegistry that embeds it) satisfies it; consumers that only clear depend on this instead of the whole tracker.
type A2AStateProvider ¶
type A2AStateProvider interface {
A2APollingState() agentdomain.TaskPollingState
}
A2AStateProvider is an optional BackgroundJob extension implemented by A2A task jobs so the supervisor can surface their live polling state (context/agent/task id and last known remote state) as the single source for the task view and status bar, without the generic JobMeta/TrackedJob carrying A2A-specific fields.
type BackgroundJob ¶
type BackgroundJob interface {
// Meta returns the job's identity/display snapshot.
Meta() JobMeta
// Run blocks until the job reaches a terminal state or ctx is cancelled,
// emitting intermediate JobSignals via emit, and returns the terminal result.
// It MUST return promptly once ctx is cancelled.
Run(ctx context.Context, emit func(JobSignal)) agentdomain.ToolExecutionResult
// Wind delivers a graceful wind-down (WindWrapUp) or hard stop (WindStop) to a
// RUNNING job. It must be safe to call from another goroutine while Run is in
// flight, be idempotent, and a no-op once the job has finished.
Wind(ctx context.Context, sig WindSignal) error
// Close tears down the resources the job owns (its per-kind tracker record, an
// interactive subagent's tmux pane and temp files, ...). The supervisor calls
// it exactly once when it reaps a finished job after the retention window. It
// must be idempotent and must not touch the running process (use Wind for that).
Close()
}
BackgroundJob is one unit of monitorable background work. The supervisor owns the single goroutine that calls Run plus all the lifecycle around it (events, queue notification, tracking, cleanup); a job only has to (a) block until it reaches a terminal state and return the outcome, and (b) honour wind-down/stop signals. This is the seam that lets A2A tasks, shells, and subagents share one fan-in implementation - each kind differs only in how it learns it is done.
type BackgroundShell ¶
type BackgroundShell struct {
ShellID string
Command string
Cmd *exec.Cmd
StartedAt time.Time
CompletedAt *time.Time
State ShellState
ExitCode *int
OutputBuffer OutputRingBuffer
CancelFunc context.CancelFunc
ReadOffset int64
ReadersDone <-chan struct{}
}
BackgroundShell represents a command running in the background.
type BackgroundShellService ¶
type BackgroundShellService interface {
// DetachToBackground moves a running command to background. readersDone, when
// non-nil, is closed once the caller's pipe readers reach EOF; the supervisor
// waits on it before reaping so trailing output is not truncated.
DetachToBackground(ctx context.Context, cmd *exec.Cmd, command string, outputBuffer OutputRingBuffer, readersDone <-chan struct{}) (string, error)
// GetShellOutput retrieves output from a shell
GetShellOutput(shellID string, fromOffset int64) (string, int64, ShellState, error)
// GetShellOutputWithFilter retrieves filtered output from a shell
GetShellOutputWithFilter(shellID string, fromOffset int64, filterPattern string) (string, int64, ShellState, error)
// GetShell returns a specific shell by ID
GetShell(shellID string) *BackgroundShell
// GetAllShells returns all tracked shells
GetAllShells() []*BackgroundShell
// CancelShell cancels a running background shell
CancelShell(shellID string) error
// RemoveShell removes a shell from tracking
RemoveShell(shellID string) error
}
BackgroundShellService defines the interface for managing background shells
type BackgroundTaskRegistry ¶
type BackgroundTaskRegistry interface {
agentdomain.A2ATaskTracker
ShellTracker
SubagentTracker
// HasPending reports whether *any* background work is still in flight,
// regardless of type. True when there is at least one A2A task being
// polled, one running background shell, OR one running HEADLESS subagent.
// It deliberately excludes interactive subagents so a one-shot `infer headless`
// does not hang at exit waiting on a user-driven tmux pane.
HasPending() bool
// Submit hands a background job to the supervisor, which spawns its monitor
// goroutine and folds its result back onto the conversation when it finishes.
// This is the single entry point every kind (A2A task, shell, subagent) uses
// instead of running its own poller.
Submit(job BackgroundJob)
// Snapshot returns the supervisor's view of all live and recently-finished
// jobs for the task view and status line.
Snapshot() []TrackedJob
// CountRunningJobs returns how many supervised jobs are running, optionally
// filtered to one kind (pass "" for all kinds).
CountRunningJobs(kind JobKind) int
// IsJobRunning reports whether the supervised job with the given id is still
// running. It is the per-id liveness query a tool uses (via the narrow
// JobLivenessReporter projection) to defer to the supervisor - the single
// source of truth - instead of racing it with a manual read.
IsJobRunning(id string) bool
// WindJob sends a graceful wind-down or hard stop to one supervised job.
WindJob(id string, sig WindSignal) error
}
BackgroundTaskRegistry is the single tracker that owns *all* in-flight background work an agent session can produce: A2A tasks (long-running work delegated to remote agents) and background bash shells (long-running commands the agent has detached from the foreground). Both are conceptually the same thing - async producers of results that need to land back on the conversation when they finish - so they live behind one type here.
The interface unifies what used to be two separate trackers (A2ATaskTracker and ShellTracker) via composition: depending on what a caller needs, it can use the narrower A2ATaskTracker or ShellTracker interface, or this full BackgroundTaskRegistry to access both plus the HasPending() aggregator method.
type BackgroundTaskService ¶
type BackgroundTaskService interface {
// GetBackgroundTasks returns all current background polling tasks
GetBackgroundTasks() []agentdomain.TaskPollingState
// CancelBackgroundTask cancels a background task by task ID
CancelBackgroundTask(taskID string) error
}
BackgroundTaskService handles background A2A task operations Only enabled when A2A is enabled - provides task cancellation and retrieval
type JobKind ¶
type JobKind string
JobKind identifies which background-work subsystem produced a job so one supervisor, one tracker, and one task view can treat A2A tasks, background shells, and subagents uniformly while still reporting per-kind counts.
type JobLivenessReporter ¶
JobLivenessReporter reports whether a supervised background job is still running, by id. It is the narrow projection of BackgroundTaskRegistry a tool uses to tell whether the supervisor is still driving a job it launched - an A2A task being polled, a background shell, or a subagent - so a manual read defers to the supervisor (the single source of truth) instead of racing it.
type JobMeta ¶
type JobMeta struct {
ID string
Kind JobKind
Label string
Description string
Detail string
StartedAt time.Time
Silent bool
HoldsSession bool
}
JobMeta is the identity/display snapshot a background job exposes. The supervisor reads it once at submit (and surfaces it in the task view); it is not on any hot path.
type JobNotifier ¶
type JobNotifier interface {
Notification(result agentdomain.ToolExecutionResult) string
}
JobNotifier is an optional BackgroundJob extension. A job that implements it formats its own completion-notification body (the text enqueued for the agent to read when it finishes) - e.g. a shell reports its exit code and duration, which a generic tool-result formatter would not. Jobs that do not implement it get the default domain formatting of their ToolExecutionResult.
type JobOutputProvider ¶
type JobOutputProvider interface {
Output() string
}
JobOutputProvider is an optional BackgroundJob extension. A job that implements it provides its output text for the /tasks detail panel - e.g. a shell's captured stdout/stderr or a subagent's final result. Jobs that do not implement it show no output section in the detail panel.
type JobSignal ¶
type JobSignal struct {
// Note is a human-facing line surfaced to the UI and, when Enqueue is set,
// landed on the message queue so the agent reads it on its next turn.
Note string
// Enqueue lands Note on the message queue and wakes the agent loop; otherwise
// Note is a UI-only status update.
Enqueue bool
// State is an optional kind-specific status token (e.g. an A2A task state).
State string
}
JobSignal is an intermediate, non-terminal event a running job emits to the supervisor (an A2A status change, a subagent that became blocked on a tool approval). The terminal outcome is Run's return value, never a signal.
type JobStatus ¶
type JobStatus string
JobStatus is the unified lifecycle state across every background-work kind.
func (JobStatus) IsTerminal ¶
IsTerminal reports whether the job has finished (completed or failed).
type JobStopper ¶
type JobStopper interface {
WindJob(id string, sig WindSignal) error
}
JobStopper ends a supervised background job by id. It is the narrow projection of BackgroundTaskRegistry that CloseSubagent uses to wind down the supervised monitor of the subagent it closes - cancelling the job's Run context so the status-line running-count drops immediately instead of lingering until the pane-watcher next polls (or never, if a killed pane is not observed as gone).
type JobSubmitter ¶
type JobSubmitter interface {
Submit(job BackgroundJob)
}
JobSubmitter hands a background job to the supervisor. It is the narrow projection of BackgroundTaskRegistry that tools use to submit work without depending on the whole registry surface.
type OutputRingBuffer ¶
type OutputRingBuffer interface {
Write(p []byte) (n int, err error)
ReadFrom(offset int64) (string, int64)
Recent(maxBytes int) string
TotalWritten() int64
Size() int
String() string
Clear()
}
OutputRingBuffer defines the interface for the circular output buffer.
type PaneObservation ¶
type PaneObservation struct {
// Harvested is the subagent chat's real last assistant message (from its
// result file); "" until its turn completes. The ONLY content ever delivered -
// the pane is never scraped for content (its TUI chrome is noise).
Harvested string
// Screen is a snapshot of the pane's current tail, used by the poller to detect
// idleness by stability: while the subagent works the chat's elapsed-time
// spinner changes this every poll; at idle it is frozen. The input-box
// placeholder ("Type your message") is NOT a usable idle signal - it is drawn
// even mid-turn - so the stability of the whole tail is used instead.
Screen string
// Gone means the pane no longer exists (closed).
Gone bool
// Dead means the pane's process exited (the pane is kept open by remain-on-exit).
Dead bool
// AwaitingApproval means the subagent is blocked on a tool-approval prompt.
AwaitingApproval bool
// ApprovalSummary describes the pending tool call (name + args) when awaiting.
ApprovalSummary string
}
PaneObservation is one probe of an interactive subagent's tmux pane, produced by a pane inspector and consumed by the interactive subagent monitor (interactiveSubagentJob) to decide when a turn completed or an approval is pending.
type RunEvent ¶
RunEvent is emitted by the scheduler as a job run progresses. Line events carry one raw agent stdout line (valid only for the duration of the callback); the terminal event has Done set, with Err populated on failure.
type RunRecord ¶
type RunRecord struct {
SessionID string `yaml:"session_id" json:"session_id"`
JobID string `yaml:"job_id" json:"job_id"`
Status RunStatus `yaml:"status" json:"status"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
StartedAt time.Time `yaml:"started_at" json:"started_at"`
FinishedAt *time.Time `yaml:"finished_at,omitempty" json:"finished_at,omitempty"`
}
RunRecord is the persisted record of one scheduled-job fire. SessionID is both the record key and the conversation ID of the `infer headless` run, so consumers (e.g. the desktop app) can load the full transcript from conversation storage.
type RunStatus ¶
type RunStatus string
RunStatus is the lifecycle state of a single scheduled-job run.
type ScheduledJob ¶
type ScheduledJob struct {
ID string `yaml:"id" json:"id"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
CronExpression string `yaml:"cron_expression" json:"cron_expression"`
Prompt string `yaml:"prompt" json:"prompt"`
Channel string `yaml:"channel,omitempty" json:"channel,omitempty"`
RecipientID string `yaml:"recipient_id,omitempty" json:"recipient_id,omitempty"`
Model string `yaml:"model,omitempty" json:"model,omitempty"`
RunOnce bool `yaml:"run_once,omitempty" json:"run_once,omitempty"`
CreatedAt time.Time `yaml:"created_at" json:"created_at"`
UpdatedAt time.Time `yaml:"updated_at" json:"updated_at"`
LastRun *time.Time `yaml:"last_run,omitempty" json:"last_run,omitempty"`
LastError string `yaml:"last_error,omitempty" json:"last_error,omitempty"`
}
ScheduledJob describes a task that the LLM has asked the system to run on a cron schedule. Jobs are persisted through the configured storage backend and executed by the scheduler running inside the `infer daemon` process.
Each fire spawns a fresh `infer headless` subprocess with its own session ID - no context is carried between fires. Channel/RecipientID are an optional delivery target: when set, run output is forwarded to that channel; when empty, the run is record-only and its output lives in storage (run record + conversation).
type ShellInfo ¶
type ShellInfo struct {
ShellID string
Command string
State ShellState
StartedAt time.Time
CompletedAt *time.Time
ExitCode *int
OutputSize int64
Elapsed time.Duration
}
ShellInfo provides summary information about a shell for UI display.
func NewShellInfo ¶
func NewShellInfo(shell *BackgroundShell) *ShellInfo
NewShellInfo creates a ShellInfo from a BackgroundShell.
type ShellState ¶
type ShellState string
ShellState represents the state of a background shell.
const ( ShellStateRunning ShellState = "running" ShellStateCompleted ShellState = "completed" ShellStateFailed ShellState = "failed" ShellStateCancelled ShellState = "cancelled" )
func (ShellState) IsTerminal ¶
func (s ShellState) IsTerminal() bool
IsTerminal returns true if the state is a terminal state (completed, failed, or cancelled).
func (ShellState) String ¶
func (s ShellState) String() string
String returns the string representation of the shell state.
type ShellTracker ¶
type ShellTracker interface {
// Add adds a new shell to the tracker.
// Returns an error if max concurrent limit is reached.
Add(shell *BackgroundShell) error
// Get retrieves a shell by ID.
// Returns nil if not found.
Get(shellID string) *BackgroundShell
// GetAll returns all tracked shells.
GetAll() []*BackgroundShell
// Remove removes a shell from the tracker.
Remove(shellID string) error
// CountRunning returns the number of shells in running state.
CountRunning() int
}
ShellTracker defines the interface for managing background shells.
type SubagentApprovalFile ¶
type SubagentApprovalFile struct {
Awaiting bool `json:"awaiting"`
Summary string `json:"summary,omitempty"`
}
SubagentApprovalFile is the JSON an interactive subagent's chat writes while it is blocked on a tool-approval prompt. It is an authoritative signal (written the moment the chat blocks, removed when it resolves) so the parent does not have to scrape the pane's TUI to detect a pending approval.
type SubagentResultFile ¶
type SubagentResultFile struct {
FinalAssistant string `json:"final_assistant"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
SessionID string `json:"session_id,omitempty"`
}
SubagentResultFile is the JSON written by `infer headless --result-file` on exit and read back by the Agent tool to harvest a subagent's outcome from a detached (tmux) run whose stdout the parent does not own.
type SubagentState ¶
type SubagentState struct {
ID string
Label string
Description string
Model string
Mode string // SubagentModeHeadless | SubagentModeInteractive
SessionID string
PaneID string
Status SubagentStatus
StartedAt time.Time
CancelFunc context.CancelFunc
Silent bool
}
SubagentState is the data record for one local subagent (an `infer headless` subprocess or tmux pane spawned by the Agent tool) that the subagent control tools (ListSubagents, CloseSubagent, ...) read. Monitoring is owned by the job supervisor (headlessSubagentJob / interactiveSubagentJob), not this struct.
type SubagentStatus ¶
type SubagentStatus string
SubagentStatus represents the lifecycle state of a local subagent.
const ( SubagentRunning SubagentStatus = "running" SubagentCompleted SubagentStatus = "completed" SubagentFailed SubagentStatus = "failed" )
type SubagentTracker ¶
type SubagentTracker interface {
// AddSubagent registers a running subagent. Returns an error if the ID
// is already tracked.
AddSubagent(state *SubagentState) error
// GetSubagent returns a subagent by ID, or nil if not tracked.
GetSubagent(id string) *SubagentState
// GetAllSubagents returns all tracked subagents.
GetAllSubagents() []*SubagentState
// RemoveSubagent removes a subagent from tracking.
RemoveSubagent(id string) error
// CountRunningSubagents returns the number of subagents in the running state.
CountRunningSubagents() int
// SetSubagentStatus atomically updates a subagent's status under the
// tracker's lock. Returns an error if the ID is not tracked.
SetSubagentStatus(id string, status SubagentStatus) error
}
SubagentTracker tracks local subagents spawned by the Agent tool. It is the third projection of BackgroundTaskRegistry (alongside A2ATaskTracker and ShellTracker); methods are suffixed with "Subagent" to avoid colliding with the shell tracker's same-named surface when embedded together.
type TaskInfo ¶
type TaskInfo struct {
// ADK Task contains: ID, ContextID, Status (with State), History, Artifacts, Metadata
Task adk.Task
// UI-specific fields
AgentURL string
StartedAt time.Time
CompletedAt time.Time
}
TaskInfo wraps ADK Task with UI-specific metadata for completed/terminal tasks Used for A2A task retention and display
type TaskRetainer ¶
type TaskRetainer interface {
RetainedTask(result agentdomain.ToolExecutionResult) (TaskInfo, bool)
}
TaskRetainer is an optional BackgroundJob extension. A job that implements it contributes a TaskInfo to the A2A task-retention view when it reaches a terminal state, so a completed/failed/canceled task stays listed in the task view after its monitor goroutine exits (the supervisor drops it from the live "active" set on finish). ok=false opts out (e.g. a non-terminal-for-retention state such as input-required). Jobs that do not implement it are never retained.
type TaskRetentionService ¶
type TaskRetentionService interface {
// AddTask adds a terminal task (completed, failed, canceled, etc.) to retention
AddTask(task TaskInfo)
// GetTasks returns all retained tasks
GetTasks() []TaskInfo
// Clear removes all retained tasks
Clear()
// SetMaxRetention updates the maximum retention count
SetMaxRetention(maxRetention int)
// GetMaxRetention returns the current maximum retention count
GetMaxRetention() int
}
TaskRetentionService manages in-memory retention of completed/terminal A2A tasks Only enabled when A2A is enabled - decouples task retention from StateManager
type TitleGenerator ¶
TitleGenerator interface for conversation title generation
type TrackedJob ¶
TrackedJob is a point-in-time snapshot of one supervised job for the task view and status line.
type WindSignal ¶
type WindSignal int
WindSignal is the one-directional graceful control signal the supervisor pushes into a running job. WindWrapUp asks it to start finishing (inject a wind-down prompt, SIGTERM, or cancel the remote task); WindStop terminates it now (kill pane, SIGKILL, cancel). Graceful shutdown sends WindWrapUp to all jobs, waits a grace window, then WindStop. The supervisor also uses WindStop as the teardown when it reaps a finished job.
const ( WindWrapUp WindSignal = iota WindStop )