agent

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT Imports: 39 Imported by: 0

Documentation

Overview

Package agent implements the core agent loop that drives the LLM ↔ Tool execution cycle and exposes a stream of events for the TUI.

Index

Constants

View Source
const (
	EventUserMessage        = "user_message"
	EventAppendContext      = "append_context" // append user message to ctx without calling LLM (e.g. !shell output)
	EventLLMResponse        = "llm_response"
	EventToolResult         = "tool_result"
	EventTurnCancelled      = "turn_cancelled"
	EventAgentError         = "agent_error"
	EventExecutePlan        = "execute_plan" // Internal: execute a plan file after user selects target agent (payload: *executePlanPayload)
	EventSessionControl     = "session_control"
	EventModelPoolSwitch    = "model_pool_switch"
	EventMCPControl         = "mcp_control"
	EventMCPControlDone     = "mcp_control_done"
	EventPendingDraftUpsert = "pending_draft_upsert"
	EventPendingDraftRemove = "pending_draft_remove"

	// Multi-agent orchestration event types.
	EventAgentDone               = "agent_done"       // SubAgent completed its task
	EventAgentIdle               = "agent_idle"       // SubAgent idle timeout (no tool calls, no Complete)
	EventAgentNotify             = "agent_notify"     // SubAgent non-blocking notify update
	EventEscalate                = "escalate"         // SubAgent requests owner/MainAgent intervention
	EventSubAgentMailbox         = "subagent_mailbox" // structured mailbox message from or about a SubAgent
	EventSubAgentStateChanged    = "subagent_state_changed"
	EventSubAgentCloseRequested  = "subagent_close_requested"
	EventSubAgentProgressUpdated = "subagent_progress_updated"
	EventSubAgentSendMessage     = "subagent_send_message"
	EventSubAgentStop            = "subagent_stop"
	EventAgentLog                = "agent_log"                  // Informational log from SubAgent (e.g. buffer overflow warning)
	EventResetNudge              = "reset_nudge"                // SubAgent activity detected; reset idle nudge counter
	EventSpawnFinished           = "background_object_finished" // Spawned background process finished; runtime-only notification
	EventContinue                = "continue"                   // re-run LLM with existing context (no new user message)
	EventLoopAssessment          = "loop_assessment"            // internal loop-controller decision point after a completed assistant round

	// Durable compaction (async worker); payloads are *compactionDraft / error.
	EventCompactionReady           = "compaction_ready"
	EventCompactionFailed          = "compaction_failed"
	EventCompactionOversizeSuspend = "compaction_oversize_suspend" // LLM call suspended due to oversize while compaction running
)

Internal event types used by the MainAgent event loop.

View Source
const DefaultIdleTimeout = 120 * time.Second

DefaultIdleTimeout is the default duration before a SubAgent is considered idle after receiving a pure-text LLM response.

Variables

View Source
var ErrAgentShutdown = fmt.Errorf("agent is shutting down")

ErrAgentShutdown is returned when the agent is shutting down and can no longer process interactive requests.

Functions

func AdvancePastID

func AdvancePastID(id string)

AdvancePastID parses the numeric suffix from an instance ID (e.g. "builder-3") and advances the global agentSeq so that future calls to NextInstanceID never collide with restored IDs.

func IsContextLengthExceededPendingCompaction

func IsContextLengthExceededPendingCompaction(err error) bool

IsContextLengthExceededPendingCompaction reports whether err is a context-length-exceeded error that should be suspended pending compaction.

func NextInstanceID

func NextInstanceID(agentType string) string

NextInstanceID returns a unique identifier for an agent instance. The name is derived from the agent type (e.g. "builder-1", "explorer-2"). It is safe to call from any goroutine.

func RebuildTouchedPathsFromMessages

func RebuildTouchedPathsFromMessages(msgs []message.Message) []string

RebuildTouchedPathsFromMessages reconstructs the session-scoped touched-file set from persisted tool history. Successful Write/Edit add files; successful Delete removes files. Read-only tools are ignored.

Types

type ActivityObserver

type ActivityObserver interface {
	// OnAgentActivity is called when an agent's activity type changes.
	// agentID is "main" for the main agent, or the instance ID for subagents.
	OnAgentActivity(agentID string, activity ActivityType)
}

ActivityObserver receives notifications when agent activity changes. The observer is called synchronously from the event emission path, so implementations should be non-blocking or spawn their own goroutines.

type ActivityType

type ActivityType string

ActivityType represents the specific technical state of an agent's LLM or tool loop.

const (
	ActivityIdle           ActivityType = "idle"
	ActivityConnecting     ActivityType = "connecting"
	ActivityWaitingHeaders ActivityType = "waiting_headers"
	ActivityWaitingToken   ActivityType = "waiting_token"
	ActivityStreaming      ActivityType = "streaming"
	ActivityExecuting      ActivityType = "executing"
	ActivityCompacting     ActivityType = "compacting"
	ActivityRetrying       ActivityType = "retrying"
	ActivityRetryingKey    ActivityType = "retrying_key"
	ActivityCooling        ActivityType = "cooling"
	ActivityVerifying      ActivityType = "verifying"
)

type AgentActivityEvent

type AgentActivityEvent struct {
	AgentID string
	Type    ActivityType
	Detail  string // e.g. "3 tools", "retry 2/6", "cooldown 5s"
}

AgentActivityEvent is emitted to the TUI to show real-time progress.

type AgentContextUsage

type AgentContextUsage struct {
	AgentID             string
	ContextCurrent      int
	ContextLimit        int
	ContextMessageCount int
}

AgentContextUsage holds context stats for one agent (main or sub) for sidebar display.

type AgentDoneEvent

type AgentDoneEvent struct {
	AgentID string // instance ID (e.g. "agent-1")
	TaskID  string // plan task ID (e.g. "3") or ad-hoc ID (e.g. "adhoc-1")
	Summary string // completion summary from Complete tool
}

AgentDoneEvent signals that a SubAgent has completed its task. Emitted to TUI so it can update the sidebar and optionally switch focus.

type AgentEvent

type AgentEvent interface {
	// contains filtered or unexported methods
}

AgentEvent is the sealed interface for events emitted to the TUI layer.

type AgentForTUI

type AgentForTUI interface {
	Events() <-chan AgentEvent
	GetMessages() []message.Message
	StartupResumeStatus() (pending bool, sessionID string)
	// ProjectRoot returns the runtime project root directory.
	ProjectRoot() string
	// InvokedSkills returns skills explicitly loaded via the Skill tool in the current session.
	InvokedSkills() []*skill.Meta
	// GetTodos returns the current todo list for sidebar display.
	GetTodos() []tools.TodoItem

	MessageSender
	PromptResolver
	ModelSelector
	SessionController
	SubAgentInspector
	LoopController
	RoleController
	UsageReporter
	KeyHealthReporter
	CompactionController
	PlanExecutor
}

AgentForTUI is the full interface required by the TUI. It is implemented by the local MainAgent and by remote client adapters used in C/S mode. New code that consumes only a slice of this surface should target the smaller sub-interfaces (MessageSender, ModelSelector, …) instead.

type AgentResult

type AgentResult struct {
	Summary  string
	Envelope *CompletionEnvelope
	Error    error
}

AgentResult is the completion payload sent via EventAgentDone when a SubAgent finishes its task (or fails).

type AgentStatusEvent

type AgentStatusEvent struct {
	AgentID string // instance ID (e.g. "agent-1")
	Status  string // e.g. "running", "idle", "error", "done"
	Message string // human-readable detail
}

AgentStatusEvent carries a SubAgent status update for the TUI sidebar. Used to reflect agent lifecycle changes (running, idle, error, etc.).

type ArtifactRef added in v0.2.0

type ArtifactRef = tools.ArtifactRef

type AssistantMessageEvent

type AssistantMessageEvent struct {
	AgentID   string // originating agent ("" = main agent)
	Text      string // assistant text content (may be empty if only tool calls)
	ToolCalls int    // number of tool calls in this response
}

AssistantMessageEvent is emitted when a finalized assistant message has been appended to the conversation context. This is the stable, post-streaming representation — no rollback or retry will change it.

Consumers that need "what the assistant just said" should use this event rather than watching StreamTextEvent deltas or waiting for IdleEvent.

type CompactionController added in v0.2.0

type CompactionController interface {
	// IsCompactionRunning reports whether a compaction goroutine is in flight.
	IsCompactionRunning() bool
	// CancelCompaction cancels an in-flight compaction. Returns true if there
	// was a running compaction to cancel.
	CancelCompaction() bool
}

CompactionController exposes durable compaction state for the status bar.

type CompactionStatusEvent

type CompactionStatusEvent struct {
	Status string
	Bytes  int64
	Events int64
}

CompactionStatusEvent drives the TUI background compaction slot precisely. Status is one of: "started", "succeeded", "failed", "cancelled". Bytes/Events are optional and currently reserved for future dedicated compaction-progress wiring.

type CompletionEnvelope

type CompletionEnvelope struct {
	Summary              string              `json:"summary,omitempty"`
	FilesChanged         []string            `json:"files_changed,omitempty"`
	VerificationRun      []string            `json:"verification_run,omitempty"`
	RemainingLimitations []string            `json:"remaining_limitations,omitempty"`
	KnownRisks           []string            `json:"known_risks,omitempty"`
	FollowUpRecommended  []string            `json:"follow_up_recommended,omitempty"`
	Artifacts            []tools.ArtifactRef `json:"artifacts,omitempty"`
}

type ConfirmFunc

type ConfirmFunc func(ctx context.Context, toolName string, args string, needsApproval []string, alreadyAllowed []string) (ConfirmResponse, error)

ConfirmFunc is the callback the agent invokes when a tool call requires user confirmation (permission action "ask"). The TUI (or test harness) supplies the implementation.

  • ctx: context for cancellation (e.g. turn cancelled while waiting)
  • toolName: the name of the tool being invoked (e.g. "Shell")
  • args: the raw JSON arguments string
  • needsApproval: explicit paths covered by this approval prompt (Delete only)
  • alreadyAllowed: explicit paths already allowed by rules in the same batch (Delete only)
  • ConfirmResponse: approved decision plus the final args JSON chosen by the user
  • err: non-nil if the confirmation flow itself fails

type ConfirmRequestEvent

type ConfirmRequestEvent struct {
	ToolName       string
	ArgsJSON       string
	RequestID      string
	Timeout        time.Duration
	NeedsApproval  []string
	AlreadyAllowed []string
}

