gage

package module
v0.0.0-...-f424d6d Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 10 Imported by: 0

README

gage

gage is a provider-agnostic Go toolkit for building agentic systems. It is a library — you import it into your own program; it never starts a server or owns a main. Everything streams end to end.

It gives you, behind clean interfaces:

  • LLM providers: Anthropic (API key), OpenRouter, vLLM, Ollama, Codex (ChatGPT/Codex plan via OAuth) and Claude Code (Claude plan via OAuth) — with reasoning round-trip (signed thinking blocks), prompt caching (cache_control), and structured output (JSON mode / JSON Schema).
  • Built-in tools: read, write, edit, bash, grep, glob, list_dir, webfetch, websearch — plus tools.Typed[T] to define your own tools from plain Go structs (schema generated by reflection).
  • MCP: connect to Model Context Protocol servers (stdio or streamable HTTP, with header/bearer auth): tools (with live tools/list_changed sync), resources, prompts, and sampling backed by any gage.Provider.
  • Skills: load Claude Code–style SKILL.md folders.
  • Memory: a MemoryStore port, in-memory adapter, and memory_remember/memory_recall/memory_forget tools.
  • Agents: an agentic loop with tool execution (sequential or parallel), permissions (allow/deny/rewrite/remember), JSON Schema tool-input validation, hooks, context compaction, sub-agents, aggregated usage, and a terminal Result.
  • Production guards: secure policy helpers, per-tool timeouts, panic recovery, structured observations, concurrency and result-size wrappers, SSRF-pinned webfetch, sandboxable/sanitized bash, root-confined filesystem tools, encrypted file stores, and durable workflow checkpoints.
  • HTTP: an SSE http.Handler to expose an agent (you mount it).

Architecture

gage is built as hexagonal ports & adapters. The root package gage holds the domain types and the ports (interfaces); everything else is an adapter that depends on the core, never the reverse.

gage/                 core: Message, Event, Usage, Result + ports (Provider, Tool, Approver, Compactor, ...)
├── providers/        Provider adapters (anthropic, openrouter, vllm, ollama, codex, claudecode)
│   ├── shared/       HTTP client, SSE parser, OAuth (PKCE, token source, stores)
│   ├── openai/       reusable Chat Completions + Responses wire formats
│   └── anthropic/    reusable Messages wire format + API-key provider
├── tools/            built-in tools, Typed[T], registry + permission/limit decorators
├── policy/           conservative Approver policies for secure defaults
├── search/           SearchProvider impls (duckduckgo, brave, tavily)
├── mcp/              MCP client → gage.Tool bridge (+ resources, prompts, sampling)
├── skills/           SKILL.md loader + the "skill" tool
├── memory/           in-memory MemoryStore + memory tools
├── workflow/         durable run/checkpoint persistence around an agent
├── jsonschema/       small JSON Schema builder for tool parameters
├── agent/            the agentic loop, hooks, compactors, sub-agents
└── httpx/            SSE handler (no server)

The two central contracts:

// A model backend.
type Provider interface {
    Stream(ctx context.Context, req Request) (<-chan Event, error)
    Name() string
}

// An executable capability the model can call.
type Tool interface {
    Name() string
    Description() string
    Schema() JSONSchema
    Execute(ctx context.Context, input json.RawMessage) (ToolResult, error)
}

A Provider streams a channel of unified Events (text_delta, reasoning_delta, tool_call_*, usage, message_done, ...). The agent relays those events, runs the requested tools, feeds results back, and repeats until a final answer — emitting a terminal done event.

Install

go get github.com/deepteams/gage

Requires Go 1.26+.

Quick start

package main

import (
    "context"
    "fmt"

    "github.com/deepteams/gage"
    "github.com/deepteams/gage/agent"
    "github.com/deepteams/gage/tools"
    "github.com/deepteams/gage/providers/openrouter"
)

func main() {
    // 1. A provider.
    provider := openrouter.New("sk-or-...", openrouter.WithDefaultModel("anthropic/claude-sonnet-4.5"))

    // 2. A tool registry with built-in tools confined to a working dir.
    reg := tools.NewRegistry()
    reg.MustRegister(tools.NewFSTools(tools.FSConfig{Root: "."})...)
    reg.MustRegister(tools.NewSearchTools(tools.FSConfig{Root: "."})...)
    reg.MustRegister(tools.NewBashTool(tools.BashConfig{Dir: "."}))

    // 3. An agent.
    ag, _ := agent.New(agent.Config{
        Provider: provider,
        Registry: reg,
        System:   "You are a coding assistant. Use tools to inspect the repo.",
    })

    // 4. Stream the run.
    stream, _ := ag.Run(context.Background(), []gage.Message{
        gage.UserText("List the Go files and summarize the project."),
    })
    for ev := range stream {
        switch ev.Type {
        case gage.EventTextDelta:
            fmt.Print(ev.Text)
        case gage.EventToolResult:
            fmt.Printf("\n[tool %s]\n", ev.ToolResult.Text())
        }
    }
}

Providers

Provider Constructor Auth
Anthropic anthropic.New(anthropic.Config{APIKey: ...}) API key
OpenRouter openrouter.New(apiKey, ...) API key
vLLM vllm.New(baseURL, ...) optional key
Ollama ollama.New(baseURL, ...) none (local)
Codex codex.New(store, ...) OAuth (ChatGPT/Codex plan)
Claude Code claudecode.New(store, console, ...) OAuth (Claude plan)

All implement gage.Provider, so they are interchangeable in agent.Config.

Generation options are uniform, and providers fail fast with gage.ErrUnsupported instead of silently dropping an option they cannot honor:

Options: gage.ApplyOptions(gage.GenerateOptions{},
    gage.WithJSONSchema("report", schema), // structured output
    gage.WithPromptCache(),                // cache_control breakpoints (Anthropic)
    gage.WithReasoningEffort(gage.ReasoningHigh),
),

Reasoning/thinking blocks are preserved across turns: providers emit reasoning_done events carrying an opaque signature, the agent replays them in history, and encoders reattach them (required for Anthropic extended thinking with tool use).

OAuth providers (Codex, Claude Code)

⚠️ Heads-up. Codex and Claude Code here authenticate against undocumented backend endpoints using the OAuth "plan" flow, presenting themselves as the official CLIs. These endpoints are not a public API, can change without notice, and their use is subject to the respective providers' terms. Use at your own risk.

You supply a gage.TokenStoreyou own how tokens are persisted. gage provides an optional file/memory store in providers/shared/oauth, but any implementation works (database, keychain, secret manager):

type TokenStore interface {
    Load(ctx context.Context) (Credentials, error)
    Save(ctx context.Context, c Credentials) error
}

Log in once to populate the store, then construct the provider:

store := oauth.NewFileStore("/secure/path/codex.json") // or your own TokenStore

// One-time login (opens a browser to the auth URL).
_, err := codex.Login(ctx, store, func(url string) { browser.Open(url) })

// Use it — tokens refresh transparently through the store.
provider := codex.New(store)

Claude Code uses a copy-paste redirect flow:

authURL, complete, _ := claudecode.Login(false)
fmt.Println("Visit:", authURL)
// user pastes the returned "code#state"
creds, _ := complete(ctx, store, pasted)
provider := claudecode.New(store, false)

Tools & permissions

Register the built-ins you want, or define your own from a plain struct with tools.Typed — the JSON Schema is generated by reflection and inputs are unmarshaled for you:

type WeatherArgs struct {
    City string `json:"city" desc:"City name"`
    Unit string `json:"unit,omitempty" desc:"Unit" enum:"celsius,fahrenheit"`
}

reg.MustRegister(tools.Typed("weather", "Get the current weather.",
    func(ctx context.Context, a WeatherArgs) (gage.ToolResult, error) {
        return gage.TextResult("", fetchWeather(a.City, a.Unit)), nil
    }))

Gate every execution behind an approver. An approval can carry a denial reason (shown to the model), a rewritten input, and a "remember" flag:

import "github.com/deepteams/gage/policy"

// Secure allows local read-only filesystem tools and pauses network, shell,
// writes, MCP, memory mutations and unknown tools for out-of-band approval.
approver := policy.Secure()

Or provide your own application policy:

approver := gage.ApproverFunc(func(ctx context.Context, r gage.PermissionRequest) (gage.Approval, error) {
    if r.Metadata.ReadOnly {
        return gage.Approval{Allow: true, Remember: true}, nil
    }
    // Your app decides how to ask the user or apply policy. r includes:
    // Tool, Input, Agent, RunID, Turn, Metadata, and Summary.
    return askUserForApproval(ctx, r.Summary)
})
// gage.RememberingPerInput caches remembered decisions by tool+arguments.
ag, _ := agent.New(agent.Config{Provider: p, Registry: reg, Approver: gage.RememberingPerInput(approver)})

