agentcore

package module
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Feb 17, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

AgentCore

AgentCore is a minimal, composable Go library for building AI agent applications.

Examples | 简体中文

Install

go get github.com/voocel/agentcore

Architecture

agentcore/            Agent core (types, loop, agent, events, subagent)
agentcore/llm/        LLM adapters (OpenAI, Anthropic, Gemini via litellm)
agentcore/tools/      Built-in tools: read, write, edit, bash
agentcore/memory/     Context compaction — auto-summarize long conversations

Core design:

  • Pure function loop (loop.go) — double loop: inner processes tool calls + steering, outer handles follow-up
  • Stateful Agent (agent.go) — consumes loop events to update state, same as any external listener
  • Event stream — single <-chan Event output drives any UI (TUI, Web, Slack, logging)
  • Two-stage pipelineTransformContext (prune/inject) → ConvertToLLM (filter to LLM messages)
  • SubAgent tool (subagent.go) — multi-agent via tool invocation, three modes: single, parallel, chain
  • Context compaction (memory/) — automatic summarization when context approaches window limit

Quick Start

Single Agent
package main

import (
    "fmt"
    "os"

    "github.com/voocel/agentcore"
    "github.com/voocel/agentcore/llm"
    "github.com/voocel/agentcore/tools"
)

func main() {
    model := llm.NewOpenAIModel("gpt-4.1-mini", os.Getenv("OPENAI_API_KEY"))

    agent := agentcore.NewAgent(
        agentcore.WithModel(model),
        agentcore.WithSystemPrompt("You are a helpful coding assistant."),
        agentcore.WithTools(
            tools.NewRead(),
            tools.NewWrite(),
            tools.NewEdit(),
            tools.NewBash("."),
        ),
    )

    agent.Subscribe(func(ev agentcore.Event) {
        if ev.Type == agentcore.EventMessageEnd {
            if msg, ok := ev.Message.(agentcore.Message); ok && msg.Role == agentcore.RoleAssistant {
                fmt.Println(msg.Content)
            }
        }
    })

    agent.Prompt("List the files in the current directory.")
    agent.WaitForIdle()
}
Multi-Agent (SubAgent Tool)

Sub-agents are invoked as regular tools with isolated contexts:

scout := agentcore.SubAgentConfig{
    Name:         "scout",
    Description:  "Fast codebase reconnaissance",
    Model:        llm.NewOpenAIModel("gpt-4.1-mini", apiKey),
    SystemPrompt: "Quickly explore and report findings. Be concise.",
    Tools:        []agentcore.Tool{tools.NewRead(), tools.NewBash(".")},
    MaxTurns:     5,
}

worker := agentcore.SubAgentConfig{
    Name:         "worker",
    Description:  "General-purpose executor",
    Model:        llm.NewOpenAIModel("gpt-4.1-mini", apiKey),
    SystemPrompt: "Implement tasks given to you.",
    Tools:        []agentcore.Tool{tools.NewRead(), tools.NewWrite(), tools.NewEdit(), tools.NewBash(".")},
}

agent := agentcore.NewAgent(
    agentcore.WithModel(model),
    agentcore.WithTools(agentcore.NewSubAgentTool(scout, worker)),
)

Three execution modes via tool call:

// Single: one agent, one task
{"agent": "scout", "task": "Find all API endpoints"}

// Parallel: concurrent execution
{"tasks": [{"agent": "scout", "task": "Find auth code"}, {"agent": "scout", "task": "Find DB schema"}]}

// Chain: sequential with {previous} context passing
{"chain": [{"agent": "scout", "task": "Find auth code"}, {"agent": "worker", "task": "Refactor based on: {previous}"}]}
Steering & Follow-Up
// Interrupt mid-run (delivered after current tool, remaining tools skipped)
agent.Steer(agentcore.Message{Role: agentcore.RoleUser, Content: "Stop and focus on tests instead."})

// Queue for after the agent finishes
agent.FollowUp(agentcore.Message{Role: agentcore.RoleUser, Content: "Now run the tests."})

// Cancel immediately
agent.Abort()
Event Stream

All lifecycle events flow through a single channel — subscribe to drive any UI:

agent.Subscribe(func(ev agentcore.Event) {
    switch ev.Type {
    case agentcore.EventMessageStart:    // assistant starts streaming
    case agentcore.EventMessageUpdate:   // streaming token delta
    case agentcore.EventMessageEnd:      // message complete
    case agentcore.EventToolExecStart:   // tool execution begins
    case agentcore.EventToolExecEnd:     // tool execution ends
    case agentcore.EventError:           // error occurred
    }
})
Custom LLM (StreamFn)