ConfirmRequestEvent is sent to the TUI when a tool invocation requires user confirmation. The TUI shows the dialog and then calls ResolveConfirm on the agent with the user's choice.

type ConfirmResponse

type ConfirmResponse struct {
	Approved      bool
	FinalArgsJSON string
	EditSummary   string
	DenyReason    string
	RuleIntent    *ConfirmRuleIntent // nil = no new rule
}

ConfirmResponse carries the user's response to a ConfirmRequestEvent.

type ConfirmRuleIntent

type ConfirmRuleIntent struct {
	Pattern string
	Scope   int // 0=session, 1=project, 2=userGlobal (matches permission.RuleScope)
}

ConfirmRuleIntent captures the user's intent to add a permission rule.

type ContextUsageUpdateEvent

type ContextUsageUpdateEvent struct{}

ContextUsageUpdateEvent is emitted by the remote client when it receives a context_usage envelope from the server. It does not carry data; the TUI should re-read GetContextStats/GetUsageStats to refresh the sidebar. Used only in C/S mode so the sidebar updates after Idle.

type DurableTaskRecord

type DurableTaskRecord struct {
	TaskID               string              `json:"task_id"`
	AgentDefName         string              `json:"agent_def_name,omitempty"`
	TaskDesc             string              `json:"task_desc,omitempty"`
	PlanTaskRef          string              `json:"plan_task_ref,omitempty"`
	SemanticTaskKey      string              `json:"semantic_task_key,omitempty"`
	ExpectedWriteScope   tools.WriteScope    `json:"expected_write_scope,omitempty"`
	OwnerAgentID         string              `json:"owner_agent_id,omitempty"`
	OwnerTaskID          string              `json:"owner_task_id,omitempty"`
	Depth                int                 `json:"depth,omitempty"`
	JoinToOwner          bool                `json:"join_to_owner,omitempty"`
	State                string              `json:"state,omitempty"`
	ResumePolicy         string              `json:"resume_policy,omitempty"`
	LatestInstanceID     string              `json:"latest_instance_id,omitempty"`
	InstanceHistory      []string            `json:"instance_history,omitempty"`
	LastSummary          string              `json:"last_summary,omitempty"`
	LastMailboxID        string              `json:"last_mailbox_id,omitempty"`
	LastReplyMessageID   string              `json:"last_reply_message_id,omitempty"`
	LastReplyToMailboxID string              `json:"last_reply_to_mailbox_id,omitempty"`
	LastReplyKind        string              `json:"last_reply_kind,omitempty"`
	LastReplySummary     string              `json:"last_reply_summary,omitempty"`
	LastArtifactRefs     []tools.ArtifactRef `json:"last_artifact_refs,omitempty"`
	LastCompletion       *CompletionEnvelope `json:"last_completion,omitempty"`
	SuspectedStallReason string              `json:"suspected_stall_reason,omitempty"`
	CreatedTurn          uint64              `json:"created_turn,omitempty"`
	LastUpdatedTurn      uint64              `json:"last_updated_turn,omitempty"`
	CreatedAt            time.Time           `json:"created_at,omitempty"`
	UpdatedAt            time.Time           `json:"updated_at,omitempty"`
	ClosedReason         string              `json:"closed_reason,omitempty"`
}

type EnvStatusUpdateEvent

type EnvStatusUpdateEvent struct{}

EnvStatusUpdateEvent signals that background environment state changed (currently MCP connectivity), so the TUI should re-read state providers.

type ErrorEvent

type ErrorEvent struct {
	Err     error
	AgentID string // originating agent ("" = main agent)
}

ErrorEvent carries an error that occurred during the agent loop.

type Event

type Event struct {
	Type     string
	TurnID   uint64
	Payload  any
	Seq      uint64
	SourceID string // identifies which agent sent the event (e.g. "main", "agent-1")
}

Event is an internal event in the MainAgent event loop.

type ForkSessionEvent

type ForkSessionEvent struct {
	Parts []message.ContentPart
}

ForkSessionEvent is emitted after a fork (ee chord) operation completes. Parts holds the content of the forked message so the TUI can load it into the composer for editing.

type HandoffEvent

type HandoffEvent struct {
	PlanPath string
}

HandoffEvent signals that plan generation has finished. The TUI prompts the user to select a target agent for execution (default: builder).

type HandoffResult

type HandoffResult struct {
	PlanPath string
}

HandoffResult wraps the data from a Handoff tool invocation.

type IdleEvent

type IdleEvent struct{}

IdleEvent signals that the agent has finished processing and is waiting for new user input.

type InfoEvent

type InfoEvent struct {
	Message string
	AgentID string // originating agent ("" = main agent)
}

InfoEvent carries an informational message for display in the TUI. Used for non-error status messages (e.g. export/resume success).

type KeyHealthReporter added in v0.2.0

type KeyHealthReporter interface {
	// KeyStats returns (available, total) API keys for the focused agent's provider.
	KeyStats() (available, total int)
	// CurrentRateLimitSnapshot returns the latest rate-limit snapshot for the active key, or nil.
	CurrentRateLimitSnapshot() *ratelimit.KeyRateLimitSnapshot
	ProxyInUseForRef(ref string) bool
}

KeyHealthReporter exposes provider key/rate-limit/proxy state for the right info panel.

type KeyPoolChangedEvent

type KeyPoolChangedEvent struct{}

KeyPoolChangedEvent signals that API key availability (cooldown / selection) changed; the TUI should re-read KeyStats and schedule key-pool ticks.

type LLMResponsePayload

type LLMResponsePayload struct {
	Content                   string
	ThinkingBlocks            []message.ThinkingBlock
	ToolCalls                 []message.ToolCall
	StopReason                string
	ThinkingToolcallMarkerHit bool
	ReasoningContent          string              // full reasoning text when marker hit
	Usage                     *message.TokenUsage // token usage for this round; nil when the provider did not return usage; persisted with the assistant message for session resume
}

LLMResponsePayload wraps an LLM response for the internal event bus.

type LSPServerDisplay

type LSPServerDisplay struct {
	Name     string
	OK       bool
	Pending  bool // not connected yet (lazy start)
	Err      string
	Errors   int
	Warnings int
}

LSPServerDisplay is one row in the ENVIRONMENT / LSP sidebar block.

type LSPStateProvider

type LSPStateProvider interface {
	// LSPServerList returns configured language servers; nil/empty hides the LSP block.
	LSPServerList() []LSPServerDisplay
}

LSPStateProvider is an optional interface for agents that can expose per-file last-review LSP diagnostics (Write/Edit target file only, excluding related files) to the TUI info panel.

type LoopAssessment

type LoopAssessment struct {
	Action  LoopAssessmentAction
	Message string
	Reasons []string
}

type LoopAssessmentAction

type LoopAssessmentAction string
const (
	LoopAssessmentActionNone            LoopAssessmentAction = "none"
	LoopAssessmentActionContinue        LoopAssessmentAction = "continue_from_context"
	LoopAssessmentActionVerify          LoopAssessmentAction = "verify"
	LoopAssessmentActionCompleted       LoopAssessmentAction = "completed"
	LoopAssessmentActionBlocked         LoopAssessmentAction = "blocked"
	LoopAssessmentActionBudgetExhausted LoopAssessmentAction = "budget_exhausted"
)

type LoopContinuationNote

type LoopContinuationNote struct {
	Title    string
	Text     string
	DedupKey string
}

type LoopController added in v0.2.0

type LoopController interface {
	// LoopKeepsMainBusy reports whether the local MainAgent remains in a
	// non-terminal loop state even if no turn is currently active.
	LoopKeepsMainBusy() bool
	// CurrentLoopState returns the current loop-controller state for the main
	// agent, or empty string when loop mode is disabled / unsupported.
	CurrentLoopState() LoopState
	CurrentLoopTarget() string
	CurrentLoopIteration() int
	CurrentLoopMaxIterations() int
	EnableLoopMode(target string)
	DisableLoopMode()
}

LoopController exposes the post-assistant loop-mode runtime state.

type LoopNoticeEvent

type LoopNoticeEvent struct {
	Title    string
	Text     string
	DedupKey string
}

LoopNoticeEvent displays the loop continuation/control note that is also sent to the model for the next continued request.

type LoopState

type LoopState string
const (
	LoopStateIdle            LoopState = "idle"
	LoopStateExecuting       LoopState = "executing"
	LoopStateVerifying       LoopState = "verifying"
	LoopStateAssessing       LoopState = "assessing"
	LoopStateCompleted       LoopState = "completed"
	LoopStateBlocked         LoopState = "blocked"
	LoopStateBudgetExhausted LoopState = "budget_exhausted"
)

type LoopStateChangedEvent

type LoopStateChangedEvent struct{}

LoopStateChangedEvent notifies the TUI that loop-controller state changed and any loop-dependent pills/activity text should refresh immediately.

type MCPControlAction added in v0.5.1

type MCPControlAction string
const (
	MCPControlEnable  MCPControlAction = "enable"
	MCPControlDisable MCPControlAction = "disable"
)

type MCPControlRequest added in v0.5.1

type MCPControlRequest struct {
	Action  MCPControlAction
	Servers []string
}

MCPControlRequest describes a runtime MCP enable/disable operation. Servers may contain one or more server names. If Servers is empty, the operation applies to all configured servers.

type MCPControlResult added in v0.5.1

type MCPControlResult struct {
	Tools       []tools.Tool
	PromptBlock string
}

MCPControlResult carries the post-operation MCP tool set and the prompt block describing connected servers.

type MCPSelectEvent added in v0.5.1

type MCPSelectEvent struct{}

MCPSelectEvent signals the TUI to open the MCP server selector overlay. Emitted in response to /mcp with no arguments.

type MCPServerDisplay

type MCPServerDisplay struct {
	Name        string
	OK          bool
	Pending     bool // not connected yet (async startup)
	Disabled    bool // explicitly disabled (manual /mcp disable)
	Manual      bool // configured as manual/on-demand; only manual servers can be changed with /mcp
	Retrying    bool // transient failure, retry still in progress
	Attempt     int
	MaxAttempts int
	Err         string
}

MCPServerDisplay is one row in the TUI MCP sidebar.

type MCPStateProvider

type MCPStateProvider interface {
	// MCPServerList returns every configured MCP with connection outcome.
	MCPServerList() []MCPServerDisplay
}

MCPStateProvider is implemented by agents that can expose MCP server status.

type MainAgent

type MainAgent struct {
	// contains filtered or unexported fields
}

MainAgent orchestrates the LLM ↔ Tool loop. It owns an internal event bus (eventCh) for sequencing work and an output channel (outputCh) that the TUI consumes.

