agent

package module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

README

go-agent

go-agent is an idiomatic Go port of @openrouter/agent: a small orchestration layer on top of the official OpenRouter Go SDK for tool execution, streaming response consumption, multi-turn state, approval gates, tool context, stop conditions, and Claude/Chat format compatibility.

This package is a port. @openrouter/agent (TypeScript) is the reference spec; this repo is kept in sync automatically. See PORTING.md.

Install

go get github.com/OpenRouterTeam/go-agent

The package depends on github.com/OpenRouterTeam/go-sdk and calls the Responses API through client.Beta.Responses.Send; it does not reimplement OpenRouter HTTP, auth, retry, or generated model types.

Quick Start

package main

import (
    "context"
    "fmt"

    agent "github.com/OpenRouterTeam/go-agent"
)

type WeatherInput struct {
    Location string `json:"location" jsonschema:"required"`
}

func main() {
    ctx := context.Background()
    client := agent.NewOpenRouter(agent.OpenRouterOptions{})

    weather := agent.MustNewTool(agent.ToolConfig[WeatherInput]{
        Name: "get_weather",
        Description: "Get the current weather for a location",
        Execute: func(ctx context.Context, in WeatherInput, tc agent.ToolExecuteContext) (any, error) {
            return map[string]any{"temperature": 72, "condition": "sunny", "location": in.Location}, nil
        },
    })

    result, err := agent.CallModel(ctx, client, agent.CallModelInput{
        Model: "openai/gpt-4o-mini",
        Input: "What is the weather in San Francisco?",
        Tools: []agent.Tool{weather},
    })
    if err != nil { panic(err) }

    text, err := result.Text(ctx)
    if err != nil { panic(err) }
    fmt.Println(text)
}

Streaming

ModelResult exposes concurrent-safe consumers. Each stream replays prior data to late subscribers.

textCh, done := result.TextStream(ctx)
for delta := range textCh {
    fmt.Print(delta)
}
if err := done(); err != nil { panic(err) }

Additional consumers include Response, FullResponsesStream, ReasoningStream, ToolStream, ToolCallsStream, ToolCalls, NewMessagesStream, ItemsStream, State, and PendingToolCalls.

Tool Variants

  • Regular tools use Execute and return a final output.
  • Generator tools use Generate and emit preliminary events through a yield callback before returning the final output.
  • Manual tools set Manual: true or omit executable callbacks; they are surfaced as pending calls instead of auto-executed.
  • HITL tools use OnToolCalled; returning proceed=false pauses the agent with pending calls. OnResponseReceived rewrites fresh human-supplied tool outputs before the next Responses request.
  • Server tools wrap SDK components.ResponsesRequestToolUnion values and are passed through to OpenRouter.

Tool input schemas are generated from Go structs with invopop/jsonschema, sanitized to remove upstream-internal ~ keys, and checked before execution. Dynamic map[string]any remains available at JSON boundaries.

State, Approval, And Context

Use CreateInitialState, AppendToMessages, UpdateState, PartitionToolCalls, and StateAccessor to persist multi-turn conversations. Approval checks can be configured per tool or per call; resume by passing ApproveToolCalls or RejectToolCalls with the saved state. go-agent replays the original function_call before its function_call_output and carries previous_response_id, matching the TypeScript resume shape. ToolContextStore provides concurrency-safe per-tool and shared context with snapshot, get, set, merge, and subscribe operations. Unresolved manual (client-executed) tool calls pause the run with ConversationStatusAwaitingClientTools, distinct from ConversationStatusAwaitingHITL.

Use SerializeConversationState / DeserializeConversationState for a versioned, durable-storage-friendly encoding of ConversationState (ConversationStateVersion). A version mismatch returns *UnsupportedStateVersionError; a malformed blob returns *InvalidStateError — callers get an explicit error instead of a silently misinterpreted state.

Stop Conditions

Use StepCountIs, HasToolCall, MaxTokensUsed, MaxCost, FinishReasonIs, and IsStopConditionMet. Multiple stop conditions are ORed, matching the TypeScript package. MaxTokensUsed compares cumulative total_tokens only.

AllowFinalResponse is default-on: when a stop condition halts the loop mid-tool-call, go-agent executes the pending tool calls and issues one more request with tool_choice: "none" (tools stay in the request so the prompt-cache prefix survives) so the run ends with a natural-language answer. Omitting the option, or setting it to true, appends agent.DefaultFinalResponseDirective as a final user message; a non-empty string overrides that wording; "" forbids tool calls without appending any message; false disables the forced final turn entirely.

Lifecycle Hooks

HooksManager (agent.NewHooksManager) supports the nine built-in lifecycle hooks — PreToolUse, PostToolUse, PostToolUseFailure, UserPromptSubmit, Stop, PermissionRequest, SessionStart, SessionEnd, and PostModelCall — plus fully custom hooks via the generic agent.On/agent.Emit. Register handlers with the typed OnXxx methods (e.g. manager.OnPreToolUse(...)) and pass the manager on CallModelInput.Hooks:

hooks := agent.NewHooksManager()
hooks.OnPreToolUse(agent.HookEntry[agent.PreToolUsePayload, agent.PreToolUseResult]{
    Handler: func(payload agent.PreToolUsePayload, hctx agent.LifecycleHookContext) (agent.HookHandlerResult[agent.PreToolUseResult], error) {
        return agent.VoidResult[agent.PreToolUseResult](), nil
    },
})

result, err := agent.CallModel(ctx, client, agent.CallModelInput{
    Model: "openai/gpt-4o-mini",
    Input: "...",
    Hooks: hooks,
})