Swap the LLM call with a proxy, mock, or custom implementation:

agent := agentcore.NewAgent(
    agentcore.WithStreamFn(func(ctx context.Context, req *agentcore.LLMRequest) (*agentcore.LLMResponse, error) {
        // Route to your own proxy/gateway
        return callMyProxy(ctx, req)
    }),
)
Context Compaction

Auto-summarize conversation history when approaching the context window limit. Hooks in via TransformContext — zero changes to core:

import "github.com/voocel/agentcore/memory"

agent := agentcore.NewAgent(
    agentcore.WithModel(model),
    agentcore.WithTransformContext(memory.NewCompaction(memory.CompactionConfig{
        Model:         model,
        ContextWindow: 128000,
    })),
    agentcore.WithConvertToLLM(memory.CompactionConvertToLLM),
)

On each LLM call, compaction checks total tokens. When they exceed ContextWindow - ReserveTokens (default 16384), it:

  1. Keeps recent messages (default 20000 tokens)
  2. Summarizes older messages via LLM into a structured checkpoint (Goal / Progress / Key Decisions / Next Steps)
  3. Tracks file operations (read/write/edit paths) across compacted messages
  4. Supports incremental updates — subsequent compactions update the existing summary rather than re-summarizing
Context Pipeline
agent := agentcore.NewAgent(
    // Stage 1: prune old messages, inject external context
    agentcore.WithTransformContext(func(ctx context.Context, msgs []agentcore.AgentMessage) ([]agentcore.AgentMessage, error) {
        if len(msgs) > 100 {
            msgs = msgs[len(msgs)-50:]
        }
        return msgs, nil
    }),
    // Stage 2: filter to LLM-compatible messages
    agentcore.WithConvertToLLM(func(msgs []agentcore.AgentMessage) []agentcore.Message {
        var out []agentcore.Message
        for _, m := range msgs {
            if msg, ok := m.(agentcore.Message); ok {
                out = append(out, msg)
            }
        }
        return out
    }),
)

Built-in Tools

Tool Description
read Read file contents with head truncation (2000 lines / 50KB)
write Write file with auto-mkdir
edit Exact text replacement with fuzzy match, BOM/line-ending normalization, unified diff output
bash Execute shell commands with tail truncation (2000 lines / 50KB)

API Reference

Agent
Method Description
NewAgent(opts...) Create agent with options
Prompt(input) Start new conversation turn
Continue() Resume from current context
Steer(msg) Inject steering message mid-run
FollowUp(msg) Queue message for after completion
Abort() Cancel current execution
WaitForIdle() Block until agent finishes
Subscribe(fn) Register event listener
State() Snapshot of current state
Options
Option Description
WithModel(m) Set LLM model
WithSystemPrompt(s) Set system prompt
WithTools(t...) Set tool list
WithMaxTurns(n) Safety limit (default: 10)
WithStreamFn(fn) Custom LLM call function
WithTransformContext(fn) Context transform (stage 1)
WithConvertToLLM(fn) Message conversion (stage 2)
WithSteeringMode(m) Queue drain mode: "all" or "one-at-a-time"
WithFollowUpMode(m) Queue drain mode: "all" or "one-at-a-time"

License

Apache License 2.0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AgentLoop

func AgentLoop(ctx context.Context, prompts []AgentMessage, agentCtx AgentContext, config LoopConfig) <-chan Event

AgentLoop starts an agent loop with new prompt messages. Prompts are added to context and events are emitted for them.

func AgentLoopContinue

func AgentLoopContinue(ctx context.Context, agentCtx AgentContext, config LoopConfig) <-chan Event

AgentLoopContinue continues from existing context without adding new messages. The last message in context must convert to user or tool role via ConvertToLLM.

func IsContextOverflow

func IsContextOverflow(err error) bool

IsContextOverflow reports whether the error indicates a context window overflow. It checks for litellm validation errors (HTTP 400) with context-related keywords.

func ReportToolProgress

func ReportToolProgress(ctx context.Context, partial json.RawMessage)

ReportToolProgress reports partial progress during tool execution. Silently ignored if no callback is registered in the context.

func WithToolProgress

func WithToolProgress(ctx context.Context, fn ToolProgressFunc) context.Context

WithToolProgress injects a progress callback into the context.

Types

type Agent

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

Agent is a stateful wrapper around the agent loop. It consumes loop events to update internal state, just like any external listener.

func NewAgent

func NewAgent(opts ...AgentOption) *Agent

NewAgent creates a new Agent with the given options.

func (*Agent) Abort

func (a *Agent) Abort()

Abort cancels the current execution.

func (*Agent) ClearAllQueues

func (a *Agent) ClearAllQueues()

