Help us shape the future of Go! Take the 2026 Go Developer Survey

handler

package
v0.9.6 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package handler defines the AgentEventHandler interface that decouples the agent runner from any specific UI implementation (TUI, ACP, Web, etc.).

All agent-loop events flow through this interface. Concrete implementations adapt the events to the target transport (BubbleTea, WebSocket, ACP JSON-RPC…).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ACPHandler

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

ACPHandler implements AgentEventHandler by sending ACP SessionUpdate notifications through an AgentSideConnection to the connected client.

func NewACPHandler

func NewACPHandler(conn *acp.AgentSideConnection, sessionID acp.SessionId, workDir string) *ACPHandler

NewACPHandler creates a handler bound to an ACP connection and session.

func (*ACPHandler) NotifyToolInProgress added in v0.4.11

func (h *ACPHandler) NotifyToolInProgress(name, args string)

NotifyToolInProgress is used by the approval state when a tool is about to execute without a visible permission prompt (auto-approval or safe tools).

func (*ACPHandler) OnAgentDone

func (h *ACPHandler) OnAgentDone(err error)

func (*ACPHandler) OnAgentStart added in v0.3.2

func (h *ACPHandler) OnAgentStart()

func (*ACPHandler) OnAgentText

func (h *ACPHandler) OnAgentText(text string)

func (*ACPHandler) OnSubagentEvent added in v0.9.5

func (h *ACPHandler) OnSubagentEvent(name, agentType string, done bool, result string, err error)

OnSubagentEvent bridges subagent lifecycle events (tools.SubagentNotifier) onto the "subagent" tool call as tool_call_update notifications. The final status and result ride the regular OnToolResult update; the done event only clears the progress mapping.

func (*ACPHandler) OnSubagentProgress added in v0.9.5

func (h *ACPHandler) OnSubagentProgress(agentName, event, toolName, detail string)

OnSubagentProgress bridges intermediate subagent tool activity (tools.SubagentProgressFn) onto the "subagent" tool call as a rolling content update — ACP replaces the content collection on each update, so the client shows the latest activity line while the subagent runs.

func (*ACPHandler) OnTodoUpdate

func (h *ACPHandler) OnTodoUpdate()

func (*ACPHandler) OnTokenUpdate

func (h *ACPHandler) OnTokenUpdate(info TokenUsage)

func (*ACPHandler) OnToolCall

func (h *ACPHandler) OnToolCall(ev ToolCallEvent)

func (*ACPHandler) OnToolResult

func (h *ACPHandler) OnToolResult(ev ToolResultEvent)

func (*ACPHandler) RequestApproval

func (h *ACPHandler) RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)

func (*ACPHandler) SetModeChangeCallback added in v0.5.0

func (h *ACPHandler) SetModeChangeCallback(fn func(mode.SessionMode))

SetModeChangeCallback registers a callback invoked whenever the handler changes the session mode (currently the "Allow All" → Full access promotion).

type AgentEventHandler

type AgentEventHandler interface {

	// OnAgentText is called when the agent emits a text chunk (streaming).
	OnAgentText(text string)

	// OnToolCall is called at the beginning of a tool invocation.
	OnToolCall(ev ToolCallEvent)

	// OnToolResult is called when a tool execution completes.
	OnToolResult(ev ToolResultEvent)

	// OnTodoUpdate is called when the todo store is mutated.
	OnTodoUpdate()

	// OnAgentStart is called when the agent begins processing a user prompt,
	// before any LLM call is made. Use this to show a "thinking" / "working"
	// indicator immediately, rather than waiting for the first text chunk.
	OnAgentStart()

	// OnAgentDone is called when the agent loop finishes (err may be nil).
	OnAgentDone(err error)

	// OnTokenUpdate reports cumulative token usage after a run.
	OnTokenUpdate(info TokenUsage)

	// RequestApproval asks the UI for tool-execution permission.
	// It blocks until the user responds or ctx is cancelled.
	// Returns (approved, newMode, error).
	RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)
}

AgentEventHandler is the primary abstraction between the agent runner and the presentation layer. It covers three concerns:

  1. Output events — one-way notifications from agent to UI.
  2. Approval flow — bidirectional: agent requests permission, UI responds.
  3. Lifecycle — done signals, token usage, etc.