The agent validates tool arguments against each tool's JSON Schema before execution by default. Set agent.Config.DisableToolInputValidation only when you deliberately need raw, schema-incompatible inputs.

gage.Remembering is also available when you deliberately want broad caching by tool name. For write, shell, network, and other argument-sensitive tools, prefer RememberingPerInput or RememberingBy with an app-specific key.

Built-in tools expose advisory ToolMetadata (ReadOnly, Filesystem, Network, Shell, Destructive, RequiresApproval, Tags) and concise call summaries. Custom tools can implement gage.ToolMetadataProvider / gage.ToolCallDescriber, or use tools.FuncWithMetadata.

For production use, add a per-tool timeout and an observer for audit logs, metrics, or traces:

observer := agent.ObserverFunc(func(ctx context.Context, o agent.Observation) {
    log.Printf("run=%s type=%s tool=%s error=%v duration=%s",
        o.RunID, o.Type, o.Tool, o.IsError, o.Duration)
})

ag, _ := agent.New(agent.Config{
    Provider:    p,
    Registry:    reg,
    Approver:    approver,
    ToolTimeout: 30 * time.Second,
    Observer:    observer,
})

Tool panics are recovered and returned to the model as failed tool results. tools.LimitConcurrency caps concurrent executions of expensive tools, and tools.LimitResultSize keeps oversized results (MCP, custom tools) from blowing the context window:

reg.MustRegister(tools.LimitConcurrency(tools.NewBashTool(tools.BashConfig{Dir: "."}), 2))
reg.MustRegister(tools.LimitResultSize(myTool, 64<<10))

The agent loop

agent.Config controls the loop end to end:

ag, _ := agent.New(agent.Config{
    Provider:         provider,
    Registry:         reg,
    MaxParallelTools: 4,                    // run a turn's tool calls concurrently
    Compactor:        agent.Summarize(provider, "", 20), // or agent.Trim(30)
    CompactAfter:     150_000,              // input-token threshold
    Hooks: agent.Hooks{
        PreToolUse: func(ctx context.Context, tc gage.ToolCall) (gage.ToolCall, error) {
            return tc, nil // rewrite the input, or return an error to block
        },
        PostToolUse: func(ctx context.Context, tc gage.ToolCall, res gage.ToolResult) gage.ToolResult {
            return redact(res) // rewrite results before the model sees them
        },
    },
})

Every run ends with a terminal done event carrying a gage.Result — the full conversation, final text, stop reason, aggregated usage, and turn count — so multi-run conversations need no event bookkeeping. ag.RunSync(ctx, msgs) is the blocking shortcut when you don't need the stream.

websearch needs a SearchProvider. DuckDuckGo needs no key:

reg.MustRegister(tools.NewWebTools(tools.WebConfig{Search: duckduckgo.New()})...)

brave.New(key) and tavily.New(key) are drop-in alternatives.

webfetch blocks localhost, private, link-local, multicast and unspecified addresses by default, including redirects. For trusted local/internal use only, set tools.WebConfig{AllowPrivateHosts: true}.

bash sanitizes the environment, applies time/output limits, and kills the process group on timeout, but direct shell execution is not an operating-system sandbox. For untrusted commands, require an external sandbox:

bash := tools.NewBashTool(tools.BashConfig{
    RequireSandbox: true,
    Sandbox: tools.ExternalSandbox{
        Label:  "firejail",
        Binary: "firejail",
        Args:   []string{"--private", "--", "{{shell}}", "-c", "{{command}}"},
    },
})

Memory

import "github.com/deepteams/gage/memory"

mem := memory.New()
reg.MustRegister(memory.NewTools(mem)...)

Memories carry optional namespace, user_id, provenance, sensitivity, confidence, expires_at, and metadata fields. Recall can be scoped by namespace/user and hides expired memories by default. The built-in store uses simple keyword recall (or cosine similarity with memory.NewWithEmbedder) and is useful for tests and local agents. Production systems can implement gage.MemoryStore with a database, vector index, user-profile service, or tenant-scoped memory layer without changing the agent loop.

Durable Sessions

sessions.NewFileStore writes atomic 0600 JSON files. For local encrypted storage, use AES-GCM:

store, _ := sessions.NewEncryptedFileStore("./sessions", key32Bytes)

OAuth helpers have the same option:

tokenStore, _ := oauth.NewEncryptedFileStore("./tokens/codex.json", key32Bytes)

workflow.Runner wraps an agent with a SessionStore: completed runs persist their full conversation, and paused approval checkpoints are saved so another process can resume later.

runner := workflow.New(ag, store)
out, err := runner.Run(ctx, "session-1", []gage.Message{gage.UserText("ship it")})
if errors.Is(err, gage.ErrApprovalPending) {
    // Persisted already; collect decisions, then:
    out, err = runner.Resume(ctx, "session-1", decisions)
}

MCP

client, _ := mcp.ConnectStdio(ctx, mcp.StdioConfig{
    Name: "fs", Command: "npx", Args: []string{"-y", "@modelcontextprotocol/server-filesystem", "/data"},
},
    mcp.WithToolSync(reg),              // keep the registry in sync on tools/list_changed
    mcp.WithSamplingProvider(provider), // let the server sample through your Provider
)
defer client.Close()
client.Register(ctx, reg) // tools appear as "fs__<tool>"

resources, _ := client.Resources(ctx)          // list server resources
parts, _ := client.ReadResource(ctx, uri)      // text and images as ContentParts
msgs, _ := client.GetPrompt(ctx, "review", nil) // server prompts as []gage.Message

HTTP servers with auth:

client, _ := mcp.ConnectHTTP(ctx, mcp.HTTPConfig{
    Name: "api", Endpoint: "https://mcp.example.com",
    Headers: mcp.BearerHeaders(token),
})

Skills

set, _ := skills.LoadDir("./skills")   // folders each holding a SKILL.md
reg.MustRegister(skills.NewTool(set))  // the model can load a skill on demand
ag, _ := agent.New(agent.Config{Provider: p, Registry: reg, Skills: set})

Sub-agents

Any agent can be exposed as a tool for another agent:

researcher, _ := agent.New(agent.Config{Provider: p, Registry: researchReg, Name: "researcher"})
reg.MustRegister(researcher.AsTool("researcher", "Delegate research tasks."))

Serving an agent over HTTP (SSE)

gage gives you the handler; you mount and serve it:

h := httpx.StreamHandler(ag, func(r *http.Request) ([]gage.Message, error) {
    var body struct{ Prompt string `json:"prompt"` }
    if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
        return nil, err
    }
    return []gage.Message{gage.UserText(body.Prompt)}, nil
})
http.Handle("/agent", h)
http.ListenAndServe(":8080", nil) // your app owns this

Testing

Everything is tested against httptest/in-memory transports — no network access:

go test ./... -race

License

MIT. See LICENSE.

Documentation

Overview

Package gage is a provider-agnostic toolkit for building agentic systems in Go.

gage follows a hexagonal (ports & adapters) architecture: the root package defines the domain types (Message, ToolCall, Event, Usage, Pricing, Result...) and the ports (interfaces) that describe the capabilities the library needs — Provider (plus the optional ModelLister and TokenCounter capabilities), Tool, ToolRegistry, SearchProvider, Approver, Compactor, MemoryStore, Embedder, TokenStore and SessionStore. Concrete implementations (adapters) live in sub-packages and depend on the core, never the other way around:

  • providers/anthropic, providers/gemini, providers/openrouter, providers/vllm, providers/ollama, providers/codex, providers/claudecode implement Provider; providers/fallback chains several Providers for failover. providers/openai.Embeddings and providers/ollama.Embedder implement Embedder.
  • tools implements the built-in Tool set, Typed tools, and a ToolRegistry.
  • policy provides conservative Approver implementations for secure defaults.
  • search implements SearchProvider (duckduckgo, brave, tavily).
  • mcp bridges Model Context Protocol servers into Tools (plus resources, prompts, and sampling).
  • skills loads SKILL.md skill folders.
  • memory implements an in-memory MemoryStore and memory tools, with optional embedding-based recall.
  • jsonschema builds JSON Schema documents for tool parameters.
  • sessions implements SessionStore (in-memory and JSON files).
  • agent runs the agentic loop and streams Events.
  • workflow persists completed sessions and paused approval checkpoints around an agent.
  • structured decodes model output into typed Go values (Generate[T]).
  • pricing ships a dated per-model rate table for Pricing.Cost.
  • gagetest provides a scripted Provider for testing agents offline.
  • httpx exposes an agent over Server-Sent Events.
  • otel (nested module github.com/deepteams/gage/otel) maps agent observations onto OpenTelemetry spans.