ClearAllQueues removes all queued steering and follow-up messages.

func (*Agent) ClearFollowUpQueue

func (a *Agent) ClearFollowUpQueue()

ClearFollowUpQueue removes all queued follow-up messages.

func (*Agent) ClearMessages

func (a *Agent) ClearMessages()

ClearMessages resets the message history.

func (*Agent) ClearSteeringQueue

func (a *Agent) ClearSteeringQueue()

ClearSteeringQueue removes all queued steering messages.

func (*Agent) ContextUsage

func (a *Agent) ContextUsage() *ContextUsage

ContextUsage returns an estimate of the current context window occupancy. Returns nil if contextWindow or contextEstimateFn is not configured.

func (*Agent) Continue

func (a *Agent) Continue() error

Continue resumes from the current context without adding new messages. If the last message is from assistant, it dequeues steering/follow-up

func (*Agent) ExportMessages

func (a *Agent) ExportMessages() []Message

ExportMessages returns concrete Messages for serialization.

func (*Agent) FollowUp

func (a *Agent) FollowUp(msg AgentMessage)

FollowUp queues a message to be processed after the agent finishes.

func (*Agent) HasQueuedMessages

func (a *Agent) HasQueuedMessages() bool

HasQueuedMessages reports whether any steering or follow-up messages are queued.

func (*Agent) ImportMessages

func (a *Agent) ImportMessages(msgs []Message) error

ImportMessages replaces message history from deserialized Messages.

func (*Agent) Messages

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

Messages returns the current message history.

func (*Agent) Prompt

func (a *Agent) Prompt(input string) error

Prompt starts a new conversation turn with the given input.

func (*Agent) PromptMessages

func (a *Agent) PromptMessages(msgs ...AgentMessage) error

PromptMessages starts a new conversation turn with arbitrary AgentMessages.

func (*Agent) Reset

func (a *Agent) Reset()

Reset clears all state and queues.

func (*Agent) SetMessages

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

SetMessages replaces the message history (e.g. to restore a previous conversation). The agent must not be running.

func (*Agent) SetModel

func (a *Agent) SetModel(m ChatModel)

SetModel changes the LLM provider. Takes effect on the next turn.

func (*Agent) SetSystemPrompt

func (a *Agent) SetSystemPrompt(s string)

SetSystemPrompt changes the system prompt. Takes effect on the next turn.

func (*Agent) SetThinkingLevel

func (a *Agent) SetThinkingLevel(level ThinkingLevel)

SetThinkingLevel changes the reasoning depth. Takes effect on the next turn.

func (*Agent) SetTools

func (a *Agent) SetTools(tools ...Tool)

SetTools replaces the tool set. Takes effect on the next turn.

func (*Agent) State

func (a *Agent) State() AgentState

State returns a snapshot of the agent's current state.

func (*Agent) Steer

func (a *Agent) Steer(msg AgentMessage)

Steer queues a steering message to interrupt the agent mid-run. Delivered after the current tool execution; remaining tools are skipped.

func (*Agent) Subscribe

func (a *Agent) Subscribe(fn func(Event)) func()

Subscribe registers a listener for agent events. Returns an unsubscribe function.

func (*Agent) TotalUsage

func (a *Agent) TotalUsage() Usage

TotalUsage returns the cumulative token usage across all turns.

func (*Agent) WaitForIdle

func (a *Agent) WaitForIdle()

WaitForIdle blocks until the agent finishes the current run.

type AgentContext

type AgentContext struct {
	SystemPrompt string
	Messages     []AgentMessage
	Tools        []Tool
}

AgentContext holds the immutable context for a single agent loop invocation.

type AgentMessage

type AgentMessage interface {
	GetRole() Role
	GetTimestamp() time.Time
	TextContent() string
	ThinkingContent() string
	HasToolCalls() bool
}

AgentMessage is the app-layer message abstraction. Message implements this interface. Users can define custom types (e.g. status notifications, UI hints) that flow through the context pipeline but get filtered out by ConvertToLLM.

func ToAgentMessages

func ToAgentMessages(msgs []Message) []AgentMessage

ToAgentMessages converts a Message slice to AgentMessage slice. Use this to restore conversation history from deserialized Messages.

type AgentOption

type AgentOption func(*Agent)

AgentOption configures an Agent.

func WithContextEstimate

func WithContextEstimate(fn ContextEstimateFn) AgentOption

WithContextEstimate sets the context token estimation function. Use memory.ContextEstimateAdapter for the default hybrid estimation.

func WithContextPipeline

func WithContextPipeline(
	transform func(ctx context.Context, msgs []AgentMessage) ([]AgentMessage, error),
	convert func([]AgentMessage) []Message,
) AgentOption