func NewMainAgent

func NewMainAgent(
	ctx context.Context,
	llmClient *llm.Client,
	ctxMgr *ctxmgr.Manager,
	toolRegistry *tools.Registry,
	hookEngine hook.Manager,
	sessionDir string,
	modelName string,
	projectRoot string,
	globalCfg *config.Config,
	projectCfg *config.Config,
	mcpClientInfo mcp.ClientInfo,
) *MainAgent

NewMainAgent creates a fully-initialised MainAgent. The caller must invoke Run in a separate goroutine to start the event loop.

projectRoot is the root directory of the project (typically cwd) and is used to load AGENTS.md and determine git repository status for the system prompt.

globalCfg is the user-level config (~/.config/chord/config.yaml). projectCfg is the project-level config (.chord/config.yaml); either may be nil.

func (*MainAgent) AddedOverlayRules

func (a *MainAgent) AddedOverlayRules() []permission.AddedRule

AddedOverlayRules returns rules added from the confirm rule picker in this session.

func (*MainAgent) AgentOverridePoolName added in v0.3.0

func (a *MainAgent) AgentOverridePoolName(agentName string) (string, bool)

func (*MainAgent) AppendContextMessage

func (a *MainAgent) AppendContextMessage(msg message.Message)

AppendContextMessage appends a user-role message to the focused agent's conversation context without invoking the LLM (e.g. TUI !shell output).

func (*MainAgent) ApplyInitialModel

func (a *MainAgent) ApplyInitialModel(providerModel string) error

ApplyInitialModel applies the given model (e.g. from config or recovery) without showing a "Switched model" toast. Used at startup.

func (*MainAgent) AskQuestions

func (a *MainAgent) AskQuestions(ctx context.Context, questions []tools.QuestionItem, timeout time.Duration) ([]tools.QuestionAnswer, error)

AskQuestions emits question request events one at a time, waits for each answer, and returns the collected responses in tool-compatible form.

func (*MainAgent) AvailableAgents

func (a *MainAgent) AvailableAgents() []string

AvailableAgents returns the names of agent roles available for Handoff selection. Only main-mode agents are eligible, and the current active role is excluded so planner cannot hand off to itself. builder remains the default when available so the selector always offers an execution agent. Non-builder roles are sorted alphabetically for deterministic selection order.

func (*MainAgent) AvailableRoles

func (a *MainAgent) AvailableRoles() []string

AvailableRoles returns the ordered list of roles the user can cycle through. Only main-mode agents are included; subagent-only configs are excluded. builder is always first; planner second (if present); custom roles after that are sorted alphabetically for deterministic Tab cycling.

func (*MainAgent) AvailableSubAgents

func (a *MainAgent) AvailableSubAgents() []tools.AgentInfo

func (*MainAgent) AwaitConfirm

func (a *MainAgent) AwaitConfirm(ctx context.Context, toolName, argsJSON string, timeout time.Duration, needsApproval []string, alreadyAllowed []string) (ConfirmResponse, error)

AwaitConfirm emits a confirmation request event, waits for the user's reply, and returns the resolved response. Only one confirm flow may be active at a time because the TUI supports a single modal dialog.

func (*MainAgent) CancelCompaction

func (a *MainAgent) CancelCompaction() bool

CancelCompaction cancels the in-flight compaction goroutine. Returns true if there was a running compaction to cancel.

func (*MainAgent) CancelCurrentTurn

func (a *MainAgent) CancelCurrentTurn() bool

CancelCurrentTurn cancels the agent's active turn (if any), aborting any in-flight LLM call or tool execution. It is safe to call from any goroutine (typically the TUI's Ctrl+C handler). Returns true if a turn was active and cancelled, false if the agent was already idle.

If the turn had pending tool calls, synthetic terminal tool-result messages are appended when the cancellation event is handled so the conversation and persisted session keep a matching output for each tool call.

func (*MainAgent) CancelSubAgent

func (a *MainAgent) CancelSubAgent(ctx context.Context, taskID, reason string) (tools.TaskHandle, error)

func (*MainAgent) ClearPendingInteractions

func (a *MainAgent) ClearPendingInteractions()

ClearPendingInteractions removes requestID mappings for any in-flight confirm/question requests. It does not close the per-request channels; any waiters are expected to exit via ctx cancellation or stoppingCh during shutdown.

func (*MainAgent) ContinueFromContext

func (a *MainAgent) ContinueFromContext()

ContinueFromContext re-runs the LLM with the existing context without appending a new user message. Routes to the focused SubAgent if active.

func (*MainAgent) CreateSubAgent

func (a *MainAgent) CreateSubAgent(ctx context.Context, description, agentType string, planTaskRef, semanticTaskKey string, expectedWriteScope tools.WriteScope) (tools.TaskHandle, error)

func (*MainAgent) CurrentLoopIteration

func (a *MainAgent) CurrentLoopIteration() int

func (*MainAgent) CurrentLoopMaxIterations

func (a *MainAgent) CurrentLoopMaxIterations() int

func (*MainAgent) CurrentLoopState

func (a *MainAgent) CurrentLoopState() LoopState

func (*MainAgent) CurrentLoopTarget

func (a *MainAgent) CurrentLoopTarget() string

func (*MainAgent) CurrentPoolName added in v0.3.0

func (a *MainAgent) CurrentPoolName() string

CurrentPoolName returns the effective pool name for the agent currently shown in the TUI (focused SubAgent if any, else current main role), or "" if no pool policy is configured.

func (*MainAgent) CurrentRateLimitSnapshot

func (a *MainAgent) CurrentRateLimitSnapshot() *ratelimit.KeyRateLimitSnapshot

CurrentRateLimitSnapshot returns the latest rate-limit snapshot for the active provider when that provider uses preset: codex. Otherwise it returns nil. Display precedence is:

  1. current provider-scoped inline snapshot cache (cleared on key switch)
  2. client-selected key inline snapshot
  3. provider/account-scoped polled usage snapshot

func (*MainAgent) CurrentRole

func (a *MainAgent) CurrentRole() string

CurrentRole returns the active role name. Goroutine-safe.

func (*MainAgent) CurrentRoleConfig

func (a *MainAgent) CurrentRoleConfig() *config.AgentConfig

CurrentRoleConfig returns the active role configuration. The returned config must be treated as read-only by callers.

func (*MainAgent) CurrentRoleModelRefs

func (a *MainAgent) CurrentRoleModelRefs() []string

CurrentRoleModelRefs returns the configured model chain for the active role. Entries preserve the original AgentConfig.Models strings, including any inline @variant suffixes. A nil/empty slice means "use global default model only". CurrentRoleModelRefs returns the effective model chain for the active role, resolved through the model pool policy. A nil/empty slice means "use global default model only" (auto mode).

func (*MainAgent) DeleteSession

func (a *MainAgent) DeleteSession(sessionID string) error

func (*MainAgent) DisableLoopMode

func (a *MainAgent) DisableLoopMode()

func (*MainAgent) EnableLoopMode

func (a *MainAgent) EnableLoopMode(target string)

func (*MainAgent) Events

func (a *MainAgent) Events() <-chan AgentEvent

Events returns a read-only channel of AgentEvents for the TUI to consume.

func (*MainAgent) ExecutePlan

func (a *MainAgent) ExecutePlan(planPath, agentName string)

func (*MainAgent) ExportSession

func (a *MainAgent) ExportSession(format, path string)

func (*MainAgent) FocusedAgentID

func (a *MainAgent) FocusedAgentID() string

FocusedAgentID returns the instance ID of the currently focused SubAgent, or "" if the main agent is focused.

func (*MainAgent) FocusedAgentName added in v0.3.0

func (a *MainAgent) FocusedAgentName() string

FocusedAgentName returns the agent definition name of the currently focused SubAgent, or "" if the main agent is focused.

func (*MainAgent) ForkSession

func (a *MainAgent) ForkSession(msgIndex int)

ForkSession queues a fork of the current session at msgIndex. The fork creates a new session seeded with messages[:msgIndex] as history; the message at msgIndex is returned via ForkSessionEvent so the TUI can load it into the composer.

func (*MainAgent) GetAllAgentsContextUsage

func (a *MainAgent) GetAllAgentsContextUsage() []AgentContextUsage

func (*MainAgent) GetContextMessageCount

func (a *MainAgent) GetContextMessageCount() int

GetContextMessageCount returns the number of messages in the focused agent's context (for sidebar).

func (*MainAgent) GetContextStats

func (a *MainAgent) GetContextStats() (current, limit int)

GetContextStats returns current context usage and limit for the focused agent. Current = input + output + cache + reasoning from last API response (all count toward context).

func (*MainAgent) GetMessages

func (a *MainAgent) GetMessages() []message.Message

GetMessages returns a thread-safe snapshot of the focused agent's conversation history. Routes to the focused SubAgent if one is active.

func (*MainAgent) GetSessionSummary

func (a *MainAgent) GetSessionSummary() *SessionSummary

func (*MainAgent) GetSidebarUsageStats

func (a *MainAgent) GetSidebarUsageStats() analytics.SessionStats

GetSidebarUsageStats returns usage for the TUI-focused agent only (main or SubAgent), matching GetContextStats / GetTokenUsage routing.

func (*MainAgent) GetSubAgents

func (a *MainAgent) GetSubAgents() []SubAgentInfo

GetSubAgents returns information about all active SubAgents for TUI sidebar display. Safe to call from any goroutine.

func (*MainAgent) GetTodos

func (a *MainAgent) GetTodos() []tools.TodoItem

GetTodos returns a copy of the current todo list. It implements the tools.TodoStore interface.

func (*MainAgent) GetTokenUsage

func (a *MainAgent) GetTokenUsage() message.TokenUsage

GetTokenUsage returns cumulative token usage statistics.

func (*MainAgent) GetUsageStats

func (a *MainAgent) GetUsageStats() analytics.SessionStats

GetUsageStats returns session-wide usage statistics (all agents in the session).

func (*MainAgent) HasAvailableSubAgents

func (a *MainAgent) HasAvailableSubAgents() bool

func (*MainAgent) InvokedSkills

func (a *MainAgent) InvokedSkills() []*skill.Meta

func (*MainAgent) IsCompactionRunning

func (a *MainAgent) IsCompactionRunning() bool

IsCompactionRunning reports whether a compaction goroutine is currently in flight, or a draft is waiting to be applied at the continuation barrier. This is the public API for TUI to query compaction state.

func (*MainAgent) KeyPoolNextTransition

func (a *MainAgent) KeyPoolNextTransition() time.Duration

