agent

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package agent provides the core agent loop and message types. Mirrors packages/agent of the pinned Pi release.

Ports packages/agent/src/types.ts.

Ports packages/ai/src/utils/validation.ts.

Index

Constants

View Source
const (
	RoleUser       = "user"
	RoleAssistant  = "assistant"
	RoleToolResult = "toolResult"
	// Custom roles used by the coding agent layer.
	RoleBashExecution     = "bashExecution"
	RoleCustom            = "custom"
	RoleBranchSummary     = "branchSummary"
	RoleCompactionSummary = "compactionSummary"
)

Role constants mirror upstream.

Variables

View Source
var ErrAlreadyProcessing = errors.New("Agent is already processing.")

ErrAlreadyProcessing is returned when Continue or Reset is called while a run is active. Mirrors upstream Agent.continue and Agent.reset.

View Source
var ErrAlreadyProcessingPrompt = errors.New("Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.")

ErrAlreadyProcessingPrompt is returned when a prompt is sent while a run is active. Mirrors upstream Agent.prompt's "Agent is already processing a prompt" error: queue the message with Steer or FollowUp instead.

View Source
var ErrMaxTurnsReached = errors.New("agent stopped at its turn limit with tool results still unanswered")

ErrMaxTurnsReached reports that a run stopped at AgentOptions.MaxTurns with tool results still waiting for a model response.

View Source
var ErrNoMessagesToContinue = errors.New("No messages to continue from")

ErrNoMessagesToContinue is returned by Continue on an empty transcript. Mirrors upstream Agent.continue's "No messages to continue from".

View Source
var ErrNoModelSelected = errors.New("no model selected")

ErrNoModelSelected is returned when a turn is requested with no usable model. Upstream agent-session.prompt() throws formatNoModelSelectedMessage() before streaming; pig surfaces the same condition as an error rather than panicking on a nil model in runLoop.

View Source
var ErrToolResultsUnanswered = errors.New("agent run ended after tool results without a model response")

ErrToolResultsUnanswered reports that a run ended with tool results the model never answered although no tool asked to terminate the run. It guards the invariant that a run never ends silently mid-task.

Functions

func BranchSummaryContextText

func BranchSummaryContextText(summary string) string

BranchSummaryContextText wraps a branch summary for LLM context. Mirrors upstream BRANCH_SUMMARY_PREFIX + summary + BRANCH_SUMMARY_SUFFIX from packages/coding-agent/src/core/messages.ts.

func CalculateContextTokens

func CalculateContextTokens(usage ai.Usage) int

CalculateContextTokens computes context tokens from usage stats. Mirrors upstream calculateContextTokens (compaction.ts:135): prefer the provider-reported total, else sum the components.

func CompactionSummaryContextText

func CompactionSummaryContextText(summary string) string

CompactionSummaryContextText wraps a compaction summary for LLM context. Mirrors upstream COMPACTION_SUMMARY_PREFIX + summary + COMPACTION_SUMMARY_SUFFIX from packages/coding-agent/src/core/messages.ts.

func ConvertToLLM

func ConvertToLLM(msgs []AgentMessage, model *ai.Model) []ai.Message

ConvertToLLM converts AgentMessages to the wire format expected by the AI provider. It calls NormalizeMessages first to filter errored/aborted assistant messages and insert synthetic tool results for orphaned tool calls.

func EstimateTokens

func EstimateTokens(msg AgentMessage) int

EstimateTokens estimates the token count for a single message using a chars/4 heuristic covering all roles. Mirrors upstream estimateTokens (compaction.ts:232).

func EventObservation

func EventObservation(event AgentEvent) *ai.StreamObservation

EventObservation returns the attached stream scope for synchronous host dispatch. Session transfers that scope before notifying its synchronous subscribers; it is not message data or wire metadata.

func SetDefaultStreamFn

func SetDefaultStreamFn(fn StreamFn)

SetDefaultStreamFn configures the stream used by newly constructed agents when their caller omits StreamFn. Nil removes the configured fallback.

func StreamProxy

func StreamProxy(ctx context.Context, model *ai.Model, transcript ai.TranscriptContext, options ProxyStreamOptions) *ai.AssistantMessageEventStream

StreamProxy streams through a server that manages provider authentication. Each toolcall_start replaces the indexed tool and its accumulated JSON. It returns immediately; request, protocol, and cancellation failures are delivered as terminal error events.

func ValidateToolArguments

func ValidateToolArguments(tool ai.ToolSchema, call ai.ToolCall) (map[string]any, error)

ValidateToolArguments clones, prepares optional nulls, coerces and validates the call's arguments without changing the provider call.

func WithMutationTicket

func WithMutationTicket(ctx context.Context, ticket *MutationTicket) context.Context

WithMutationTicket returns ctx carrying a reservation for the tool call it is passed into. Execute implementations that also implement QueueOrderable must check MutationTicketFromContext and honor an existing reservation instead of registering a second time.

func WithToolEnvironment

func WithToolEnvironment(ctx context.Context, env ToolEnvironment) context.Context

WithToolEnvironment returns ctx carrying env for the tools it executes.

Types

type AfterToolCallHook

type AfterToolCallHook func(ctx context.Context, toolCallID, toolName string, args json.RawMessage, result AgentToolResult) AfterToolCallResult

AfterToolCallHook is called after a tool executes and may override its result. Full upstream contract, including terminate signals and result overrides. Replaces the old void-return signature which had no override capability. upstream: agent-loop.ts afterToolCall({ toolCall, args, result, isError })

type AfterToolCallResult

type AfterToolCallResult struct {
	Content   []ai.ToolResultMessageContent
	Details   any
	IsError   *bool // nil = keep original
	Usage     *ai.Usage
	Terminate *bool
	// StructuredContent replaces the result's structured content. When Content is replaced without it, the structured content is dropped, because it may no longer match the content.
	StructuredContent json.RawMessage
}

AfterToolCallResult carries per-tool overrides returned by AfterToolCallHook. Mirrors upstream types.ts AfterToolCallResult (agent-loop.ts:617-640). Merge semantics: field-by-field replace; no deep merge. Nil fields keep the original; a non-nil empty Content replaces it with an empty array.

  • Content: if non-nil, replaces the ordered content, including an empty array.
  • IsError: if non-nil, replaces the tool result error flag.
  • Usage: if non-nil, replaces the tool result usage.
  • Terminate: if non-nil, replaces the early-termination hint. The agent stops after the batch only when every finalized result terminates (upstream shouldTerminateToolBatch).

type Agent

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

Agent is the agent loop.

Mirrors upstream packages/agent/src/agent.ts (class Agent).

func NewAgent

func NewAgent(opts AgentOptions) *Agent

NewAgent creates a new Agent.

func (*Agent) Abort

func (a *Agent) Abort()

Abort cancels the active run. Calling Abort on an idle agent does nothing.

func (*Agent) AddAfterToolCallHook

func (a *Agent) AddAfterToolCallHook(h AfterToolCallHook)

AddAfterToolCallHook appends a hook that fires after each tool execution. Mirrors upstream agent.afterToolCall assignment (agent-session.ts:396).

func (*Agent) AddBeforeToolCallHook

func (a *Agent) AddBeforeToolCallHook(h BeforeToolCallHook)

AddBeforeToolCallHook appends a hook that fires before each tool execution. Mirrors upstream agent.beforeToolCall assignment (agent-session.ts:373).

func (*Agent) BeginSendContent

func (a *Agent) BeginSendContent(ctx context.Context, content []ai.UserContentBlock) (*PromptRun, error)

BeginSendContent claims the agent and binds its cancellation context synchronously without starting events. Pending preflight is not an active run; a claimed prompt is active before its first awaited listener finishes.

func (*Agent) BeginSendMessages

func (a *Agent) BeginSendMessages(ctx context.Context, messages []AgentMessage) (*PromptRun, error)

BeginSendMessages claims a turn for already-built messages without starting events. The owner calls Run exactly once, even if Start fails, just as for BeginSendContent.

func (*Agent) ClearAllQueues

func (a *Agent) ClearAllQueues() (steering, followUp []AgentMessage)

ClearAllQueues removes and returns all pending messages from both queues.

func (*Agent) ClearFollowUpQueue

func (a *Agent) ClearFollowUpQueue() []AgentMessage