Implementations must be safe for concurrent use; the runner may call methods from multiple goroutines (e.g. streaming text while a tool result arrives).

type ApprovalMode

type ApprovalMode int

ApprovalMode mirrors tui.ApprovalMode so that handler consumers don't import tui.

const (
	ModeManual ApprovalMode = iota
	ModeAuto
)

type ApprovalRequest

type ApprovalRequest struct {
	ToolName    string
	ToolArgs    string
	ToolCallID  string // unique ID of this tool invocation (from the LLM)
	IsExternal  bool   // true when accessing paths outside workpath
	WorkerName  string // non-empty for teammate agents
	WorkerColor string
}

ApprovalRequest describes a tool that needs user permission.

type ApprovalResponse

type ApprovalResponse struct {
	Approved bool
	Mode     ApprovalMode
}

ApprovalResponse is what the UI returns for an approval request.

type NotifyingHandler added in v0.1.1

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

NotifyingHandler wraps another AgentEventHandler and adds delayed notification capabilities for approval requests and agent completion events. It also supports a list of channel.Notifier instances for lightweight one-way status pushes (e.g. BLE IoT devices).

func NewNotifyingHandler added in v0.1.1

func NewNotifyingHandler(inner AgentEventHandler, delay time.Duration) *NotifyingHandler

NewNotifyingHandler creates a handler that wraps inner and can fire external notifications for approvals (after a delay) and agent completion.

func (*NotifyingHandler) AddNotifier added in v0.3.1

func (h *NotifyingHandler) AddNotifier(n channel.Notifier)

AddNotifier registers a lightweight notifier (e.g. BLE device). Notifiers receive automatic status pushes for agent lifecycle events.

func (*NotifyingHandler) CloseNotifiers added in v0.3.1

func (h *NotifyingHandler) CloseNotifiers()

CloseNotifiers closes all registered notifiers.

func (*NotifyingHandler) OnAgentDone added in v0.1.1

func (h *NotifyingHandler) OnAgentDone(err error)

func (*NotifyingHandler) OnAgentStart added in v0.3.2

func (h *NotifyingHandler) OnAgentStart()

func (*NotifyingHandler) OnAgentText added in v0.1.1

func (h *NotifyingHandler) OnAgentText(text string)

func (*NotifyingHandler) OnTodoUpdate added in v0.1.1

func (h *NotifyingHandler) OnTodoUpdate()

func (*NotifyingHandler) OnTokenUpdate added in v0.1.1

func (h *NotifyingHandler) OnTokenUpdate(info TokenUsage)

func (*NotifyingHandler) OnToolCall added in v0.1.1

func (h *NotifyingHandler) OnToolCall(ev ToolCallEvent)

func (*NotifyingHandler) OnToolResult added in v0.1.1

func (h *NotifyingHandler) OnToolResult(ev ToolResultEvent)

func (*NotifyingHandler) RequestApproval added in v0.1.1

func (h *NotifyingHandler) RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)

func (*NotifyingHandler) SetApprovalNotifier added in v0.1.1

func (h *NotifyingHandler) SetApprovalNotifier(fn func(toolName, toolArgs string))

SetApprovalNotifier sets the callback fired when an approval is not resolved within the delay.

func (*NotifyingHandler) SetDoneNotifier added in v0.1.1

func (h *NotifyingHandler) SetDoneNotifier(fn func(summary string, err error))

SetDoneNotifier sets the callback fired when the agent finishes.

type TUIHandler

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

TUIHandler adapts AgentEventHandler to a BubbleTea *tea.Program. It translates every interface method into a p.Send(msg) call using the existing TUI message types.

func NewTUIHandler

func NewTUIHandler(p *tea.Program) *TUIHandler

NewTUIHandler creates a handler backed by a BubbleTea program.

func (*TUIHandler) OnAgentDone

func (h *TUIHandler) OnAgentDone(err error)

func (*TUIHandler) OnAgentStart added in v0.3.2

func (h *TUIHandler) OnAgentStart()

func (*TUIHandler) OnAgentText

func (h *TUIHandler) OnAgentText(text string)

func (*TUIHandler) OnTodoUpdate

func (h *TUIHandler) OnTodoUpdate()

func (*TUIHandler) OnTokenUpdate