KeyPoolNextTransition returns how soon the key pool sidebar line may need a refresh (cooldown expiry or Codex rate-limit window reset). Zero means no scheduled transition or single-key pool. Uses the same agent as KeyStats.

func (*MainAgent) KeyStats

func (a *MainAgent) KeyStats() (confirmed, total int)

KeyStats returns (healthy, total) API keys for the focused agent's provider (SubAgent when focused, else MainAgent), aligned with RunningModelRef. healthy = selectable AND not recovering (re-proven healthy since last failure/reset).

func (*MainAgent) LSPServerList

func (a *MainAgent) LSPServerList() []LSPServerDisplay

func (*MainAgent) ListSessionSummaries

func (a *MainAgent) ListSessionSummaries() ([]SessionSummary, error)

func (*MainAgent) ListSkills

func (a *MainAgent) ListSkills() []*skill.Meta

func (*MainAgent) LoadSkill

func (a *MainAgent) LoadSkill(name string) (*skill.Skill, error)

func (*MainAgent) LoopKeepsMainBusy

func (a *MainAgent) LoopKeepsMainBusy() bool

func (*MainAgent) MCPServerList

func (a *MainAgent) MCPServerList() []MCPServerDisplay

func (*MainAgent) MainModelPoolName added in v0.5.1

func (a *MainAgent) MainModelPoolName() string

func (*MainAgent) MainModelPoolNames added in v0.5.1

func (a *MainAgent) MainModelPoolNames() []string

func (*MainAgent) MarkSkillInvoked

func (a *MainAgent) MarkSkillInvoked(meta *skill.Meta)

func (*MainAgent) MarkSkillInvokedByName

func (a *MainAgent) MarkSkillInvokedByName(name string)

func (*MainAgent) MarkSkillsReady

func (a *MainAgent) MarkSkillsReady()

func (*MainAgent) ModelName

func (a *MainAgent) ModelName() string

ModelName returns the name of the model the agent is using.

func (*MainAgent) ModelPoolPolicy added in v0.3.0

func (a *MainAgent) ModelPoolPolicy() *RuntimeModelPoolPolicy

ModelPoolPolicy returns the current runtime model pool policy (read-only).

func (*MainAgent) ModelsStatusText added in v0.3.0

func (a *MainAgent) ModelsStatusText() string

func (*MainAgent) NewSession

func (a *MainAgent) NewSession()

func (*MainAgent) NotifyEnvStatusUpdated

func (a *MainAgent) NotifyEnvStatusUpdated()

func (*MainAgent) NotifySubAgent

func (a *MainAgent) NotifySubAgent(ctx context.Context, taskID, message, kind string) (tools.TaskHandle, error)

func (*MainAgent) Overlay

func (a *MainAgent) Overlay() *permission.Overlay

Overlay returns the overlay for external access (e.g., from TUI).

func (*MainAgent) PendingUserMessageCount

func (a *MainAgent) PendingUserMessageCount() int

PendingUserMessageCount returns the number of queued user messages waiting to be drained after the current turn ends.

func (*MainAgent) PoolNames added in v0.3.0

func (a *MainAgent) PoolNames() []string

func (*MainAgent) PrewarmModelPolicy

func (a *MainAgent) PrewarmModelPolicy() error

PrewarmModelPolicy prepares the current main-agent model policy in the background so the first real LLM request doesn't pay the setup cost.

func (*MainAgent) ProjectRoot

func (a *MainAgent) ProjectRoot() string

func (*MainAgent) ProviderModelRef

func (a *MainAgent) ProviderModelRef() string

ProviderModelRef returns the selected model reference string for unique identification. It may include an inline @variant suffix when the selected model was configured that way, which lets the TUI distinguish model presets that share the same base provider/model.

func (*MainAgent) ProxyInUseForRef

func (a *MainAgent) ProxyInUseForRef(ref string) bool

ProxyInUseForRef reports whether the given provider/model ref uses a proxy. ref is "providerName/modelID"; if empty, the main agent's ProviderModelRef is used. Used by the TUI status bar to show a proxy indicator for the current (or focused) agent.

func (*MainAgent) QueuePendingUserDraft

func (a *MainAgent) QueuePendingUserDraft(draftID string, parts []message.ContentPart) bool

QueuePendingUserDraft mirrors a busy local TUI draft into the agent's pending queue so it can be consumed in-turn or at the next idle drain.

func (*MainAgent) RegisterMainMCPServers

func (a *MainAgent) RegisterMainMCPServers(serverNames []string)

RegisterMainMCPServers registers the main-agent's MCP server names as sentinels in mcpServerCache so that SubAgents never reconnect them. Called after the main-agent MCP servers are connected.

func (*MainAgent) ReloadAgentsMD

func (a *MainAgent) ReloadAgentsMD() bool

ReloadAgentsMD reloads project AGENTS.md from disk and marks the startup gate (agentsMDReady) so ensureSessionBuilt can proceed. The content is consumed the next time ensureSessionBuilt rebuilds the session-context reminder (on session-head events). Mid-session edits to AGENTS.md are not picked up until the next /new, /resume, or equivalent reset; AGENTS.md is treated as a session-scope snapshot.

func (*MainAgent) RemoveLastMessage

func (a *MainAgent) RemoveLastMessage()

RemoveLastMessage removes the last message from context and rewrites the persistence log. Routes to the focused SubAgent if active. Only valid when the agent is idle.

func (*MainAgent) RemoveOverlayAddedRule

func (a *MainAgent) RemoveOverlayAddedRule(index int) error

RemoveOverlayAddedRule removes one picker-added rule and refreshes merged ruleset.

func (*MainAgent) RemovePendingUserDraft

func (a *MainAgent) RemovePendingUserDraft(draftID string) bool

RemovePendingUserDraft removes a queued draft before it is consumed.

func (*MainAgent) ResetMCPReady added in v0.5.1

func (a *MainAgent) ResetMCPReady()

ResetMCPReady creates a new MCP readiness channel. It is used when MCP startup or runtime control begins so the next request blocks until the new tool surface is ready.

func (*MainAgent) ResolveConfirm

func (a *MainAgent) ResolveConfirm(action, finalArgsJSON, editSummary, denyReason, requestID string)

ResolveConfirm sends the user's confirmation response back to the waiting ConfirmFunc goroutine via the requestID→channel map. Only acquires confirmMapMu (never confirmFlowMu) to avoid deadlock.

func (*MainAgent) ResolveConfirmWithRuleIntent

func (a *MainAgent) ResolveConfirmWithRuleIntent(action, finalArgsJSON, editSummary, denyReason, requestID string, ruleIntent *ConfirmRuleIntent)

ResolveConfirmWithRuleIntent sends the confirmation response with an optional rule intent for adding a permission overlay rule.

func (*MainAgent) ResolveQuestion

func (a *MainAgent) ResolveQuestion(answers []string, cancelled bool, requestID string)

ResolveQuestion sends the user's question response back to the waiting QuestionFunc goroutine via the requestID→channel map. Only acquires questionMapMu (never questionFlowMu) to avoid deadlock.

func (*MainAgent) RestoreSessionAtStartup

func (a *MainAgent) RestoreSessionAtStartup() error

RestoreSessionAtStartup preloads the current session directory before the event loop starts, so the first on_session_start hook and all new writes target the resumed session directly.

func (*MainAgent) ResumeSession

func (a *MainAgent) ResumeSession()

func (*MainAgent) ResumeSessionID

func (a *MainAgent) ResumeSessionID(sessionID string)

func (*MainAgent) Run

func (a *MainAgent) Run(ctx context.Context) error

Run starts the blocking event loop. It returns when ctx is cancelled, the agent's parent context is cancelled, or an unrecoverable error occurs. The caller should run this in a dedicated goroutine:

go agent.Run(ctx)

func (*MainAgent) RunningModelRef

func (a *MainAgent) RunningModelRef() string

RunningModelRef returns the effective provider/model for the TUI sidebar (focused SubAgent if any, else MainAgent). It may differ from ProviderModelRef() while fallback is in effect on that agent's client.

func (*MainAgent) RunningVariant

func (a *MainAgent) RunningVariant() string

RunningVariant returns the active variant name for the running model (focused SubAgent if any, else MainAgent), or empty string if none.

func (*MainAgent) SendAgentEvent

func (a *MainAgent) SendAgentEvent(eventType, sourceID string, payload any)

SendAgentEvent maps tool event type strings to internal event constants and forwards the event through the event bus. It implements the tools.EventSender interface.

func (*MainAgent) SendUserMessage

func (a *MainAgent) SendUserMessage(content string)

SendUserMessage enqueues a user message for processing. It is safe to call from any goroutine (typically the TUI input handler).

If a SubAgent is currently focused (via Tab), the message is routed directly to that SubAgent instead of the MainAgent's event loop. Local-only slash commands (/export, /models) bypass SubAgent routing because they belong to the main agent — they're sent to the main event loop unchanged.

func (*MainAgent) SendUserMessageWithParts

func (a *MainAgent) SendUserMessageWithParts(parts []message.ContentPart)

SendUserMessageWithParts enqueues a multi-part user message (text + images).

func (*MainAgent) SessionID

func (a *MainAgent) SessionID() string

SessionID returns the unique session identifier for this agent instance. Used by the C/S server to scope event subscriptions. Goroutine-safe.

func (*MainAgent) SetActivityObserver

func (a *MainAgent) SetActivityObserver(obs ActivityObserver)

SetActivityObserver registers an observer for activity events. Only one observer can be registered at a time; setting a new one replaces the previous. Pass nil to remove the observer.

func (*MainAgent) SetAgentConfigs

func (a *MainAgent) SetAgentConfigs(configs map[string]*config.AgentConfig)

SetAgentConfigs stores the pre-resolved agent configurations (built-in → global → project merged). If the current active role is present in configs, it is preserved; otherwise the active role defaults to "builder". The permission ruleset is rebuilt accordingly.

Call this after NewMainAgent and before Run.

func (*MainAgent) SetAgentModelPool added in v0.3.0

func (a *MainAgent) SetAgentModelPool(agentName, pool string) error

func (*MainAgent) SetConfirmFunc

func (a *MainAgent) SetConfirmFunc(fn ConfirmFunc)

SetConfirmFunc sets the callback used when a tool invocation requires user confirmation (permission action "ask"). This must be called before Run when the active ruleset can yield ActionAsk.

func (*MainAgent) SetCurrentModelPool added in v0.5.1

func (a *MainAgent) SetCurrentModelPool(pool string) error

func (*MainAgent) SetCustomCommands

