loop

package
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package loop contains Glue's provider-agnostic agent loop.

The loop streams assistant responses from a provider, executes requested tools, appends tool results, and repeats until the provider stops or the context is canceled. It must not depend on the public glue package, provider packages, stores, CLI code, or Markdown context discovery.

The entry point is Run, which executes a RunRequest until the provider stops or the context is canceled. Tools run sequentially in source order by default; set RunRequest.Parallel to dispatch a single assistant message's tool calls concurrently while preserving transcript order. RunRequest.MaxTurns bounds the turn count and surfaces budget exhaustion as StopReasonMaxTurns.

Index

Constants

This section is empty.

Variables

View Source
var ErrSkipTool = errors.New("glue: skip tool")

ErrSkipTool returned from Hook.PreTool records a skipped tool call as an error tool result without executing the tool.

Functions

This section is empty.

Types

type AllowAll

type AllowAll struct{}

AllowAll allows every PermissionRequest. It is intended for tests and explicitly trusted hosts.

func (AllowAll) Decide

Decide implements Permission.

type ContentPart

type ContentPart struct {
	Type      ContentType   `json:"type"`
	Text      string        `json:"text,omitempty"`
	Thinking  string        `json:"thinking,omitempty"`
	Image     *ImageContent `json:"image,omitempty"`
	ToolCall  *ToolCall     `json:"tool_call,omitempty"`
	Signature string        `json:"signature,omitempty"`
}

ContentPart is a provider-neutral message content block.

Type selects which payload field is meaningful; other fields stay zero. The struct shape is intentionally JSON-friendly so transcripts can be persisted by file-backed stores without a separate wire format.

type ContentType

type ContentType string

ContentType identifies the active payload field in a ContentPart.

const (
	ContentTypeText     ContentType = "text"
	ContentTypeThinking ContentType = "thinking"
	ContentTypeImage    ContentType = "image"
	ContentTypeToolCall ContentType = "tool_call"
)

type DenyAll

type DenyAll struct {
	Reason string
}

DenyAll denies every PermissionRequest. It is intended for tests and explicitly locked-down hosts.

func (DenyAll) Decide

Decide implements Permission.

type Event

type Event struct {
	Type       EventType      `json:"type"`
	Message    *Message       `json:"message,omitempty"`
	Messages   []Message      `json:"messages,omitempty"`
	Delta      string         `json:"delta,omitempty"`
	ToolCall   *ToolCall      `json:"tool_call,omitempty"`
	ToolCallID string         `json:"tool_call_id,omitempty"`
	ToolName   string         `json:"tool_name,omitempty"`
	ToolResult *ToolResult    `json:"tool_result,omitempty"`
	Error      string         `json:"error,omitempty"`
	Metadata   map[string]any `json:"metadata,omitempty"`
}

Event is emitted by the agent loop for callers such as sessions and CLIs. The active fields depend on Type.

type EventType

type EventType string

EventType identifies events emitted by the agent loop.

const (
	EventLoopStart    EventType = "loop_start"
	EventLoopEnd      EventType = "loop_end"
	EventTurnStart    EventType = "turn_start"
	EventTurnEnd      EventType = "turn_end"
	EventMessageStart EventType = "message_start"
	EventMessageEnd   EventType = "message_end"
	EventTextDelta    EventType = "text_delta"
	EventToolStart    EventType = "tool_start"
	EventToolEnd      EventType = "tool_end"
	EventError        EventType = "error"
)

type Hook

type Hook interface {
	PreTool(ctx context.Context, call ToolCall) error
	PostTool(ctx context.Context, call ToolCall, result *ToolResult) error
}

Hook observes or alters tool-call execution.

type ImageContent

type ImageContent struct {
	Data     string `json:"data"`
	MIMEType string `json:"mime_type"`
}

ImageContent stores an inline base64-encoded image.

type Message