WithContextPipeline sets both TransformContext and ConvertToLLM in one call. This is the recommended way to configure context compaction:

agentcore.WithContextPipeline(
    memory.NewCompaction(cfg),
    memory.CompactionConvertToLLM,
)

func WithContextWindow

func WithContextWindow(n int) AgentOption

WithContextWindow sets the model's context window size in tokens. Used by ContextUsage() to calculate context occupancy percentage.

func WithConvertToLLM

func WithConvertToLLM(fn func([]AgentMessage) []Message) AgentOption

WithConvertToLLM sets the message conversion function.

func WithFollowUpMode

func WithFollowUpMode(mode QueueMode) AgentOption

WithFollowUpMode sets the follow-up queue drain mode. QueueModeAll (default) delivers all queued follow-up messages at once. QueueModeOneAtATime delivers one per turn.

func WithGetApiKey

func WithGetApiKey(fn func(provider string) (string, error)) AgentOption

WithGetApiKey sets a dynamic API key resolver called before each LLM call. The provider parameter identifies which provider is being called (e.g. "openai", "anthropic"). Enables per-provider key resolution, key rotation, OAuth short-lived tokens, and multi-tenant scenarios.

func WithMaxRetries

func WithMaxRetries(n int) AgentOption

WithMaxRetries sets the LLM call retry limit for retryable errors.

func WithMaxToolErrors

func WithMaxToolErrors(n int) AgentOption

WithMaxToolErrors sets the consecutive failure threshold per tool. After reaching this limit, the tool is disabled for the rest of the loop. 0 means unlimited (no circuit breaker).

func WithMaxTurns

func WithMaxTurns(n int) AgentOption

WithMaxTurns sets the max turns safety limit.

func WithModel

func WithModel(model ChatModel) AgentOption

WithModel sets the LLM model.

func WithPermission

func WithPermission(fn PermissionFunc) AgentOption

WithPermission sets a function called before each tool execution. Return nil to allow, or an error to deny (error becomes tool error result).

func WithSessionID

func WithSessionID(id string) AgentOption

WithSessionID sets a session identifier for provider-level caching. Forwarded to providers that support session-based prompt caching.

func WithSteeringMode

func WithSteeringMode(mode QueueMode) AgentOption

WithSteeringMode sets the steering queue drain mode. QueueModeAll (default) delivers all queued steering messages at once. QueueModeOneAtATime delivers one per turn, letting the agent respond to each individually.

func WithStreamFn

func WithStreamFn(fn StreamFn) AgentOption

WithStreamFn sets a custom LLM call function (for proxy/mock).

func WithSystemPrompt

func WithSystemPrompt(prompt string) AgentOption

WithSystemPrompt sets the system prompt.

func WithThinkingBudgets

func WithThinkingBudgets(budgets map[ThinkingLevel]int) AgentOption

WithThinkingBudgets sets per-level thinking token budgets. Each ThinkingLevel maps to a max thinking token count.

func WithThinkingLevel

func WithThinkingLevel(level ThinkingLevel) AgentOption

WithThinkingLevel sets the reasoning depth for models that support it.

func WithTools

func WithTools(tools ...Tool) AgentOption

WithTools sets the tool list.

func WithTransformContext

func WithTransformContext(fn func(ctx context.Context, msgs []AgentMessage) ([]AgentMessage, error)) AgentOption

WithTransformContext sets the context transform function.

type AgentState

type AgentState struct {
	SystemPrompt     string
	Messages         []AgentMessage
	Tools            []Tool
	IsRunning        bool
	StreamMessage    AgentMessage        // partial message being streamed, nil when idle
	PendingToolCalls map[string]struct{} // tool call IDs currently executing
	TotalUsage       Usage               // cumulative token usage across all turns
	Error            string
}

AgentState is a snapshot of the agent's current state.

type CallConfig

type CallConfig struct {
	ThinkingLevel  ThinkingLevel
	ThinkingBudget int    // max thinking tokens, 0 = use provider default
	APIKey         string // per-call API key override, empty = use model default
	SessionID      string // provider session caching identifier
}

CallConfig holds per-call configuration resolved from CallOptions.

func ResolveCallConfig

func ResolveCallConfig(opts []CallOption) CallConfig

ResolveCallConfig applies options and returns the resolved config.

type CallOption

type CallOption func(*CallConfig)

CallOption configures per-call LLM parameters.

func WithAPIKey

func WithAPIKey(key string) CallOption

WithAPIKey overrides the API key for a single LLM call. Enables key rotation, OAuth short-lived tokens, and multi-tenant scenarios.

func WithCallSessionID

func WithCallSessionID(id string) CallOption