Everything streams end to end: a Provider returns a channel of Event values, and the agent relays those events (plus its own tool-result events) to the caller. gage is a library — it never starts a server or owns a main function.

Example

Example wires a provider, a tool registry and an agent, then drains the event stream — the canonical way to embed gage in another program.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/deepteams/gage"
	"github.com/deepteams/gage/agent"
	"github.com/deepteams/gage/tools"
)

// echoProvider is a minimal Provider that asks to call the "shout" tool once,
// then reports the tool's result. It stands in for a real model backend so the
// example runs offline.
type echoProvider struct{ calls int }

func (p *echoProvider) Name() string { return "example" }

func (p *echoProvider) Stream(ctx context.Context, req gage.Request) (<-chan gage.Event, error) {
	ch := make(chan gage.Event)
	turn := p.calls
	p.calls++
	go func() {
		defer close(ch)
		ch <- gage.MessageStart()
		if turn == 0 {
			ch <- gage.ToolCallDone(gage.ToolCall{ID: "1", Name: "shout", Input: json.RawMessage(`{"text":"hi"}`)})
			ch <- gage.MessageDone("tool_use")
			return
		}
		// Second turn: echo the last tool result back as the final answer.
		last := req.Messages[len(req.Messages)-1]
		var toolText string
		for _, p := range last.Content {
			if p.Kind == gage.PartToolResult && p.ToolResult != nil {
				toolText = p.ToolResult.Text()
			}
		}
		ch <- gage.TextDelta("final: " + toolText)
		ch <- gage.MessageDone("end_turn")
	}()
	return ch, nil
}

// Example wires a provider, a tool registry and an agent, then drains the event
// stream — the canonical way to embed gage in another program.
func main() {
	reg := tools.NewRegistry()
	reg.MustRegister(tools.ToolFuncMust("shout", "uppercase the text",
		func(ctx context.Context, input json.RawMessage) (gage.ToolResult, error) {
			var a struct {
				Text string `json:"text"`
			}
			_ = json.Unmarshal(input, &a)
			return gage.TextResult("", "HELLO "+a.Text), nil
		}))

	ag, err := agent.New(agent.Config{
		Provider: &echoProvider{},
		Registry: reg,
		System:   "You are a helpful assistant.",
	})
	if err != nil {
		panic(err)
	}

	stream, err := ag.Run(context.Background(), []gage.Message{gage.UserText("shout hi")})
	if err != nil {
		panic(err)
	}

	for ev := range stream {
		switch ev.Type {
		case gage.EventToolResult:
			fmt.Println("tool:", ev.ToolResult.Text())
		case gage.EventTextDelta:
			fmt.Print(ev.Text)
		case gage.EventDone:
			fmt.Println()
		}
	}
}
Output:
tool: HELLO hi
final: HELLO hi

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrAuth indicates missing, invalid or expired credentials.
	ErrAuth = errors.New("gage: authentication failed")
	// ErrRateLimited indicates the provider throttled the request (HTTP 429).
	ErrRateLimited = errors.New("gage: rate limited")
	// ErrToolNotFound indicates a tool call referenced an unregistered tool.
	ErrToolNotFound = errors.New("gage: tool not found")
	// ErrMaxTurns indicates the agent loop hit its turn budget without a final answer.
	ErrMaxTurns = errors.New("gage: max turns exceeded")
	// ErrNoProvider indicates an agent was configured without a Provider.
	ErrNoProvider = errors.New("gage: no provider configured")
	// ErrUnsupported indicates an explicitly requested option (e.g. a
	// ResponseFormat or ToolChoice) that the provider cannot honor. Providers
	// fail fast with this error instead of silently dropping the option.
	ErrUnsupported = errors.New("gage: option not supported by this provider")
	// ErrBudgetExceeded indicates the agent run consumed its configured token
	// budget before producing a final answer.
	ErrBudgetExceeded = errors.New("gage: token budget exceeded")
	// ErrLoopDetected indicates the agent kept issuing the same tool call with
	// the same input past the configured repeat threshold.
	ErrLoopDetected = errors.New("gage: tool call loop detected")
	// ErrApprovalPending is returned by an Approver that cannot decide
	// synchronously (e.g. the decision belongs to a human reviewing out of
	// band). The agent then pauses the run: it emits an EventPaused carrying a
	// Checkpoint and closes the stream; the caller persists the checkpoint and
	// later resumes with the recorded decisions.
	ErrApprovalPending = errors.New("gage: approval pending")
	// ErrSessionNotFound indicates a SessionStore has no session for the id.
	ErrSessionNotFound = errors.New("gage: session not found")
)

Sentinel errors returned across the library. Callers can test them with errors.Is.

Functions

func CallSummaryOf

func CallSummaryOf(t Tool, input json.RawMessage) string

CallSummaryOf returns a short human-readable summary for a tool invocation.

func EstimateTextTokens

func EstimateTextTokens(s string) int

EstimateTextTokens roughly estimates the token count of a piece of text.

func EstimateTokens

func EstimateTokens(msgs []Message) int

EstimateTokens roughly estimates the total token count of a conversation. It is intentionally conservative and provider-agnostic: use it for thresholds (compaction, budgets), never for billing.

func ToolAndInputPermissionKey

func ToolAndInputPermissionKey(req PermissionRequest) string

ToolAndInputPermissionKey caches remembered decisions by tool name and canonical JSON input. It is the safer default for approvals of write, shell, network, and other argument-sensitive tools.

func ToolPermissionKey

func ToolPermissionKey(req PermissionRequest) string

ToolPermissionKey caches remembered decisions by tool name only. This is convenient for broad policies such as "always allow read-only tools", but it is too coarse for tools whose risk depends on arguments.

func Unsupported

func Unsupported(provider, option string) error

Unsupported builds an ErrUnsupported-wrapping error naming the provider and the offending option.

func ValidateToolInput

func ValidateToolInput(schema JSONSchema, input json.RawMessage) error

ValidateToolInput validates raw tool input against the JSON Schema subset gage emits for tools. Unsupported schema keywords are ignored deliberately: the goal is a portable safety net before Execute, not a full JSON Schema implementation.

Types

type APIError

type APIError struct {
	// Provider names the adapter that produced the error.
	Provider string
	// Status is the HTTP status code.
	Status int
	// Body is the (possibly truncated) response body for diagnostics.
	Body string
}

APIError wraps a non-2xx HTTP response from a provider or search backend.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Is

func (e *APIError) Is(target error) bool

Is lets APIError match the sentinel errors for common status codes so callers can write errors.Is(err, ErrAuth) / ErrRateLimited regardless of provider.

type Approval

type Approval struct {
	// Allow permits the tool execution; false blocks it and reports an error
	// result to the model.
	Allow bool
	// Reason is shown to the model on denial so it can adapt (optional).
	Reason string
	// UpdatedInput, if non-nil on an allowed call, replaces the tool input
	// before execution (e.g. a sanitized path or a narrowed command).
	UpdatedInput json.RawMessage
	// Remember asks the caller to reuse this decision for future invocations
	// of the same tool without consulting the Approver again (see Remembering).
	Remember bool
}

Approval is the outcome of a permission check.

func Allowed

func Allowed() Approval

Allowed is a convenience Approval that permits execution.

func Denied

func Denied(reason string) Approval

Denied is a convenience Approval that blocks execution with a reason.

type Approver

type Approver interface {
	Approve(ctx context.Context, req PermissionRequest) (Approval, error)
}

Approver decides whether a tool may run. It is invoked before every tool Execute when configured on an agent (or via the tools permission decorator).

func Remembering

func Remembering(inner Approver) Approver

Remembering wraps an Approver so decisions marked Remember are cached per tool name and reused without consulting the inner Approver again. It is concurrency-safe. The cache lives for the lifetime of the wrapper: scope it to a session by creating one wrapper per session.

For tools whose risk depends on their arguments, prefer RememberingPerInput or RememberingBy with a policy-specific key.

func RememberingBy

func RememberingBy(inner Approver, key PermissionCacheKeyFunc) Approver

RememberingBy wraps an Approver with a caller-defined cache key. Decisions are cached only when the approval has Remember set and key returns non-empty.

func RememberingPerInput