type Message struct {
	ID         string         `json:"id,omitempty"`
	Role       MessageRole    `json:"role"`
	Content    []ContentPart  `json:"content,omitempty"`
	ToolCallID string         `json:"tool_call_id,omitempty"`
	ToolName   string         `json:"tool_name,omitempty"`
	IsError    bool           `json:"is_error,omitempty"`
	Provider   string         `json:"provider,omitempty"`
	Model      string         `json:"model,omitempty"`
	StopReason StopReason     `json:"stop_reason,omitempty"`
	Usage      *Usage         `json:"usage,omitempty"`
	CreatedAt  time.Time      `json:"created_at,omitempty"`
	Metadata   map[string]any `json:"metadata,omitempty"`
}

Message is a normalized transcript entry.

type MessageRole

type MessageRole string

MessageRole identifies which actor produced a transcript message.

const (
	MessageRoleUser      MessageRole = "user"
	MessageRoleAssistant MessageRole = "assistant"
	MessageRoleTool      MessageRole = "tool"
)

type Permission

type Permission interface {
	Decide(ctx context.Context, req PermissionRequest) (PermissionDecision, error)
}

Permission decides whether a side-effecting tool call may run.

type PermissionDecision

type PermissionDecision struct {
	Allow       bool          `json:"allow"`
	Reason      string        `json:"reason,omitempty"`
	RememberFor RememberScope `json:"remember_for,omitempty"`
}

PermissionDecision is the host's answer to a PermissionRequest.

type PermissionRequest

type PermissionRequest struct {
	Tool      string          `json:"tool"`
	Action    string          `json:"action"`
	Target    string          `json:"target"`
	Args      json.RawMessage `json:"args,omitempty"`
	SessionID string          `json:"session_id,omitempty"`
}

PermissionRequest describes one side-effecting tool call for the host.

type Provider

type Provider interface {
	Stream(ctx context.Context, req ProviderRequest) (<-chan ProviderEvent, error)
}

Provider streams assistant events for a single assistant turn. Implementations must close the returned channel when the turn ends, including on error.

type ProviderEvent

type ProviderEvent struct {
	Type         ProviderEventType `json:"type"`
	Message      *Message          `json:"message,omitempty"`
	Delta        string            `json:"delta,omitempty"`
	ContentIndex int               `json:"content_index,omitempty"`
	ToolCall     *ToolCall         `json:"tool_call,omitempty"`
	Error        string            `json:"error,omitempty"`
}

ProviderEvent is a provider-neutral streaming event. The active fields depend on Type: TextDelta uses Delta, ToolCall uses ToolCall, Done uses Message, Error uses Error.

type ProviderEventType

type ProviderEventType string

ProviderEventType identifies events emitted by a provider stream.

const (
	ProviderEventStart         ProviderEventType = "start"
	ProviderEventTextDelta     ProviderEventType = "text_delta"
	ProviderEventThinkingDelta ProviderEventType = "thinking_delta"
	ProviderEventToolCall      ProviderEventType = "tool_call"
	ProviderEventDone          ProviderEventType = "done"
	ProviderEventError         ProviderEventType = "error"
)

type ProviderRequest

type ProviderRequest struct {
	Model        string         `json:"model"`
	SystemPrompt string         `json:"system_prompt,omitempty"`
	Messages     []Message      `json:"messages,omitempty"`
	Tools        []ToolSpec     `json:"tools,omitempty"`
	Options      map[string]any `json:"options,omitempty"`
}

ProviderRequest is the normalized input sent to a provider for one assistant turn.

type RememberScope

type RememberScope int

RememberScope tells a host permission implementation how broadly a positive decision may be remembered. Core loop code does not cache decisions.

const (
	RememberNever RememberScope = iota
	RememberSession
	RememberSessionTarget
	RememberForever
)

type RunRequest

type RunRequest struct {
	Provider     Provider
	Model        string
	SystemPrompt string
	Messages     []Message
	Tools        []Tool
	Options      map[string]any
	MaxTurns     int
	Parallel     bool
	SessionID    string
	Permission   Permission
	Hooks        []Hook
	Emit         func(Event)
}