func (h *TUIHandler) OnTokenUpdate(info TokenUsage)

func (*TUIHandler) OnToolCall

func (h *TUIHandler) OnToolCall(ev ToolCallEvent)

func (*TUIHandler) OnToolResult

func (h *TUIHandler) OnToolResult(ev ToolResultEvent)

func (*TUIHandler) RequestApproval

func (h *TUIHandler) RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)

func (*TUIHandler) SetProgram

func (h *TUIHandler) SetProgram(p *tea.Program)

SetProgram replaces the underlying BubbleTea program (e.g. after the program is created lazily).

type TokenUsage

type TokenUsage struct {
	TotalTokens       int64
	PromptTokens      int64
	CompletionTokens  int64
	CachedTokens      int64
	ReasoningTokens   int64
	CacheWriteTokens  int64
	CallCount         int64
	CacheHitRate      float64
	CacheSupported    bool
	ModelContextLimit int // 0 if unknown
}

TokenUsage carries token usage info to the UI surfaces.

TotalTokens is the LAST call's total — i.e. current context-window occupancy, used to drive the context-usage bar. The remaining token counters (Prompt/Completion/Cached/Reasoning/CacheWrite/CallCount) are CUMULATIVE for the run's tracker, and CacheHitRate is the cumulative cached/prompt ratio. CacheSupported is false when the provider never reported any cached tokens, so the UI can show "—" instead of a misleading 0%.

NOTE: the field order/types here must stay identical to WebTokenData (internal/handler/web.go) so OnTokenUpdate's direct struct conversion keeps compiling.

type ToolCallEvent added in v0.9.4

type ToolCallEvent struct {
	Name       string
	Args       string
	ToolCallID string
	BatchID    string    // batch identity: one assistant message = one batch
	BatchIndex int       // 0-based position inside the batch
	BatchSize  int       // number of tool calls in the batch
	StartedAt  time.Time // when the runner announced the call
}

ToolCallEvent describes a single tool invocation announced by the agent. All tool calls issued by one assistant message share a BatchID so UIs can group concurrent invocations; a single-tool message still forms a batch (BatchSize == 1).

type ToolDisplayInfo added in v0.3.3

type ToolDisplayInfo struct {
	Title       string `json:"title"`              // Human-readable tool name (e.g. "Read", "Edit", "Shell")
	Subtitle    string `json:"subtitle,omitempty"` // Context info (file path, command description, pattern)
	Icon        string `json:"icon,omitempty"`     // Icon identifier
	Category    string `json:"category,omitempty"` // "context" (read-only), "mutation", "execution"
	Kind        string `json:"kind,omitempty"`     // presentation kind: read|search|list|shell|edit|agent|other
	Collapsible bool   `json:"collapsible,omitempty"`
}

ToolDisplayInfo carries human-readable tool metadata for UI rendering.

type ToolResultEvent added in v0.9.4

type ToolResultEvent struct {
	Name       string
	Output     string
	ToolCallID string
	Err        error
	// Duration is result arrival minus call announcement, with any time spent
	// blocked on user approval subtracted (pure execution latency); 0 when
	// unknown.
	Duration time.Duration
	// Denied is true when the user rejected this tool call at the approval
	// prompt. UIs should render this as "declined" (e.g. strikethrough), not
	// as an execution error.
	Denied bool
}

ToolResultEvent describes a completed tool execution.

type WebApprovalRequestData

type WebApprovalRequestData struct {
	ID         string `json:"id"`
	ToolName   string `json:"tool_name"`
	ToolArgs   string `json:"tool_args"`
	ToolCallID string `json:"tool_call_id,omitempty"`
	IsExternal bool   `json:"is_external"`
}

WebApprovalRequestData carries an approval request. ToolCallID (when known) ties the prompt to the exact pending tool_call row so the UI can paint that row as "waiting for approval".

type WebAskUserRequestData added in v0.5.2

type WebAskUserRequestData struct {
	ID        string                  `json:"id"`
	Questions []tools.AskUserQuestion `json:"questions"`
}

WebAskUserRequestData carries an ask_user question request to web clients.

type WebDoneData

type WebDoneData struct {
	Error string `json:"error,omitempty"`
}

WebDoneData signals agent completion.

type WebEvent

type WebEvent struct {
	Event string `json:"event"`
	Data  any    `json:"data"`
}