func RememberingPerInput(inner Approver) Approver

RememberingPerInput wraps an Approver so remembered decisions are cached by tool name plus canonical JSON input. This avoids reusing an approval for one path, command, URL, or payload on a different invocation of the same tool.

type ApproverFunc

type ApproverFunc func(ctx context.Context, req PermissionRequest) (Approval, error)

ApproverFunc adapts a function into an Approver.

func (ApproverFunc) Approve

type Checkpoint

type Checkpoint struct {
	// Messages is the conversation up to and including the assistant message
	// whose tool calls triggered the pause. Tool results of the paused turn
	// are NOT yet appended; they live in Results until the turn completes.
	Messages []Message `json:"messages"`
	// Turn is the loop iteration that paused.
	Turn int `json:"turn"`
	// Usage is the token usage accumulated up to the pause.
	Usage Usage `json:"usage"`
	// StopReason is the stop reason of the paused assistant message.
	StopReason StopReason `json:"stop_reason,omitempty"`
	// Calls are all tool calls of the paused turn, in the order the model
	// issued them.
	Calls []ToolCall `json:"calls"`
	// Results holds the results of the calls that already completed before
	// the pause (approved-and-executed or denied ones). Calls without a
	// matching CallID here are pending a decision.
	Results []ToolResult `json:"results,omitempty"`
}

Checkpoint captures a run suspended mid-turn because one or more tool calls await an out-of-band approval (the Approver returned ErrApprovalPending). It is fully JSON-serializable so callers can persist it (see SessionStore) and resume the run later — in another process if needed — with agent.Agent.Resume.

func (*Checkpoint) Pending

func (c *Checkpoint) Pending() []ToolCall

Pending returns the tool calls that still await a decision: the Calls with no matching entry in Results.

type Compactor

type Compactor interface {
	// Compact returns the replacement conversation. usage is the token usage
	// of the latest provider call, giving the current input size. The returned
	// Usage reports what the compaction itself consumed (zero for local
	// strategies, the summary call's usage for model-backed ones) so the agent
	// can account for it in the run total. Returning the input slice unchanged
	// (with a zero Usage and nil error) is a valid no-op.
	Compact(ctx context.Context, msgs []Message, usage Usage) ([]Message, Usage, error)
}

Compactor shrinks a conversation that is approaching the model's context window. The agent loop invokes it between turns when the configured token threshold is crossed; implementations may summarize old turns, drop them, or rewrite the history entirely.

Implementations must preserve the invariants providers rely on: every PartToolUse must keep its matching PartToolResult (drop or keep them as a pair), and the first message should remain a user message.

type CompactorFunc

type CompactorFunc func(ctx context.Context, msgs []Message, usage Usage) ([]Message, Usage, error)

CompactorFunc adapts a function into a Compactor.

func (CompactorFunc) Compact

func (f CompactorFunc) Compact(ctx context.Context, msgs []Message, usage Usage) ([]Message, Usage, error)

type ContentPart

type ContentPart struct {
	Kind       PartKind        `json:"kind"`
	Text       string          `json:"text,omitempty"`        // PartText / PartReasoning
	Image      *ImageSource    `json:"image,omitempty"`       // PartImage
	Document   *DocumentSource `json:"document,omitempty"`    // PartDocument
	ToolCall   *ToolCall       `json:"tool_call,omitempty"`   // PartToolUse
	ToolResult *ToolResult     `json:"tool_result,omitempty"` // PartToolResult
	// Signature is an opaque provider token attached to a PartReasoning that
	// must be replayed verbatim with the reasoning text for the provider to
	// accept the block in a later turn (Anthropic thinking signatures, OpenAI
	// Responses encrypted reasoning). Empty when the provider needs none.
	Signature string `json:"signature,omitempty"`
}

ContentPart is a tagged union: Kind selects which field is meaningful.

func DocumentPart

func DocumentPart(d DocumentSource) ContentPart

DocumentPart builds a document ContentPart.

func ReasoningPart

func ReasoningPart(s string) ContentPart

ReasoningPart builds a reasoning ContentPart.

func SignedReasoningPart

func SignedReasoningPart(s, signature string) ContentPart

SignedReasoningPart builds a reasoning ContentPart carrying the provider's replay signature.

func TextPart

func TextPart(s string) ContentPart

TextPart builds a text ContentPart.

func ToolResultPart

func ToolResultPart(tr ToolResult) ContentPart

ToolResultPart builds a tool-result ContentPart.

func ToolUsePart

func ToolUsePart(tc ToolCall) ContentPart

ToolUsePart builds a tool-use ContentPart.

type Credentials

type Credentials struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token,omitempty"`
	ExpiresAt    time.Time `json:"expires_at,omitzero"`
	// AccountID is a provider-specific account identifier (e.g. Codex's
	// chatgpt_account_id). Empty when not applicable.
	AccountID string `json:"account_id,omitempty"`
	// Extra carries any additional provider fields (scopes, token type, ...).
	Extra map[string]string `json:"extra,omitempty"`
}

Credentials holds OAuth tokens (and provider-specific extras) for a premium provider such as Codex or Claude Code.

func (Credentials) Expired

func (c Credentials) Expired(skew time.Duration, now time.Time) bool

Expired reports whether the access token is expired (or will be within skew). A zero ExpiresAt is treated as "never expires".

type DocumentSource

type DocumentSource struct {
	// URL references a remote document. Mutually exclusive with Data.
	URL string `json:"url,omitempty"`
	// MediaType is the MIME type of Data (e.g. "application/pdf").
	MediaType string `json:"media_type,omitempty"`
	// Data is base64-encoded document bytes. Mutually exclusive with URL.
	Data string `json:"data,omitempty"`
	// Filename optionally names the document; some providers display it to the
	// model or require it for format detection.
	Filename string `json:"filename,omitempty"`
}

DocumentSource carries a document (e.g. a PDF), either as a URL or inline base64 bytes. Providers that cannot accept documents fail the request with ErrUnsupported rather than silently dropping the part.

type Embedder

type Embedder interface {
	// Embed returns one vector per input text, in the same order. An empty
	// input returns an empty (or nil) slice. Implementations must not return
	// fewer vectors than inputs without an error.
	Embed(ctx context.Context, texts []string) ([][]float32, error)
	// Name identifies the embedder for telemetry and logs.
	Name() string
}

Embedder is the port for computing vector embeddings of text. Adapters live under providers/ (OpenAI-compatible APIs, Ollama); consumers can plug any implementation into retrieval layers such as memory.Store.

type Event

type Event struct {
	Type EventType `json:"type"`
	// Text holds delta text for EventTextDelta / EventReasoningDelta.
	Text string `json:"text,omitempty"`
	// ToolCall is set for EventToolCallStart/Delta/Done.
	ToolCall *ToolCall `json:"tool_call,omitempty"`
	// ToolResult is set for EventToolResult.
	ToolResult *ToolResult `json:"tool_result,omitempty"`
	// Usage is set for EventUsage.
	Usage *Usage `json:"usage,omitempty"`
	// StopReason is set for EventMessageDone.
	StopReason StopReason `json:"stop_reason,omitempty"`
	// Signature is set for EventReasoningDone when the provider requires the
	// reasoning block to be replayed with an opaque token.
	Signature string `json:"signature,omitempty"`
	// Result summarizes the whole run. It is set on the terminal EventDone
	// emitted by an agent (never by raw providers).
	Result *Result `json:"result,omitempty"`
	// Checkpoint is set on the terminal EventPaused emitted by an agent whose
	// run is suspended awaiting tool approval.
	Checkpoint *Checkpoint `json:"checkpoint,omitempty"`
	// Err is set for EventError. It is not serialized directly; use ErrorString.
	Err error `json:"-"`
	// ErrorString mirrors Err for JSON transports.
	ErrorString string `json:"error,omitempty"`
	// Turn is the agent loop iteration (0 for raw provider events).
	Turn int `json:"turn,omitempty"`
	// Raw carries the provider's raw event payload for debugging/extension.
	Raw json.RawMessage `json:"raw,omitempty"`
}

Event is the unified streaming unit produced by Providers and Agents. It is a tagged struct (rather than an interface) so it serializes cleanly to SSE/JSON and routes naturally through a select. Only the fields relevant to Type are populated.

func DoneEvent

func DoneEvent(res *Result) Event

DoneEvent builds an EventDone carrying the run summary.

func ErrorEvent

func ErrorEvent(err error) Event

ErrorEvent builds an EventError, populating both Err and ErrorString.

func MessageDone

func MessageDone(stopReason StopReason) Event