func (a *MainAgent) SetCustomCommands(defs []*command.Definition)

func (*MainAgent) SetLLMFactory

func (a *MainAgent) SetLLMFactory(fn func(systemPrompt string, agentModels []string, variant string) *llm.Client)

func (*MainAgent) SetLSPSessionFuncs

func (a *MainAgent) SetLSPSessionFuncs(reset func(), load func([]message.Message))

func (*MainAgent) SetLSPStatusFunc

func (a *MainAgent) SetLSPStatusFunc(serverList func() []LSPServerDisplay)

func (*MainAgent) SetMCPControlFunc added in v0.5.1

func (a *MainAgent) SetMCPControlFunc(fn func(context.Context, MCPControlRequest) (MCPControlResult, error))

SetMCPControlFunc installs the runtime callback used to connect/disconnect MCP servers. The callback runs in a background goroutine; results are applied on the agent event loop.

func (*MainAgent) SetMCPServerEnabled added in v0.5.1

func (a *MainAgent) SetMCPServerEnabled(server string, enabled bool) error

SetMCPServerEnabled requests enabling/disabling an MCP server. It is safe to call from any goroutine.

func (*MainAgent) SetMCPServersPromptBlock

func (a *MainAgent) SetMCPServersPromptBlock(block string)

SetMCPServersPromptBlock sets the MCP section appended to the system prompt and refreshes the installed system prompt on the LLM client and context manager.

func (*MainAgent) SetMCPStatusFunc

func (a *MainAgent) SetMCPStatusFunc(serverList func() []MCPServerDisplay)

func (*MainAgent) SetModelPoolPolicy added in v0.3.0

func (a *MainAgent) SetModelPoolPolicy(policy *RuntimeModelPoolPolicy, statePath string)

SetModelPoolPolicy installs the runtime model pool policy. Must be called before Run. statePath is the per-project file for persisting pool selections.

func (*MainAgent) SetModelSwitchFactory

func (a *MainAgent) SetModelSwitchFactory(fn func(providerModel string) (*llm.Client, string, int, error))

SetModelSwitchFactory sets the factory used by SwitchModel to create a new LLM client from a "provider/model" reference string. Must be called before Run. The factory returns (client, displayModelName, contextLimit, error).

func (*MainAgent) SetPendingMCPDiscovery

func (a *MainAgent) SetPendingMCPDiscovery(mcpTools []tools.Tool, block string)

func (*MainAgent) SetProviderModelRef

func (a *MainAgent) SetProviderModelRef(ref string)

SetProviderModelRef sets the initial selected model reference for the agent. The ref is usually "provider/model" and may optionally include an inline @variant suffix. Called from startup/model-switch wiring after construction.

func (*MainAgent) SetSessionArtifactsDirFunc

func (a *MainAgent) SetSessionArtifactsDirFunc(fn func() string)

SetSessionArtifactsDirFunc installs a callback that returns the active session artifacts directory. When unset, exports fall back to the historical project-level path.

func (*MainAgent) SetSessionLock

func (a *MainAgent) SetSessionLock(lock *recovery.SessionLock)

SetSessionLock installs the ownership lock handle for the currently active session.

func (*MainAgent) SetSessionTargetChangedFunc

func (a *MainAgent) SetSessionTargetChangedFunc(fn func(string))

SetSessionTargetChangedFunc installs a callback invoked after the active session directory changes.

func (*MainAgent) SetSkills

func (a *MainAgent) SetSkills(skills []*skill.Meta)

func (*MainAgent) SetUsageEventSink

func (a *MainAgent) SetUsageEventSink(fn func(event analytics.UsageEvent))

func (*MainAgent) Shutdown

func (a *MainAgent) Shutdown(timeout time.Duration) error

Shutdown cancels any in-flight work and waits for the event loop to exit (up to the given timeout). The caller should cancel the context passed to Run as well.

func (*MainAgent) StartupResumeStatus

func (a *MainAgent) StartupResumeStatus() (pending bool, sessionID string)

func (*MainAgent) SwapLLMClient

func (a *MainAgent) SwapLLMClient(newClient *llm.Client, modelName string, contextLimit int)

SwapLLMClient atomically replaces the MainAgent's LLM client, model name, and context manager token budget. Thread-safe: called from the TUI goroutine while the event loop may be reading llmClient.

func (*MainAgent) SwitchFocus

func (a *MainAgent) SwitchFocus(agentID string)

func (*MainAgent) SwitchModel

func (a *MainAgent) SwitchModel(providerModel string) error

SwitchModel switches the MainAgent to a different model at runtime. It is kept for internal tests and pool-driven client rebuilds; user-facing commands should switch pools via /models rather than choosing provider/model refs directly.

func (*MainAgent) SwitchRole

func (a *MainAgent) SwitchRole(role string)

SwitchRole switches the MainAgent to the named role and emits RoleChangedEvent. Goroutine-safe (posts to eventCh).

func (*MainAgent) UpdatePendingUserDraft

func (a *MainAgent) UpdatePendingUserDraft(draftID string, parts []message.ContentPart) bool

UpdatePendingUserDraft replaces a queued draft before it is consumed.

func (*MainAgent) UpdateTodos

func (a *MainAgent) UpdateTodos(todos []tools.TodoItem) error

UpdateTodos replaces the todo list and saves a snapshot via the recovery manager. It implements the tools.TodoStore interface.

func (*MainAgent) WakeCodexRateLimitPolling added in v0.3.0

func (a *MainAgent) WakeCodexRateLimitPolling()

WakeCodexRateLimitPolling triggers an on-demand /wham/usage poll for the currently focused agent's provider, when configured with preset: codex. It is a best-effort hint used by the TUI when a reset timestamp is reached.

type MessageSender added in v0.2.0

type MessageSender interface {
	SendUserMessage(content string)
	// SendUserMessageWithParts sends a user message that may include images.
	SendUserMessageWithParts(parts []message.ContentPart)
	// AppendContextMessage appends a user message to the model context without
	// invoking the LLM. Content may differ from Parts when persistence needs a
	// machine-readable form but the live context should stay human-readable.
	AppendContextMessage(msg message.Message)
	CancelCurrentTurn() bool

	// QueuePendingUserDraft mirrors a busy local draft into the agent's pending
	// queue so it can be consumed later without showing in the transcript early.
	QueuePendingUserDraft(draftID string, parts []message.ContentPart) bool
	// UpdatePendingUserDraft replaces a queued draft before it is consumed.
	UpdatePendingUserDraft(draftID string, parts []message.ContentPart) bool
	// RemovePendingUserDraft removes a queued draft before it is consumed.
	RemovePendingUserDraft(draftID string) bool

	// ContinueFromContext re-runs the LLM using the existing context without
	// appending a new user message. Routes to focused SubAgent if one is active.
	ContinueFromContext()
	// RemoveLastMessage removes the last message from context and rewrites
	// persistence. Used before ContinueFromContext when last message is a
	// thinking-only assistant block that was interrupted.
	RemoveLastMessage()
}

MessageSender covers user-message submission, queued drafts, and turn continuation. Implemented by MainAgent and remote-client adapters.

type ModelOption

type ModelOption struct {
	ProviderModel string // e.g. "anthropic-main/claude-opus-4.7" or "anthropic-main/claude-opus-4.7@high"
	ProviderName  string // e.g. "anthropic-main"
	ModelID       string // e.g. "claude-opus-4.7"
	ContextLimit  int
	OutputLimit   int
}

ModelOption describes a model available for runtime switching.

type ModelPoolSelectorTarget added in v0.3.0

type ModelPoolSelectorTarget struct {
	Kind      ModelPoolSelectorTargetKind `json:"kind"`
	AgentName string                      `json:"agent_name,omitempty"`
}

ModelPoolSelectorTarget describes the explicit target edited by the model-pool selector. Current-view selection follows the active TUI focus (main role or focused SubAgent). Main role selection changes the current project-scoped model pool. Agent override selection changes the named agent's explicit fixed pool and may also offer a restore-default action.

type ModelPoolSelectorTargetKind added in v0.3.0

type ModelPoolSelectorTargetKind string

ModelPoolSelectorTargetKind identifies which runtime model-pool setting the TUI selector should edit.

const (
	ModelPoolSelectorTargetCurrentView   ModelPoolSelectorTargetKind = "current_view"
	ModelPoolSelectorTargetMainRole      ModelPoolSelectorTargetKind = "main_role"
	ModelPoolSelectorTargetAgentOverride ModelPoolSelectorTargetKind = "agent_override"
)

type ModelSelectEvent

type ModelSelectEvent struct {
	Target ModelPoolSelectorTarget
}

ModelSelectEvent signals the TUI to open the model pool selector overlay. Emitted in response to /models for the current view.

type ModelSelector added in v0.2.0

type ModelSelector interface {
	ProviderModelRef() string
	RunningModelRef() string
	RunningVariant() string
	// CurrentPoolName returns the effective pool name for the agent currently shown
	// in the TUI (focused SubAgent if any, else current main role), or "" if no pool
	// policy is configured.
	CurrentPoolName() string
	// PoolNames returns the pool names for the agent currently shown in the TUI.
	PoolNames() []string
	// MainModelPoolName returns the effective pool name for the current main
	// role regardless of focused SubAgent state.
	MainModelPoolName() string
	// MainModelPoolNames returns the pool names for the current main role regardless
	// of focused SubAgent state.
	MainModelPoolNames() []string
	// AgentOverridePoolName returns the explicit override for the named agent, if any.
	AgentOverridePoolName(agentName string) (string, bool)
	// SetCurrentModelPool sets the current main model pool.
	SetCurrentModelPool(pool string) error
	// SetAgentModelPool sets the named agent's pool.
	SetAgentModelPool(agentName, pool string) error
}

ModelSelector exposes model identity for the status bar and model pool controls.

type PendingDraftConsumedEvent

type PendingDraftConsumedEvent struct {
	DraftID string
	Parts   []message.ContentPart
	AgentID string // "" = main agent
}

PendingDraftConsumedEvent signals that a queued draft was actually appended to the conversation context and is now part of the transcript.

type PendingToolCall

type PendingToolCall struct {
	CallID   string
	Name     string
	ArgsJSON string
	AgentID  string
	Audit    *message.ToolArgsAudit
}

PendingToolCall records the minimal metadata needed to close a pending tool card when a turn is cancelled before a normal ToolResultEvent arrives.

type PlanExecutor added in v0.2.0

type PlanExecutor interface {
	// ExecutePlan triggers execution of a plan with the specified target agent.
	// agentName may be empty (defaults to "builder").
	ExecutePlan(planPath, agentName string)
}