WithCallSessionID sets a session identifier for a single LLM call.

func WithThinking

func WithThinking(level ThinkingLevel) CallOption

WithThinking sets the thinking level for a single LLM call.

func WithThinkingBudget

func WithThinkingBudget(tokens int) CallOption

WithThinkingBudget sets the max thinking tokens for a single LLM call.

type ChatModel

type ChatModel interface {
	Generate(ctx context.Context, messages []Message, tools []ToolSpec, opts ...CallOption) (*LLMResponse, error)
	GenerateStream(ctx context.Context, messages []Message, tools []ToolSpec, opts ...CallOption) (<-chan StreamEvent, error)
	SupportsTools() bool
}

ChatModel is the LLM provider interface.

type ContentBlock

type ContentBlock struct {
	Type     ContentType `json:"type"`
	Text     string      `json:"text,omitempty"`
	Thinking string      `json:"thinking,omitempty"`
	ToolCall *ToolCall   `json:"tool_call,omitempty"`
	Image    *ImageData  `json:"image,omitempty"`
}

ContentBlock is a tagged union for message content. Exactly one payload field is populated, matching the Type value.

func ImageBlock

func ImageBlock(data, mimeType string) ContentBlock

func TextBlock

func TextBlock(text string) ContentBlock

func ThinkingBlock

func ThinkingBlock(thinking string) ContentBlock

func ToolCallBlock

func ToolCallBlock(tc ToolCall) ContentBlock

type ContentType

type ContentType string

ContentType identifies the kind of content in a ContentBlock.

const (
	ContentText     ContentType = "text"
	ContentThinking ContentType = "thinking"
	ContentToolCall ContentType = "toolCall"
	ContentImage    ContentType = "image"
)

type ContextEstimateFn

type ContextEstimateFn func(msgs []AgentMessage) (tokens, usageTokens, trailingTokens int)

ContextEstimateFn estimates the current context token consumption from messages. Returns total tokens, tokens from LLM Usage, and estimated trailing tokens.

type ContextUsage

type ContextUsage struct {
	Tokens         int     `json:"tokens"`          // estimated total tokens in context
	ContextWindow  int     `json:"context_window"`  // model's context window size
	Percent        float64 `json:"percent"`         // tokens / contextWindow * 100
	UsageTokens    int     `json:"usage_tokens"`    // from last LLM-reported Usage
	TrailingTokens int     `json:"trailing_tokens"` // chars/4 estimate for trailing messages
}

ContextUsage represents the current context window occupancy estimate.

type Event

type Event struct {
	Type        EventType
	Message     AgentMessage    // for message_start/update/end, turn_end
	Delta       string          // text delta for message_update
	ToolID      string          // for tool_exec_*
	Tool        string          // tool name for tool_exec_*
	ToolLabel   string          // human-readable tool label (from ToolLabeler)
	Args        json.RawMessage // tool args for tool_exec_start
	Result      json.RawMessage // tool result for tool_exec_end/update
	IsError     bool            // tool error flag for tool_exec_end
	ToolResults []ToolResult    // for turn_end: all tool results from this turn
	Err         error           // for error events
	NewMessages []AgentMessage  // for agent_end: messages added during this loop
	RetryInfo   *RetryInfo      // for retry events
}

Event is a lifecycle event emitted by the agent loop. This is the single output channel for all lifecycle information.

type EventType

type EventType string

EventType identifies agent lifecycle event types.

const (
	EventAgentStart     EventType = "agent_start"
	EventAgentEnd       EventType = "agent_end"
	EventTurnStart      EventType = "turn_start"
	EventTurnEnd        EventType = "turn_end"
	EventMessageStart   EventType = "message_start"
	EventMessageUpdate  EventType = "message_update"
	EventMessageEnd     EventType = "message_end"
	EventToolExecStart  EventType = "tool_exec_start"
	EventToolExecUpdate EventType = "tool_exec_update"
	EventToolExecEnd    EventType = "tool_exec_end"
	EventRetry          EventType = "retry"
	EventError          EventType = "error"
)

type FuncTool

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

FuncTool wraps a function as a Tool (convenience helper).

func NewFuncTool

func NewFuncTool(name, description string, schema map[string]any, fn func(ctx context.Context, args json.RawMessage) (json.RawMessage, error)) *FuncTool

func (*FuncTool) Description

func (t *FuncTool) Description() string

func (*FuncTool) Execute

func (t *FuncTool) Execute(ctx context.Context, args json.RawMessage) (json.RawMessage, error)

func (*FuncTool) Name

func (t *FuncTool) Name() string

func (*FuncTool) Schema

func (t *FuncTool) Schema() map[string]any