MessageDone builds an EventMessageDone.

func MessageStart

func MessageStart() Event

MessageStart builds an EventMessageStart.

func PausedEvent

func PausedEvent(cp *Checkpoint) Event

PausedEvent builds an EventPaused carrying the resume checkpoint.

func ReasoningDelta

func ReasoningDelta(s string) Event

ReasoningDelta builds an EventReasoningDelta.

func ReasoningDone

func ReasoningDone(signature string) Event

ReasoningDone builds an EventReasoningDone carrying the block's replay signature (may be empty).

func TextDelta

func TextDelta(s string) Event

TextDelta builds an EventTextDelta.

func ToolCallDelta

func ToolCallDelta(tc ToolCall) Event

ToolCallDelta builds an EventToolCallDelta.

func ToolCallDone

func ToolCallDone(tc ToolCall) Event

ToolCallDone builds an EventToolCallDone.

func ToolCallStart

func ToolCallStart(tc ToolCall) Event

ToolCallStart builds an EventToolCallStart.

func ToolResultEvent

func ToolResultEvent(tr ToolResult) Event

ToolResultEvent builds an EventToolResult.

func UsageEvent

func UsageEvent(u Usage) Event

UsageEvent builds an EventUsage.

func (Event) WithTurn

func (e Event) WithTurn(turn int) Event

WithTurn returns a copy of the event tagged with the given loop turn.

type EventType

type EventType string

EventType tags a streaming Event.

const (
	// EventMessageStart marks the beginning of an assistant message.
	EventMessageStart EventType = "message_start"
	// EventTextDelta carries a chunk of visible assistant text.
	EventTextDelta EventType = "text_delta"
	// EventReasoningDelta carries a chunk of reasoning/thinking text.
	EventReasoningDelta EventType = "reasoning_delta"
	// EventReasoningDone closes a reasoning block. Signature carries the
	// provider's opaque replay token for the block, when one exists. Providers
	// that stream reasoning without block boundaries may omit this event.
	EventReasoningDone EventType = "reasoning_done"
	// EventToolCallStart signals a tool call has begun; ToolCall has ID+Name.
	EventToolCallStart EventType = "tool_call_start"
	// EventToolCallDelta carries a partial chunk of the tool call arguments;
	// ToolCall.Input holds the accumulated JSON so far.
	EventToolCallDelta EventType = "tool_call_delta"
	// EventToolCallDone signals the tool call arguments are complete.
	EventToolCallDone EventType = "tool_call_end"
	// EventToolResult carries the result of executing a tool (emitted by the agent).
	EventToolResult EventType = "tool_result"
	// EventUsage carries a token-usage update.
	EventUsage EventType = "usage"
	// EventMessageDone marks the end of an assistant message with a StopReason.
	EventMessageDone EventType = "message_done"
	// EventError carries a terminal error; the stream closes after it.
	EventError EventType = "error"
	// EventPaused marks a run suspended awaiting out-of-band tool approval
	// (emitted by the agent; the stream closes after it). Checkpoint carries
	// everything needed to resume the run later.
	EventPaused EventType = "paused"
	// EventDone marks the end of the entire stream (emitted by the agent).
	EventDone EventType = "done"
)

type GenerateOptions

type GenerateOptions struct {
	Temperature     *float64
	TopP            *float64
	MaxTokens       int
	StopSequences   []string
	ToolChoice      *ToolChoice
	ReasoningEffort ReasoningEffort
	// ResponseFormat constrains the model output (JSON mode / JSON Schema).
	ResponseFormat *ResponseFormat
	// PromptCache asks the provider to mark stable prefixes (system prompt,
	// tool schemas, conversation head) as cacheable. It is a hint: providers
	// with implicit caching (OpenAI) ignore it, providers with explicit
	// breakpoints (Anthropic cache_control) act on it. It never fails.
	PromptCache bool
	// Extra passes provider-specific fields verbatim into the request body.
	// Keys are merged at the top level; use with care.
	Extra map[string]any
}

GenerateOptions carries per-request generation parameters. All fields are optional; providers apply their own defaults for zero values. Pointer fields distinguish "unset" from a meaningful zero (e.g. Temperature 0).

func ApplyOptions

func ApplyOptions(base GenerateOptions, opts ...Option) GenerateOptions

ApplyOptions builds a GenerateOptions from a base value and a set of Options.

type ImageSource

type ImageSource struct {
	// URL references a remote image. Mutually exclusive with Data.
	URL string `json:"url,omitempty"`
	// MediaType is the MIME type of Data (e.g. "image/png").
	MediaType string `json:"media_type,omitempty"`
	// Data is base64-encoded image bytes. Mutually exclusive with URL.
	Data string `json:"data,omitempty"`
}

ImageSource carries image data, either as a URL or inline base64 bytes.

type JSONSchema

type JSONSchema = json.RawMessage

JSONSchema is a JSON Schema document describing a tool's parameters. It is a raw JSON message so callers can supply any valid schema without a struct dependency; the jsonschema package offers helpers to build common shapes.

type Memory

type Memory struct {
	ID          string            `json:"id"`
	Text        string            `json:"text"`
	Metadata    map[string]string `json:"metadata,omitempty"`
	CreatedAt   time.Time         `json:"created_at,omitempty"`
	Namespace   string            `json:"namespace,omitempty"`
	UserID      string            `json:"user_id,omitempty"`
	Provenance  string            `json:"provenance,omitempty"`
	Sensitivity string            `json:"sensitivity,omitempty"`
	Confidence  float64           `json:"confidence,omitempty"`
	ExpiresAt   time.Time         `json:"expires_at,omitempty"`
}

Memory is one durable fact, preference, decision, or note an agent may use across runs. Stores may attach their own IDs when the caller leaves ID empty.

type MemoryQuery

type MemoryQuery struct {
	Query          string            `json:"query,omitempty"`
	Limit          int               `json:"limit,omitempty"`
	Metadata       map[string]string `json:"metadata,omitempty"`
	Namespace      string            `json:"namespace,omitempty"`
	UserID         string            `json:"user_id,omitempty"`
	IncludeExpired bool              `json:"include_expired,omitempty"`
}

MemoryQuery describes a recall request. Query is interpreted by the concrete store (keyword search, vector search, SQL full text, ...). Metadata entries, when set, are exact-match filters. Limit <= 0 lets the store choose a default.

type MemoryStore

type MemoryStore interface {
	// Remember saves m and returns the stored record, including generated ID and
	// CreatedAt values when the store owns them.
	Remember(ctx context.Context, m Memory) (Memory, error)
	// Recall returns memories relevant to q, newest/relevant first.
	Recall(ctx context.Context, q MemoryQuery) ([]Memory, error)
	// Forget removes one memory. Deleting a missing memory is a no-op.
	Forget(ctx context.Context, id string) error
}

MemoryStore persists and retrieves long-lived agent memories. The memory package provides a small in-memory implementation and tools; production apps can back this port with a database, vector index, or user-profile service.

type Message

type Message struct {
	Role    Role          `json:"role"`
	Content []ContentPart `json:"content"`
	// Name is an optional participant/tool name (provider-dependent).
	Name string `json:"name,omitempty"`
}

Message is a single turn of the conversation.

func AssistantText

func AssistantText(s string) Message

AssistantText is a convenience constructor for a plain assistant message.

func ToolResultMessage

func ToolResultMessage(tr ToolResult) Message

ToolResultMessage builds a RoleTool message carrying one tool result.

func UserText

func UserText(s string) Message

UserText is a convenience constructor for a plain user message.

func (Message) Text

func (m Message) Text() string

Text returns the concatenation of all text parts (ignoring reasoning).

func (Message) ToolCalls

func (m Message) ToolCalls() []ToolCall

ToolCalls returns the tool-use parts of the message, if any.

type ModelInfo

type ModelInfo struct {
	// ID is the provider-specific model identifier (e.g. "anthropic/claude-3.5").
	ID string `json:"id"`
	// Name is a human-friendly label, when available.
	Name string `json:"name,omitempty"`
	// ContextWindow is the max total tokens, when known.
	ContextWindow int `json:"context_window,omitempty"`
	// MaxOutputTokens is the max tokens the model can emit, when known.
	MaxOutputTokens int `json:"max_output_tokens,omitempty"`
}

ModelInfo describes a model advertised by a provider.

type ModelLister

type ModelLister interface {
	Models(ctx context.Context) ([]ModelInfo, error)
}

ModelLister is an optional capability: a provider that can enumerate models.

type ModelRef