ClearFollowUpQueue removes and returns all pending follow-up messages.

func (*Agent) ClearSteeringQueue

func (a *Agent) ClearSteeringQueue() []AgentMessage

ClearSteeringQueue removes and returns all pending steering messages.

func (*Agent) ClearSystemPrompt

func (a *Agent) ClearSystemPrompt()

ClearSystemPrompt removes a SetSystemPrompt projection, so later requests use the transcript's own system prompt again.

func (*Agent) Continue

func (a *Agent) Continue(ctx context.Context) ([]AgentMessage, error)

Continue runs the agent loop from the current message state without prepending a new user message. Used to retry a turn after auto-compaction when an overflow error is recovered by compacting and replaying.

If the last message is an assistant message, Continue first checks the steering queue, then the follow-up queue. Without queued input it rejects the assistant tail. Mirrors upstream Agent.continue (packages/agent/src/agent.ts).

func (*Agent) ErrorMessage

func (a *Agent) ErrorMessage() string

ErrorMessage returns the last failed turn's error, cleared at the next run or reset.

func (*Agent) FinishTurnHook

func (a *Agent) FinishTurnHook() FinishTurn

FinishTurnHook returns the current FinishTurn hook, or nil.

func (*Agent) FollowUp

func (a *Agent) FollowUp(msg AgentMessage)

FollowUp enqueues a follow-up message. Follow-up messages are delivered only after the agent has no more tool calls AND no steering messages, starting a fresh outer-loop iteration.

func (*Agent) FollowUpMode

func (a *Agent) FollowUpMode() QueueMode

FollowUpMode returns the current follow-up drain policy.

func (*Agent) HasPendingMessages

func (a *Agent) HasPendingMessages() bool

HasPendingMessages reports whether either queue has messages.

func (*Agent) HasQueuedMessages

func (a *Agent) HasQueuedMessages() bool

HasQueuedMessages reports whether either queue still contains pending messages. Mirrors upstream agent.ts:290.

func (*Agent) IsStreaming

func (a *Agent) IsStreaming() bool

IsStreaming returns true when the agent is actively in a Send/Continue loop. Mirrors upstream AgentState.isStreaming for extension queries.

func (*Agent) Messages

func (a *Agent) Messages() []AgentMessage

Messages returns the current message history.

func (*Agent) MessagesSnapshot

func (a *Agent) MessagesSnapshot() []AgentMessage

MessagesSnapshot copies the message history. Unlike Messages it is safe to call from any goroutine while a run appends.

func (*Agent) Model

func (a *Agent) Model() *ai.Model

Model returns the active model. New agents use the upstream unknown descriptor until one is selected; SetModel(nil) clears it.

func (*Agent) ObserveRunSignal

func (a *Agent) ObserveRunSignal(observe func()) func()

ObserveRunSignal registers observe to run after a run claims the agent, when Signal already returns the run's context, and after the run releases it, when Signal already returns nil. Pi's `agent.signal` is a getter (agent.ts:336-338), so an owner that replicates the signal elsewhere learns of both instants here. observe runs on the goroutine that changes the run, after the agent's lock is released, so it may call Signal; the run waits for observe to return. The returned function removes the observer and can be called repeatedly.

func (*Agent) PeekQueuedMessages

func (a *Agent) PeekQueuedMessages() []AgentMessage

PeekQueuedMessages previews the messages selected for the next turn without consuming them: the steering queue's selection, or the follow-up queue's when no steering is queued. Mirrors upstream Agent.peekQueuedMessages.

func (*Agent) PendingMessages

func (a *Agent) PendingMessages() (steering, followUp []AgentMessage)

PendingMessages returns a snapshot of both queues without draining. Used for UI display (e.g. "2 steering, 1 follow-up pending").

func (*Agent) PendingToolCalls

func (a *Agent) PendingToolCalls() []string

PendingToolCalls returns the active tool call IDs in start order.

func (*Agent) PrepareNextTurnHook

func (a *Agent) PrepareNextTurnHook() PrepareNextTurn

PrepareNextTurnHook returns the current PrepareNextTurn hook, or nil.

func (*Agent) PrepareRequestHook

func (a *Agent) PrepareRequestHook() PrepareRequest

PrepareRequestHook returns the current PrepareRequest hook, or nil.

func (*Agent) QueueNextTurn

func (a *Agent) QueueNextTurn(msg AgentMessage)

QueueNextTurn defers a message to the start of the next user turn, where it is injected beside the user message as context for the first model call.

This is not FollowUp. A follow-up continues the turn that is running, so it reaches the model only after the current response completes; a message meant to inform the next prompt would arrive a model call too late, and on an idle agent it would sit in a queue with no goroutine to drain it.

func (*Agent) Reset

func (a *Agent) Reset() error

Reset retains the replayed system baseline and clears conversation state and queues. It refuses while a run is active, as upstream Agent.reset throws. Mirrors upstream Agent.reset.

func (*Agent) Send

func (a *Agent) Send(ctx context.Context, content string) ([]AgentMessage, error)

Send sends a plain-text user message and runs the agent loop until the LLM stops calling tools. Steering and follow-up messages queued via Steer/FollowUp are polled at the appropriate points in the loop.

func (*Agent) SendContent

func (a *Agent) SendContent(ctx context.Context, content []ai.UserContentBlock) ([]AgentMessage, error)

SendContent is the structured-content variant of Send.

func (*Agent) SendMessages

func (a *Agent) SendMessages(ctx context.Context, msgs []AgentMessage) ([]AgentMessage, error)

SendMessages seeds a turn with already-built messages instead of a user prompt, mirroring upstream agent.prompt(messages) (agent-session.ts:1063). An extension delivering a custom message while the agent is idle needs a turn to start from that message; without this the message can only be enqueued for a turn that may never run.

func (*Agent) SetBeforeProviderHook

func (a *Agent) SetBeforeProviderHook(fn func(payload any, model *ai.Model) (any, error))

SetBeforeProviderHook transforms each provider's final wire payload. Mirrors upstream before_provider_request through StreamOptions.OnPayload.

func (*Agent) SetFinishTurn

func (a *Agent) SetFinishTurn(fn FinishTurn)

SetFinishTurn replaces the FinishTurn hook for later runs. Mirrors assigning upstream's public Agent.finishTurn; FinishTurnHook returns the current one so a caller can chain it.

func (*Agent) SetFollowUpMode

func (a *Agent) SetFollowUpMode(mode QueueMode)

SetFollowUpMode changes the follow-up queue's drain policy.

func (*Agent) SetMessages

func (a *Agent) SetMessages(msgs []AgentMessage)

SetMessages replaces the message history (used for session restore). The agent keeps a copy of the top-level slice, as upstream's state.messages setter copies the assigned array.

func (*Agent) SetModel

func (a *Agent) SetModel(m *ai.Model)

SetModel swaps the active LLM model. The next streaming turn uses the new provider/model. The agent's tools, system prompt, and message history are unchanged. Mid-session model switch.

func (*Agent) SetPrepareNextTurn

func (a *Agent) SetPrepareNextTurn(fn PrepareNextTurn)

SetPrepareNextTurn replaces the PrepareNextTurn hook for later runs. Mirrors assigning upstream's public Agent.prepareNextTurnWithContext.

func (*Agent) SetPrepareRequest

func (a *Agent) SetPrepareRequest(fn PrepareRequest)

SetPrepareRequest replaces the PrepareRequest hook for later runs. Mirrors assigning upstream's public Agent.prepareRequest.

func (*Agent) SetSessionID

func (a *Agent) SetSessionID(id string)

SetSessionID updates the session ID used for prompt caching. Called after session creation/resume when the stable session ID is known.

func (*Agent) SetSteeringMode

func (a *Agent) SetSteeringMode(mode QueueMode)

SetSteeringMode changes the steering queue's drain policy.

func (*Agent) SetStreamFunction

func (a *Agent) SetStreamFunction(streamFn StreamFn)

SetStreamFunction replaces the provider request function for subsequent requests, matching Agent.streamFunction assignment in Pi. Nil restores the stock stream.

func (*Agent) SetSystemPrompt

func (a *Agent) SetSystemPrompt(prompt string)

SetSystemPrompt projects a replacement system prompt onto subsequent provider requests without rewriting transcript history.