type ImageData

type ImageData struct {
	Data     string `json:"data"`
	MimeType string `json:"mime_type"`
}

ImageData holds base64-encoded image content.

type LLMRequest

type LLMRequest struct {
	Messages []Message
	Tools    []ToolSpec
}

LLMRequest is the request passed to StreamFn.

type LLMResponse

type LLMResponse struct {
	Message Message
}

LLMResponse is the response from StreamFn.

type LoopConfig

type LoopConfig struct {
	Model         ChatModel
	StreamFn      StreamFn      // nil = use Model directly
	MaxTurns      int           // safety limit, default 10
	MaxRetries    int           // LLM call retry limit for retryable errors, default 3
	MaxToolErrors int           // consecutive tool failure threshold per tool, 0 = unlimited
	ThinkingLevel ThinkingLevel // reasoning depth

	// Two-stage pipeline: TransformContext → ConvertToLLM
	TransformContext func(ctx context.Context, msgs []AgentMessage) ([]AgentMessage, error)
	ConvertToLLM     func(msgs []AgentMessage) []Message

	// CheckPermission is called before each tool execution.
	// Return nil to allow, or error to deny (error becomes tool error result).
	// When nil, all tools are allowed.
	CheckPermission PermissionFunc

	// GetApiKey resolves the API key before each LLM call.
	// The provider parameter identifies which provider is being called (e.g. "openai", "anthropic").
	// Enables per-provider key resolution, key rotation, OAuth tokens, and multi-tenant scenarios.
	// When nil or returns empty string, the model's default key is used.
	GetApiKey func(provider string) (string, error)

	// ThinkingBudgets maps each ThinkingLevel to a max thinking token count.
	// When set, the resolved budget is passed to the model alongside the level.
	ThinkingBudgets map[ThinkingLevel]int

	// SessionID enables provider-level session caching (e.g. Anthropic prompt cache).
	SessionID string

	// Steering: called after each tool execution to check for user interruptions.
	GetSteeringMessages func() []AgentMessage

	// FollowUp: called when the agent would otherwise stop.
	GetFollowUpMessages func() []AgentMessage
}

LoopConfig configures the agent loop.

type Message

type Message struct {
	Role       Role           `json:"role"`
	Content    []ContentBlock `json:"content"`
	StopReason StopReason     `json:"stop_reason,omitempty"`
	Usage      *Usage         `json:"usage,omitempty"`
	Metadata   map[string]any `json:"metadata,omitempty"`
	Timestamp  time.Time      `json:"timestamp"`
}

Message is an LLM-level message with structured content blocks.

func CollectMessages

func CollectMessages(msgs []AgentMessage) []Message

CollectMessages extracts concrete Messages from an AgentMessage slice, dropping custom types. Use this to serialize conversation history.

func DefaultConvertToLLM

func DefaultConvertToLLM(msgs []AgentMessage) []Message

DefaultConvertToLLM filters AgentMessages to LLM-compatible Messages. Custom message types are dropped; only user/assistant/system/tool messages pass through.

func RepairMessageSequence

func RepairMessageSequence(msgs []Message) []Message

RepairMessageSequence ensures tool call / tool result pairs are complete. Orphaned tool calls (no matching result) get a synthetic error result inserted. Orphaned tool results (no matching call) are removed. This prevents LLM providers from rejecting malformed message sequences.

func SystemMsg

func SystemMsg(text string) Message

SystemMsg creates a system message.

func ToolResultMsg

func ToolResultMsg(toolCallID string, content json.RawMessage, isError bool) Message

ToolResultMsg creates a tool result message.

func UserMsg

func UserMsg(text string) Message

UserMsg creates a user message from plain text.

func (Message) GetRole

func (m Message) GetRole() Role

func (Message) GetTimestamp

func (m Message) GetTimestamp() time.Time

func (Message) HasToolCalls

func (m Message) HasToolCalls() bool

HasToolCalls reports whether any tool call blocks exist.

func (Message) IsEmpty

func (m Message) IsEmpty() bool

IsEmpty reports whether the message has no meaningful content.

func (Message) TextContent

func (m Message) TextContent() string

TextContent returns the concatenated text from all text blocks.

func (Message) ThinkingContent

func (m Message) ThinkingContent() string

ThinkingContent returns the concatenated thinking text.

func (Message) ToolCalls

func (m Message) ToolCalls() []ToolCall

ToolCalls returns all tool call blocks.

type PermissionFunc

type PermissionFunc func(ctx context.Context, call ToolCall) error

PermissionFunc is called before each tool execution. Return nil to allow execution, or a non-nil error to deny. The error message is sent back to the LLM as a tool error result. Receives context.Context to support I/O (e.g. TUI confirmation, remote policy).