type ModelRef struct {
	Provider string `json:"provider"`
	Model    string `json:"model"`
}

ModelRef points at a model on a given provider. It is a convenience for callers that route across providers; the library itself only needs the Model string inside a Request.

func (ModelRef) String

func (r ModelRef) String() string

type Option

type Option func(*GenerateOptions)

Option mutates GenerateOptions. Providers and the agent accept variadic Options for ergonomic configuration.

func WithExtra

func WithExtra(key string, value any) Option

WithExtra sets a provider-specific field.

func WithJSONSchema

func WithJSONSchema(name string, schema JSONSchema) Option

WithJSONSchema constrains the output to a named JSON Schema (strict).

func WithMaxTokens

func WithMaxTokens(n int) Option

WithMaxTokens caps the number of generated tokens.

func WithPromptCache

func WithPromptCache() Option

WithPromptCache enables prompt-cache breakpoints on providers that support explicit caching.

func WithReasoningEffort

func WithReasoningEffort(e ReasoningEffort) Option

WithReasoningEffort sets the reasoning effort hint.

func WithResponseFormat

func WithResponseFormat(rf ResponseFormat) Option

WithResponseFormat constrains the model output.

func WithStopSequences

func WithStopSequences(seqs ...string) Option

WithStopSequences sets stop sequences.

func WithTemperature

func WithTemperature(t float64) Option

WithTemperature sets the sampling temperature.

func WithToolChoice

func WithToolChoice(tc ToolChoice) Option

WithToolChoice constrains tool selection.

func WithTopP

func WithTopP(p float64) Option

WithTopP sets nucleus sampling.

type PartKind

type PartKind string

PartKind is the discriminator of a ContentPart union.

const (
	PartText       PartKind = "text"
	PartImage      PartKind = "image"
	PartDocument   PartKind = "document"
	PartToolUse    PartKind = "tool_use"
	PartToolResult PartKind = "tool_result"
	PartReasoning  PartKind = "reasoning"
)

type PermissionCacheKeyFunc

type PermissionCacheKeyFunc func(req PermissionRequest) string

PermissionCacheKeyFunc derives the cache key used by RememberingBy. Returning an empty key disables caching for that request.

type PermissionRequest

type PermissionRequest struct {
	// Tool is the tool being invoked.
	Tool string
	// Input is the raw JSON arguments.
	Input json.RawMessage
	// Agent is the name of the agent requesting execution (may be empty).
	Agent string
	// RunID identifies the agent run requesting execution (may be empty when
	// approval is performed outside agent.Agent).
	RunID string
	// Turn is the agent loop iteration requesting execution. A zero value can be
	// either the first turn or unknown when approval is performed outside
	// agent.Agent.
	Turn int
	// Metadata carries advisory information about the tool's effects.
	Metadata ToolMetadata
	// Summary is a short human-readable description of the concrete invocation.
	Summary string
}

PermissionRequest describes a tool execution awaiting approval.

type Pricing

type Pricing struct {
	// InputPerMTok is the rate for non-cached input tokens.
	InputPerMTok float64 `json:"input_per_mtok,omitempty"`
	// OutputPerMTok is the rate for output tokens (reasoning tokens are billed
	// as output by providers that report them).
	OutputPerMTok float64 `json:"output_per_mtok,omitempty"`
	// CacheReadPerMTok is the rate for prompt-cache reads.
	CacheReadPerMTok float64 `json:"cache_read_per_mtok,omitempty"`
	// CacheWritePerMTok is the rate for prompt-cache writes.
	CacheWritePerMTok float64 `json:"cache_write_per_mtok,omitempty"`
}

Pricing holds a model's USD rates per million tokens. Zero-valued fields simply contribute nothing, so a table can fill only the rates it knows. The pricing sub-package ships a dated snapshot for common models; rates drift, so treat any built-in table as a default to override, never as a billing source of truth.

func (Pricing) Cost

func (p Pricing) Cost(u Usage) float64

Cost returns the USD cost of u at rates p. Usage fields a provider does not report are zero and cost nothing.

type Provider

type Provider interface {
	// Stream starts a generation and returns a read-only channel of Events. The
	// channel is closed when generation finishes (after EventMessageDone) or on
	// a terminal error (after EventError). Cancelling ctx must stop the stream
	// and close the channel. Stream returns an error only for failures that
	// occur before streaming begins (e.g. request construction, initial dial).
	Stream(ctx context.Context, req Request) (<-chan Event, error)
	// Name identifies the provider for telemetry and logs.
	Name() string
}

Provider is the core port for a model backend. Implementations map a Request onto their wire protocol and stream the response back as Events.

type ReasoningEffort

type ReasoningEffort string

ReasoningEffort hints how much internal reasoning the model should spend, for providers that support it (Codex/Responses, Anthropic thinking, etc.).

It is an open string, not a closed enum: gateways (llm-router, vLLM, OpenRouter) publish their own levels per model, and OpenAI-compatible providers forward the value verbatim. The constants below are the portable levels, ordered from least to most reasoning; Canonical folds the spellings seen in the wild onto them so providers that need a thinking-token budget (anthropic, gemini) can still map an arbitrary label.

const (
	// ReasoningNone leaves the effort unset: the provider's own default applies
	// and nothing is sent on the wire.
	ReasoningNone ReasoningEffort = ""
	// ReasoningOff asks for reasoning to be disabled explicitly, for providers
	// that can say so (Anthropic thinking.disabled, Gemini budget 0, ollama
	// think:false, OpenAI "none").
	ReasoningOff     ReasoningEffort = "none"
	ReasoningMinimal ReasoningEffort = "minimal"
	ReasoningLow     ReasoningEffort = "low"
	ReasoningMedium  ReasoningEffort = "medium"
	ReasoningHigh    ReasoningEffort = "high"
	ReasoningXHigh   ReasoningEffort = "xhigh"
	ReasoningMax     ReasoningEffort = "max"
)

func (ReasoningEffort) Canonical

func (e ReasoningEffort) Canonical() (level ReasoningEffort, ok bool)

Canonical folds e onto one of the portable levels. ok is false when the label is not recognized: OpenAI-compatible providers pass such values through verbatim (the gateway or backend knows them), while providers that must translate the effort into a budget fail with ErrUnsupported rather than silently dropping it.

type Request

type Request struct {
	// Model is the provider-specific model identifier. May be empty when the
	// provider is pinned to a single model.
	Model string
	// Messages is the conversation history (excluding the System prompt).
	Messages []Message
	// Tools are the schemas advertised to the model.
	Tools []ToolSchema
	// System is the system prompt (may be empty).
	System string
	// Options are the generation parameters.
	Options GenerateOptions
}

Request is everything a provider needs to produce one assistant turn.

type ResponseFormat

type ResponseFormat struct {
	Type ResponseFormatType `json:"type"`
	// Name labels the schema (required by some providers for json_schema).
	Name string `json:"name,omitempty"`
	// Schema is the JSON Schema of the expected output (json_schema only).
	Schema JSONSchema `json:"schema,omitempty"`
	// Strict requests exact schema adherence where the provider supports it.
	Strict bool `json:"strict,omitempty"`
}

ResponseFormat constrains the shape of the model's final answer (structured output). Providers that cannot honor an explicitly requested format must fail the request with ErrUnsupported rather than silently ignore it.

type ResponseFormatType

type ResponseFormatType string

ResponseFormatType selects how the model's final answer is constrained.

const (
	// ResponseText is the default free-form output.
	ResponseText ResponseFormatType = "text"
	// ResponseJSON asks for syntactically valid JSON without a schema.
	ResponseJSON ResponseFormatType = "json"
	// ResponseJSONSchema constrains the output to Schema.
	ResponseJSONSchema ResponseFormatType = "json_schema"
)

type Result

type Result struct {
	// Messages is the full conversation: the input messages followed by every
	// assistant message and tool result produced during the run.
	Messages []Message `json:"messages"`
	// Text is the text of the final assistant message.
	Text string `json:"text"`
	// StopReason is the stop reason of the final assistant message.
	StopReason StopReason `json:"stop_reason,omitempty"`
	// Usage is the token usage accumulated across every provider call of the run.
	Usage Usage `json:"usage"`
	// Turns is the number of provider calls the run made.
	Turns int `json:"turns"`
}

Result summarizes a completed agent run. It travels on the terminal EventDone so streaming consumers get it for free, and is also returned by the blocking helpers.

func (*Result) LastAssistant

func (r *Result) LastAssistant() (Message, bool)

LastAssistant returns the final assistant message of the run, if any.

type Role

type Role string

Role identifies who produced a Message.

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

