Files
2026-08-14 23:41:57 +08:00

218 lines
9.3 KiB
Go

package agentcore
// AgentEvent is the sealed interface implemented by every event the loop emits.
// Consumers dispatch with a type switch, consistent with Content. pigo covers
// all 10 of pi's event types (PRD FR-24).
type AgentEvent interface {
isAgentEvent()
// EventType returns the discriminant string, useful for logging and for
// serialising events to the stream-json/stdio protocol (US-020).
EventType() string
}
// Event type discriminants.
const (
EventAgentStart = "agent_start"
EventAgentEnd = "agent_end"
EventTurnStart = "turn_start"
EventTurnEnd = "turn_end"
EventMessageStart = "message_start"
EventMessageUpdate = "message_update"
EventMessageEnd = "message_end"
EventToolExecutionStart = "tool_execution_start"
EventToolExecutionUpdate = "tool_execution_update"
EventToolExecutionEnd = "tool_execution_end"
EventCompaction = "compaction"
EventCompactionStart = "compaction_start"
EventTelemetry = "telemetry"
EventSubAgentProgress = "subagent_progress"
)
// AgentStartEvent is emitted once when a loop run begins. SessionID, when set,
// is the id of the session backing this run; it is carried in the first
// stream-json event so a caller can associate output with a session and resume
// it later (mirrors pi/Claude Code, which put a session id in the first event).
type AgentStartEvent struct {
SessionID string
}
// AgentEndEvent is emitted once when a loop run ends, carrying the messages
// newly produced during this run (the EventStream result).
type AgentEndEvent struct {
Messages []AgentMessage
}
// TurnStartEvent marks the start of a turn (a single assistant response cycle).
type TurnStartEvent struct{}
// TurnEndEvent marks the end of a turn, with the assistant message and any tool
// results produced during it.
type TurnEndEvent struct {
Message AssistantMessage
ToolResults []ToolResultMessage
}
// MessageStartEvent is emitted when a message begins streaming.
type MessageStartEvent struct {
Message AgentMessage
}
// MessageUpdateEvent is emitted for each streaming delta, carrying the current
// partial message and the raw provider-level event that produced it.
type MessageUpdateEvent struct {
Message AgentMessage
AssistantMessageEvent any
}
// MessageEndEvent is emitted when a message finishes streaming.
type MessageEndEvent struct {
Message AgentMessage
}
// ToolExecutionStartEvent is emitted before a tool runs.
type ToolExecutionStartEvent struct {
ToolCallID string
ToolName string
Args any
}
// ToolExecutionUpdateEvent carries a partial result during tool execution.
type ToolExecutionUpdateEvent struct {
ToolCallID string
ToolName string
PartialResult AgentToolResult
}
// ToolExecutionEndEvent is emitted when a tool finishes.
type ToolExecutionEndEvent struct {
ToolCallID string
ToolName string
Result AgentToolResult
IsError bool
}
// CompactionEvent is emitted when the loop compacts the context window, either
// automatically (threshold/overflow) or on an explicit /compact request. It
// carries before/after token counts and how many messages were summarized vs.
// retained. When compaction fails it is still emitted with ErrorMessage set and
// the token/count fields describing the unchanged context, so consumers can
// surface the failure without the session aborting (US-004).
type CompactionEvent struct {
// Reason is why compaction ran: "manual", "threshold", or "overflow".
Reason string
// TokensBefore is the estimated context tokens prior to compaction.
TokensBefore int
// TokensAfter is the estimated context tokens after compaction (equals
// TokensBefore when compaction failed or was a no-op).
TokensAfter int
// SummarizedCount is the number of messages folded into the summary.
SummarizedCount int
// KeptCount is the number of recent messages retained verbatim.
KeptCount int
// ErrorMessage is non-empty when compaction failed; the original context is
// preserved in that case.
ErrorMessage string
}
// CompactionStartEvent is emitted immediately before the loop runs compaction,
// so a front-end can show an in-progress "Compacting conversation…" indicator
// while the summarization request is in flight. The matching CompactionEvent is
// emitted when it completes (or fails). Reason mirrors CompactionEvent.Reason.
type CompactionStartEvent struct {
// Reason is why compaction is running: "manual", "threshold", or "overflow".
Reason string
// TokensBefore is the estimated context tokens that triggered compaction.
TokensBefore int
}
// SubAgentProgressEvent carries structured progress from a running sub-agent
// (dispatched by the task tool). It is reported at the sub-agent's tool
// execution / turn boundaries so a TUI (multi-line status panel) or headless
// mode (stderr line) can display live progress. Elapsed time is intentionally
// omitted: consumers compute it themselves (TUI from tool-start time, headless
// from when the id was first seen) to avoid emitting an event per frame.
type SubAgentProgressEvent struct {
// ToolCallID is the parent task call's tool-call id, used as the key for
// the status line.
ToolCallID string
// Description is the task call's description, for display (may be empty).
Description string
// Activity is the current activity: tool name / phase, e.g. "Editing",
// "Running bash", "Thinking".
Activity string
// Tokens is the estimated sub-agent output token count (0 = unknown).
Tokens int
}
// ToolTiming records how long one tool invocation took, keyed by tool name in
// TelemetryEvent.ToolDurationsMs. It aggregates repeated calls of the same tool
// so a summary stays compact regardless of turn count.
type ToolTiming struct {
// Count is how many times the tool was invoked over the run.
Count int
// TotalMs is the summed wall-clock duration of every invocation, in
// milliseconds.
TotalMs int64
}
// TelemetryEvent is a lightweight, additive observability summary emitted once
// at run end (just before agent_end) so scripts consuming the stream-json
// output can read structured metrics without a new dependency (no
// Prometheus/OTLP). It is purely observational: consumers that ignore it behave
// exactly as before. Metrics covered (observability — structured telemetry collection):
// - per-tool wall-clock durations (ToolDurationsMs, aggregated by tool name),
// - how many turns ran (Turns),
// - how many assistant responses were truncated by the output cap
// (TruncationCount),
// - how many times the context was compacted (CompactionCount),
// - the latest context-utilization ratio (ContextUtilization = used tokens /
// ContextWindow) and the raw numbers behind it.
type TelemetryEvent struct {
// Turns is the number of turns (turn_start events) the run executed.
Turns int
// ToolDurationsMs maps a tool name to its aggregated timing over the run.
ToolDurationsMs map[string]ToolTiming
// TruncationCount is how many assistant responses stopped with reason
// "length" (truncated by the output token cap), each triggering a resend.
TruncationCount int
// CompactionCount is how many successful context compactions occurred.
CompactionCount int
// ContextUtilization is the latest used/window ratio in [0,1], or 0 when the
// context window is unknown. Computed as ContextTokens / ContextWindow.
ContextUtilization float64
// ContextTokens is the most recently observed estimated context-token usage.
ContextTokens int
// ContextWindow is the model's total context-token budget (0 when unknown).
ContextWindow int
}
func (AgentStartEvent) isAgentEvent() {}
func (AgentEndEvent) isAgentEvent() {}
func (TurnStartEvent) isAgentEvent() {}
func (TurnEndEvent) isAgentEvent() {}
func (MessageStartEvent) isAgentEvent() {}
func (MessageUpdateEvent) isAgentEvent() {}
func (MessageEndEvent) isAgentEvent() {}
func (ToolExecutionStartEvent) isAgentEvent() {}
func (ToolExecutionUpdateEvent) isAgentEvent() {}
func (ToolExecutionEndEvent) isAgentEvent() {}
func (CompactionEvent) isAgentEvent() {}
func (CompactionStartEvent) isAgentEvent() {}
func (TelemetryEvent) isAgentEvent() {}
func (SubAgentProgressEvent) isAgentEvent() {}
func (AgentStartEvent) EventType() string { return EventAgentStart }
func (AgentEndEvent) EventType() string { return EventAgentEnd }
func (TurnStartEvent) EventType() string { return EventTurnStart }
func (TurnEndEvent) EventType() string { return EventTurnEnd }
func (MessageStartEvent) EventType() string { return EventMessageStart }
func (MessageUpdateEvent) EventType() string { return EventMessageUpdate }
func (MessageEndEvent) EventType() string { return EventMessageEnd }
func (ToolExecutionStartEvent) EventType() string { return EventToolExecutionStart }
func (ToolExecutionUpdateEvent) EventType() string { return EventToolExecutionUpdate }
func (ToolExecutionEndEvent) EventType() string { return EventToolExecutionEnd }
func (CompactionEvent) EventType() string { return EventCompaction }
func (CompactionStartEvent) EventType() string { return EventCompactionStart }
func (TelemetryEvent) EventType() string { return EventTelemetry }
func (SubAgentProgressEvent) EventType() string { return EventSubAgentProgress }