func (*Agent) SetThinkingLevel

func (a *Agent) SetThinkingLevel(level ai.ThinkingLevel)

SetThinkingLevel updates the reasoning depth used for future Send calls. Wired by InteractiveMode.cycleThinkingLevel() on Shift+Tab. The current in-flight call (if any) is unaffected; the new level applies starting with the next agent.Send invocation.

func (*Agent) SetTools

func (a *Agent) SetTools(tools []AgentTool)

SetTools replaces the active tool list for subsequent turns.

func (*Agent) SetTransformContext

func (a *Agent) SetTransformContext(fn func(msgs []AgentMessage) []AgentMessage)

SetTransformContext sets a hook that fires before each LLM call to allow extensions to modify the message context. Mirrors upstream context event.

func (*Agent) SetTransformContextWithContext

func (a *Agent) SetTransformContextWithContext(fn func(context.Context, []AgentMessage) ([]AgentMessage, error))

SetTransformContextWithContext installs a request transform with cancellation and error propagation owned by the active agent run.

func (*Agent) SetTransformHeaders

func (a *Agent) SetTransformHeaders(fn func(context.Context, ai.ProviderHeaders) (ai.ProviderHeaders, error))

SetTransformHeaders sets the hook that rewrites each provider request's merged HTTP headers. Mirrors upstream StreamOptions.transformHeaders.

func (*Agent) SetTransport

func (a *Agent) SetTransport(transport ai.Transport)

SetTransport updates the preferred provider transport for future Send calls.

func (*Agent) Signal

func (a *Agent) Signal() context.Context

Signal returns the active run's cancellation context, or nil when idle.

func (*Agent) Steer

func (a *Agent) Steer(msg AgentMessage)

Steer enqueues a steering message. Steering messages are delivered after the current tool batch finishes but before the next LLM call, allowing the user to redirect the agent mid-turn.

func (*Agent) SteeringMode

func (a *Agent) SteeringMode() QueueMode

SteeringMode returns the current steering drain policy.

func (*Agent) StreamFunction

func (a *Agent) StreamFunction() StreamFn

Ports packages/agent/src/agent.ts StreamFunction returns the caller's stream override. Nil denotes the stock stream, allowing summarization to distinguish optional custom-stream auth from required stock auth without comparing Go function values.

func (*Agent) StreamingMessage

func (a *Agent) StreamingMessage() *AgentMessage

StreamingMessage returns a snapshot of the message currently streaming.

func (*Agent) Subscribe

func (a *Agent) Subscribe(listener func(context.Context, AgentEvent) error) func()

Subscribe registers a lifecycle listener. Listeners run in subscription order with the active run context; the run waits for each callback to return. The returned function removes this subscription and can be called repeatedly.

func (*Agent) SystemPrompt

func (a *Agent) SystemPrompt() string

SystemPrompt returns the current replayed instructions or an explicit provider prompt override. It is safe to call while the agent appends messages.

func (*Agent) SystemPromptOverride

func (a *Agent) SystemPromptOverride() (string, bool)

SystemPromptOverride returns only the explicit provider projection and its presence, not replayed transcript instructions.

func (*Agent) SystemPromptSnapshot

func (a *Agent) SystemPromptSnapshot() (string, bool)

SystemPromptSnapshot reads the prompt and whether it is present under one lock. Presence distinguishes an empty projected prompt from an uninitialized transcript.

func (*Agent) ThinkingLevel

func (a *Agent) ThinkingLevel() ai.ThinkingLevel

ThinkingLevel returns the reasoning depth used for future Send calls.

func (*Agent) Timings

func (a *Agent) Timings() *Recorder

Timings returns the agent's timing recorder. Read-only consumers (status line, /cost, --diagnose) hold this reference for the session's lifetime and call Snapshot()/Elapsed() on demand.

func (*Agent) ToolExecutionMode

func (a *Agent) ToolExecutionMode() ToolExecutionMode

ToolExecutionMode returns the batch strategy for assistant messages with several tool calls. Mirrors upstream Agent.toolExecution.

func (*Agent) Tools

func (a *Agent) Tools() []AgentTool

Tools returns the agent's current tool list. Defensive copy.

func (*Agent) WaitForIdle

func (a *Agent) WaitForIdle()

WaitForIdle waits for the current run and its synchronous listeners to settle. It returns immediately when no run is active.

type AgentEndEvent

type AgentEndEvent struct {
	Messages  []AgentMessage
	WillRetry bool
}

type AgentEvent

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

AgentEvent is emitted by the agent loop to notify observers.

func RefreshEvent

func RefreshEvent(event AgentEvent) AgentEvent

RefreshEvent returns the message event with the assistant message's and the provider event's partial fields set to the stream state at this call. A host dispatcher calls it when it delivers a retained event to a listener after an awaited step. Other events are returned unchanged.

func WithEventObservation

func WithEventObservation(event AgentEvent, observation *ai.StreamObservation) AgentEvent

WithEventObservation carries a stream scope across synchronous host dispatch without changing message identity or serialized fields. Events outside the message lifecycle are unchanged.

type AgentLoopTurnUpdate

type AgentLoopTurnUpdate struct {
	// Context replaces the loop context for the next provider request.
	Context []AgentMessage
	// Messages are appended before the next provider request, with normal
	// lifecycle events.
	Messages []AgentMessage
	// Model is the model for the next provider request.
	Model *ai.Model
	// ThinkingLevel is the thinking level for the next provider request.
	ThinkingLevel *ai.ThinkingLevel
}

AgentLoopTurnUpdate carries optional next-turn overrides returned by PrepareNextTurn. Mirrors upstream AgentLoopTurnUpdate.

type AgentMessage

type AgentMessage struct {
	System     *ai.SystemMessage
	User       *UserMessage
	Assistant  *AssistantMessage
	ToolResult *ToolResultMessage
	// Custom message types (bash execution, branch summary, etc.)
	Custom map[string]any // raw JSON for custom types
}

AgentMessage is the union of all message types. Only one field is non-nil.

func NormalizeMessages

func NormalizeMessages(msgs []AgentMessage, model *ai.Model) []AgentMessage

NormalizeMessages filters and normalizes msgs before they are converted to the provider wire format by convertToLLM. Two guarantees are provided:

  1. Assistant messages with StopReason "error" or "aborted" are dropped. Replaying them causes API errors (partial reasoning, orphaned tool calls, OpenAI "reasoning without following item").

  2. Orphaned tool calls: tool_use blocks in an assistant message with no matching tool_result: get a synthetic ToolResultMessage injected immediately after the assistant message. This keeps the message sequence valid for all providers.

  3. Cross-model thinking blocks are normalized against the target model (upstream transform-messages.ts isSameModel branch): redacted thinking is dropped for a different model, same-model signed thinking is kept for replay, empty thinking is dropped, and other cross-model thinking is converted to plain text so a foreign reasoning signature never reaches a provider converter. model may be nil on contextless/test paths, which is treated as same-model (thinking passes through).

The returned slice may be shorter than msgs (dropped aborted/errored entries) or longer (inserted synthetic tool results). Assistant entries whose thinking is normalized are copied; other non-modified entries share their underlying struct pointers with msgs.

func (AgentMessage) Clone

func (m AgentMessage) Clone() AgentMessage

Clone copies the message envelope and mutable top-level slices/maps. A streaming assistant becomes an independently owned current observation.

func (AgentMessage) ContentBlocks

func (m AgentMessage) ContentBlocks() []ai.ContentBlock

func (AgentMessage) MarshalJSON

func (m AgentMessage) MarshalJSON() ([]byte, error)

MarshalJSON emits the upstream flat role-discriminated AgentMessage union. User strings and text blocks retain their JavaScript UTF-16 units.

func (AgentMessage) Role

func (m AgentMessage) Role() string

Role returns the role string.

func (*AgentMessage) UnmarshalJSON

func (m *AgentMessage) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the upstream flat role-discriminated AgentMessage union and retains lone UTF-16 units in user content as WTF-8.

type AgentOptions