PlanExecutor triggers plan-execution workflows.

type PromptResolver added in v0.2.0

type PromptResolver interface {
	// ResolveConfirm sends the user's confirmation response back to the pending
	// confirm flow.
	ResolveConfirm(action, finalArgsJSON, editSummary, denyReason, requestID string)
	// ResolveQuestion sends the user's question response back to the pending
	// question flow.
	ResolveQuestion(answers []string, cancelled bool, requestID string)
}

PromptResolver delivers user responses for confirm/question dialogs back to the agent's pending interaction flow.

type QuestionRequestEvent

type QuestionRequestEvent struct {
	ToolName      string
	Header        string
	Question      string
	Options       []string
	OptionDetails []string
	DefaultAnswer string
	Multiple      bool
	RequestID     string
	Timeout       time.Duration
}

QuestionRequestEvent is sent to the TUI when the agent asks a structured question. The TUI shows the dialog and then calls ResolveQuestion.

type QuestionResponse

type QuestionResponse struct {
	Answers   []string
	Cancelled bool
}

QuestionResponse carries the user's response to a QuestionRequestEvent.

type RateLimitUpdatedEvent

type RateLimitUpdatedEvent struct {
	Snapshot *ratelimit.KeyRateLimitSnapshot
}

RateLimitUpdatedEvent is emitted when a new rate-limit snapshot is available for the current API key. The TUI should re-read CurrentRateLimitSnapshot.

type RequestCycleStartedEvent

type RequestCycleStartedEvent struct {
	AgentID string // "" = main agent
	TurnID  uint64
}

RequestCycleStartedEvent signals that the runtime has started a new main request cycle. TUI should reset request-scoped display progress immediately; subsequent transport progress belongs to the new cycle.

type RequestProgressEvent

type RequestProgressEvent struct {
	AgentID string
	Bytes   int64
	Events  int64
	Done    bool
}

RequestProgressEvent reports cumulative visible response progress for the currently active request of an agent. Bytes are cumulative received bytes for visible response payload/status metadata tracked by the agent; Events counts visible stream/status events received so far. Zero values mean "no known visible progress yet".

type RoleChangedEvent

type RoleChangedEvent struct {
	Role string // new active role name (e.g. "planner", "builder")
}

RoleChangedEvent signals that the MainAgent's active role has changed. The TUI uses this to update the role pill in the status bar.

type RoleController added in v0.2.0

type RoleController interface {
	// SwitchRole requests the agent to switch its active role.
	// In embedded mode this calls switchRole directly; in C/S mode it sends
	// TypeSwitchRole to the server. The new role is broadcast as RoleChangedEvent.
	SwitchRole(role string)
	// AvailableRoles returns the ordered list of role names the user can cycle
	// through with the Tab key in the main agent view.
	AvailableRoles() []string
	CurrentRole() string
	// AvailableAgents returns the names of agent roles available for Handoff.
	AvailableAgents() []string
}

RoleController exposes role/handoff lifecycle for the active agent.

type RunningModelChangedEvent

type RunningModelChangedEvent struct {
	AgentID          string // "" = main agent, non-empty = sub-agent instance ID
	ProviderModelRef string // selected model (user's choice)
	RunningModelRef  string // actually running model (may differ during fallback)
}

RunningModelChangedEvent signals that the active running model has changed. Emitted after a manual model switch or fallback switch so the TUI can update the sidebar without waiting for the next idle snapshot.

type RuntimeModelPoolPolicy added in v0.3.0

type RuntimeModelPoolPolicy struct {
	// contains filtered or unexported fields
}

func NewRuntimeModelPoolPolicy added in v0.3.0

func NewRuntimeModelPoolPolicy() *RuntimeModelPoolPolicy

func (*RuntimeModelPoolPolicy) AgentOverride added in v0.3.0

func (p *RuntimeModelPoolPolicy) AgentOverride(agentName string) (string, bool)

func (*RuntimeModelPoolPolicy) ClearAgentOverride added in v0.3.0

func (p *RuntimeModelPoolPolicy) ClearAgentOverride(agentName string)

func (*RuntimeModelPoolPolicy) CurrentModelPool added in v0.5.1

func (p *RuntimeModelPoolPolicy) CurrentModelPool() string

func (*RuntimeModelPoolPolicy) EffectiveModels added in v0.3.0

func (p *RuntimeModelPoolPolicy) EffectiveModels(agentName string, cfg *config.AgentConfig) []string

func (*RuntimeModelPoolPolicy) EffectivePool added in v0.3.0

func (p *RuntimeModelPoolPolicy) EffectivePool(agentName string, cfg *config.AgentConfig) string

func (*RuntimeModelPoolPolicy) LastPicked added in v0.3.0

func (p *RuntimeModelPoolPolicy) LastPicked(roleName, poolName string) (string, bool)

func (*RuntimeModelPoolPolicy) Overrides added in v0.3.0

func (p *RuntimeModelPoolPolicy) Overrides() map[string]string

func (*RuntimeModelPoolPolicy) ReplaceSelections added in v0.5.0

func (p *RuntimeModelPoolPolicy) ReplaceSelections(currentModelPool string, overrides map[string]string)

func (*RuntimeModelPoolPolicy) ReplaceSelectionsForSessionRestore added in v0.5.0

func (p *RuntimeModelPoolPolicy) ReplaceSelectionsForSessionRestore(currentModelPool string, overrides map[string]string)

func (*RuntimeModelPoolPolicy) ResolveInitialModelRef added in v0.3.0

func (p *RuntimeModelPoolPolicy) ResolveInitialModelRef(agentName string, cfg *config.AgentConfig) string

func (*RuntimeModelPoolPolicy) SetAgentOverride added in v0.3.0

func (p *RuntimeModelPoolPolicy) SetAgentOverride(agentName, pool string)

func (*RuntimeModelPoolPolicy) SetCurrentModelPool added in v0.5.1

func (p *RuntimeModelPoolPolicy) SetCurrentModelPool(pool string)

func (*RuntimeModelPoolPolicy) SetLastPicked added in v0.3.0

func (p *RuntimeModelPoolPolicy) SetLastPicked(roleName, poolName, modelRef string)

type SessionController added in v0.2.0

type SessionController interface {
	ListSessionSummaries() ([]SessionSummary, error)
	GetSessionSummary() *SessionSummary
	DeleteSession(sessionID string) error
	ExportSession(format, path string)
	ResumeSession()
	ResumeSessionID(sessionID string)
	NewSession()
	// ForkSession creates a new session branching from the message at msgIndex.
	// The message at msgIndex becomes the draft loaded into the composer.
	ForkSession(msgIndex int)
}

SessionController exposes session lifecycle controls (resume, fork, delete, export). In remote mode some methods may be unavailable until a dedicated protocol/API is defined.

type SessionRestoredEvent

type SessionRestoredEvent struct{}

SessionRestoredEvent signals that the conversation was restored from a persisted session (e.g. after /resume <id>). The TUI should rebuild the viewport from the current messages so the restored history is visible.

type SessionSelectEvent

type SessionSelectEvent struct {
	Sessions []SessionSummary // optional: pre-fetched list from server
}

SessionSelectEvent signals the TUI to open the session picker overlay. Emitted when the user runs /resume with no arguments; the user then chooses a session from the list to restore. Sessions, when non-nil, is the list from the server (C/S mode); the TUI uses it instead of calling ListSessionSummaries() so remote clients get the list.

type SessionSummary

type SessionSummary struct {
	ID                                          string
	LastModTime                                 time.Time
	FirstUserMessage                            string
	FirstUserMessageIsCompactionSummary         bool
	OriginalFirstUserMessage                    string // preserved across compaction
	OriginalFirstUserMessageIsCompactionSummary bool   // legacy-only: see recovery.SessionInfo
	ForkedFrom                                  string
	Locked                                      bool
}

SessionSummary holds display info for one session (current or list entry).

type SessionSwitchStartedEvent

type SessionSwitchStartedEvent struct {
	Kind      string
	SessionID string // optional target session ID (for /resume)
}

SessionSwitchStartedEvent signals that a local session-control operation has started and the TUI should show transient loading feedback until the switch either completes (SessionRestoredEvent) or fails (ErrorEvent/IdleEvent). Kind is one of: "resume", "new", "fork".

type SkillsStateProvider

type SkillsStateProvider interface {
	// ListSkills returns currently discoverable skills visible to the runtime.
	ListSkills() []*skill.Meta
}

SkillsStateProvider is implemented by agents that can expose currently available skill metadata to the TUI info panel.

type SpawnFinishedEvent

type SpawnFinishedEvent struct {
	BackgroundID  string
	AgentID       string // originating agent ("" = main agent)
	Kind          string
	Status        string
	Command       string
	Description   string
	MaxRuntimeSec int
	Message       string
}

SpawnFinishedEvent is emitted when a background process started by Spawn completes. It is a lightweight runtime notification; stdout/stderr remain in the returned log_file.

func (SpawnFinishedEvent) EffectiveID

func (e SpawnFinishedEvent) EffectiveID() string

type StreamRollbackEvent

type StreamRollbackEvent struct {
	Reason  string
	AgentID string
}

StreamRollbackEvent asks the UI to discard the currently streaming assistant output for an agent (used when a provider-side incremental attempt must be rolled back and retried with full input).

type StreamTextEvent

type StreamTextEvent struct {
	Text    string
	AgentID string // originating agent ("" = main agent)
}

StreamTextEvent carries an incremental text chunk from the LLM.

type StreamThinkingDeltaEvent

type StreamThinkingDeltaEvent struct {
	Text    string // incremental thinking content since last delta
	AgentID string // originating agent ("" = main agent)
}

StreamThinkingDeltaEvent carries an incremental thinking chunk for streaming display. Emitted every ~150ms while thinking is in progress, so users see thinking as it evolves. The final complete thinking block is still sent via StreamThinkingEvent on thinking_end.

type StreamThinkingEvent

type StreamThinkingEvent struct {
	Text    string // full thinking content for this block
	AgentID string // originating agent ("" = main agent)
}

StreamThinkingEvent carries a complete thinking block from the LLM. It is emitted once per thinking block (after the block is fully assembled), not incrementally — this avoids flooding the output channel during extended thinking sessions that may produce thousands of small deltas.

type StreamingToolDiscardInfo added in v0.5.0

type StreamingToolDiscardInfo struct {
	CallID      string
	Name        string
	ArgsJSON    string
	Reason      string
	Started     bool
	Completed   bool
	StartedAt   time.Time
	CompletedAt time.Time
}