type SearchProvider

type SearchProvider interface {
	// Search returns up to limit results for the query. A limit <= 0 means the
	// implementation's default.
	Search(ctx context.Context, query string, limit int) ([]SearchResult, error)
}

SearchProvider is the port behind the websearch tool. Implementations live in the search sub-packages (duckduckgo, brave, tavily) or are supplied by the consumer.

type SearchResult

type SearchResult struct {
	Title   string `json:"title"`
	URL     string `json:"url"`
	Snippet string `json:"snippet"`
}

SearchResult is a single web search hit.

type Session

type Session struct {
	// Messages is the conversation so far (typically Result.Messages after a
	// completed run).
	Messages []Message `json:"messages"`
	// Checkpoint is non-nil while a run is paused awaiting tool approval.
	Checkpoint *Checkpoint `json:"checkpoint,omitempty"`
}

Session is the persistable state of a conversation with an agent: the message history and, when a run is suspended awaiting approval, the resume checkpoint.

type SessionStore

type SessionStore interface {
	// SaveSession persists the session under id, replacing any previous value.
	SaveSession(ctx context.Context, id string, s Session) error
	// LoadSession returns the stored session. It returns an error wrapping
	// ErrSessionNotFound when no session exists for id.
	LoadSession(ctx context.Context, id string) (Session, error)
	// DeleteSession removes the session. Deleting a missing session is a no-op.
	DeleteSession(ctx context.Context, id string) error
	// ListSessions returns the ids of all stored sessions.
	ListSessions(ctx context.Context) ([]string, error)
}

SessionStore persists and retrieves Sessions. The consumer implements it (database, KV store, ...); the sessions package provides in-memory and file-based implementations as conveniences. Implementations must be safe for concurrent use.

type StopReason

type StopReason string

StopReason explains why a generation ended. Providers normalize their wire values onto these constants; unknown values pass through verbatim.

const (
	// StopEndTurn is the normal completion of an assistant message.
	StopEndTurn StopReason = "end_turn"
	// StopToolUse means the model stopped to call one or more tools.
	StopToolUse StopReason = "tool_use"
	// StopMaxTokens means generation was truncated by the token limit.
	StopMaxTokens StopReason = "max_tokens"
	// StopSequence means a configured stop sequence was hit.
	StopSequence StopReason = "stop_sequence"
	// StopContentFilter means the provider suppressed the output.
	StopContentFilter StopReason = "content_filter"
	// StopRefusal means the model refused to answer.
	StopRefusal StopReason = "refusal"
)

func (StopReason) Truncated

func (s StopReason) Truncated() bool

Truncated reports whether the message ended before the model was done.

type TokenCounter

type TokenCounter interface {
	CountTokens(ctx context.Context, req Request) (int, error)
}

TokenCounter is an optional capability: a provider that can count the exact input tokens of a request through its API (Anthropic count_tokens, Gemini countTokens). It costs an extra HTTP round-trip; EstimateTokens remains the free heuristic when precision is not required.

type TokenStore

type TokenStore interface {
	// Load returns the stored credentials. It should return a wrapped ErrAuth
	// when no credentials are available.
	Load(ctx context.Context) (Credentials, error)
	// Save persists refreshed credentials.
	Save(ctx context.Context, c Credentials) error
}

TokenStore persists and retrieves Credentials. The consumer implements it (database, keychain, encrypted file, ...); gage provides an optional file store in providers/shared/oauth as a convenience. Implementations must be safe for concurrent use.

type Tool

type Tool interface {
	// Name is the identifier the model uses to call the tool.
	Name() string
	// Description tells the model what the tool does and when to use it.
	Description() string
	// Schema returns the JSON Schema of the tool's input parameters.
	Schema() JSONSchema
	// Execute runs the tool with the given raw JSON input and returns its
	// result. Returning a non-nil error is reserved for infrastructure failures;
	// tool-level failures should be reported via a ToolResult with IsError set,
	// so the model can see and react to them.
	Execute(ctx context.Context, input json.RawMessage) (ToolResult, error)
}

Tool is the executable port for a capability the model can invoke.

type ToolCall

type ToolCall struct {
	// ID uniquely identifies this call within a message; used to correlate the
	// result. Providers that do not supply one get a generated id.
	ID string `json:"id"`
	// Name is the tool being called.
	Name string `json:"name"`
	// Input holds the raw JSON arguments. It may be built incrementally while
	// streaming (see Event) and is only guaranteed complete on EventToolCallDone.
	Input json.RawMessage `json:"input"`
}

ToolCall is a request from the model to invoke a tool.

type ToolCallDescriber

type ToolCallDescriber interface {
	DescribeCall(input json.RawMessage) string
}

ToolCallDescriber is an optional capability implemented by tools that can summarize a concrete invocation for approval UIs and audit logs.

type ToolChoice

type ToolChoice struct {
	Mode ToolChoiceMode `json:"mode"`
	Name string         `json:"name,omitempty"` // used when Mode == ToolChoiceTool
}

ToolChoice expresses a tool-selection constraint for a request.

type ToolChoiceMode

type ToolChoiceMode string

ToolChoiceMode controls whether/which tool the model must call.

const (
	// ToolChoiceAuto lets the model decide (default).
	ToolChoiceAuto ToolChoiceMode = "auto"
	// ToolChoiceNone forbids tool calls.
	ToolChoiceNone ToolChoiceMode = "none"
	// ToolChoiceRequired forces the model to call some tool.
	ToolChoiceRequired ToolChoiceMode = "required"
	// ToolChoiceTool forces a specific tool named by ToolChoice.Name.
	ToolChoiceTool ToolChoiceMode = "tool"
)

type ToolFunc

type ToolFunc struct {
	ToolName    string
	Desc        string
	Params      JSONSchema
	Meta        ToolMetadata
	CallSummary func(input json.RawMessage) string
	Fn          func(ctx context.Context, input json.RawMessage) (ToolResult, error)
}

ToolFunc adapts a plain function into a Tool. It is handy for defining ad-hoc tools without a dedicated type.

func (ToolFunc) DescribeCall

func (t ToolFunc) DescribeCall(input json.RawMessage) string

func (ToolFunc) Description

func (t ToolFunc) Description() string

func (ToolFunc) Execute

func (t ToolFunc) Execute(ctx context.Context, input json.RawMessage) (ToolResult, error)

func (ToolFunc) Metadata

func (t ToolFunc) Metadata() ToolMetadata

func (ToolFunc) Name

func (t ToolFunc) Name() string

func (ToolFunc) Schema

func (t ToolFunc) Schema() JSONSchema

type ToolMetadata

type ToolMetadata struct {
	// ReadOnly reports that the tool is expected not to mutate external state.
	ReadOnly bool `json:"read_only,omitempty"`
	// Filesystem reports that the tool reads or writes the local filesystem.
	Filesystem bool `json:"filesystem,omitempty"`
	// Network reports that the tool can access network resources.
	Network bool `json:"network,omitempty"`
	// Shell reports that the tool can execute shell commands or subprocesses.
	Shell bool `json:"shell,omitempty"`
	// Destructive reports that the tool may delete, overwrite, or otherwise
	// irreversibly change state.
	Destructive bool `json:"destructive,omitempty"`
	// LongRunning reports that the tool may naturally run for a while.
	LongRunning bool `json:"long_running,omitempty"`
	// RequiresApproval is an advisory hint for clients that want a conservative
	// default policy.
	RequiresApproval bool `json:"requires_approval,omitempty"`
	// Tags are free-form labels for client policy and UI grouping.
	Tags []string `json:"tags,omitempty"`
}

ToolMetadata describes a tool's broad operational effects. It is advisory: callers can use it in Approvers, UI prompts, audit logs, and policy engines, but gage does not impose a policy from these flags.

func MetadataOf

func MetadataOf(t Tool) ToolMetadata

MetadataOf returns a tool's advisory metadata, if provided.

type ToolMetadataProvider

type ToolMetadataProvider interface {
	Metadata() ToolMetadata
}

ToolMetadataProvider is an optional capability implemented by tools that can describe their operational effects.

type ToolRegistry

type ToolRegistry interface {
	// Register adds a tool. It returns an error if a tool with the same name is
	// already registered.
	Register(t Tool) error
	// Unregister removes the tool with the given name, reporting whether it was
	// present. It enables dynamic tool sets (e.g. MCP tools/list_changed).
	Unregister(name string) bool
	// Get returns the tool with the given name.
	Get(name string) (Tool, bool)
	// List returns all registered tools.
	List() []Tool
	// Schemas returns the ToolSchema of every registered tool, for Request.Tools.
	Schemas() []ToolSchema
}