type AgentOptions struct {
	Model           *ai.Model
	Tools           []AgentTool
	SystemPrompt    string
	MaxTurns        int // 0 = unlimited, as upstream, which has no turn cap
	ThinkingLevel   ai.ThinkingLevel
	ThinkingBudgets *ai.ThinkingBudgets
	Transport       ai.Transport
	// SessionID is forwarded to the provider for prompt caching
	// (OpenAI prompt_cache_key). Set by the session wrapper when
	// the session has a stable identifier.
	SessionID string
	// SessionFile, when set, reports the session file tools may expose as
	// PI_SESSION_FILE. Nil or an empty result exports nothing.
	SessionFile func() string

	// SteeringMode controls how steering messages are drained (default: one-at-a-time).
	// Mirrors upstream packages/agent/src/agent.ts:104.
	SteeringMode QueueMode
	// FollowUpMode controls how follow-up messages are drained (default: one-at-a-time).
	// Mirrors upstream packages/agent/src/agent.ts:105.
	FollowUpMode QueueMode

	BeforeToolCall []BeforeToolCallHook
	AfterToolCall  []AfterToolCallHook
	// PrepareToolResult processes the final extension-modified result before
	// events and persistence. Processing failures preserve the original result.
	PrepareToolResult func(context.Context, AgentToolResult) AgentToolResult
	// FinishTurn runs after a turn's assistant message and tool results and
	// before TurnEndEvent; it may end the run or request one more request.
	FinishTurn FinishTurn
	// PrepareRequest runs immediately before every provider request and may
	// replace the context, model, and thinking level for the run.
	PrepareRequest PrepareRequest
	// ConvertToLlm converts the transformed agent context to provider messages.
	// The run waits for it and reports conversion failures through its lifecycle.
	// Nil uses the standard message conversion.
	ConvertToLlm func([]AgentMessage) ([]ai.Message, error)
	// TransformLLMMessages post-processes the provider messages that the
	// built-in conversion produced for each request. The coding session uses
	// it as upstream createAgentSession wraps convertToLlm
	// (convertToLlmWithBlockImages in sdk.ts).
	TransformLLMMessages func([]ai.Message) []ai.Message
	// PrepareNextTurn runs before the next turn starts when the loop
	// continues, and may replace or extend the next request's state.
	PrepareNextTurn PrepareNextTurn
	// PreparePrompt lets the owning Session prepare new prompt messages before
	// their lifecycle events and persistence. It does not run for continuation
	// or queued steering/follow-up messages.
	PreparePrompt func(context.Context, []AgentMessage) ([]AgentMessage, error)
	// ToolExecution is the batch strategy for assistant messages with several
	// tool calls (default parallel). A tool whose ExecutionMode is sequential
	// forces its batch to run sequentially. Mirrors upstream Agent.toolExecution.
	ToolExecution ToolExecutionMode

	// EventCh receives agent events. If nil, events are discarded. Every
	// event is delivered, including after the run's context is cancelled, as
	// upstream awaits every listener; emit blocks until the event is received
	// or EventDone is closed.
	EventCh chan<- AgentEvent
	// EventDone, when closed, reports that EventCh has no receiver anymore.
	// Events emitted after that are dropped instead of blocking the agent.
	EventDone <-chan struct{}

	// OnEvent, if set, runs synchronously for every event before the event is
	// persisted or delivered, as upstream Agent awaits its listeners before it
	// continues. It may replace a message_end message in place (mutating the
	// pointed-to message or custom map), and the replacement is what agent
	// state, persistence and EventCh observe.
	OnEvent func(AgentEvent)
	// AfterEvent runs after persistence and channel publication, before the loop polls queued work. An error fails the same run.
	AfterEvent func(AgentEvent) error
	// StreamFn sends each provider request. Nil uses DefaultStreamFn or the model's native provider.
	// Mirrors upstream AgentOptions.streamFn.
	StreamFn StreamFn
	// DefaultStreamFn supplies the host's stock request path without making it a caller override. Nil uses the configured host default.
	DefaultStreamFn StreamFn

	// OnMessagePersist, if set, is invoked exactly once for each NEW message
	// the agent produces during a turn: the user prompt, steering / follow-up
	// user messages, assistant messages (including aborted/error ones), and
	// tool-result messages: in production order. It is driven by message_end in
	// emit() and is NOT called for replayed/resumed context (the runLoop replay
	// emits message_end only for this run's new messages, never loaded history).
	// Mirrors upstream's single incremental persistence site (agent-session.ts
	// message_end handler) so a turn interrupted mid-flight is durably recorded
	// instead of lost on the next resume. An error fails the run the way a
	// throwing message_end listener does upstream: the loop stops and the run
	// ends with an error assistant message.
	OnMessagePersist func(AgentMessage) error
	// OnProviderStreamEvent is forwarded to every provider request. Mirrors upstream AgentOptions.onProviderStreamEvent (packages/agent/src/agent.ts:122).
	OnProviderStreamEvent func(ctx context.Context, data any, model *ai.Model) error
}

AgentOptions configures the agent loop.

type AgentRequestUpdate

type AgentRequestUpdate struct {
	Context       []AgentMessage
	Model         *ai.Model
	ThinkingLevel *ai.ThinkingLevel
}

AgentRequestUpdate replaces runtime state for the provider request being prepared and later requests in the run. Mirrors upstream AgentRequestUpdate (AgentLoopTurnUpdate without messages).

type AgentSettledEvent

type AgentSettledEvent struct{}

AgentSettledEvent fires once an agent run has fully settled: no automatic retry, compaction, or queued continuation will follow. Upstream emits it from the _runAgentPrompt finally (agent-session.ts:1073) to both the extension runner and the session event stream.

type AgentStartEvent

type AgentStartEvent struct{}

type AgentTool

type AgentTool interface {
	// Name is the tool's name as known to the LLM.
	Name() string
	// Label returns a human-readable display name for the tool header.
	// If empty, Name() is used for display. Mirrors upstream AgentTool.label.
	Label() string
	// Schema returns the JSON Schema for the tool's parameters.
	Schema() ai.ToolSchema
	// Execute runs the tool.
	Execute(ctx context.Context, toolCallID string, params json.RawMessage,
		onUpdate ToolUpdateCallback) (AgentToolResult, error)
	// ExecutionMode returns the tool's parallelism preference.
	ExecutionMode() ToolExecutionMode
}

AgentTool is the interface that all tools must implement.

type AgentToolCall

type AgentToolCall = ai.ToolCall

AgentToolCall is a tool call block of an assistant message. Mirrors upstream AgentToolCall.

type AgentToolCallOutcome

type AgentToolCallOutcome struct {
	ToolCall AgentToolCall
	Result   AgentToolResult
	IsError  bool
}

AgentToolCallOutcome is the final outcome of a tool call after the hooks ran. Mirrors upstream AgentToolCallOutcome (packages/agent/src/types.ts).

func RunToolCall

func RunToolCall(ctx context.Context, call AgentToolCall, options RunToolCallOptions) (AgentToolCallOutcome, error)

RunToolCall runs one tool call through the same steps as a model-issued call: argument preparation, schema validation, the before hooks, execution, and the after hooks. It emits no events and adds no messages. Tools that call other tools use it so the hooks (for example permission checks) apply to those calls too. Mirrors upstream runToolCall (agent-loop.ts:800-820).

It never fails for tool failures: unknown tools, validation errors, blocked calls, and panics come back as an outcome with IsError set. It returns the first error of options.OnUpdate after the tool returned, without running the after hooks, as upstream's promise rejects.

type AgentToolResult