type ProviderNamer

type ProviderNamer interface {
	ProviderName() string
}

ProviderNamer is an optional interface for ChatModel implementations to expose their provider name (e.g. "openai", "anthropic", "gemini"). Used by the agent loop to pass provider context to GetApiKey callbacks.

type ProxyEvent

type ProxyEvent struct {
	Type       ProxyEventType `json:"type"`
	Delta      string         `json:"delta,omitempty"`
	ToolCallID string         `json:"tool_call_id,omitempty"`
	ToolName   string         `json:"tool_name,omitempty"`
	StopReason StopReason     `json:"stop_reason,omitempty"`
	Usage      *Usage         `json:"usage,omitempty"`
	Err        error          `json:"-"`
}

ProxyEvent is a bandwidth-optimized event from a remote proxy server. The client reconstructs the full message incrementally from these deltas.

type ProxyEventType

type ProxyEventType string

ProxyEventType identifies proxy streaming event types. Proxy events are bandwidth-optimized: they carry only deltas, not the full partial message on each event.

const (
	ProxyEventTextDelta     ProxyEventType = "text_delta"
	ProxyEventThinkingDelta ProxyEventType = "thinking_delta"
	ProxyEventToolCallStart ProxyEventType = "toolcall_start"
	ProxyEventToolCallDelta ProxyEventType = "toolcall_delta"
	ProxyEventDone          ProxyEventType = "done"
	ProxyEventError         ProxyEventType = "error"
)

type ProxyModel

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

ProxyModel implements ChatModel by forwarding to a remote proxy server. It reconstructs streaming events from bandwidth-optimized ProxyEvents.

Usage:

proxy := agentcore.NewProxyModel(myProxyFn)
agent := agentcore.NewAgent(agentcore.WithModel(proxy))

func NewProxyModel

func NewProxyModel(fn ProxyStreamFn) *ProxyModel

NewProxyModel creates a ChatModel that delegates to a proxy stream function.

func (*ProxyModel) Generate

func (p *ProxyModel) Generate(ctx context.Context, messages []Message, tools []ToolSpec, opts ...CallOption) (*LLMResponse, error)

Generate collects the full streamed response synchronously.

func (*ProxyModel) GenerateStream

func (p *ProxyModel) GenerateStream(ctx context.Context, messages []Message, tools []ToolSpec, opts ...CallOption) (<-chan StreamEvent, error)

GenerateStream converts proxy events into standard StreamEvents.

func (*ProxyModel) SupportsTools

func (p *ProxyModel) SupportsTools() bool

SupportsTools reports that the proxy can handle tool calls.

type ProxyStreamFn

type ProxyStreamFn func(ctx context.Context, req *LLMRequest) (<-chan ProxyEvent, error)

ProxyStreamFn makes an LLM call through a remote proxy and returns a channel of bandwidth-optimized ProxyEvents.

type QueueMode

type QueueMode string

QueueMode controls how steering/follow-up queues are drained.

const (
	QueueModeAll        QueueMode = "all"
	QueueModeOneAtATime QueueMode = "one-at-a-time"
)

type RetryInfo

type RetryInfo struct {
	Attempt    int
	MaxRetries int
	Delay      time.Duration
	Err        error
}

RetryInfo carries retry context for EventRetry events.

type Role

type Role string

Role defines message roles.

const (
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleSystem    Role = "system"
	RoleTool      Role = "tool"
)

type StopReason

type StopReason string

StopReason indicates why the LLM stopped generating.

const (
	StopReasonStop    StopReason = "stop"
	StopReasonLength  StopReason = "length"
	StopReasonToolUse StopReason = "toolUse"
	StopReasonError   StopReason = "error"
	StopReasonAborted StopReason = "aborted"
)

type StreamEvent

type StreamEvent struct {
	Type         StreamEventType
	ContentIndex int        // which content block is being updated
	Delta        string     // text/thinking/toolcall argument delta
	Message      Message    // partial (during streaming) or final (done)
	StopReason   StopReason // finish reason (for done events)
	Err          error      // for error events
}

StreamEvent is a streaming event from the LLM.

type StreamEventType

type StreamEventType string

StreamEventType identifies LLM streaming event types.