RunRequest configures one Run.

Provider is required. MaxTurns defaults to 32 when zero or negative. Emit receives a snapshot of every loop event in source order; nil disables event delivery. Messages, Tools, and Options are defensively copied so callers may reuse the input slices and maps.

Parallel controls within-turn tool execution. When false (the default), tool calls in a single assistant message are executed sequentially in source order. When true, the loop fans them out concurrently and waits for all of them before appending tool-result messages — but the appended results, EventToolStart, and EventToolEnd are still emitted in assistant source order so the transcript stays deterministic.

type RunResult

type RunResult struct {
	Messages    []Message
	NewMessages []Message
}

RunResult is returned by Run. Messages is the full transcript including the input messages and every message produced during this run. NewMessages is just the messages produced during this run, in append order.

func Run

func Run(ctx context.Context, req RunRequest) (RunResult, error)

Run executes a provider-agnostic agent loop until the assistant stops requesting tools, the provider errors, the context is canceled, or MaxTurns is reached.

Tool execution is sequential and deterministic: requested tool calls are executed in the order they appear in the assistant message, their result messages are appended in that same order, and unknown tools, invalid JSON arguments, missing executors, and executor errors all become tool-result messages with IsError=true so the model can see and react instead of the loop crashing.

type StopReason

type StopReason string

StopReason explains why a provider finished an assistant turn.

const (
	StopReasonStop     StopReason = "stop"
	StopReasonLength   StopReason = "length"
	StopReasonToolUse  StopReason = "tool_use"
	StopReasonError    StopReason = "error"
	StopReasonCanceled StopReason = "canceled"
	// StopReasonMaxTurns marks the last assistant message in a run
	// that exited because the loop turn budget (RunRequest.MaxTurns)
	// was exhausted while the assistant still had pending tool calls.
	// Distinguishes "we ran out of budget" from "the model finished"
	// (Stop) and from provider-side truncation (Length), so agents can
	// retry with a higher budget cleanly.
	StopReasonMaxTurns StopReason = "max_turns"
)

type Tool

type Tool struct {
	ToolSpec
	Execute ToolExecutor `json:"-"`
}

Tool combines the provider-visible specification with its local executor. The Execute field is intentionally tagged json:"-" so transcripts and provider payloads exclude it.

type ToolCall

type ToolCall struct {
	ID        string          `json:"id"`
	Name      string          `json:"name"`
	Arguments json.RawMessage `json:"arguments,omitempty"`
}

ToolCall is a model request to invoke a named tool with JSON arguments.

type ToolExecutor

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

ToolExecutor runs a tool call locally and returns a normalized result.

type ToolResult

type ToolResult struct {
	Content  []ContentPart  `json:"content,omitempty"`
	IsError  bool           `json:"is_error,omitempty"`
	Metadata map[string]any `json:"metadata,omitempty"`
}

ToolResult is the local result produced by a ToolExecutor.

type ToolSpec

type ToolSpec struct {
	Name        string          `json:"name"`
	Description string          `json:"description"`
	Parameters  json.RawMessage `json:"parameters,omitempty"`

	RequiresPermission bool                  `json:"-"`
	PermissionAction   string                `json:"-"`
	PermissionTarget   func(ToolCall) string `json:"-"`
}

ToolSpec is the provider-visible description of a tool.

type Usage

type Usage struct {
	InputTokens      int64 `json:"input_tokens,omitempty"`
	OutputTokens     int64 `json:"output_tokens,omitempty"`
	CacheReadTokens  int64 `json:"cache_read_tokens,omitempty"`
	CacheWriteTokens int64 `json:"cache_write_tokens,omitempty"`
	TotalTokens      int64 `json:"total_tokens,omitempty"`
}

Usage captures token accounting reported by a provider when available.

Jump to

Keyboard shortcuts

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