type AgentToolResult struct {
	// MemberOrder names the members of the result object in the order its tool wrote them (content, details,
	// isError, structuredContent, usage, terminate). Upstream hands the tool's own object on, so JSON.stringify
	// writes its members in that order; a result without it (a built-in tool, a Go tool) has the declared order.
	// upstream: agent-loop.ts:778-786, 912-919
	MemberOrder []string
	Content     []ai.ToolResultMessageContent
	Details     any
	// StructuredContent is the machine-readable result of a tool that declares
	// an output schema. upstream: structuredContent?: JsonValue
	StructuredContent json.RawMessage
	// IsError is upstream's `result.isError`: the tool returned a failure instead of throwing it, so
	// details and structuredContent are kept. A thrown error, an unknown tool, invalid arguments and a
	// blocked call produce a result without it; the failure is reported by ToolExecutionEndEvent.IsError
	// and AgentToolCallOutcome.IsError. upstream: types.ts:436-441, agent-loop.ts:906-910
	IsError bool
	// Thrown marks a result that stands for an error upstream's tool throws. The agent reports the call as failed, drops
	// the result's IsError and Thrown, and keeps the rest, which is createErrorToolResult's content and empty details
	// (agent-loop.ts:906-910). A tool that returns a Go error has the same effect.
	Thrown bool
	// StructuredContentAppended records that afterToolCall gave a result that had no structuredContent one. Upstream's
	// `{...result}` spread keeps the tool's key order and assigning an existing key keeps its position, so only a key the
	// hook adds follows every key the tool set (agent-loop.ts:877-889). It only orders the serialized result.
	StructuredContentAppended bool
	// Usage is the usage of the tool execution itself, if available. It is
	// not used for main LLM context accounting. upstream: usage?: Usage
	Usage *ai.Usage
	// Terminate hints that the agent should stop after the current tool
	// batch. Early termination happens only when every finalized result in
	// the batch sets it. upstream: terminate?: boolean
	Terminate bool
	// Preview is an optional short summary shown when the tool result is
	// collapsed in the TUI. When non-empty, overrides the default
	// tail-preview behavior. Extensions can set this to provide a
	// custom collapsed view (e.g. a table header, a line count, or a
	// brief summary) without implementing a full BodyRenderer.
	// pig extension: no upstream equivalent (Pi extensions use in-process
	// renderResult callbacks; subprocess extensions need a serializable
	// alternative).
	Preview string
}

AgentToolResult carries the ordered text/image blocks returned by a tool. Ports packages/agent/src/types.ts.

func (AgentToolResult) DetailsNull

func (r AgentToolResult) DetailsNull() bool

DetailsNull reports a result that holds details: null: the tool wrote the details member with no value. A Go tool that leaves Details nil and records no MemberOrder holds no details.

func (AgentToolResult) Images

func (r AgentToolResult) Images() []ai.ImageContent

Images selects image blocks for displays with a separate image area.

func (AgentToolResult) MemberNames

func (r AgentToolResult) MemberNames() []string

MemberNames lists the members of the result object this result holds (content, details, structuredContent, isError, usage, terminate), in the order upstream's object keeps them. Without a recorded MemberOrder that is the order the built-in tools build theirs in, with a structuredContent a hook added last. With one it is the tool's own order, and a member the tool did not set, which a hook added, follows the tool's in the order of the hook's spread, `{...result, content, details, usage, terminate}`, then the structuredContent assigned after it (agent-loop.ts:877-889). Content is always listed. A member MemberOrder names is held even when its value is a zero value (details or usage null, isError or terminate false): the tool wrote it, so upstream's object has it and JSON.stringify writes it. upstream: agent-loop.ts:778-786, 877-889, 912-919; coding-agent/src/core/tools/bash.ts:403-409

func (AgentToolResult) Text

func (r AgentToolResult) Text() string

Text joins text blocks for text-only displays; Content retains their boundaries and image positions.

type AgentTurnAction

type AgentTurnAction string

AgentTurnAction is the action of an AgentTurnDecision.

const (
	// AgentTurnContinue ensures one more provider request after the turn.
	AgentTurnContinue AgentTurnAction = "continue"
	// AgentTurnEnd ends the run without polling queues or preparing another request.
	AgentTurnEnd AgentTurnAction = "end"
)

type AgentTurnContext

type AgentTurnContext struct {
	// Message is the assistant message that completed the turn.
	Message *AssistantMessage
	// ToolResults are the tool result messages emitted for the completed turn.
	ToolResults []ToolResultMessage
	// Context is the loop context after the turn's assistant message and tool
	// results have been appended.
	Context []AgentMessage
	// NewMessages are the messages this run returns if it exits now. Prompt
	// runs include the initial prompt messages; continuation runs do not
	// include pre-existing context messages.
	NewMessages []AgentMessage
}

AgentTurnContext is passed to completed-turn callbacks (FinishTurn and PrepareNextTurn). Mirrors upstream packages/agent/src/types.ts AgentTurnContext.

type AgentTurnDecision

type AgentTurnDecision struct {
	Action AgentTurnAction
}

AgentTurnDecision is returned by FinishTurn. A nil decision preserves normal scheduling. Mirrors upstream AgentTurnDecision.

type ArgumentPreparer

type ArgumentPreparer interface {
	PrepareArguments(raw json.RawMessage) (json.RawMessage, error)
}

ArgumentPreparer is an optional interface tools may implement to normalize / coerce their raw input BEFORE schema validation runs. Mirrors upstream's per-tool `prepareArguments` hook (.upstream/current/packages/coding-agent/src/core/tools/edit.ts:308, `parameters: editSchema, ... prepareArguments: prepareEditArguments`).

Use cases:

  • edit tool: rewrites the legacy `{path, oldText, newText}` flat input shape into `{path, edits:[{oldText, newText}]}` so the strict editSchema accepts it (LLMs like Opus 4.6 / GLM-5.1 still emit the flat shape).
  • any future tool that accepts multiple wire shapes for the same semantic input.

Without this hook, schema validation in dispatch() rejects valid legacy inputs before tool.Execute() ever runs, breaking parity with upstream which normalizes first and validates after.

type AssistantMessage

type AssistantMessage struct {
	Role                  string                     `json:"role"` // "assistant"
	Content               []ai.AssistantContentBlock `json:"content"`
	Thinking              string                     `json:"-"`
	Timestamp             int64                      `json:"timestamp"`
	Usage                 *ai.Usage                  `json:"usage,omitempty"`
	API                   ai.API                     `json:"api,omitempty"`
	Provider              string                     `json:"provider,omitempty"`
	ModelID               string                     `json:"model,omitempty"`
	ResponseModel         string                     `json:"responseModel,omitempty"`
	ResponseID            string                     `json:"responseId,omitempty"`
	ProviderThinkingLevel string                     `json:"providerThinkingLevel,omitempty"`
	// ThinkingLevel is the level the agent loop requested for this response; see ai.AssistantMessage.ThinkingLevel.
	ThinkingLevel ai.ModelThinkingLevel           `json:"thinkingLevel,omitempty"`
	Diagnostics   []ai.AssistantMessageDiagnostic `json:"diagnostics,omitempty"`
	Deferred      *ai.DeferredHandle              `json:"deferred,omitempty"`
	// StopReason records why the assistant turn ended. Mirrors upstream
	// AssistantMessage.stopReason in packages/ai/src/types.ts.
	// Values: "stop" (normal), "toolUse" (tool calls present),
	//         "aborted" (context cancelled), "error" (EventError received).
	StopReason ai.StopReason `json:"stopReason,omitempty"`
	// ErrorMessage holds the error string when StopReason == "error".
	ErrorMessage  string `json:"errorMessage,omitempty"`
	RawStopReason string `json:"rawStopReason,omitempty"`
	EndTurn       *bool  `json:"endTurn,omitempty"`
	// ThinkingSignature is a legacy runtime convenience; durable signatures
	// live on ThinkingContent blocks in the upstream wire shape.
	ThinkingSignature string `json:"-"`
	// contains filtered or unexported fields
}

AssistantMessage is a streamed response from the LLM. Streaming events retain a shallow provider view; Observe and JSON serialization read its current nested state without mutating these exported fields.

func (*AssistantMessage) LLMMessage

func (m *AssistantMessage) LLMMessage() ai.AssistantMessage

LLMMessage returns the provider-facing form of an assistant message. Plain messages share content and diagnostics slices with m; retained streaming views produce an owned observation.

func (AssistantMessage) MarshalJSON

func (m AssistantMessage) MarshalJSON() ([]byte, error)

MarshalJSON observes retained streaming messages at the serialization boundary.

func (*AssistantMessage) Observe

func (m *AssistantMessage) Observe() *AssistantMessage

Observe returns an independently owned observation of the message. A streaming shallow view keeps its copied top-level values while observing its retained nested objects.

func (*AssistantMessage) ObserveUsage

func (m *AssistantMessage) ObserveUsage() *ai.Usage

ObserveUsage returns an owned usage snapshot without traversing message content or metadata. Streaming views observe their retained usage reference; ordinary absent usage stays absent.

type AutoRetryEndEvent

type AutoRetryEndEvent struct {
	Success    bool
	Attempt    int
	FinalError string // set when Success==false
}