const (
	// Text content streaming
	StreamEventTextStart StreamEventType = "text_start"
	StreamEventTextDelta StreamEventType = "text_delta"
	StreamEventTextEnd   StreamEventType = "text_end"

	// Thinking/reasoning streaming
	StreamEventThinkingStart StreamEventType = "thinking_start"
	StreamEventThinkingDelta StreamEventType = "thinking_delta"
	StreamEventThinkingEnd   StreamEventType = "thinking_end"

	// Tool call streaming
	StreamEventToolCallStart StreamEventType = "toolcall_start"
	StreamEventToolCallDelta StreamEventType = "toolcall_delta"
	StreamEventToolCallEnd   StreamEventType = "toolcall_end"

	// Terminal events
	StreamEventDone  StreamEventType = "done"
	StreamEventError StreamEventType = "error"
)

type StreamFn

type StreamFn func(ctx context.Context, req *LLMRequest) (*LLMResponse, error)

StreamFn is an injectable LLM call function. When nil, the loop uses model.Generate / model.GenerateStream directly.

type SubAgentConfig

type SubAgentConfig struct {
	Name         string
	Description  string
	Model        ChatModel
	SystemPrompt string
	Tools        []Tool
	StreamFn     StreamFn
	MaxTurns     int
}

SubAgentConfig defines a sub-agent's identity and capabilities.

type SubAgentTool

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

SubAgentTool implements the Tool interface. The main agent calls this tool to delegate tasks to specialized sub-agents with isolated contexts

func NewSubAgentTool

func NewSubAgentTool(agents ...SubAgentConfig) *SubAgentTool

NewSubAgentTool creates a subagent tool from a set of agent configs.

func (*SubAgentTool) Description

func (t *SubAgentTool) Description() string

func (*SubAgentTool) Execute

func (t *SubAgentTool) Execute(ctx context.Context, args json.RawMessage) (json.RawMessage, error)

func (*SubAgentTool) Label

func (t *SubAgentTool) Label() string

func (*SubAgentTool) Name

func (t *SubAgentTool) Name() string

func (*SubAgentTool) Schema

func (t *SubAgentTool) Schema() map[string]any

type ThinkingLevel

type ThinkingLevel string

ThinkingLevel configures the reasoning depth for models that support it.

const (
	ThinkingOff     ThinkingLevel = "off"
	ThinkingMinimal ThinkingLevel = "minimal"
	ThinkingLow     ThinkingLevel = "low"
	ThinkingMedium  ThinkingLevel = "medium"
	ThinkingHigh    ThinkingLevel = "high"
	ThinkingXHigh   ThinkingLevel = "xhigh"
)

type Tool

type Tool interface {
	Name() string
	Description() string
	Schema() map[string]any
	Execute(ctx context.Context, args json.RawMessage) (json.RawMessage, error)
}

Tool defines the minimal tool interface. Timeout control goes through context.Context. Tools can report execution progress via ReportToolProgress(ctx, partial).

type ToolCall

type ToolCall struct {
	ID   string          `json:"id"`
	Name string          `json:"name"`
	Args json.RawMessage `json:"args"`
}

ToolCall represents a tool invocation request from the LLM.

type ToolLabeler

type ToolLabeler interface {
	Label() string
}

ToolLabeler is an optional interface for tools to provide a human-readable label.

type ToolProgressFunc

type ToolProgressFunc func(partialResult json.RawMessage)

ToolProgressFunc is a callback for reporting tool execution progress. Tools call ReportToolProgress to emit partial results during long operations.

type ToolResult

type ToolResult struct {
	ToolCallID string          `json:"tool_call_id"`
	Content    json.RawMessage `json:"content,omitempty"`
	IsError    bool            `json:"is_error,omitempty"`
	Details    any             `json:"details,omitempty"` // optional metadata for UI display/logging
}

ToolResult represents a tool execution outcome.

type ToolSpec

type ToolSpec struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Parameters  any    `json:"parameters"`
}

ToolSpec describes a tool for the LLM (name + description + JSON schema).

type Usage

type Usage struct {
	Input       int `json:"input"`
	Output      int `json:"output"`
	CacheRead   int `json:"cache_read"`
	CacheWrite  int `json:"cache_write"`
	TotalTokens int `json:"total_tokens"`
}

Usage tracks token consumption for a single LLM call.

Field semantics:

  • Input: prompt tokens sent to the model (includes cached tokens for some providers)
  • Output: completion tokens generated (includes reasoning tokens if applicable)
  • CacheRead: tokens served from prompt cache (Anthropic: cache_read_input_tokens)
  • CacheWrite: tokens written to prompt cache (Anthropic: cache_creation_input_tokens)
  • TotalTokens: provider-reported total, typically Input + Output

func (*Usage) Add

func (u *Usage) Add(other *Usage)

Add accumulates another Usage into this one (nil-safe).

Directories

Path Synopsis
examples
multi command
single command
Package schema provides a fluent builder for JSON Schema objects.
Package schema provides a fluent builder for JSON Schema objects.

Jump to

Keyboard shortcuts

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