type StreamingToolExecutor added in v0.5.0

type StreamingToolExecutor struct {
	// contains filtered or unexported fields
}

func NewStreamingToolExecutor added in v0.5.0

func NewStreamingToolExecutor(turnID uint64, ctx context.Context, emit func(AgentEvent), execute func(context.Context, message.ToolCall) (ToolExecutionResult, error)) *StreamingToolExecutor

func (*StreamingToolExecutor) AcquireExecutionSlot added in v0.5.0

func (e *StreamingToolExecutor) AcquireExecutionSlot(ctx context.Context) func()

AcquireExecutionSlot blocks until a shared tool execution slot is available or ctx is canceled. The returned release function must be called exactly once.

func (*StreamingToolExecutor) DiscardAll added in v0.5.0

func (e *StreamingToolExecutor) DiscardAll(reason string) []PendingToolCall

func (*StreamingToolExecutor) DiscardCall added in v0.5.0

func (e *StreamingToolExecutor) DiscardCall(callID, reason string) (StreamingToolDiscardInfo, bool)

func (*StreamingToolExecutor) DiscardExcept added in v0.5.0

func (e *StreamingToolExecutor) DiscardExcept(valid map[string]struct{}, reason string) []PendingToolCall

func (*StreamingToolExecutor) DiscardExceptInfo added in v0.5.0

func (e *StreamingToolExecutor) DiscardExceptInfo(valid map[string]struct{}, reason string) []StreamingToolDiscardInfo

func (*StreamingToolExecutor) Promote added in v0.5.0

func (*StreamingToolExecutor) SetTraceCallbacks added in v0.5.0

func (e *StreamingToolExecutor) SetTraceCallbacks(onStart func(callID, toolName string, at time.Time), onFirstVisible func(callID, toolName string, at time.Time), onDiscard func(info StreamingToolDiscardInfo))

func (*StreamingToolExecutor) Start added in v0.5.0

func (e *StreamingToolExecutor) Start(call message.ToolCall) bool

type SubAgent

type SubAgent struct {
	// contains filtered or unexported fields
}

SubAgent runs an independent event loop that executes a single task delegated by the MainAgent. It has its own LLM client, context manager, and tool registry, but shares the recovery manager and hook engine with its parent MainAgent.

All mutable state is confined to the runLoop goroutine (single-writer); external user input is enqueued via InjectUserMessage / InjectUserMessageWithParts.

func NewSubAgent

func NewSubAgent(cfg SubAgentConfig) *SubAgent

NewSubAgent creates a fully-initialised SubAgent. The caller must invoke runLoop in a separate goroutine to start the event loop.

func (*SubAgent) CancelCurrentTurn

func (s *SubAgent) CancelCurrentTurn() bool

CancelCurrentTurn cancels the SubAgent's active turn and persists synthetic terminal tool results for any pending calls so session restore shows them as cancelled instead of pending forever.

func (*SubAgent) ContinueFromContext

func (s *SubAgent) ContinueFromContext()

ContinueFromContext signals the SubAgent to re-run the LLM with its existing context without appending a new user message. Non-blocking.

func (*SubAgent) Depth

func (s *SubAgent) Depth() int

func (*SubAgent) GetContextMessageCount

func (s *SubAgent) GetContextMessageCount() int

GetContextMessageCount returns the number of messages in this agent's context (for sidebar).

func (*SubAgent) GetContextStats

func (s *SubAgent) GetContextStats() (current, limit int)

GetContextStats returns current context usage and limit for this SubAgent. Current = input + output + cache + reasoning from last API response.

func (*SubAgent) GetMessages

func (s *SubAgent) GetMessages() []message.Message

GetMessages returns a thread-safe snapshot of the SubAgent's conversation history (for TUI display when the user tabs to this agent).

func (*SubAgent) InjectUserMessage

func (s *SubAgent) InjectUserMessage(content string)