ToolRegistry holds the tools available to an agent and exposes their schemas.

type ToolResult

type ToolResult struct {
	// CallID matches the originating ToolCall.ID.
	CallID string `json:"call_id"`
	// Content is the result payload, most often a single text part.
	Content []ContentPart `json:"content"`
	// IsError reports that the tool failed; the content describes the error.
	IsError bool `json:"is_error,omitempty"`
}

ToolResult is the outcome of executing a ToolCall.

func ErrorResult

func ErrorResult(callID, msg string) ToolResult

ErrorResult builds a failed ToolResult carrying an error message.

func TextResult

func TextResult(callID, text string) ToolResult

TextResult builds a successful ToolResult carrying a single text part.

func (ToolResult) Text

func (r ToolResult) Text() string

Text returns the concatenated text content of the result.

type ToolSchema

type ToolSchema struct {
	Name        string     `json:"name"`
	Description string     `json:"description"`
	Parameters  JSONSchema `json:"parameters"`
}

ToolSchema is the declaration of a tool exposed to the model. It is distinct from the executable Tool port: a Provider only needs the schema to advertise the tool, while the agent needs the Tool to run it.

func SchemaOf

func SchemaOf(t Tool) ToolSchema

SchemaOf builds a ToolSchema from a Tool.

type Usage

type Usage struct {
	InputTokens      int `json:"input_tokens"`
	OutputTokens     int `json:"output_tokens"`
	ReasoningTokens  int `json:"reasoning_tokens,omitempty"`
	CacheReadTokens  int `json:"cache_read_tokens,omitempty"`
	CacheWriteTokens int `json:"cache_write_tokens,omitempty"`
}

Usage reports token accounting for a generation. Fields are zero when the provider does not report them.

func (Usage) Add

func (u Usage) Add(o Usage) Usage

Add returns the element-wise sum of two Usage values. It is used to accumulate usage across the turns of an agentic loop.

func (Usage) Total

func (u Usage) Total() int

Total returns the sum of input and output tokens (reasoning included in output by most providers).

Directories

Path Synopsis
Package agent runs the agentic loop: it calls a gage.Provider, executes the tool calls the model requests, feeds the results back, and iterates until the model produces a final answer or a limit is reached.
Package agent runs the agentic loop: it calls a gage.Provider, executes the tool calls the model requests, feeds the results back, and iterates until the model produces a final answer or a limit is reached.
Package gagetest provides a scripted, in-memory gage.Provider for testing agents built on gage without any network access.
Package gagetest provides a scripted, in-memory gage.Provider for testing agents built on gage without any network access.
Package httpx exposes a gage agent's event stream over HTTP using Server-Sent Events.
Package httpx exposes a gage agent's event stream over HTTP using Server-Sent Events.
Package jsonschema provides small helpers to build JSON Schema documents for tool parameters without pulling in a full schema library.
Package jsonschema provides small helpers to build JSON Schema documents for tool parameters without pulling in a full schema library.
Package mcp bridges Model Context Protocol servers into gage: it connects to a server over stdio or streamable HTTP, discovers its tools, and adapts each one to the gage.Tool port so it can be registered on an agent.
Package mcp bridges Model Context Protocol servers into gage: it connects to a server over stdio or streamable HTTP, discovers its tools, and adapts each one to the gage.Tool port so it can be registered on an agent.
Package memory provides a small in-memory gage.MemoryStore implementation and agent tools for long-lived memories.
Package memory provides a small in-memory gage.MemoryStore implementation and agent tools for long-lived memories.
Package policy provides conservative Approver implementations for agents that expose model-driven tools.
Package policy provides conservative Approver implementations for agents that expose model-driven tools.
Package pricing provides model-keyed pricing tables for estimating the USD cost of gage.Usage values.
Package pricing provides model-keyed pricing tables for estimating the USD cost of gage.Usage values.
providers
anthropic
Package anthropic implements the Anthropic Messages API wire format: the request encoder, the SSE stream pump, and a gage.Provider that authenticates with a plain API key.
Package anthropic implements the Anthropic Messages API wire format: the request encoder, the SSE stream pump, and a gage.Provider that authenticates with a plain API key.
claudecode
Package claudecode implements a gage.Provider that uses a Claude subscription via Anthropic's OAuth (PKCE) flow against the Messages API, presenting itself as the Claude Code CLI.
Package claudecode implements a gage.Provider that uses a Claude subscription via Anthropic's OAuth (PKCE) flow against the Messages API, presenting itself as the Claude Code CLI.
codex
Package codex implements a gage.Provider that uses a ChatGPT/Codex plan via OpenAI's OAuth (PKCE) flow and the Responses API backend.
Package codex implements a gage.Provider that uses a ChatGPT/Codex plan via OpenAI's OAuth (PKCE) flow and the Responses API backend.
fallback
Package fallback provides a gage.Provider that tries a sequence of providers in order, failing over to the next one when a provider errors before producing any content.
Package fallback provides a gage.Provider that tries a sequence of providers in order, failing over to the next one when a provider errors before producing any content.
gemini
Package gemini implements a native Google Gemini provider over the generativelanguage.googleapis.com REST API.
Package gemini implements a native Google Gemini provider over the generativelanguage.googleapis.com REST API.
ollama
Package ollama implements a gage.Provider backed by a local Ollama server.
Package ollama implements a gage.Provider backed by a local Ollama server.
openai
Package openai implements the OpenAI-compatible wire formats reused by several providers: the Chat Completions API (chat.go) and the Responses API (responses.go).
Package openai implements the OpenAI-compatible wire formats reused by several providers: the Chat Completions API (chat.go) and the Responses API (responses.go).
openrouter
Package openrouter implements a gage.Provider backed by the OpenRouter API (https://openrouter.ai), which speaks the OpenAI Chat Completions protocol.
Package openrouter implements a gage.Provider backed by the OpenRouter API (https://openrouter.ai), which speaks the OpenAI Chat Completions protocol.
shared
Package shared holds infrastructure reused by the concrete providers: an HTTP client with retry, and an SSE stream parser.
Package shared holds infrastructure reused by the concrete providers: an HTTP client with retry, and an SSE stream parser.
shared/oauth
Package oauth provides the PKCE flow helpers, an in-memory/file TokenStore, and a refreshing token source shared by the Codex and Claude Code providers.
Package oauth provides the PKCE flow helpers, an in-memory/file TokenStore, and a refreshing token source shared by the Codex and Claude Code providers.
vllm
Package vllm implements a gage.Provider backed by a vLLM server, which exposes the OpenAI Chat Completions protocol at <baseURL>/v1.
Package vllm implements a gage.Provider backed by a vLLM server, which exposes the OpenAI Chat Completions protocol at <baseURL>/v1.
search
brave
Package brave implements gage.SearchProvider using the Brave Search API, which requires an API key (a free tier is available).
Package brave implements gage.SearchProvider using the Brave Search API, which requires an API key (a free tier is available).
duckduckgo
Package duckduckgo implements gage.SearchProvider against DuckDuckGo's HTML "lite" endpoint, which requires no API key.
Package duckduckgo implements gage.SearchProvider against DuckDuckGo's HTML "lite" endpoint, which requires no API key.
tavily
Package tavily implements gage.SearchProvider using the Tavily API, a search service optimized for LLMs.
Package tavily implements gage.SearchProvider using the Tavily API, a search service optimized for LLMs.
Package sessions provides gage.SessionStore implementations: an in-memory store for tests and single-process use, and a JSON file store for simple durable persistence.
Package sessions provides gage.SessionStore implementations: an in-memory store for tests and single-process use, and a JSON file store for simple durable persistence.
Package skills loads Claude Code-style SKILL.md skill folders and exposes them to an agent: their name+description are advertised in the system prompt, and a "skill" tool loads a skill's full body on demand.
Package skills loads Claude Code-style SKILL.md skill folders and exposes them to an agent: their name+description are advertised in the system prompt, and a "skill" tool loads a skill's full body on demand.
Package structured turns model output into typed Go values.
Package structured turns model output into typed Go values.
Package tools provides the built-in gage.Tool set (filesystem, shell, search, web) and a concurrency-safe ToolRegistry implementation.
Package tools provides the built-in gage.Tool set (filesystem, shell, search, web) and a concurrency-safe ToolRegistry implementation.
Package workflow adds durable session/checkpoint handling around an agent.
Package workflow adds durable session/checkpoint handling around an agent.

Jump to

Keyboard shortcuts

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