AutoRetryEndEvent fires when an auto-retry cycle completes (success or exhausted). Mirrors upstream auto_retry_end (agent-session.ts:131).

type AutoRetryStartEvent

type AutoRetryStartEvent struct {
	Attempt      int
	MaxAttempts  int
	DelayMs      int
	ErrorMessage string
}

AutoRetryStartEvent fires when pig is about to retry a failed request due to a transient error (overload, rate limit, server error). Mirrors upstream auto_retry_start (agent-session.ts:130).

type BashExecutionUpdateEvent

type BashExecutionUpdateEvent struct {
	ID    *string
	Delta string
}

BashExecutionUpdateEvent reports one command-output delta. A nil ID omits the correlation identifier; a non-nil empty ID remains explicit.

type BeforeToolCallHook

type BeforeToolCallHook func(ctx context.Context, toolCallID, toolName string, args json.RawMessage) ToolCallHookResult

BeforeToolCallHook is called before a tool executes. Return ToolCallHookResult{Block: true} to prevent execution.

type CompactionEndEvent

type CompactionEndEvent struct {
	Reason               string
	Aborted              bool
	WillRetry            bool
	Summary              string
	FirstKeptEntryID     string
	TokensBefore         int
	EstimatedTokensAfter int
	Usage                *ai.Usage
	Details              any
	ErrorMessage         string
}

CompactionEndEvent fires when compaction completes, aborts, or errors. Mirrors upstream compaction_end event (agent-session.ts).

type CompactionStartEvent

type CompactionStartEvent struct {
	Reason string // "manual" | "threshold" | "overflow"
}

CompactionStartEvent fires when auto-compaction or /compact begins. Mirrors upstream compaction_start event (agent-session.ts).

type ContextUsageEstimate

type ContextUsageEstimate struct {
	Tokens         int
	UsageTokens    int
	TrailingTokens int
	LastUsageIndex int // -1 if not found
}

ContextUsageEstimate is the result of EstimateContextTokens. Mirrors upstream ContextUsageEstimate (compaction.ts:177).

func EstimateContextTokens

func EstimateContextTokens(messages []AgentMessage) ContextUsageEstimate

EstimateContextTokens estimates context token usage from a message slice. Uses the last assistant usage that is not aborted, errored, or all-zero (upstream getAssistantUsage), plus a chars/4 estimate of the messages trailing it; falls back to a full estimate when no usage is present. Mirrors upstream estimateContextTokens (compaction.ts:186).

type EntryAppendedEvent

type EntryAppendedEvent struct {
	Entry json.RawMessage
}

EntryAppendedEvent reports a Session entry appended outside the agent loop, such as cache-warming usage. Entry is the persisted entry's JSON. Mirrors upstream AgentSessionEvent entry_appended (agent-session.ts:178).

type FinishTurn

type FinishTurn func(ctx context.Context, turn AgentTurnContext) (*AgentTurnDecision, error)

FinishTurn is called after a completed assistant turn and all of its tool-result messages, but before TurnEndEvent. On a normal turn, AgentTurnContinue ensures one next provider request: tool-result, steering, or follow-up scheduling satisfies that request without adding another; otherwise the loop continues once with the current context. Error and aborted responses remain hard exits. ctx is the run's context (upstream's abort signal). A rejected callback enters the owning failed-turn lifecycle. Mirrors upstream FinishTurn.

type MessageEndEvent

type MessageEndEvent struct {
	Message AgentMessage
	// contains filtered or unexported fields
}

type MessageStartEvent

type MessageStartEvent struct {
	Message AgentMessage
	// contains filtered or unexported fields
}

type MessageUpdateEvent

type MessageUpdateEvent struct {
	Message               AgentMessage
	AssistantMessageEvent ai.AssistantMessageEvent
	// contains filtered or unexported fields
}

type MutationTicket

type MutationTicket struct {
	Wait    func()
	Release func()
}

MutationTicket is a reserved position in a shared per-path mutation queue (internal/codingagent/tools.FileMutationQueue), obtained synchronously, in tool-call source order, before the parallel dispatcher spawns this call's goroutine. Wait blocks until it is this ticket's turn; Release must run exactly once, after the tool's mutation completes, regardless of outcome.

func MutationTicketFromContext

func MutationTicketFromContext(ctx context.Context) (*MutationTicket, bool)

MutationTicketFromContext returns the ticket the dispatcher reserved for this call, if any.

type OutputSchemaProvider

type OutputSchemaProvider interface {
	OutputSchema() json.RawMessage
}

OutputSchemaProvider is implemented by a tool that declares the JSON Schema of the structured content in its successful results. Mirrors upstream AgentTool.outputSchema.

type PendingMessageQueue

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

PendingMessageQueue is a thread-safe FIFO of agent messages with a configurable drain policy. Used for steering and follow-up queues.

Mirrors upstream packages/agent/src/agent.ts:113 (class PendingMessageQueue).

func NewPendingMessageQueue

func NewPendingMessageQueue(mode QueueMode) *PendingMessageQueue

NewPendingMessageQueue creates a queue with the given drain mode.

func (*PendingMessageQueue) Clear

func (q *PendingMessageQueue) Clear() []AgentMessage

Clear removes and returns all queued messages regardless of mode.

func (*PendingMessageQueue) Drain

func (q *PendingMessageQueue) Drain() []AgentMessage

Drain removes and returns pending messages according to the queue's mode. "all" drains every message; "one-at-a-time" drains the oldest single message. Returns nil when the queue is empty. Mirrors upstream PendingMessageQueue.drain.

func (*PendingMessageQueue) Enqueue

func (q *PendingMessageQueue) Enqueue(msg AgentMessage)

Enqueue appends a message to the queue.

func (*PendingMessageQueue) HasItems

func (q *PendingMessageQueue) HasItems() bool

HasItems reports whether the queue has any pending messages.

func (*PendingMessageQueue) Len

func (q *PendingMessageQueue) Len() int

Len returns the number of queued messages.

func (*PendingMessageQueue) Messages

func (q *PendingMessageQueue) Messages() []AgentMessage

Messages returns copies of all queued messages without draining, whatever the mode. Used for UI display of pending steering/follow-up messages.

func (*PendingMessageQueue) Mode

func (q *PendingMessageQueue) Mode() QueueMode

Mode returns the current drain mode.

func (*PendingMessageQueue) Peek

func (q *PendingMessageQueue) Peek() []AgentMessage

Peek returns the messages the next Drain would remove, without removing them: every message in "all" mode, the oldest one in "one-at-a-time" mode. Mirrors upstream PendingMessageQueue.peek.

func (*PendingMessageQueue) SetMode

func (q *PendingMessageQueue) SetMode(mode QueueMode)

SetMode changes the drain mode.

type PrepareNextTurn

type PrepareNextTurn func(ctx context.Context, turn PrepareNextTurnContext) (*AgentLoopTurnUpdate, error)

PrepareNextTurn is called after TurnEndEvent when the loop continues, immediately before the next turn starts. ctx is the run's context. Mirrors upstream prepareNextTurnWithContext. An error preserves Promise rejection and prevents the next provider request.

type PrepareNextTurnContext

type PrepareNextTurnContext = AgentTurnContext

PrepareNextTurnContext mirrors upstream's PrepareNextTurnContext, which extends AgentTurnContext without adding fields.

type PrepareRequest

type PrepareRequest func(ctx context.Context, request PrepareRequestContext) (*AgentRequestUpdate, error)

PrepareRequest is called immediately before every provider request, including the first. Pending messages have already been appended and emitted when it runs, and it does not poll queues. A rejected callback enters the owning failed-turn lifecycle. Mirrors upstream PrepareRequest.

type PrepareRequestContext

type PrepareRequestContext struct {
	Context       []AgentMessage
	Model         *ai.Model
	ThinkingLevel ai.ThinkingLevel
}

PrepareRequestContext is the runtime state available immediately before a provider request. Mirrors upstream PrepareRequestContext.

type PromptRun

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

PromptRun owns a claimed prompt and its cancellation context. Start dispatches agent_start; Run drains the remaining events and releases the claim. The owner must call Run exactly once, even if Start fails or the context is cancelled.

func (*PromptRun) Run

func (r *PromptRun) Run() ([]AgentMessage, error)