WebEvent is an event sent from the agent to web clients.

type WebHandler

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

WebHandler implements AgentEventHandler by sending events to web clients through a channel-based event broker.

func NewWebHandler

func NewWebHandler() *WebHandler

NewWebHandler creates a handler that sends events to the given channel.

func (*WebHandler) Emit added in v0.1.1

func (h *WebHandler) Emit(event string, data any)

Emit sends a custom event to all connected web clients.

func (*WebHandler) Events

func (h *WebHandler) Events() <-chan WebEvent

Events returns the read-only event channel.

func (*WebHandler) OnAgentDone

func (h *WebHandler) OnAgentDone(err error)

func (*WebHandler) OnAgentStart added in v0.3.2

func (h *WebHandler) OnAgentStart()

func (*WebHandler) OnAgentText

func (h *WebHandler) OnAgentText(text string)

func (*WebHandler) OnSubagentEvent

func (h *WebHandler) OnSubagentEvent(name, agentType string, done bool, result string, err error)

func (*WebHandler) OnSubagentProgress

func (h *WebHandler) OnSubagentProgress(agentName, event, toolName, detail string)

func (*WebHandler) OnTodoUpdate

func (h *WebHandler) OnTodoUpdate()

func (*WebHandler) OnTokenUpdate

func (h *WebHandler) OnTokenUpdate(info TokenUsage)

func (*WebHandler) OnToolCall

func (h *WebHandler) OnToolCall(ev ToolCallEvent)

func (*WebHandler) OnToolResult

func (h *WebHandler) OnToolResult(ev ToolResultEvent)

func (*WebHandler) PendingApprovalRequests added in v0.6.2

func (h *WebHandler) PendingApprovalRequests() []WebApprovalRequestData

PendingApprovalRequests returns the still-unanswered approval requests so a reloaded/reconnecting client can re-surface the approval card (the approval_request WS event is fire-once and ephemeral). Without this, a page refresh or WS reconnect while an approval is pending would drop the card and leave the agent blocked forever. Mirrors PendingAskUserRequests.

func (*WebHandler) PendingAskUserRequests added in v0.5.2

func (h *WebHandler) PendingAskUserRequests() []WebAskUserRequestData

PendingAskUserRequests returns the still-unanswered ask_user requests so a reloaded/reconnecting client can re-surface the question (the ask_user_request WS event is fire-once and ephemeral). Without this, a page refresh while a question is pending would leave the agent blocked with no way to answer.

func (*WebHandler) RequestApproval

func (h *WebHandler) RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)

func (*WebHandler) RequestAskUser added in v0.5.2

func (h *WebHandler) RequestAskUser(ctx context.Context, questions []tools.AskUserQuestion) (tools.AskUserBatchResponse, error)

RequestAskUser emits the question(s) to web clients and blocks until the user answers (via the /api/ask endpoint → ResolveAskUser) or the context is cancelled. It mirrors RequestApproval: a per-request id keys a one-shot response channel so the API handler can route the answer back. This is wired into the ask_user tool's BatchRequestFn for the web frontend.

func (*WebHandler) ResolveApproval

func (h *WebHandler) ResolveApproval(id string, approved, approveAll bool) error

ResolveApproval resolves a pending approval request. Called by API handler. approveAll distinguishes "approve all" (promote the session to auto-approve, like the TUI's "Approve All" and ACP's "Allow Always") from a plain "approve once" that leaves the session mode untouched. Previously every approve was treated as auto, silently flipping the whole session to Full access on a single Allow click.

func (*WebHandler) ResolveAskUser added in v0.5.2

func (h *WebHandler) ResolveAskUser(id string, resp tools.AskUserBatchResponse) error

ResolveAskUser delivers the user's answers to a pending ask_user request. Called by the API handler when the frontend submits answers.

type WebSubagentData

type WebSubagentData struct {
	Name      string `json:"name"`
	AgentType string `json:"agent_type"`
	Done      bool   `json:"done"`
	Result    string `json:"result,omitempty"`
	Error     string `json:"error,omitempty"`
}

WebSubagentData carries subagent lifecycle events.

type WebSubagentProgressData