SessionStart/SessionEnd fire once per non-resuming run (SessionEnd carries aggregated token usage across that run's model calls); an approval/HITL resume call is a continuation of the same session and does not get its own pair. PostModelCall fires once per model response, tagged initial/resume/tool_round/final/retry. PreToolUse/PostToolUse/PostToolUseFailure fire around every client-tool execution path, including during a resume. PermissionRequest fires before the human-approval pause and can allow/deny/ask_user (default) a gated call. Stop fires whenever a stop condition halts the loop mid-tool-call and can force a resume and/or inject a prompt. UserPromptSubmit fires once per non-resuming run against the initial user input and can mutate or reject it. Session identity is threaded per emit, so one HooksManager is safe to share across concurrent CallModel runs. Call manager.Drain() to await fire-and-forget handler work; go-agent always drains on every exit path, including no-tools error paths.

Format Compatibility

ToClaudeMessage / FromClaudeMessages and ToChatMessage / FromChatMessages convert between OpenRouter Responses output and Claude or Chat-style messages. Unsupported content is carried with the structured original_type, data, and reason shape so it can round-trip without being silently lost.

Notes

The TypeScript package re-exports SDKHooks; the Go SDK keeps hooks in an internal package, so go-agent adapts the same intent with OpenRouterOptions middleware installed through openrouter.WithClient. This keeps request and response interception working for Responses calls without importing internal SDK packages.

Documentation

Overview

Package agent provides an idiomatic Go port of @openrouter/agent.

Index

Constants

View Source
const (
	ClaudeContentBlockTypeText               = "text"
	ClaudeContentBlockTypeToolUse            = "tool_use"
	ClaudeContentBlockTypeThinking           = "thinking"
	ClaudeContentBlockTypeUnsupportedContent = "unsupported_content"
	NonClaudeMessageRoleDeveloper            = "developer"
	NonClaudeMessageRoleSystem               = "system"
)
View Source
const ConversationStateVersion = 1

ConversationStateVersion is the current supported ConversationState serialization-contract version (upstream #66). Bump this, add a migration branch in DeserializeConversationState, and update the supported-versions list when the wire shape changes.

View Source
const DefaultAsyncTimeout int64 = 30_000

DefaultAsyncTimeout is the default number of milliseconds the manager waits for a handler's detached AsyncOutput.Work before abandoning the wait.

View Source
const DefaultFinalResponseDirective = "" /* 179-byte string literal not displayed */

DefaultFinalResponseDirective is appended as a final user message on the forced final turn (AllowFinalResponse defaulting to on, or explicitly true). Forbidding tools via ToolChoice=none alone is not enough: models that emit tool-call syntax as text (e.g. GLM) will attempt another call and leak it into content as unparsed text unless they are told this is the final turn. Pass a non-empty string to AllowFinalResponse to override the wording, or "" to append no message at all (legacy behavior).

View Source
const DefaultMaxTurns = 5

DefaultMaxTurns matches the upstream agent loop default: execute at most five tool turns unless the caller provides a smaller stop condition or larger cap.

View Source
const SharedContextKey = "shared"

Variables

View Source
var ErrHITLPause = errors.New("hitl tool paused awaiting human response")
View Source
var ErrManualTool = errors.New("manual tool is not executable")
View Source
var ErrReservedToolName = errors.New("tool name 'shared' is reserved")

Functions

func ApplyNextTurnParamsToRequest

func ApplyNextTurnParamsToRequest(request components.ResponsesRequest, params map[string]any) components.ResponsesRequest

func ExecuteNextTurnParamsFunctions

func ExecuteNextTurnParamsFunctions(ctx context.Context, funcs NextTurnParamsFunctions, ntctx NextTurnParamsContext) (map[string]any, error)

func ExtractReasoningDeltas

func ExtractReasoningDeltas(events []components.StreamEvents) []string

func ExtractTextDeltas

func ExtractTextDeltas(events []components.StreamEvents) []string

func ExtractTextFromResponse

func ExtractTextFromResponse(resp components.OpenResponsesResult) string

func ExtractToolDeltas

func ExtractToolDeltas(events []components.StreamEvents) []string

func FromChatMessages

func FromChatMessages(messages []ChatMessage) ([]components.InputsUnion1, error)

FromChatMessages converts OpenAI Chat-style messages into OpenRouter input items. It returns an error rather than degrading an encoding failure into a fallback value, matching the contract's errors-are-values rule.

func FromClaudeMessages

func FromClaudeMessages(messages []ClaudeMessage) ([]components.InputsUnion1, error)

FromClaudeMessages converts Claude-style messages back into OpenRouter input items, restoring metadata and unsupported content carried by ToClaudeMessage so a ToClaudeMessage -> FromClaudeMessages round trip is lossless. It returns an error if a tool-use input cannot be encoded.

func GenerateConversationID

func GenerateConversationID() string

func GetToolExecutionErrors

func GetToolExecutionErrors(results []ToolExecutionResult) []error

func GetUnsupportedContentSummary

func GetUnsupportedContentSummary(msg ClaudeMessage) map[string]int

GetUnsupportedContentSummary returns a count of unsupported-content carriers by original type (mirrors upstream getUnsupportedContentSummary(message)).

func HasApprovalRequiredTools

func HasApprovalRequiredTools(tools []Tool) bool

func HasAsyncFunctions

func HasAsyncFunctions(input CallModelInput) bool

func HasExecuteFunction

func HasExecuteFunction(t Tool) bool

func HasToolExecutionErrors

func HasToolExecutionErrors(results []ToolExecutionResult) bool

func HasUnsupportedContent

func HasUnsupportedContent(msg ClaudeMessage) bool

HasUnsupportedContent reports whether a Claude message carries any unsupported content (mirrors upstream hasUnsupportedContent(message)).

func IsAutoResolvableTool

func IsAutoResolvableTool(t Tool) bool

func IsBuiltInHookName

func IsBuiltInHookName(name string) bool

IsBuiltInHookName reports whether name is one of the nine built-in hooks.

func IsClaudeStyleMessages

func IsClaudeStyleMessages(v any) bool

func IsClientTool

func IsClientTool(t Tool) bool

func IsFunctionCallArgsDeltaEvent

func IsFunctionCallArgsDeltaEvent(e components.StreamEvents) bool

func IsGeneratorTool

func IsGeneratorTool(t Tool) bool

func IsHITLTool

func IsHITLTool(t Tool) bool

func IsManualTool

func IsManualTool(t Tool) bool

func IsMcpTool

func IsMcpTool(t Tool) bool

IsMcpTool reports whether tool carries the additive MCP brand (see MarkMcp).

func IsReasoningDeltaEvent

func IsReasoningDeltaEvent(e components.StreamEvents) bool

func IsRegularExecuteTool

func IsRegularExecuteTool(t Tool) bool

func IsResponseCompletedEvent

func IsResponseCompletedEvent(e components.StreamEvents) bool

func IsServerTool

func IsServerTool(t Tool) bool

func IsStopConditionMet

func IsStopConditionMet(ctx context.Context, conditions []StopCondition, steps []StepResult) (bool, error)

func IsTextDeltaEvent

func IsTextDeltaEvent(e components.StreamEvents) bool

func IsToolCallOutputEvent

func IsToolCallOutputEvent(e ToolStreamEvent) bool

func IsToolPreliminaryResultEvent

func IsToolPreliminaryResultEvent(e ToolStreamEvent) bool

func IsToolResultEvent

func IsToolResultEvent(e ToolStreamEvent) bool

func IsTurnEndEvent

func IsTurnEndEvent(e ResponseStreamEvent) bool

func IsTurnStartEvent

func IsTurnStartEvent(e ResponseStreamEvent) bool

func MatchesTool

func MatchesTool(matcher ToolMatcher, toolName string) bool

MatchesTool evaluates a ToolMatcher against a tool name.

  • nil -> wildcard, matches all tools
  • string -> exact match
  • *regexp.Regexp -> MatchString
  • func(string) bool -> arbitrary predicate

Unlike upstream's JS RegExp (whose `/g`/`/y` flags advance `lastIndex` across calls, making repeated `.test()` calls alternate true/false), Go's regexp.Regexp has no such stateful footgun, so MatchesTool needs no lastIndex reset — it is stateless by construction.

func NormalizeInputToArray

func NormalizeInputToArray(input components.InputsUnion) []components.InputsUnion1

func Off

func Off[P, R any](m *HooksManager, hookName string, handler HookHandler[P, R]) bool

Off removes a specific handler function from a hook. Returns true if found and removed.

Go function values are not comparable, so this is a best-effort match on the handler's code pointer via reflection (works for named functions and most closures, but two independently-created closures over identical code can share a pointer). The unsubscribe function returned by On is the precise removal path; prefer it when available.

func On

func On[P, R any](m *HooksManager, hookName string, entry HookEntry[P, R]) func()

On registers a handler for hookName (built-in or custom) and returns an unsubscribe function.

func ParseToolArguments

func ParseToolArguments(raw string) (json.RawMessage, any, error)

func PartitionToolCalls

func PartitionToolCalls(ctx context.Context, tools []Tool, calls []ParsedToolCall, turn TurnContext, callLevel ToolApprovalCheck) (approved []ParsedToolCall, pending []ParsedToolCall, err error)

func SanitizeSchema

func SanitizeSchema(schema map[string]any) map[string]any

func SerializeConversationState

func SerializeConversationState(state ConversationState) (string, error)

SerializeConversationState serializes state to a stable JSON string for durable storage. Guarantees the version field is present (injects ConversationStateVersion when the input state lacks one). Treat the returned JSON as opaque: round-trip via SerializeConversationState / DeserializeConversationState rather than introspecting the shape directly.

Compat policy: additive field changes within a major version. On version bumps, migrations run inside DeserializeConversationState. The StateAccessor Load/Save contract is unchanged — these helpers are opt-in.

func SummarizeToolExecutions

func SummarizeToolExecutions(results []ToolExecutionResult) []string

func ToolHasApprovalConfigured

func ToolHasApprovalConfigured(t Tool) bool

func ToolRequiresApproval

func ToolRequiresApproval(ctx context.Context, t Tool, call ParsedToolCall, turn TurnContext, callLevel ToolApprovalCheck) (bool, error)

func ToolResultsToMap

func ToolResultsToMap(results []ToolExecutionResult) map[string]ToolExecutionResult

func UnsentResultsToAPIFormat

func UnsentResultsToAPIFormat(results []UnsentToolResult) []components.InputsUnion1

func UnsentResultsToAPIFormatWithError

func UnsentResultsToAPIFormatWithError(results []UnsentToolResult) ([]components.InputsUnion1, error)

func ValidateAgainstSchema

func ValidateAgainstSchema(raw json.RawMessage, schema map[string]any) error

Types

type AssistantMessageItem

type AssistantMessageItem = components.OutputMessageItem

type AsyncOutput

type AsyncOutput struct {
	// Work is optional; nil means "no work to track".
	Work <-chan error
	// AsyncTimeout bounds how long the manager waits for Work before giving
	// up and logging a warning. Zero means DefaultAsyncTimeout.
	AsyncTimeout int64 // milliseconds
}

AsyncOutput signals fire-and-forget mode: the chain proceeds immediately without waiting for completion. Any background work the handler kicked off should be attached via Work so the manager can track it for Drain and enforce AsyncTimeout.

Work is a channel (Go's stream-oriented stand-in for upstream's `work?: Promise<unknown>`) that the handler's background goroutine should close (optionally sending a non-nil error first) when it finishes.

type BaseInputsUnion

type BaseInputsUnion = components.InputsUnion

type CallFileSearchItem

type CallFileSearchItem = components.OutputFileSearchCallItem

type CallFunctionToolItem

type CallFunctionToolItem = components.OutputFunctionCallItem

type CallModelInput

type CallModelInput struct {
	Model              string
	ModelFunc          DynamicValue[string]
	Input              any
	Tools              []Tool
	StopWhen           []StopCondition
	AllowFinalResponse any
	// StrictFinalResponse: when true, skips the one-shot retry that would
	// otherwise fire when a completed run's final response has no output
	// after at least one tool round. Default false: that empty final is
	// retried once via a `toolChoice: none` resend. Either way an empty
	// final response is never a hard error here — unlike upstream's
	// `validateFinalResponse`, an empty `Output` items array is not on its
	// own a reliable invalidity signal against go-sdk's response shape,
	// which also carries a separate `OutputText` convenience field. See
	// upstreamer-changelog.md's compatibility note.
	StrictFinalResponse    bool
	State                  *ConversationState
	StateAccessor          StateAccessor
	Context                ContextInput
	Approval               ToolApprovalCheck
	ApproveToolCalls       []string
	RejectToolCalls        []string
	Request                components.ResponsesRequest
	MetadataLevel          *components.MetadataLevel
	MaxTurns               int
	BeforeTurn             func(context.Context, TurnContext) error
	AfterTurn              func(context.Context, TurnContext, StepResult) error
	AdditionalInstructions string
	InstructionsFunc       DynamicValue[string]
	// Hooks accepts either a *HooksManager or an InlineHookConfig. See
	// ResolveHooks. nil means no hooks.
	Hooks any
}

type CallModelInputWithState

type CallModelInputWithState = CallModelInput

type CallWebSearchItem

type CallWebSearchItem = components.OutputWebSearchCallItem

type ChatAssistantMessage

type ChatAssistantMessage = components.ChatAssistantMessage

type ChatMessage

type ChatMessage struct {
	Role       string           `json:"role"`
	Content    any              `json:"content"`
	ToolCallID string           `json:"tool_call_id,omitempty"`
	ToolCalls  []ParsedToolCall `json:"tool_calls,omitempty"`
	Metadata   map[string]any   `json:"metadata,omitempty"`
}

func ToChatMessage

func ToChatMessage(resp components.OpenResponsesResult) ChatMessage

ToChatMessage converts an OpenRouter Responses result into an OpenAI Chat-style assistant message, carrying id/model/status metadata and any unsupported content so a round trip through FromChatMessages is lossless.

type ChatMessages

type ChatMessages = components.ChatMessages

type ChatStreamEvent

type ChatStreamEvent = ResponseStreamEvent

type ClaudeContentBlock

type ClaudeContentBlock struct {
	Type        string              `json:"type"`
	Text        string              `json:"text,omitempty"`
	ID          string              `json:"id,omitempty"`
	Name        string              `json:"name,omitempty"`
	Input       any                 `json:"input,omitempty"`
	ToolUseID   string              `json:"tool_use_id,omitempty"`
	Content     any                 `json:"content,omitempty"`
	Source      *ClaudeImageSource  `json:"source,omitempty"`
	IsError     bool                `json:"is_error,omitempty"`
	Thinking    string              `json:"thinking,omitempty"`
	Signature   string              `json:"signature,omitempty"`
	Unsupported *UnsupportedContent `json:"unsupported_content,omitempty"`
	Data        any                 `json:"data,omitempty"`
}

type ClaudeContentBlockType

type ClaudeContentBlockType string

type ClaudeImageSource

type ClaudeImageSource struct {
	Type      string `json:"type"`
	URL       string `json:"url,omitempty"`
	MediaType string `json:"media_type,omitempty"`
	Data      string `json:"data,omitempty"`
}

type ClaudeMessage

type ClaudeMessage struct {
	ID                 string               `json:"id"`
	Type               string               `json:"type"`
	Role               string               `json:"role"`
	Model              string               `json:"model"`
	Content            []ClaudeContentBlock `json:"content"`
	StopReason         string               `json:"stop_reason"`
	StopSequence       *string              `json:"stop_sequence"`
	Usage              ClaudeUsage          `json:"usage"`
	UnsupportedContent []UnsupportedContent `json:"unsupported_content,omitempty"`
	// Metadata retains the legacy ad-hoc metadata bag for callers that relied
	// on it; the structured fields above are the faithful upstream surface.
	Metadata map[string]any `json:"metadata,omitempty"`
}

ClaudeMessage is the Anthropic Claude assistant-message response shape produced by ToClaudeMessage. It carries Claude message metadata (id, model, stop_reason, stop_sequence, usage) and a top-level unsupported_content array for content Claude cannot represent, matching upstream's ClaudeMessage type.

func ToClaudeMessage

func ToClaudeMessage(resp components.OpenResponsesResult) (ClaudeMessage, error)

ToClaudeMessage converts an OpenRouter Responses result into a Claude-style assistant message, preserving message metadata (id, model, status, usage) and round-tripping any content Claude cannot represent through the structured unsupported-content carrier. It returns an error rather than degrading a real failure (e.g. an un-encodable output item) into a fallback value, matching the contract's errors-are-values rule.

type ClaudeUsage

type ClaudeUsage struct {
	InputTokens              int64 `json:"input_tokens"`
	OutputTokens             int64 `json:"output_tokens"`
	CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
	CacheReadInputTokens     int64 `json:"cache_read_input_tokens"`
}

ClaudeUsage mirrors the Anthropic Claude message usage shape (distinct from the OpenRouter SDK usage type). Field names match upstream stream-transformers.ts convertToClaudeMessage.

type ClientTool

type ClientTool interface{ Tool }

type ContextInput

type ContextInput map[string]map[string]any

type ConversationState

type ConversationState struct {
	// Version is the serialization-contract version for this state blob.
	// Zero (the Go zero value) is treated as version 1 by
	// DeserializeConversationState/SerializeConversationState, matching
	// upstream's "absence means v1" legacy-blob policy.
	Version            int
	ID                 string
	Messages           []components.InputsUnion1
	PreviousResponseID *string
	PendingToolCalls   []ParsedToolCall
	UnsentToolResults  []UnsentToolResult
	PartialResponse    *PartialResponse
	InterruptedBy      *string
	Status             ConversationStatus
	CreatedAt          int64
	UpdatedAt          int64
}

func AppendToMessages

func AppendToMessages(state ConversationState, items ...components.InputsUnion1) ConversationState

func CreateInitialState

func CreateInitialState() ConversationState

func DeserializeConversationState

func DeserializeConversationState(data string) (ConversationState, error)

DeserializeConversationState parses and validates a previously serialized ConversationState.

Accepts version-less legacy blobs and states with version 1, normalizing both to ConversationStateVersion. Returns *UnsupportedStateVersionError for any other version (fail loudly so stores don't silently misinterpret future shapes) and *InvalidStateError for malformed JSON or missing required fields (id, messages, status, createdAt, updatedAt).

Compat policy: absence of version means v1. Migrations for future versions happen here; callers should treat the JSON as opaque.

func UpdateState

func UpdateState(state ConversationState, mutate func(*ConversationState)) ConversationState

type ConversationStatus

type ConversationStatus string
const (
	ConversationStatusComplete    ConversationStatus = "complete"
	ConversationStatusInterrupted ConversationStatus = "interrupted"
	// ConversationStatusAwaitingApproval means one or more tool calls need
	// human approval before executing (see ApproveToolCalls/RejectToolCalls).
	ConversationStatusAwaitingApproval ConversationStatus = "awaiting_approval"
	// ConversationStatusAwaitingHITL means a HITL tool returned nil from its
	// OnToolCalled hook, pausing execution so the caller can supply an output.
	ConversationStatusAwaitingHITL ConversationStatus = "awaiting_hitl"
	// ConversationStatusAwaitingClientTools means one or more manual
	// (execute:false / no OnToolCalled) tool calls are unresolved; the loop
	// stopped so the caller can execute them client-side and continue.
	// Distinct from ConversationStatusAwaitingHITL — HITL tools have an
	// OnToolCalled hook, manual tools do not (upstream #64).
	ConversationStatusAwaitingClientTools ConversationStatus = "awaiting_client_tools"
	ConversationStatusInProgress          ConversationStatus = "in_progress"
)

type DeveloperMessageItem

type DeveloperMessageItem = components.EasyInputMessage

type DynamicValue

type DynamicValue[T any] func(context.Context, TurnContext) (T, error)

type EasyInputMessage

type EasyInputMessage = components.EasyInputMessage

type EasyInputMessageContentUnion1

type EasyInputMessageContentUnion1 = components.EasyInputMessageContentUnion1

type EasyInputMessageRoleUnion

type EasyInputMessageRoleUnion = components.EasyInputMessageRoleUnion

type EmitOptions

type EmitOptions struct {
	// ToolName scopes tool-matcher-gated hooks (PreToolUse, PostToolUse, ...).
	ToolName string
	// SessionID overrides the manager-level default from SetSessionID for
	// this emit only. Pass it whenever the manager instance may be shared
	// across concurrent runs (CallModel always does).
	SessionID string
}

EmitOptions configures one Emit call.

type EmitResult

type EmitResult[R, P any] struct {
	// Results are the sync results returned by handlers that produced one.
	Results []R
	// Pending are handles to detached async handler work (see Drain).
	Pending []<-chan struct{}
	// FinalPayload is the payload after all mutation piping has been applied.
	FinalPayload P
	// Blocked is true if any handler triggered a block/reject short-circuit.
	Blocked bool
	// BlockReason is the first non-empty block/reject reason, if any.
	BlockReason string
	// Mutated is true if any handler's result actually piped a mutation into
	// the payload (e.g. PreToolUse MutatedInput, UserPromptSubmit MutatedPrompt).
	Mutated bool
}

EmitResult is the result of emitting a hook through its handler chain.

func Emit

func Emit[P, R any](m *HooksManager, hookName string, payload P, opts EmitOptions) (EmitResult[R, P], error)

Emit emits a custom (non-built-in) hook. Built-in hooks have typed EmitXxx methods on HooksManager (EmitPreToolUse, EmitPostToolUse, ...).

type EmptyHookResult

type EmptyHookResult struct{}

EmptyHookResult is the result type for observation-only hooks (PostToolUse, PostToolUseFailure, SessionStart, SessionEnd, PostModelCall): handlers have no meaningful result to return. Stands in for upstream's `result: undefined` / `z.void()` built-in definitions.

type EnhancedResponseStreamEvent

type EnhancedResponseStreamEvent = ResponseStreamEvent

type ErrorEvent

type ErrorEvent = components.ErrorEvent

type ErrorItem

type ErrorItem = components.ErrorEvent

type ErrorMiddleware

type ErrorMiddleware func(context.Context, *http.Request, error) error

ErrorMiddleware observes a transport or SDK HTTP error.

type FunctionCallItem

type FunctionCallItem = components.FunctionCallItem

type FunctionCallOutputItem

type FunctionCallOutputItem = components.FunctionCallOutputItem

type FunctionProgressItem

type FunctionProgressItem = ToolPreliminaryResultEvent

type FunctionResultItem

type FunctionResultItem = ToolResultEvent

type GetResponseOptions

type GetResponseOptions struct{ Refresh bool }

type HITLTool

type HITLTool interface{ Tool }

type HasApprovalTools

type HasApprovalTools interface{ Tool }

type Hook

type Hook = RequestMiddleware

Hook is a shorthand request middleware, matching the upstream idea without exposing SDK internals.

type HookDefinition

type HookDefinition struct {
	PayloadType string
	ResultType  string
}

HookDefinition describes a hook's payload/result Go types. Go has no runtime schema equivalent to Zod (see hooks_schemas.go); this exists so a HookRegistry can be introspected the way upstream's HookRegistry can, without carrying runtime validators.

type HookEntry

type HookEntry[P, R any] struct {
	Handler HookHandler[P, R]
	Matcher ToolMatcher
	Filter  func(P) bool
}

HookEntry is one registered handler for a hook.

type HookHandler

type HookHandler[P, R any] func(payload P, hctx LifecycleHookContext) (HookHandlerResult[R], error)

HookHandler receives the payload and context for one hook invocation.

type HookHandlerResult

type HookHandlerResult[R any] struct {
	Result    R
	HasResult bool
	Async     *AsyncOutput
}

HookHandlerResult is what a hook handler returns: either a synchronous result (HasResult true), a fire-and-forget AsyncOutput signal (Async non-nil), or neither (void/observation-only handlers).

func AsyncResult

func AsyncResult[R any](async AsyncOutput) HookHandlerResult[R]

AsyncResult wraps a fire-and-forget AsyncOutput signal.

func SyncResult

func SyncResult[R any](result R) HookHandlerResult[R]

SyncResult wraps a synchronous handler result.

func VoidResult

func VoidResult[R any]() HookHandlerResult[R]

VoidResult signals a side-effect-only handler outcome (no result to collect).

type HookName

type HookName string

HookName identifies a built-in lifecycle hook. Ported from upstream hooks-schemas.ts's `HookName` const object (upstream #7/#67, the 0.8.0 headline). Custom hook names are plain strings; only the built-ins get a typed constant.

const (
	HookNamePreToolUse         HookName = "PreToolUse"
	HookNamePostToolUse        HookName = "PostToolUse"
	HookNamePostToolUseFailure HookName = "PostToolUseFailure"
	HookNameUserPromptSubmit   HookName = "UserPromptSubmit"
	HookNameStop               HookName = "Stop"
	HookNamePermissionRequest  HookName = "PermissionRequest"
	HookNameSessionStart       HookName = "SessionStart"
	HookNameSessionEnd         HookName = "SessionEnd"
	HookNamePostModelCall      HookName = "PostModelCall"
)

type HookRegistry

type HookRegistry = map[string]HookDefinition

HookRegistry maps custom hook names to their definitions. Provided for API parity with upstream's HookRegistry; Go's custom-hook registration (see On) is fully generic and does not require populating a HookRegistry up front.

type HooksManager

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

HooksManager is a typed, extensible hook system for agent lifecycle events (upstream #7/#67, the 0.8.0 headline). Supports both the nine built-in hooks (PreToolUse, PostToolUse, ...) and user-defined custom hooks registered via the package-level generic On/Emit functions (Go's generics stand-in for upstream's `AllHooks<Custom>` conditional-type merging — idiomatic divergence #5 in upstreamer.md).

func NewHooksManager

func NewHooksManager(opts ...HooksManagerOptions) *HooksManager

NewHooksManager constructs a HooksManager. opts is variadic so the common zero-options case (agent.NewHooksManager()) needs no empty struct literal.

func ResolveHooks

func ResolveHooks(hooks any) *HooksManager

ResolveHooks normalizes a CallModelInput.Hooks value into a *HooksManager.

  • nil -> nil (no hooks)
  • *HooksManager -> passthrough
  • InlineHookConfig -> construct a HooksManager and register every entry

Any other value logs a warning and is treated as nil, mirroring upstream's defensive handling of a config value that bypassed the typed surface.

func (*HooksManager) AbortInflight

func (m *HooksManager) AbortInflight()

AbortInflight cancels the context passed to every in-flight Emit call. Handlers that kick off async work should observe hctx.Ctx to honor this.

Does not remove pending async work from the drain set — callers that want to wait for handlers to wind down should still call Drain afterward.

func (*HooksManager) Drain

func (m *HooksManager) Drain()

Drain awaits all in-flight async handler work. Used for graceful shutdown; CallModel calls this unconditionally on every exit path so fire-and-forget hook work is never silently dropped.

func (*HooksManager) EmitPostModelCall

func (*HooksManager) EmitPostToolUse

func (*HooksManager) EmitPreToolUse

func (*HooksManager) EmitSessionEnd

func (*HooksManager) EmitSessionStart

func (*HooksManager) EmitStop

func (m *HooksManager) EmitStop(payload StopPayload, opts EmitOptions) (EmitResult[StopResult, StopPayload], error)

func (*HooksManager) HasHandlers

func (m *HooksManager) HasHandlers(hookName string) bool

HasHandlers reports whether any handlers are registered for hookName.

func (*HooksManager) OnPermissionRequest

func (m *HooksManager) OnPermissionRequest(entry HookEntry[PermissionRequestPayload, PermissionRequestResult]) func()

func (*HooksManager) OnPostModelCall

func (m *HooksManager) OnPostModelCall(entry HookEntry[PostModelCallPayload, EmptyHookResult]) func()

func (*HooksManager) OnPostToolUse

func (m *HooksManager) OnPostToolUse(entry HookEntry[PostToolUsePayload, EmptyHookResult]) func()

func (*HooksManager) OnPostToolUseFailure

func (m *HooksManager) OnPostToolUseFailure(entry HookEntry[PostToolUseFailurePayload, EmptyHookResult]) func()

func (*HooksManager) OnPreToolUse

func (m *HooksManager) OnPreToolUse(entry HookEntry[PreToolUsePayload, PreToolUseResult]) func()

func (*HooksManager) OnSessionEnd

func (m *HooksManager) OnSessionEnd(entry HookEntry[SessionEndPayload, EmptyHookResult]) func()

func (*HooksManager) OnSessionStart

func (m *HooksManager) OnSessionStart(entry HookEntry[SessionStartPayload, EmptyHookResult]) func()

func (*HooksManager) OnStop

func (m *HooksManager) OnStop(entry HookEntry[StopPayload, StopResult]) func()

func (*HooksManager) OnUserPromptSubmit

func (m *HooksManager) OnUserPromptSubmit(entry HookEntry[UserPromptSubmitPayload, UserPromptSubmitResult]) func()

func (*HooksManager) RemoveAll

func (m *HooksManager) RemoveAll(hookName string)

RemoveAll removes all handlers for a specific hook.

func (*HooksManager) RemoveAllHooks

func (m *HooksManager) RemoveAllHooks()

RemoveAllHooks removes every handler for every hook.

func (*HooksManager) SetSessionID

func (m *HooksManager) SetSessionID(sessionID string)

SetSessionID sets the manager-level default session ID exposed as hctx.SessionID to handler invocations.

This is a single mutable default on the manager instance: when one manager is shared by concurrent runs, callers MUST pass SessionID in EmitOptions instead (as CallModel does), otherwise the last SetSessionID call wins and concurrent emits observe the wrong id.

type HooksManagerOptions

type HooksManagerOptions struct {
	// ThrowOnHandlerError: if true, a handler error stops the chain and
	// propagates the error. If false (default), the error is logged as a
	// warning and execution continues.
	ThrowOnHandlerError bool
}

HooksManagerOptions configures a HooksManager.

type InferToolEvent

type InferToolEvent = any

type InferToolEventsUnion

type InferToolEventsUnion = any

type InferToolInput

type InferToolInput = any

type InferToolOutput

type InferToolOutput = any

type InferToolOutputsUnion

type InferToolOutputsUnion = any

type InlineHookConfig

InlineHookConfig is a lightweight alternative to constructing a HooksManager by hand: pass built-in hook entries directly on CallModelInput.Hooks. Only built-in hooks are supported inline; register custom hooks through a HooksManager instance via On.

type InputAudio

type InputAudio = components.InputAudio

type InputFile

type InputFile = components.InputFile

type InputImage

type InputImage = components.InputImage

type InputMessageItem

type InputMessageItem = components.InputMessageItem

type InputText

type InputText = components.InputText

type InputVideo

type InputVideo = components.InputVideo

type InputsUnion

type InputsUnion = components.InputsUnion

type InvalidStateError

type InvalidStateError struct {
	Message string
}

InvalidStateError is returned by DeserializeConversationState when the input is not well-formed JSON, or is missing/has the wrong type for a required ConversationState field.

func (*InvalidStateError) Error

func (e *InvalidStateError) Error() string

type Item

type Item = components.OutputItems

type LifecycleHookContext

type LifecycleHookContext struct {
	Ctx context.Context
	// HookName is the name of the hook currently emitting (useful for shared
	// handlers registered against multiple hooks).
	HookName string
	// SessionID is the current session id. This is the single source for
	// session identity in handlers — payloads deliberately do not repeat it.
	// The engine threads it per emit (safe for a manager shared across
	// concurrent runs); direct Emit callers get the manager-level default
	// from SetSessionID unless they pass a per-emit override.
	SessionID string
}

LifecycleHookContext is provided to every lifecycle-hook handler invocation.

Ctx is the idiomatic-Go stand-in for upstream's `AbortSignal`: it is canceled if the manager's AbortInflight is called while the emit is still running (idiomatic divergence: context.Context for cancellation, not AbortSignal — see upstreamer.md). Handlers that kick off background work via AsyncOutput should observe Ctx.Done() for cancellation.

type ManualTool

type ManualTool interface{ Tool }

type ModelCallTurnType

type ModelCallTurnType string

ModelCallTurnType classifies which kind of model request PostModelCall is reporting on.

const (
	ModelCallTurnTypeInitial   ModelCallTurnType = "initial"
	ModelCallTurnTypeResume    ModelCallTurnType = "resume"
	ModelCallTurnTypeToolRound ModelCallTurnType = "tool_round"
	ModelCallTurnTypeFinal     ModelCallTurnType = "final"
	ModelCallTurnTypeRetry     ModelCallTurnType = "retry"
)

type ModelCallUsage

type ModelCallUsage struct {
	InputTokens     int64
	OutputTokens    int64
	TotalTokens     int64
	CachedTokens    int64
	ReasoningTokens int64
	// Cost is nil when the response carried no cost figure.
	Cost *float64
}

ModelCallUsage is the per-call usage summary handed to PostModelCall and folded into SessionEnd's SessionUsageTotals.

type ModelResult

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

func CallModel

func CallModel(ctx context.Context, client ResponseSender, input CallModelInput) (*ModelResult, error)

func NewModelResultFromResponse

func NewModelResultFromResponse(resp components.OpenResponsesResult) *ModelResult

func (*ModelResult) Cancel

func (m *ModelResult) Cancel()

func (*ModelResult) ContextUpdates

func (m *ModelResult) ContextUpdates(ctx context.Context) (<-chan map[string]map[string]any, func() error)

func (*ModelResult) FullResponsesStream

func (m *ModelResult) FullResponsesStream(ctx context.Context) (<-chan ResponseStreamEvent, func() error)

func (*ModelResult) ItemsStream

func (m *ModelResult) ItemsStream(ctx context.Context) (<-chan components.OutputItems, func() error)

func (*ModelResult) NewMessagesStream

func (m *ModelResult) NewMessagesStream(ctx context.Context) (<-chan components.InputsUnion1, func() error)

func (*ModelResult) PendingToolCalls

func (m *ModelResult) PendingToolCalls(ctx context.Context) ([]ParsedToolCall, error)

func (*ModelResult) ReasoningStream

func (m *ModelResult) ReasoningStream(ctx context.Context) (<-chan string, func() error)

func (*ModelResult) RequiresApproval

func (m *ModelResult) RequiresApproval(ctx context.Context) (bool, error)

func (*ModelResult) Response

func (*ModelResult) State

func (*ModelResult) String

func (m *ModelResult) String() string

func (*ModelResult) Text

func (m *ModelResult) Text(ctx context.Context) (string, error)

func (*ModelResult) TextStream

func (m *ModelResult) TextStream(ctx context.Context) (<-chan string, func() error)

func (*ModelResult) ToolCalls

func (m *ModelResult) ToolCalls(ctx context.Context) ([]ParsedToolCall, error)

func (*ModelResult) ToolCallsStream

func (m *ModelResult) ToolCallsStream(ctx context.Context) (<-chan ParsedToolCall, func() error)

func (*ModelResult) ToolStream

func (m *ModelResult) ToolStream(ctx context.Context) (<-chan ToolStreamEvent, func() error)

type NewUserMessageItem

type NewUserMessageItem = components.EasyInputMessage

type NextTurnParamsContext

type NextTurnParamsContext struct {
	ToolCall ParsedToolCall
	Result   ToolExecutionResult
	Request  components.ResponsesRequest
}

type NextTurnParamsFunctions

type NextTurnParamsFunctions map[string]func(context.Context, NextTurnParamsContext) (any, error)

type NonClaudeMessageRole

type NonClaudeMessageRole string

type OpenAIResponsesToolChoiceUnion

type OpenAIResponsesToolChoiceUnion = components.OpenAIResponsesToolChoiceUnion

type OpenResponsesResult

type OpenResponsesResult = components.OpenResponsesResult

type OpenRouter

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

OpenRouter wraps the official OpenRouter Go SDK and exposes agent-oriented helpers.

func NewOpenRouter

func NewOpenRouter(opts OpenRouterOptions) *OpenRouter

NewOpenRouter constructs an agent wrapper. Middleware is installed through WithClient because the generated Go SDK keeps its hook package internal.

func (*OpenRouter) CallModel

func (o *OpenRouter) CallModel(ctx context.Context, input CallModelInput) (*ModelResult, error)

CallModel invokes the package-level CallModel with this wrapper.

func (*OpenRouter) SDK

func (o *OpenRouter) SDK() *openrouter.OpenRouter

SDK returns the wrapped generated SDK client.

func (*OpenRouter) SendResponse

SendResponse calls the generated Responses API endpoint.

type OpenRouterOptions

type OpenRouterOptions struct {
	SDK           SDKOptions
	BeforeRequest []RequestMiddleware
	AfterResponse []ResponseMiddleware
	AfterError    []ErrorMiddleware
	RequestHooks  []Hook
}

OpenRouterOptions configures the agent wrapper.

type OutputFileSearchCallItem

type OutputFileSearchCallItem = components.OutputFileSearchCallItem

type OutputFunctionCallItem

type OutputFunctionCallItem = components.OutputFunctionCallItem

type OutputImage

type OutputImage = components.OutputImage

type OutputImageGenerationCallItem

type OutputImageGenerationCallItem = components.OutputImageGenerationCallItem

type OutputItems

type OutputItems = components.OutputItems

type OutputMessage

type OutputMessage = components.OutputMessageItem

type OutputReasoningItem

type OutputReasoningItem = components.OutputReasoningItem

type OutputWebSearchCallItem

type OutputWebSearchCallItem = components.OutputWebSearchCallItem

type ParsedToolCall

type ParsedToolCall struct {
	ID        string
	CallID    string
	Name      string
	Arguments any
	RawArgs   string
}

func ExtractToolCallsFromResponse

func ExtractToolCallsFromResponse(resp components.OpenResponsesResult) []ParsedToolCall

type PartialResponse

type PartialResponse struct {
	Response components.OpenResponsesResult
}

type PermissionDecision

type PermissionDecision string

PermissionDecision is PermissionRequestResult's outcome.

const (
	// PermissionDecisionAllow promotes the call past the approval gate.
	PermissionDecisionAllow PermissionDecision = "allow"
	// PermissionDecisionDeny synthesizes a rejection without executing the tool.
	PermissionDecisionDeny PermissionDecision = "deny"
	// PermissionDecisionAskUser falls through to the normal human approval flow (default).
	PermissionDecisionAskUser PermissionDecision = "ask_user"
)

type PermissionRequestPayload

type PermissionRequestPayload struct {
	ToolName  string
	ToolInput map[string]any
	RiskLevel RiskLevel
}

PermissionRequestPayload is delivered before the engine blocks for human approval, letting a hook allow/deny/pass-through the decision.

type PermissionRequestResult

type PermissionRequestResult struct {
	Decision PermissionDecision
	Reason   string
}

PermissionRequestResult is a handler's decision for a gated tool call. Last-wins when multiple handlers disagree.

type PostModelCallPayload

type PostModelCallPayload struct {
	SessionID  string
	ResponseID string
	Model      string
	DurationMs float64
	TurnType   ModelCallTurnType
	TurnNumber int
	Usage      *ModelCallUsage
}

PostModelCallPayload is delivered once per materialized model response.

type PostToolUseFailurePayload

type PostToolUseFailurePayload struct {
	ToolName  string
	ToolInput map[string]any
	Error     error
}

PostToolUseFailurePayload is delivered when a tool execution throws or returns an error. Deliberately NOT fired when a tool never ran (a PermissionRequest deny, a user rejection on resume, or a PreToolUse block all synthesize a rejected result without execution).

type PostToolUsePayload

type PostToolUsePayload struct {
	ToolName   string
	ToolInput  map[string]any
	ToolOutput any
	DurationMs float64
}

PostToolUsePayload is delivered after a client tool executes successfully.

type PreToolUsePayload

type PreToolUsePayload struct {
	ToolName  string
	ToolInput map[string]any
}

PreToolUsePayload is delivered before a client tool executes.

type PreToolUseResult

type PreToolUseResult struct {
	// MutatedInput replaces ToolInput for the actual tool call when non-nil.
	MutatedInput map[string]any
	// Block, if true, denies the call with a generic reason.
	Block bool
	// BlockReason, if non-empty, denies the call and doubles as Block=true.
	BlockReason string
}

PreToolUseResult can mutate the tool's input or block execution.

func (PreToolUseResult) Blocked

func (r PreToolUseResult) Blocked() bool

Blocked reports whether this result triggers the PreToolUse short-circuit.

type ReasoningItem

type ReasoningItem = components.OutputReasoningItem

type RequestMiddleware

type RequestMiddleware func(context.Context, *http.Request) error

RequestMiddleware observes or modifies an outgoing HTTP request.

type ResolvedCallModelInput

type ResolvedCallModelInput = CallModelInput

func ResolveAsyncFunctions

func ResolveAsyncFunctions(ctx context.Context, input CallModelInput, turn TurnContext) (ResolvedCallModelInput, error)

type ResponseMiddleware

type ResponseMiddleware func(context.Context, *http.Response) error

ResponseMiddleware observes a completed HTTP response.

type ResponseOutputText

type ResponseOutputText = components.ResponseOutputText

type ResponseSender

type ResponseSender interface {
	SendResponse(ctx context.Context, request components.ResponsesRequest, metadata *components.MetadataLevel, opts ...operations.Option) (*operations.CreateResponsesResponse, error)
}

ResponseSender is the narrow Responses API surface used by CallModel.

type ResponseStreamEvent

type ResponseStreamEvent struct {
	Type     string                          `json:"type"`
	Turn     int                             `json:"turn,omitempty"`
	Response *components.OpenResponsesResult `json:"response,omitempty"`
	Event    *components.StreamEvents        `json:"event,omitempty"`
	Text     string                          `json:"text,omitempty"`
}

type ResponsesRequest

type ResponsesRequest = components.ResponsesRequest

type ResponsesRequestToolUnion

type ResponsesRequestToolUnion = components.ResponsesRequestToolUnion

type ReusableStream

type ReusableStream[T any] struct {
	// contains filtered or unexported fields
}

ReusableStream is a fan-out broadcast of a sequence of values that supports multiple concurrent consumers, each of which sees every value plus the terminal error — the Go-idiomatic stand-in for the upstream ReusableReadableStream. New subscribers also replay the full history.

Each subscriber gets its own goroutine and an internal queue, so a slow consumer never blocks the producer and the producer never sends on a channel that is being closed concurrently (which would panic).

func NewReusableStream

func NewReusableStream[T any]() *ReusableStream[T]

func (*ReusableStream[T]) Complete

func (r *ReusableStream[T]) Complete(err error)

func (*ReusableStream[T]) Err

func (r *ReusableStream[T]) Err() error

func (*ReusableStream[T]) IsComplete

func (r *ReusableStream[T]) IsComplete() bool

IsComplete reports whether the source has been fully read into the buffer. A fresh consumer created after this point replays the retained buffer without waiting on a producer.

func (*ReusableStream[T]) Push

func (r *ReusableStream[T]) Push(v T)

func (*ReusableStream[T]) Snapshot

func (r *ReusableStream[T]) Snapshot() []T

func (*ReusableStream[T]) Subscribe

func (r *ReusableStream[T]) Subscribe(buffer int) (<-chan T, func() error)

Subscribe returns a channel of values plus a cancel/wait function that returns the terminal error after the stream completes (or the consumer stops early).

type RiskLevel

type RiskLevel string

RiskLevel is PermissionRequest's coarse risk classification, derived from the approval gate's shape: a callback => high, blanket true => medium, otherwise low.

const (
	RiskLevelLow    RiskLevel = "low"
	RiskLevelMedium RiskLevel = "medium"
	RiskLevelHigh   RiskLevel = "high"
)

type SDKClient

type SDKClient struct{ Client *openrouter.OpenRouter }

SDKClient adapts a raw generated SDK client to ResponseSender.

func (SDKClient) SendResponse

SendResponse calls Beta.Responses.Send on the wrapped generated SDK client.

type SDKOptions

type SDKOptions struct {
	APIKey      string
	HTTPReferer string
	XTitle      string
	Timeout     time.Duration
	HTTPClient  openrouter.HTTPClient
}

SDKOptions configures the underlying OpenRouter Go SDK client.

type ServerTool

type ServerTool interface{ Tool }

type ServerToolConfig

type ServerToolConfig struct {
	Name   string
	Config components.ResponsesRequestToolUnion
}

type ServerToolResultItem

type ServerToolResultItem = components.OutputItems

type SessionEndPayload

type SessionEndPayload struct {
	Reason     SessionEndReason
	TotalUsage *SessionUsageTotals
}

SessionEndPayload is delivered exactly once per run that reached SessionStart, carrying the aggregated usage totals when any model call was made.

type SessionEndReason

type SessionEndReason string

SessionEndReason explains why the run ended.

const (
	SessionEndReasonUser     SessionEndReason = "user"
	SessionEndReasonError    SessionEndReason = "error"
	SessionEndReasonMaxTurns SessionEndReason = "max_turns"
	SessionEndReasonComplete SessionEndReason = "complete"
)

type SessionStartPayload

type SessionStartPayload struct {
	Config map[string]any
}

SessionStartPayload is delivered once per run, before the first model call.

type SessionUsageTotals

type SessionUsageTotals struct {
	ModelCallUsage
	ModelCalls int
}

SessionUsageTotals aggregates ModelCallUsage across every model call made during a run, plus a call count.

type StateAccessor

type StateAccessor interface {
	Load(context.Context) (*ConversationState, error)
	Save(context.Context, ConversationState) error
}

type StepResult

type StepResult struct {
	Text              string
	ToolCalls         []ParsedToolCall
	ClientToolResults []ToolExecutionResult
	ServerToolResults []ServerToolResultItem
	Response          components.OpenResponsesResult
	Usage             *components.Usage
	FinishReason      string
}

type StopCondition

type StopCondition func(context.Context, []StepResult) (bool, error)

func FinishReasonIs

func FinishReasonIs(reason string) StopCondition

func HasToolCall

func HasToolCall(name string) StopCondition

func MaxCost

func MaxCost(limit float64) StopCondition

func MaxTokensUsed

func MaxTokensUsed(limit int64) StopCondition

func StepCountIs

func StepCountIs(limit int) StopCondition

type StopPayload

type StopPayload struct {
	Reason StopReason
}

StopPayload is delivered when a stopWhen condition halts the loop.

type StopReason

type StopReason string

StopReason is the reason the tool loop halted, passed to the Stop hook.

const StopReasonMaxTurns StopReason = "max_turns"

StopReasonMaxTurns is currently the only reason the engine emits: the configured stopWhen condition (default StepCountIs) fired.

type StopResult

type StopResult struct {
	ForceResume  bool
	AppendPrompt string
}

StopResult lets a handler force the loop to resume and/or inject a prompt.

ForceResume alone does not change any state: the stop condition typically fires again immediately, so a bare ForceResume burns through the consecutive-override cap in rapid succession and then stops. Pair it with AppendPrompt (which injects a user message, advancing the conversation) to make resumption useful. AppendPrompt is honored independently of ForceResume. Multiple handlers' AppendPrompt values are concatenated with newlines.

type StopWhen

type StopWhen []StopCondition

type StreamEvents

type StreamEvents = components.StreamEvents

type StreamableOutputItem

type StreamableOutputItem = components.OutputItems

type SystemMessageItem

type SystemMessageItem = components.EasyInputMessage

type ToModelOutputFunction

type ToModelOutputFunction func(any) (any, error)

type ToModelOutputResult

type ToModelOutputResult struct{ Output any }

type Tool

type Tool interface {
	ToolName() string
	ToolDescription() string
	ToolType() ToolType
	InputSchema() map[string]any
	OutputSchema() map[string]any
	EventSchema() map[string]any
	RequiresApproval(context.Context, ParsedToolCall, TurnContext) (bool, error)
	HandleResponseReceived(context.Context, any, ToolExecuteContext) (any, error)
	ToAPITool() components.ResponsesRequestToolUnion
}

func FindToolByName

func FindToolByName(tools []Tool, name string) Tool

func MarkMcp

func MarkMcp(t Tool) Tool

MarkMcp adds the additive MCP brand to an already-built client tool. The tool's runtime behavior and wire shape are unchanged; only IsMcpTool's classification (and downstream Source discrimination on ToolExecutionResult/ToolStreamEvent) now identifies it as MCP-originated. Intended for use by an MCP integration package that wraps remote tools.

func NewServerTool

func NewServerTool(config ServerToolConfig) Tool

type ToolApprovalCheck

type ToolApprovalCheck func(context.Context, ParsedToolCall, TurnContext) (bool, error)

type ToolCallOutputEvent

type ToolCallOutputEvent struct {
	Type   string `json:"type"`
	CallID string `json:"call_id"`
	Name   string `json:"name"`
	Output any    `json:"output,omitempty"`
	Error  string `json:"error,omitempty"`
}

type ToolConfig

type ToolConfig[In any] struct {
	Name               string
	Description        string
	InputSchema        map[string]any
	OutputSchema       map[string]any
	EventSchema        map[string]any
	Execute            func(context.Context, In, ToolExecuteContext) (any, error)
	Generate           func(context.Context, In, ToolExecuteContext, func(any) error) (any, error)
	Manual             bool
	RequireApproval    bool
	Approval           ToolApprovalCheck
	OnToolCalled       func(context.Context, In, ToolExecuteContext) (any, bool, error)
	OnResponseReceived func(context.Context, any, ToolExecuteContext) (any, error)
}

type ToolContextStore

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

func NewToolContextStore

func NewToolContextStore(initial ContextInput) *ToolContextStore

func (*ToolContextStore) Get

func (s *ToolContextStore) Get(name string) map[string]any

func (*ToolContextStore) Merge

func (s *ToolContextStore) Merge(name string, value map[string]any)

func (*ToolContextStore) Set

func (s *ToolContextStore) Set(name string, value map[string]any)

func (*ToolContextStore) Snapshot

func (s *ToolContextStore) Snapshot() map[string]map[string]any

func (*ToolContextStore) Subscribe

func (s *ToolContextStore) Subscribe(listener func(string, map[string]any))

type ToolEventBroadcaster

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

func NewToolEventBroadcaster

func NewToolEventBroadcaster() *ToolEventBroadcaster

func (*ToolEventBroadcaster) Complete

func (b *ToolEventBroadcaster) Complete()

func (*ToolEventBroadcaster) Error

func (b *ToolEventBroadcaster) Error(err error)

func (*ToolEventBroadcaster) Push

func (b *ToolEventBroadcaster) Push(event ToolStreamEvent)

func (*ToolEventBroadcaster) Snapshot

func (b *ToolEventBroadcaster) Snapshot() []ToolStreamEvent

func (*ToolEventBroadcaster) Subscribe

func (b *ToolEventBroadcaster) Subscribe() (<-chan ToolStreamEvent, func() error)

type ToolExecuteContext

type ToolExecuteContext struct {
	ToolCall ParsedToolCall
	Turn     TurnContext
	Context  map[string]any
	Shared   map[string]any
	Store    *ToolContextStore
	Emit     func(any)
}

func BuildToolExecuteContext

func BuildToolExecuteContext(call ParsedToolCall, turn TurnContext, store *ToolContextStore, emit func(any)) ToolExecuteContext

func (ToolExecuteContext) LocalContext

func (tc ToolExecuteContext) LocalContext() map[string]any

func (ToolExecuteContext) MergeContext

func (tc ToolExecuteContext) MergeContext(value map[string]any)

func (ToolExecuteContext) MergeSharedContext

func (tc ToolExecuteContext) MergeSharedContext(value map[string]any)

func (ToolExecuteContext) SetContext

func (tc ToolExecuteContext) SetContext(value map[string]any)

func (ToolExecuteContext) SetSharedContext

func (tc ToolExecuteContext) SetSharedContext(value map[string]any)

func (ToolExecuteContext) SharedContext

func (tc ToolExecuteContext) SharedContext() map[string]any

type ToolExecutionResult

type ToolExecutionResult struct {
	CallID string
	Name   string
	// Source is "mcp" for a tool marked via MarkMcp, "client" otherwise.
	// Zero value ("") means the executor path did not set it (treat as client).
	Source ToolSource
	Output any
	Error  error
	Events []any
}

func ExecuteTool

func ExecuteTool(ctx context.Context, t Tool, call ParsedToolCall, execCtx ToolExecuteContext) (ToolExecutionResult, error)

func ExecuteToolLoop

func ExecuteToolLoop(ctx context.Context, tools []Tool, calls []ParsedToolCall, store *ToolContextStore) ([]ToolExecutionResult, error)

type ToolExecutionResultUnion

type ToolExecutionResultUnion = ToolExecutionResult

type ToolHasApproval

type ToolHasApproval interface{ Tool }

type ToolMatcher

type ToolMatcher = any

ToolMatcher filters tool-scoped hook invocation by tool name. Accepted dynamic values: nil (wildcard), string (exact match), *regexp.Regexp (MatchString), or func(string) bool (arbitrary predicate). See MatchesTool.

type ToolPreliminaryResultEvent

type ToolPreliminaryResultEvent struct {
	Type   string `json:"type"`
	CallID string `json:"call_id"`
	Name   string `json:"name"`
	Event  any    `json:"event"`
}

type ToolResultEvent

type ToolResultEvent struct {
	Type   string     `json:"type"`
	CallID string     `json:"call_id"`
	Name   string     `json:"name"`
	Source ToolSource `json:"source,omitempty"`
	Result any        `json:"result,omitempty"`
	Error  string     `json:"error,omitempty"`
}

type ToolResultItem

type ToolResultItem = components.FunctionCallOutputItem

type ToolSource

type ToolSource string

ToolSource discriminates a tool result's origin. "mcp" identifies a tool wrapped from a remote MCP server (see McpBranded/MarkMcp/IsMcpTool); "client" is every locally-defined tool. Mirrors upstream's `source` field added to ToolExecutionResult/ToolResultEvent so a consumer can identify MCP-originated (dynamically typed) results without that collapsing every other tool's result to `unknown`.

const (
	ToolSourceClient ToolSource = "client"
	ToolSourceMCP    ToolSource = "mcp"
)

type ToolStreamEvent

type ToolStreamEvent struct {
	Type   string     `json:"type"`
	CallID string     `json:"call_id,omitempty"`
	Name   string     `json:"name,omitempty"`
	Source ToolSource `json:"source,omitempty"`
	Event  any        `json:"event,omitempty"`
	Result any        `json:"result,omitempty"`
	Output any        `json:"output,omitempty"`
	Error  string     `json:"error,omitempty"`
	Turn   int        `json:"turn,omitempty"`
}

type ToolType

type ToolType string

ToolType identifies which upstream tool shape a Go tool represents.

const (
	ToolTypeRegular   ToolType = "regular"
	ToolTypeGenerator ToolType = "generator"
	ToolTypeManual    ToolType = "manual"
	ToolTypeHITL      ToolType = "hitl"
	ToolTypeServer    ToolType = "server"
)

type ToolWithExecute

type ToolWithExecute interface {
	Tool
	Execute(context.Context, json.RawMessage, ToolExecuteContext) (ToolExecutionResult, error)
}

type ToolWithGenerator

type ToolWithGenerator interface {
	ToolWithExecute
}

type TurnContext

type TurnContext struct {
	ToolCall      *components.OutputFunctionCallItem
	NumberOfTurns int
	TurnRequest   *components.ResponsesRequest
}

type TurnEndEvent

type TurnEndEvent struct {
	Type string `json:"type"`
	Turn int    `json:"turn"`
}

type TurnStartEvent

type TurnStartEvent struct {
	Type string `json:"type"`
	Turn int    `json:"turn"`
}

type TypedTool

type TypedTool[In any] struct {
	// contains filtered or unexported fields
}

func MustNewTool

func MustNewTool[In any](config ToolConfig[In]) *TypedTool[In]

func NewTool

func NewTool[In any](config ToolConfig[In]) (*TypedTool[In], error)

func (*TypedTool[In]) EventSchema

func (t *TypedTool[In]) EventSchema() map[string]any

func (*TypedTool[In]) Execute

func (t *TypedTool[In]) Execute(ctx context.Context, raw json.RawMessage, execCtx ToolExecuteContext) (ToolExecutionResult, error)

func (*TypedTool[In]) HandleResponseReceived

func (t *TypedTool[In]) HandleResponseReceived(ctx context.Context, output any, execCtx ToolExecuteContext) (any, error)

func (*TypedTool[In]) InputSchema

func (t *TypedTool[In]) InputSchema() map[string]any

func (*TypedTool[In]) OutputSchema

func (t *TypedTool[In]) OutputSchema() map[string]any

func (*TypedTool[In]) RequiresApproval

func (t *TypedTool[In]) RequiresApproval(ctx context.Context, call ParsedToolCall, turn TurnContext) (bool, error)

func (*TypedTool[In]) ToAPITool

func (t *TypedTool[In]) ToAPITool() components.ResponsesRequestToolUnion

func (*TypedTool[In]) ToolDescription

func (t *TypedTool[In]) ToolDescription() string

func (*TypedTool[In]) ToolName

func (t *TypedTool[In]) ToolName() string

func (*TypedTool[In]) ToolType

func (t *TypedTool[In]) ToolType() ToolType

type TypedToolCall

type TypedToolCall = ParsedToolCall

type TypedToolCallUnion

type TypedToolCallUnion = ParsedToolCall

type UnsentToolResult

type UnsentToolResult struct {
	CallID string
	Name   string
	Output any
	Error  string
}

func CreateRejectedResult

func CreateRejectedResult(call ParsedToolCall, reason string) UnsentToolResult

func CreateUnsentResult

func CreateUnsentResult(call ParsedToolCall, output any) UnsentToolResult

type UnsupportedContent

type UnsupportedContent struct {
	OriginalType string `json:"original_type"`
	Data         any    `json:"data"`
	Reason       string `json:"reason"`
}

func ExtractUnsupportedContent

func ExtractUnsupportedContent(msg ClaudeMessage, originalType string) []UnsupportedContent

ExtractUnsupportedContent returns the unsupported-content carriers on a Claude message filtered by original type (mirrors upstream extractUnsupportedContent(message, originalType)). An empty originalType returns all carriers.

func ScanUnsupportedContent

func ScanUnsupportedContent(v any) []UnsupportedContent

ScanUnsupportedContent is a best-effort inspection helper that recursively finds unsupported-content carriers anywhere in an arbitrary value (e.g. an input-item slice produced by FromClaudeMessages/FromChatMessages or a raw response). It is intentionally non-core: it returns a best-effort result and never errors, and is not part of the upstream public surface.

type UnsupportedStateVersionError

type UnsupportedStateVersionError struct {
	Found     int
	Supported []int
}

UnsupportedStateVersionError is returned by DeserializeConversationState when a state blob's version is not supported by this SDK build.

func (*UnsupportedStateVersionError) Error

type Usage

type Usage = components.Usage

type UserMessageItem

type UserMessageItem = components.EasyInputMessage

type UserPromptSubmitPayload

type UserPromptSubmitPayload struct {
	Prompt string
}

UserPromptSubmitPayload carries the extracted user prompt text for the current turn's input.

type UserPromptSubmitResult

type UserPromptSubmitResult struct {
	// MutatedPrompt replaces Prompt when non-nil (a pointer distinguishes
	// "not set" from "explicitly set to the empty string").
	MutatedPrompt *string
	Reject        bool
	RejectReason  string
}

UserPromptSubmitResult can mutate the prompt or reject it outright.

func (UserPromptSubmitResult) Rejected

func (r UserPromptSubmitResult) Rejected() bool

Rejected reports whether this result triggers the UserPromptSubmit short-circuit.

type Warning

type Warning struct{ Message string }

Jump to

Keyboard shortcuts

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