Run drains the claimed prompt and releases its active-run state, including when Start failed. The owner calls Run once; it calls Start when needed.

func (*PromptRun) Start

func (r *PromptRun) Start() error

Start prepares the prompt and dispatches agent_start without requesting a Provider response or committing unfinished prompt messages. Repeated calls return the first result.

type ProxyAssistantMessageEvent

type ProxyAssistantMessageEvent struct {
	Type                  string        `json:"type"`
	ContentIndex          int           `json:"contentIndex"`
	Delta                 string        `json:"delta"`
	ContentSignature      *string       `json:"contentSignature,omitempty"`
	ID                    string        `json:"id"`
	ToolName              string        `json:"toolName"`
	ToolCall              *ai.ToolCall  `json:"toolCall,omitempty"`
	Reason                ai.StopReason `json:"reason"`
	Usage                 ai.Usage      `json:"usage"`
	ErrorMessage          *string       `json:"errorMessage,omitempty"`
	ProviderThinkingLevel *string       `json:"providerThinkingLevel,omitempty"`
}

ProxyAssistantMessageEvent is one compact event returned by a proxy server. Partial assistant messages are omitted and reconstructed by StreamProxy.

func (ProxyAssistantMessageEvent) MarshalJSON

func (event ProxyAssistantMessageEvent) MarshalJSON() ([]byte, error)

MarshalJSON emits only the fields carried by the selected proxy event variant.

type ProxyStreamOptions

type ProxyStreamOptions struct {
	Temperature     *float64
	SamplingParams  map[string]any
	MaxTokens       *int
	Reasoning       ai.ThinkingLevel
	CacheRetention  ai.CacheRetention
	SessionID       string
	Headers         ai.ProviderHeaders
	Metadata        map[string]any
	Transport       ai.Transport
	ThinkingBudgets *ProxyThinkingBudgets
	MaxRetryDelayMs *int
	AuthToken       string
	ProxyURL        string
}

ProxyStreamOptions configures StreamProxy. Context cancellation is the Go equivalent of upstream's optional AbortSignal. Non-nil maps retain empty objects. ThinkingBudgets preserves each budget's presence, including explicit zeros.

type ProxyThinkingBudgets

type ProxyThinkingBudgets struct {
	Minimal *int `json:"minimal,omitempty"`
	Low     *int `json:"low,omitempty"`
	Medium  *int `json:"medium,omitempty"`
	High    *int `json:"high,omitempty"`
}

ProxyThinkingBudgets carries optional per-level token budgets to the proxy server. Nil fields stay omitted; non-nil fields send their value, including zero. A non-nil empty struct sends an empty object without overriding server defaults.

type QueueMode

type QueueMode string

QueueMode controls how PendingMessageQueue.Drain behaves. Mirrors upstream packages/agent/src/agent.ts:55 (type QueueMode).

const (
	// QueueModeAll drains all queued messages at once.
	QueueModeAll QueueMode = "all"
	// QueueModeOneAtATime drains one message per call.
	QueueModeOneAtATime QueueMode = "one-at-a-time"
)

type QueueOrderable

type QueueOrderable interface {
	ReserveMutationOrder(params json.RawMessage) (*MutationTicket, bool)
}

QueueOrderable is an optional interface a tool may implement so the parallel dispatcher reserves its place in a shared mutation queue in tool-call order, before any goroutine starts, rather than leaving admission order to goroutine-arrival order.

Upstream dispatches parallel tool calls via Promise.all(calls.map(tool => executeToolCall(tool))): Array.prototype.map runs its callback synchronously per element, in array order, so every call's synchronous prefix — including withFileMutationQueue's registration step (file-mutation-queue.ts:32-45), which chains the new call onto whatever is currently in the per-path map — completes for call N before call N+1's synchronous prefix begins, even though the calls then run concurrently (agent-loop.ts executeToolCallsParallel). Go's per-call goroutines (tool_execution.go executeToolCallsParallel) give no equivalent guarantee: nothing stops goroutine N+1 from reaching the queue before goroutine N. ReserveMutationOrder lets the dispatcher perform that registration step itself, synchronously, in its own call-ordered loop, closing that gap.

ReserveMutationOrder returns ok=false when the call will not reach the queue at all (a tool implementing this must return exactly the same ok/no-ok decision its Execute would reach, or a reservation could be left registered but never released). The returned params are the same (possibly ArgumentPreparer-normalized) arguments Execute receives.

type QueueUpdateEvent

type QueueUpdateEvent struct {
	Steering []string
	FollowUp []string
}

QueueUpdateEvent reports the current pending steering and follow-up text. It mirrors the coding-agent session queue_update event.

type Recorder

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

Recorder captures per-turn and per-tool timing data for one agent session. Mirrors the data side of upstream pi's `core/timings.ts` startup profiler, but extended to cover the agent loop's lifecycle (turn start/end + tool execution latencies) so the status line, /cost, and --diagnose can all read from a single source.

All methods are safe for concurrent use. Tool execution may run in parallel goroutines, so RecordTool can be called from multiple goroutines at once.

func NewRecorder

func NewRecorder() *Recorder

NewRecorder creates a Recorder. SessionStart is set to time.Now().

func (*Recorder) CurrentTurnElapsed

func (r *Recorder) CurrentTurnElapsed() time.Duration

CurrentTurnElapsed returns the elapsed time of the in-flight turn, or 0 if no turn is in flight.

func (*Recorder) Elapsed

func (r *Recorder) Elapsed() time.Duration

Elapsed returns wall-clock time since session start.

func (*Recorder) EndTurn

func (r *Recorder) EndTurn() time.Duration

EndTurn appends the elapsed turn duration and clears turn state. Returns the recorded duration. If StartTurn was not called, returns 0.

func (*Recorder) LastTurnDuration

func (r *Recorder) LastTurnDuration() time.Duration

LastTurnDuration returns the duration of the most recently completed turn, or 0 if no turn has completed yet. This is a static snapshot (not a live counter), suitable for display in the status line when the agent is idle.

func (*Recorder) RecordTool

func (r *Recorder) RecordTool(name string, d time.Duration)

RecordTool aggregates a tool execution latency.

func (*Recorder) Snapshot

func (r *Recorder) Snapshot() TimingSnapshot

Snapshot returns an immutable copy of recorder state.

func (*Recorder) StartTurn

func (r *Recorder) StartTurn()

StartTurn marks the beginning of an LLM turn.

type RunToolCallOptions

type RunToolCallOptions struct {
	ToolCallHooks
	// Tools the call resolves against.
	Tools []AgentTool
	// OnUpdate receives the tool's progress updates.
	OnUpdate ToolUpdateSink
}

RunToolCallOptions configures RunToolCall. Mirrors upstream RunToolCallOptions (.upstream/v0.99.1/packages/agent/src/agent-loop.ts:789-798). Upstream's assistantMessage and context options only feed the hooks' context argument, which Go hook signatures do not carry, so they are not modeled; the context argument of RunToolCall is upstream's signal.

type SessionInfoChangedEvent

type SessionInfoChangedEvent struct {
	Name string
}

SessionInfoChangedEvent reports the effective Session name; an empty name clears it.

type StreamFn

type StreamFn func(ctx context.Context, model *ai.Model, transcript ai.TranscriptContext, options ai.StreamOptions) (*ai.AssistantMessageEventStream, error)

StreamFn starts one provider request for an already normalized transcript. Mirrors upstream StreamFn.

func GetDefaultStreamFn

func GetDefaultStreamFn() (StreamFn, error)

GetDefaultStreamFn returns the configured host stream function, or an error when no host has installed one. Models with native provider runtimes can still stream directly when an agent has no explicit or configured stream.

type SummarizationRetryAttemptStartEvent

type SummarizationRetryAttemptStartEvent struct {
	Source string
	Reason string
}

SummarizationRetryAttemptStartEvent fires after the backoff sleep, right before the retried summarization call. Source is "compaction" or "branchSummary"; Reason ("manual"|"threshold"|"overflow") applies to compaction and lets the TUI recreate the right status indicator. Mirrors upstream summarization_retry_attempt_start (agent-session.ts:173).

type SummarizationRetryFinishedEvent

type SummarizationRetryFinishedEvent struct{}