type WebSubagentProgressData struct {
	AgentName string `json:"agent_name"`
	Event     string `json:"event"` // "tool_call" or "tool_result"
	ToolName  string `json:"tool_name"`
	Detail    string `json:"detail"`
}

WebSubagentProgressData carries intermediate subagent tool call/result events.

type WebTextData

type WebTextData struct {
	Text string `json:"text"`
}

WebTextData carries a streaming text chunk.

type WebTokenData

type WebTokenData struct {
	TotalTokens       int64   `json:"total_tokens"`
	PromptTokens      int64   `json:"prompt_tokens"`
	CompletionTokens  int64   `json:"completion_tokens"`
	CachedTokens      int64   `json:"cached_tokens"`
	ReasoningTokens   int64   `json:"reasoning_tokens"`
	CacheWriteTokens  int64   `json:"cache_write_tokens"`
	CallCount         int64   `json:"call_count"`
	CacheHitRate      float64 `json:"cache_hit_rate"`
	CacheSupported    bool    `json:"cache_supported"`
	ModelContextLimit int     `json:"model_context_limit"`
}

WebTokenData carries token usage to the browser. Field order/types MUST match handler.TokenUsage so OnTokenUpdate's WebTokenData(info) conversion compiles. total_tokens is current context occupancy (last call); the rest are cumulative for the session.

type WebToolCallData

type WebToolCallData struct {
	Name        string           `json:"name"`
	Args        string           `json:"args"`
	ToolCallID  string           `json:"tool_call_id,omitempty"`
	DisplayInfo *ToolDisplayInfo `json:"display_info,omitempty"`
	BatchID     string           `json:"batch_id,omitempty"`
	BatchIndex  int              `json:"batch_index,omitempty"`
	BatchSize   int              `json:"batch_size,omitempty"`
	StartedAt   int64            `json:"started_at,omitempty"`
}

WebToolCallData carries tool invocation info. The batch fields group tool calls issued by the same assistant message (batch_size > 1 → concurrent batch); started_at is unix milliseconds.

type WebToolResultData

type WebToolResultData struct {
	Name          string                     `json:"name"`
	Output        string                     `json:"output"`
	DisplayOutput string                     `json:"display_output,omitempty"` // clean output for UI display
	Error         string                     `json:"error,omitempty"`
	ToolCallID    string                     `json:"tool_call_id,omitempty"`
	Streams       *WebToolResultStreams      `json:"streams,omitempty"`
	Meta          *WebToolResultMeta         `json:"meta,omitempty"`
	Presentation  *WebToolResultPresentation `json:"presentation,omitempty"`
	// DurationMs is the runner-measured call→result latency (approval wait
	// already subtracted), provided for all tools. It coexists with
	// Meta.DurationMs, which only execute-style tools report (in-sandbox
	// execution time).
	DurationMs int64 `json:"duration_ms,omitempty"`
	// Denied is true when the user rejected this call at the approval prompt.
	// The UI renders it struck-through/muted (declined), not as an error.
	Denied bool `json:"denied,omitempty"`
}

WebToolResultData carries tool completion info. Legacy fields (output / display_output) are always populated for old clients; streams/meta/presentation are additive dual-channel fields.

type WebToolResultMeta added in v0.9.4

type WebToolResultMeta struct {
	ExitCode   int    `json:"exit_code"`
	DurationMs int64  `json:"duration_ms"`
	TimedOut   bool   `json:"timed_out,omitempty"`
	Truncated  bool   `json:"truncated,omitempty"`
	SpillPath  string `json:"spill_path,omitempty"`
}

WebToolResultMeta is structured execution metadata for UI consumers.

type WebToolResultPresentation added in v0.9.4

type WebToolResultPresentation struct {
	Kind        string `json:"kind,omitempty"`
	Title       string `json:"title,omitempty"`
	Subtitle    string `json:"subtitle,omitempty"`
	Collapsible bool   `json:"collapsible,omitempty"`
}

WebToolResultPresentation carries UI presentation hints on a tool result.

type WebToolResultStreams added in v0.9.4

type WebToolResultStreams struct {
	Stdout     string `json:"stdout,omitempty"`
	Stderr     string `json:"stderr,omitempty"`
	Aggregated string `json:"aggregated,omitempty"`
}

WebToolResultStreams is the structured stream payload for execute-style tools.

Jump to

Keyboard shortcuts

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