InjectUserMessage receives user messages directly (non-blocking enqueue). Overflow is preserved in-memory so older messages are not silently dropped. This is safe to call from any goroutine (typically MainAgent's event loop).

func (*SubAgent) InjectUserMessageWithMailboxAck

func (s *SubAgent) InjectUserMessageWithMailboxAck(content, mailboxAckID string)

func (*SubAgent) InjectUserMessageWithParts

func (s *SubAgent) InjectUserMessageWithParts(parts []message.ContentPart)

InjectUserMessageWithParts enqueues a multi-part user message for the SubAgent.

func (*SubAgent) InvokedSkills

func (s *SubAgent) InvokedSkills() []*skill.Meta

func (*SubAgent) LastArtifact

func (s *SubAgent) LastArtifact() tools.ArtifactRef

func (*SubAgent) LastMailboxID

func (s *SubAgent) LastMailboxID() string

func (*SubAgent) LastReplyThread

func (s *SubAgent) LastReplyThread() (replyMessageID, replyToMailboxID, replyKind, replySummary string)

func (*SubAgent) LastSummary

func (s *SubAgent) LastSummary() string

func (*SubAgent) ListSkills

func (s *SubAgent) ListSkills() []*skill.Meta

func (*SubAgent) LoadSkill

func (s *SubAgent) LoadSkill(name string) (*skill.Skill, error)

func (*SubAgent) MarkSkillInvoked

func (s *SubAgent) MarkSkillInvoked(meta *skill.Meta)

func (*SubAgent) OwnerAgentID

func (s *SubAgent) OwnerAgentID() string

func (*SubAgent) OwnerTaskID

func (s *SubAgent) OwnerTaskID() string

func (*SubAgent) PendingCompleteIntent

func (s *SubAgent) PendingCompleteIntent() *AgentResult

func (*SubAgent) RemoveLastMessage

func (s *SubAgent) RemoveLastMessage()

RemoveLastMessage removes the last message from the SubAgent's context and rewrites the persistence log. Only safe when idle (turn == nil), but since this is called from the TUI goroutine and the actual mutation happens on the runLoop goroutine via handleContinue, we use DropLastMessage which is mutex-protected. The persistence rewrite is best-effort.

func (*SubAgent) RestoreMessages

func (s *SubAgent) RestoreMessages(msgs []message.Message)

RestoreMessages loads a previously persisted message history into the SubAgent's context manager. Used during session restore to rebuild a SubAgent's conversation without replaying LLM calls.

func (*SubAgent) State

func (s *SubAgent) State() SubAgentState

func (*SubAgent) StateChangedAt

func (s *SubAgent) StateChangedAt() time.Time

func (*SubAgent) TryEnqueueContextAppend

func (s *SubAgent) TryEnqueueContextAppend(msg message.Message) bool

type SubAgentCloseRequestedPayload

type SubAgentCloseRequestedPayload struct {
	Reason       string
	ClosedReason string
	FinalState   SubAgentState
}

type SubAgentConfig

type SubAgentConfig struct {
	InstanceID    string
	TaskID        string
	AgentDefName  string
	TaskDesc      string
	PlanTaskRef   string
	SemanticKey   string
	WriteScope    tools.WriteScope
	OwnerAgentID  string
	OwnerTaskID   string
	Depth         int
	JoinToOwner   bool
	Delegation    config.DelegationConfig
	Color         string
	SystemPrompt  string // custom role instructions from agent YAML body; empty = use built-in
	LLMClient     *llm.Client
	Recovery      *recovery.RecoveryManager
	Parent        *MainAgent
	ParentCtx     context.Context
	Cancel        context.CancelFunc
	BaseTools     *tools.Registry // shared base tool registry (Read, Write, Edit, Shell, Grep, Glob, etc.)
	ExtraMCPTools []tools.Tool    // agent-specific MCP tools
	Ruleset       permission.Ruleset
	WorkDir       string
	VenvPath      string // absolute path to detected Python virtual environment, or ""
	SessionDir    string
	AgentsMD      string
	Skills        []*skill.Meta
	ModelName     string
	IdleTimeout   time.Duration // 0 → DefaultIdleTimeout
}

SubAgentConfig holds the parameters for creating a new SubAgent.

type SubAgentInfo

type SubAgentInfo struct {
	InstanceID       string
	TaskID           string
	AgentDefName     string
	TaskDesc         string
	ModelName        string
	SelectedRef      string
	RunningRef       string
	State            string
	Color            string // optional ANSI color code from agent config
	LastSummary      string
	UrgentInboxCount int
	LastArtifact     tools.ArtifactRef
}

SubAgentInfo carries read-only information about a running SubAgent for TUI display (sidebar listing). The fields are snapshot values safe to read from any goroutine.

type SubAgentInspector added in v0.2.0

type SubAgentInspector interface {
	GetSubAgents() []SubAgentInfo
	SwitchFocus(agentID string)
	// FocusedAgentID returns the instance ID of the focused SubAgent, or "" when
	// the main agent is focused.
	FocusedAgentID() string
	// FocusedAgentName returns the agent definition name of the focused SubAgent,
	// or "" when the main agent is focused.
	FocusedAgentName() string
}

SubAgentInspector lets the TUI list, focus, and follow subagents.

type SubAgentMailboxAckRecord

type SubAgentMailboxAckRecord struct {
	MessageID        string    `json:"message_id"`
	Outcome          string    `json:"outcome"`
	TurnID           uint64    `json:"turn_id,omitempty"`
	InReplyTo        string    `json:"in_reply_to,omitempty"`
	ReplyMessageID   string    `json:"reply_message_id,omitempty"`
	ReplyToMailboxID string    `json:"reply_to_mailbox_id,omitempty"`
	ReplySummary     string    `json:"reply_summary,omitempty"`
	ReplyKind        string    `json:"reply_kind,omitempty"`
	ArtifactID       string    `json:"artifact_id,omitempty"`
	ArtifactRelPath  string    `json:"artifact_rel_path,omitempty"`
	ArtifactType     string    `json:"artifact_type,omitempty"`
	AckedAt          time.Time `json:"acked_at"`
}

type SubAgentMailboxKind

type SubAgentMailboxKind string
const (
	SubAgentMailboxKindProgress         SubAgentMailboxKind = "progress"
	SubAgentMailboxKindCompleted        SubAgentMailboxKind = "completed"
	SubAgentMailboxKindBlocked          SubAgentMailboxKind = "blocked"
	SubAgentMailboxKindDecisionRequired SubAgentMailboxKind = "decision_required"
	SubAgentMailboxKindRiskAlert        SubAgentMailboxKind = "risk_alert"
	SubAgentMailboxKindDirectionChange  SubAgentMailboxKind = "direction_change_request"
)

type SubAgentMailboxMessage

type SubAgentMailboxMessage struct {
	MessageID    string                  `json:"message_id"`
	AgentID      string                  `json:"agent_id"`
	TaskID       string                  `json:"task_id"`
	OwnerAgentID string                  `json:"owner_agent_id,omitempty"`
	OwnerTaskID  string                  `json:"owner_task_id,omitempty"`
	InReplyTo    string                  `json:"in_reply_to,omitempty"`
	Kind         SubAgentMailboxKind     `json:"kind"`
	Priority     SubAgentMailboxPriority `json:"priority"`
	Summary      string                  `json:"summary"`
	Payload      string                  `json:"payload,omitempty"`
	Completion   *CompletionEnvelope     `json:"completion,omitempty"`
	RequiresAck  bool                    `json:"requires_ack,omitempty"`
	Consumed     bool                    `json:"consumed,omitempty"`
	CreatedAt    time.Time               `json:"created_at"`
}

type SubAgentMailboxPriority

type SubAgentMailboxPriority string
const (
	SubAgentMailboxPriorityNotify    SubAgentMailboxPriority = "notify"
	SubAgentMailboxPriorityUrgent    SubAgentMailboxPriority = "urgent"
	SubAgentMailboxPriorityInterrupt SubAgentMailboxPriority = "interrupt"
)

type SubAgentProgressUpdatedPayload

type SubAgentProgressUpdatedPayload struct {
	Summary string
}

type SubAgentSendMessagePayload

type SubAgentSendMessagePayload struct {
	Ctx           context.Context
	CallerAgentID string
	CallerTaskID  string
	TaskID        string
	Message       string
	Kind          string
	Reply         chan subAgentControlResult
}

type SubAgentState

type SubAgentState string
const (
	SubAgentStateRunning           SubAgentState = "running"
	SubAgentStateWaitingMain       SubAgentState = "waiting_main"
	SubAgentStateWaitingDescendant SubAgentState = "waiting_descendant"
	SubAgentStateCompleted         SubAgentState = "completed"
	SubAgentStateFailed            SubAgentState = "failed"
	SubAgentStateCancelled         SubAgentState = "cancelled"
	SubAgentStateIdle              SubAgentState = "idle"
)

type SubAgentStateChangedPayload

type SubAgentStateChangedPayload struct {
	State   SubAgentState
	Summary string
}

type SubAgentStopPayload

type SubAgentStopPayload struct {
	Ctx           context.Context
	CallerAgentID string
	CallerTaskID  string
	TaskID        string
	Reason        string
	Reply         chan subAgentControlResult
}

type ThinkingStartedEvent

type ThinkingStartedEvent struct{}

ThinkingStartedEvent is emitted when the first thinking delta is received in a block, so the TUI can start the "thought duration" timer.

type ToastEvent

type ToastEvent struct {
	Message string
	Level   string
	AgentID string // originating agent ("" = main agent)
}

ToastEvent carries a transient notification message. Level is one of: "info", "warn", "error".

type TodosUpdatedEvent

type TodosUpdatedEvent struct {
	Todos []tools.TodoItem
}

TodosUpdatedEvent is emitted when the todo list changes via TodoWrite.

type ToolCallExecutionEvent

type ToolCallExecutionEvent struct {
	ID       string
	Name     string
	ArgsJSON string
	State    ToolCallExecutionState
	AgentID  string // originating agent ("" = main agent)
}

ToolCallExecutionEvent updates the live execution phase for an already-visible tool card after finalize. It distinguishes truly queued work from actively running work so the TUI does not animate tools that are only waiting on a later batch.

type ToolCallExecutionState

type ToolCallExecutionState string

ToolCallExecutionState is the live execution phase of a visible tool card.

const (
	ToolCallExecutionStateQueued  ToolCallExecutionState = "queued"
	ToolCallExecutionStateRunning ToolCallExecutionState = "running"
)

type ToolCallStartEvent

type ToolCallStartEvent struct {
	ID       string
	Name     string
	ArgsJSON string
	AgentID  string // originating agent ("" = main agent)
}

ToolCallStartEvent is emitted when the LLM begins a tool invocation.

type ToolCallUpdateEvent

type ToolCallUpdateEvent struct {
	ID                string
	Name              string
	ArgsJSON          string
	ArgsStreamingDone bool
	AgentID           string // originating agent ("" = main agent)
}

ToolCallUpdateEvent refreshes the visible arguments for an already-started tool call. Used for streaming providers that deliver tool arguments incrementally. When ArgsStreamingDone is true, ArgsJSON is the final accumulated argument JSON for this speculative card and the temporary "chars received" indicator should be cleared immediately even before execution-state/result events arrive.

type ToolExecutionResult

type ToolExecutionResult struct {
	Result            string
	EffectiveArgsJSON string
	Audit             *message.ToolArgsAudit
	LSPReviews        []message.LSPReview
	FileState         *message.ToolFileState
	PreFilePath       string
	PreContent        string
	PreExisted        bool
	// contains filtered or unexported fields
}

type ToolProgressEvent

type ToolProgressEvent struct {
	CallID   string
	Name     string
	AgentID  string // originating agent ("" = main agent)
	Progress ToolProgressSnapshot
}

ToolProgressEvent updates the visible progress for an already-started tool call. It never replaces start/end lifecycle events.

type ToolProgressSnapshot

type ToolProgressSnapshot struct {
	Label   string
	Current int64
	Total   int64
	Text    string
}

ToolProgressSnapshot is a best-effort structured progress snapshot for a visible running tool card. Zero values mean "no known progress".

type ToolResultEvent

type ToolResultEvent struct {
	CallID      string
	Name        string
	ArgsJSON    string // full tool arguments (available after streaming completes)
	Audit       *message.ToolArgsAudit
	Result      string
	Status      ToolResultStatus
	AgentID     string // originating agent ("" = main agent)
	Diff        string // unified diff for Write/Edit tools (not sent to LLM)
	DiffAdded   int    // full added-line count before any diff truncation
	DiffRemoved int    // full removed-line count before any diff truncation
	FileCreated bool   // true when Write created a file that did not previously exist
}

ToolResultEvent is emitted after a tool execution completes.

type ToolResultPayload

type ToolResultPayload struct {
	CallID      string
	Name        string
	ArgsJSON    string
	Audit       *message.ToolArgsAudit
	Result      string
	Error       error
	TurnID      uint64
	Duration    time.Duration
	Diff        string              // unified diff for Write/Edit tools; not sent to LLM
	DiffAdded   int                 // full added-line count before any diff truncation
	DiffRemoved int                 // full removed-line count before any diff truncation
	FileCreated bool                // true when Write created a file that did not previously exist
	LSPReviews  []message.LSPReview // last-review snapshot for the directly edited file only
	FileState   *message.ToolFileState
	// contains filtered or unexported fields
}

ToolResultPayload wraps a tool execution result for the internal event bus.

type ToolResultStatus

type ToolResultStatus string

ToolResultStatus is the terminal state of a tool call for UI/protocol purposes.

const (
	ToolResultStatusSuccess   ToolResultStatus = "success"
	ToolResultStatusError     ToolResultStatus = "error"
	ToolResultStatusCancelled ToolResultStatus = "cancelled"
)

type Turn

type Turn struct {
	ID     uint64
	Epoch  uint64
	Ctx    context.Context
	Cancel context.CancelFunc
	// PendingToolCalls and TotalToolCalls are accessed from both the event-loop
	// goroutine (writes) and external goroutines like CancelCurrentTurn (reads),
	// so they must be accessed atomically.
	PendingToolCalls atomic.Int32 // number of tool results not yet received
	TotalToolCalls   atomic.Int32 // total tool calls in this turn (set when dispatching)

	PendingToolMeta map[string]PendingToolCall

	// MalformedCount tracks consecutive LLM rounds where tool calls had
	// abnormal arguments — either the malformed sentinel (invalid JSON) or
	// empty "{}" for tools with required parameters (output truncation).
	// When this reaches maxMalformedToolCalls the turn is aborted.
	MalformedCount                     int
	LengthRecoveryCount                int
	InLengthRecovery                   bool
	LastTruncatedToolName              string
	LengthRecoveryAutoCompactAttempted bool
	OversizeRecoveryCount              int

	CompletedToolCalls []any
	ChangedFiles       []any
	// contains filtered or unexported fields
}

Turn represents a single user-initiated interaction cycle. Each user message starts a new turn; starting a new turn cancels any in-flight work from the previous one.

type TurnCancelledPayload

type TurnCancelledPayload struct {
	TurnID uint64
	Calls  []PendingToolCall
	// MarkToolCallsFailed turns synthetic terminal tool results for already
	// declared calls into error results instead of cancelled results.
	MarkToolCallsFailed bool
	// KeepPendingUserMessagesQueued suppresses the usual idle-time pending-input
	// drain so cancellation does not immediately auto-run any remaining queued
	// work on the IdleEvent it just produced.
	KeepPendingUserMessagesQueued bool
	// CommitPendingUserMessagesWithoutTurn appends queued user messages to the
	// durable context/transcript but does not start a follow-up LLM turn.
	CommitPendingUserMessagesWithoutTurn bool
}

TurnCancelledPayload carries the pending tool calls that must be explicitly closed in the UI when a turn is cancelled. The main agent also persists synthetic terminal tool-result messages for these calls so session restore can show them as cancelled instead of pending forever.

type UsageReporter added in v0.2.0

type UsageReporter interface {
	GetTokenUsage() message.TokenUsage
	// GetUsageStats returns session-wide totals (e.g. $ /stats Session overview and per-agent table).
	GetUsageStats() analytics.SessionStats
	// GetSidebarUsageStats returns token/cost totals for the focused agent only, aligned with
	// GetContextStats and GetTokenUsage for the right info panel and footer pills.
	GetSidebarUsageStats() analytics.SessionStats
	// GetContextStats returns current context usage and limit for the focused agent.
	// current is the last input token count (approximate context window usage); limit is the model context limit (0 if unknown).
	GetContextStats() (current, limit int)
	// GetContextMessageCount returns the number of messages in the focused agent's context (for sidebar). -1 if unknown.
	GetContextMessageCount() int
}

UsageReporter aggregates token usage and context-window stats for status, sidebar, and stats overlay rendering.

type UsageUpdatedEvent

type UsageUpdatedEvent struct{}

UsageUpdatedEvent signals that session usage (input/output tokens, cost) was just updated after an LLM round. In C/S mode the server uses this to push context_usage to clients so the sidebar updates during tool-call loops, not only after the turn ends (IdleEvent).

Source Files

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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