SummarizationRetryFinishedEvent fires once when a retried summarization loop ends. Mirrors upstream summarization_retry_finished (agent-session.ts:179).

type SummarizationRetryScheduledEvent

type SummarizationRetryScheduledEvent struct {
	Attempt      int
	MaxAttempts  int
	DelayMs      int
	ErrorMessage string
}

SummarizationRetryScheduledEvent fires before the backoff sleep of each retry of a compaction or branch-summary summarization call. Mirrors upstream summarization_retry_scheduled (agent-session.ts:167).

type ThinkingLevelChangedEvent

type ThinkingLevelChangedEvent struct {
	Level ai.ThinkingLevel
}

ThinkingLevelChangedEvent reports an effective reasoning-level change.

type TimingEvent deprecated

type TimingEvent = timingEvent

TimingEvent retains the former diagnostic event shape for source compatibility. The agent no longer emits timing events, matching Pi's event stream.

Deprecated: Read Agent.Timings or observe TurnEndEvent and ToolExecutionEndEvent.

type TimingSnapshot

type TimingSnapshot struct {
	Total       time.Duration
	Turns       []time.Duration
	ToolTotals  map[string]time.Duration
	ToolCounts  map[string]int
	CurrentTurn time.Duration // 0 if idle

	// Token usage. Populated externally (not by Recorder itself) -
	// the caller fills these before passing to formatCostSummary.
	TotalIn    int
	TotalOut   int
	CacheRead  int
	CacheWrite int
	// Cost is the accumulated per-turn cost (USD), tier/cache-aware.
	Cost float64
}

TimingSnapshot is an immutable copy of recorder state.

type ToolCallHookResult

type ToolCallHookResult struct {
	Block     bool
	Reason    string
	Terminate bool
	// Mutated args (if the hook wants to rewrite them). They execute without
	// revalidation, as upstream executes a hook's mutated args.
	Args json.RawMessage
}

ToolCallHookResult controls whether tool execution proceeds.

Mirrors upstream BeforeToolCallResult. Terminate on a blocked call joins the batch early-termination rule: the agent stops after the batch only when every finalized result in it terminates.

type ToolCallHooks

type ToolCallHooks struct {
	BeforeToolCall    []BeforeToolCallHook
	AfterToolCall     []AfterToolCallHook
	PrepareToolResult func(context.Context, AgentToolResult) AgentToolResult
}

ToolCallHooks are the hooks a tool call runs through. Mirrors upstream ToolCallHooks.

type ToolEnvironment

type ToolEnvironment struct {
	SessionID      string
	SessionFile    string
	Provider       string
	Model          string
	ThinkingLevel  string
	InputLimits    *ai.ModelInputLimits
	SupportsImages *bool
}

ToolEnvironment is the session state a tool may expose to the processes it starts. Upstream's bash tool exports it as PI_SESSION_ID, PI_SESSION_FILE, PI_PROVIDER, PI_MODEL, and PI_REASONING_LEVEL. Empty fields are not exported. Image metadata supplies the selected model profile to read tools.

func ToolEnvironmentFrom

func ToolEnvironmentFrom(ctx context.Context) (ToolEnvironment, bool)

ToolEnvironmentFrom returns the environment the agent attached to a tool call.

type ToolExecutionEndEvent

type ToolExecutionEndEvent struct {
	ToolCallID string
	ToolName   string
	Result     AgentToolResult
	// IsError is upstream's `event.isError`: the call failed, whether the tool threw, returned `isError`, or an
	// afterToolCall hook said so. It is not Result.IsError. upstream: agent-loop.ts:912-919
	IsError  bool
	Duration time.Duration
	// ParentToolCallID is set on the events of a call another tool made through `ctx.executeTool()`. upstream: agent-session.ts:182-189 (WithParentToolCallId)
	ParentToolCallID string
}

type ToolExecutionMode

type ToolExecutionMode string

ToolExecutionMode controls parallelism.

const (
	ToolModeSequential ToolExecutionMode = "sequential"
	ToolModeParallel   ToolExecutionMode = "parallel"
)

type ToolExecutionStartEvent

type ToolExecutionStartEvent struct {
	ToolCallID string
	ToolName   string
	ToolLabel  string // human-readable display name (empty = use ToolName)
	Args       json.RawMessage
	// ParentToolCallID is set on the events of a call another tool made through `ctx.executeTool()`. upstream: agent-session.ts:182-189 (WithParentToolCallId)
	ParentToolCallID string
}

type ToolExecutionUpdateEvent

type ToolExecutionUpdateEvent struct {
	ToolCallID string
	ToolName   string
	// PartialResult is the tool's streaming progress. Subsequent updates carry the full accumulated result so far (upstream's onUpdate contract: every update is a complete-so-far snapshot, not a delta). Tool components in the TUI replace their visible body with each update, giving a live tail. upstream: agent-loop.ts:778-786
	PartialResult AgentToolResult
	// Args is the original tool call arguments (same as ToolExecutionStartEvent.Args).
	// Passed through for extensions that need to correlate updates with inputs.
	Args json.RawMessage
	// ParentToolCallID is set on the events of a call another tool made through `ctx.executeTool()`. upstream: agent-session.ts:182-189 (WithParentToolCallId)
	ParentToolCallID string
}

type ToolResultMessage

type ToolResultMessage struct {
	Role       string                        `json:"role"` // "toolResult"
	ToolCallID string                        `json:"toolCallId"`
	ToolName   string                        `json:"toolName"`
	Content    []ai.ToolResultMessageContent `json:"content"`
	// Details is the tool's details value. Nil is no details unless DetailsNull is set.
	Details any `json:"details,omitempty"`
	// DetailsNull marks a nil Details as an explicit JSON null: the tool wrote details: null, and JSON writes
	// `"details":null`. Without it a nil Details is absent and JSON omits the key. It has no effect when Details is set.
	DetailsNull bool      `json:"-"`
	Usage       *ai.Usage `json:"usage,omitempty"`
	// NestedCalls are the calls this tool made to other tools. Kept for the session record; not sent to the model.
	// upstream: .upstream/v0.99.1/packages/ai/src/types.ts:604
	NestedCalls *ai.NestedToolCalls `json:"nestedCalls,omitempty"`
	IsError     bool                `json:"isError"`
	Timestamp   int64               `json:"timestamp"`
}

ToolResultMessage carries one tool execution result back to the LLM. Mirrors upstream pi-ai ToolResultMessage.

func (ToolResultMessage) Images

func (m ToolResultMessage) Images() []ai.ImageContent

func (ToolResultMessage) Result

Result is the result a display renders for this message: its content, details and error flag. A message with details: null gives a result that holds details: null.

func (ToolResultMessage) Text

func (m ToolResultMessage) Text() string

type ToolUpdateCallback

type ToolUpdateCallback func(partial AgentToolResult)

ToolUpdateCallback is called when a tool emits a streaming progress update. The partial result is the complete-so-far AgentToolResult, which the tool_execution_update event carries verbatim as `partialResult`. upstream: packages/agent/src/types.ts AgentToolUpdateCallback, agent-loop.ts:778-786

type ToolUpdateSink

type ToolUpdateSink = func(partial AgentToolResult) error

ToolUpdateSink receives the progress updates of a tool call that RunToolCall runs. A non-nil error is upstream's rejected promise: the update is not delivered further, later updates still reach the sink, and the call fails with the first error once the tool returns.

upstream: agent-loop.ts:685 (ToolUpdateSink), 820-849 (executePreparedToolCall)

type TurnEndEvent

type TurnEndEvent struct {
	TurnIndex          int
	Message            AgentMessage
	ToolResults        []ToolResultMessage
	MessageEntryID     string
	ToolResultEntryIDs []string
}

type TurnStartEvent

type TurnStartEvent struct {
	TurnIndex int
	Timestamp time.Time
}

type UserMessage

type UserMessage struct {
	Role      string         `json:"role"` // "user"
	Content   ai.UserContent `json:"content"`
	Timestamp int64          `json:"timestamp"`
}

UserMessage is a user turn whose content retains the upstream string-or-block-array shape.

func (*UserMessage) LLMMessage

func (m *UserMessage) LLMMessage() ai.UserMessage

LLMMessage retains the user content variant and normalizes an omitted Go content value to an empty array.

Jump to

Keyboard shortcuts

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