Help us shape the future of Go! Take the 2026 Go Developer Survey

loop

package
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Overview

Package loop is celeste's one tool loop (2.0 F2): every mode streams a request, runs the model's tool calls and feeds the results back through Loop.Run. Permissions (Gate), hooks, limits and compaction are separate control planes supplied by each adopter.

Index

Constants

View Source
const (
	// DefaultMaxTurns bounds a Run whose Limits.MaxTurns is unset.
	DefaultMaxTurns = 25
	// DefaultToolTimeout bounds a tool without its own tools.Timeouter.
	DefaultToolTimeout = 45 * time.Second
	// DefaultHookBudget covers one PreToolUse and one PostToolUse hook at
	// F0's default 30 s timeout each.
	DefaultHookBudget = 60 * time.Second
)
View Source
const DefaultMaxRuleInterrupts = 2

DefaultMaxRuleInterrupts bounds re-runs of one turn. Past it, steering fails open: the reply goes through and the interrupt is dropped.

View Source
const MetaReminder = "steering_reminder"

MetaReminder marks a reminder message in the history; its value is the Reminder's Source.

View Source
const ToolDiscoveryThreshold = 40

ToolDiscoveryThreshold turns on discovery mode (MCP tools hidden until find_tools activates them) once the registry is this big.

Variables

View Source
var ErrRuleInterrupt = llm.ErrRuleInterrupt

ErrRuleInterrupt marks a request a stream rule or the watchdog cut short (2.0 W3). The loop re-runs the turn; Run never returns it.

View Source
var ErrTurnTimeout = errors.New("loop: request exceeded its per-turn timeout")

ErrTurnTimeout matches (errors.Is) a request that hit Limits.RequestTimeout.

Functions

func ConfigStamp

func ConfigStamp(ws string) string

ConfigStamp fingerprints (path, mtime, size) the config files Setup bakes into an Env, so an owner that keeps an Env can replace it as soon as one changes: a new deny rule, hook, trust approval, skill, MCP server, grimoire or AGENTS.md / CLAUDE.md edit applies to the next run. A directory's mtime covers files added to or removed from it; an edit inside one is not seen. MCP chat's Env cache and Parent use it.

func HookContextBlock

func HookContextBlock(context string) string

HookContextBlock is how UserPromptSubmit context joins the prompt the model receives: after the prompt, in a <hook-context> block.

func UserPromptSubmit

func UserPromptSubmit(ctx context.Context, h *hooks.Runner, msg Message) (Message, PromptVerdict, error)

UserPromptSubmit runs h's UserPromptSubmit hooks on msg. The chat, MCP chat and agent goals share it (2.0 F2e). A deny, or a hook that failed, blocks msg with the hook's reason ("no reason given" when it has none). Additional context rides in msg.Metadata[tui.MetaHookContext]; the loop appends it to the copy it sends (HookContextBlock). A hook cut short by ctx ending returns ctx's error: an interrupt, not a verdict. With no UserPromptSubmit hooks (h may be nil) msg is allowed unchanged.

Types

type Boundary

type Boundary int

Boundary is where reminders join the history.

const (
	BoundaryRun   Boundary = iota // before a Run's first request (queued reminders)
	BoundaryTools                 // before every later request, after a turn's tool results
	BoundaryRetry                 // before re-running an interrupted turn
)

type Compactor

type Compactor interface {
	Compact(ctx context.Context, history []Message, lastUsage *llm.TokenUsage, force bool) (out []Message, notes []string, changed bool)
}

Compactor keeps the history inside the window. The loop calls it before every request, and once with force set after a context-overflow error. lastUsage is the provider's count for the previous request (nil when there was none since the last call). notes become EventCompacted. Compaction hooks (PreCompact/PostCompact) are the compactor's business: it knows when a summary is written.

type Env

type Env struct {
	Mode           Mode
	Workspace      string
	Registry       *tools.Registry
	Checker        *permissions.Checker
	ToolMode       tools.RuntimeMode
	MCP            *mcp.Manager
	Indexer        *codegraph.Indexer
	Hooks          *hooks.Runner // tool hooks run in Registry; nil when loading failed
	Files          *checkpoints.FileTracker
	Snapshots      *checkpoints.SnapshotManager
	ProjectContext string // grimoire, context files, code-graph summary
	GitSnapshot    string
	// Memories is the "# Project Memories" block. It follows git in the
	// prompt (W5 ruling 8); GrimoireContext still shows it.
	Memories         string
	GrimoireContext  string // grimoire and "# Project Memories" (the /grimoire view)
	CodeGraphSummary string // the code graph's project summary (the /index view)
	// Rules are the stream rules for this workspace (2.0 W3): built-ins,
	// ~/.celeste/rules/*.md, then the grimoire's "## Stream Rules" (a repo
	// grimoire's only once trusted, like a repo hook).
	Rules *rules.Set
	// SandboxPolicy is bash's OS sandbox (2.0 W4): defaults, the user's
	// "sandbox" settings, then the workspace's (loosening only once
	// trusted). A nested Env in the same workspace inherits it; one in
	// another workspace resolves its own.
	SandboxPolicy sandbox.Policy
	// contains filtered or unexported fields
}

Env is everything a mode needs around the loop.

func Setup

func Setup(mode Mode, cfg *config.Config, workspace string, opts SetupOptions) (*Env, error)

Setup builds the registry, MCP clients, custom skills, permission checker, memories, grimoire, git snapshot, code graph and hooks for mode (2.0 F2). The caller owns Close.

func (*Env) Close

func (e *Env) Close()

Close releases this Env. A Setup Env and its Nested children share MCP clients and a code graph, which stop when the last of them closes, so a child still running keeps them open after its parent closes. A child in another workspace also closes its own code graph. A code-graph update still running is cancelled before it is waited on, so this returns promptly. Safe to call more than once (and concurrently).

func (*Env) Nested

func (e *Env) Nested(opts NestedOptions) (*Env, error)

Nested builds the Env for a runner started by another run (a subagent, an orchestrator lane), skipping MCP and code-graph startup. The child shares the parent's MCP clients (their tools are mirrored into its registry) and, in the same workspace, the parent's hooks runner, code graph and project context; it re-captures the git state and brings the shared code graph up to date first (bounded by nestedCodeGraphTimeout). It always gets its own registry (builtins, custom skills; never spawn_agent), its own permission checker reloaded from permissions.json (never a parent's Trust()), and its own file tracker and snapshots. The shared parts stay open until the parent and every child have closed. Agent-mode and chat Envs built by Setup can nest; a child is always agent mode. A chat parent (subagents and /agent in the chat, 2.0 F2e) shares only its global MCP servers: a child is a non-interactive run, and those never run a repo's MCP servers.

func (*Env) PersistRules

func (e *Env) PersistRules()

PersistRules makes the checker save "always allow" and "always deny" answers to permissions.json, as the chat's does. An adopter calls it only when the answers come from the user's interactive prompt (the TUI modal behind /agent and /orchestrate lanes); headless runs never write. A rule that can't be saved (a damaged permissions.json is never overwritten) still applies for this run, and the failure is warned.

func (*Env) RefreshDiscovery

func (e *Env) RefreshDiscovery()

RefreshDiscovery turns tool discovery on when tools added after Setup (the chat's config-backed skills, collections, spawn_agent, post_message) take the registry past ToolDiscoveryThreshold. It never turns it off.

func (*Env) RenderState

func (e *Env) RenderState() string

RenderState renders the authoritative state a compaction summary carries (#200): the workspace todo list (read from its file, which the todo tool rewrites on every change), the files this Env's checkpoint session changed (F4), relative to the workspace when inside it, and the voice rule (the persona is always on, W5).

func (*Env) SessionStartContext

func (e *Env) SessionStartContext(ctx context.Context, source string) string

SessionStartContext fires SessionStart and returns its additional context, leaving the Env unchanged, so several runs sharing one Env each get their own session. Pass the result as PromptOptions.Session.

func (*Env) StartSession

func (e *Env) StartSession(ctx context.Context, source string)

StartSession fires SessionStart for a top-level run and adds its context to the project context, under the TUI's heading. Call it before SystemPrompt; nested runs (subagents, orchestrator lanes) don't call it. A shared Env (MCP chat) uses SessionStartContext instead.

func (*Env) SystemPrompt

func (e *Env) SystemPrompt(o PromptOptions) prompts.Prompt

SystemPrompt composes the mode's system prompt (W5): the persona profile as the byte-stable Static part, stepped down for the window, then sliders, identity, mode rules, project context, git, memories and the date. A Notice in the result is the small-window guard's one-time message; the caller decides where it shows (W5 ruling 7).

func (*Env) Trust

func (e *Env) Trust()

Trust switches the checker to trust mode, keeping the deny rules. An adopter calls it when invoking the run is the approval (agent -auto-approve, subagents).

type Event

type Event struct {
	Kind      EventKind
	Turn      int
	Text      string
	ToolNames []string
	Usage     *llm.TokenUsage
	Elapsed   time.Duration
	Call      ToolCall
	Msg       Message
	Metadata  map[string]any // EventToolResult: the tool's ToolResult.Metadata (images, subagent names)
	IsError   bool
	History   []Message
	Result    Result
	Err       error
	// At is EventToolStart's start time, taken on the loop's goroutine, so
	// it precedes every message the loop stamps after the call (C5).
	At time.Time
}

Event is one step of a Run, for renderers and adopters.

type EventKind

type EventKind int

EventKind identifies an Event.

const (
	// EventTurnStart (Turn) may repeat with the same Turn number after an
	// overflow retry re-runs the turn: consumers should key on Turn, not
	// count events.
	EventTurnStart      EventKind = iota
	EventTextDelta                // Text
	EventAssistant                // Turn, Text, ToolNames, Usage, Elapsed: the model's reply
	EventToolStart                // Call
	EventToolResult               // Call, Text (what the model receives), IsError
	EventCompacted                // Text
	EventSteered                  // Text, Msg: a steer joined (Msg as it joined, metadata included)
	EventNotice                   // Text: a non-fatal problem (hook error, spill failure)
	EventTurnEnd                  // Turn, History: a consistent history snapshot
	EventDone                     // Result, Err: always the last event of a Run
	EventPromptBlocked            // Msg, Text (the reason): CheckPrompt blocked a prompt; it left the history
	EventSteerBlocked             // Msg, Text (the reason): CheckPrompt blocked a steer; it was dropped
	EventPromptsChecked           // History: CheckPrompt allowed a prompt; the checked history, before the first request
	EventCallsRecorded            // Turn, History: the snapshot with this turn's assistant tool_calls message, before any call runs
	EventRule                     // Text (the Reminder's Source), Msg: a steering reminder joined the history (W3)
	EventRuleInterrupt            // Turn, Text (the re-run's reminder sources, ", "-joined), Usage, Elapsed: steering cut the turn's reply short; it is dropped and the turn re-runs (W3)
	EventThinking                 // Text: a delta of the model's reasoning, for a thinking indicator; never reply text or history (L4)
)

type Gate

type Gate interface {
	Ask(ctx context.Context, req tools.PermissionRequest) tools.PermissionResponse
}

Gate answers a permission Ask for one run. Its lifetime differs by mode (TUI modal, agent prompt or none, MCP none), so each adopter passes it in. A nil Gate means an Ask is denied. Loop serializes calls to Ask itself (gateMu in exec.go), so a Gate implementation need not handle concurrent calls even when parallel-safe tool calls all Ask in the same batch.

func PromptGate

func PromptGate(fn tools.PromptFunc) Gate

PromptGate adapts a blocking tools.PromptFunc (the TUI modal) to a Gate. A nil fn gives a nil Gate. Asks are serialized: parallel calls in one batch must not open two modals at once. When ctx ends first (the call was abandoned, Esc, or the chat quitting) the ask answers deny at once; the prompt's own goroutine keeps the lock until the prompt returns, so a later ask never opens a second modal over one still showing. An ask whose ctx ended while it waited for the lock never calls fn.

type GateFunc

GateFunc adapts a function to Gate.

func (GateFunc) Ask

type LLM

type LLM interface {
	SendMessageStreamEvents(ctx context.Context, messages []tui.ChatMessage, tools []tui.SkillDefinition, cb llm.StreamEventCallback) error
	// GetSkills is the tools a request offers, and so the only ones the
	// loop runs: an implementation that offers none returns an empty,
	// non-nil slice (nil places no restriction; test stubs only).
	GetSkills() []tui.SkillDefinition
}

LLM is the part of *llm.Client the loop uses.

type Limits

type Limits struct {
	MaxTurns           int           // <=0: DefaultMaxTurns
	MaxCallsPerTurn    int           // calls beyond it are dropped before the turn is recorded
	IdenticalCalls     int           // stop when the identical call batch repeats this many turns
	NoProgressTurns    int           // stop when tools return identical results this many turns
	SpillBytes         int           // results larger than this spill to disk
	ToolTimeout        time.Duration // <=0: DefaultToolTimeout; a tools.Timeouter overrides it
	RequestTimeout     time.Duration // per-request deadline; the client's per-attempt deadline still applies
	MaxInvalidArgTurns int           // stop after this many turns in a row with bad tool arguments
	TextToolCalls      bool          // parse <tool_call> blocks from text when a turn has no native calls
	// KeepToolMetadata puts an image result's ToolResult.Metadata on its
	// tool message (read_file for vision models), with a marker in the
	// content. The chat sets it; agent and MCP runs send text only.
	KeepToolMetadata bool
	// MaxRuleInterrupts bounds re-runs of one turn by steering (W3);
	// <=0: DefaultMaxRuleInterrupts.
	MaxRuleInterrupts int
	// HookBudget is how long PreToolUse/PostToolUse hooks may add to a call
	// before the watchdog abandons it (tool timeout + budget; gated runs:
	// counted from the Gate's answer). <=0: DefaultHookBudget.
	HookBudget time.Duration
}

Limits is plain data. For guards, caps and spill, zero means off.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits are the spec's values: 25 turns, identical-call guard 3, progress guard 6, spill at 128 KiB, 45 s per tool.

type Loop

type Loop struct {
	Client  LLM
	Tools   *tools.Registry
	Limits  Limits
	Gate    Gate      // nil: an Ask is denied
	Compact Compactor // nil: no compaction
	// CheckPrompt is UserPromptSubmit for this run (2.0 F2d; nil: none).
	CheckPrompt PromptCheck
	// Steering is stream rules and the watchdog (2.0 W3; nil: none).
	Steering Steering
	// Advisor is jev_gate (2.0 W3; nil: none): it may turn an allowed call
	// into an Ask, never the reverse.
	Advisor tools.AskAdvisor
	// Refuse, when set, is asked for every call to a known tool before it
	// reaches the registry; a non-empty reason settles the call as an
	// error without running it, its hooks or its permission check (the
	// chat's plan mode, 2.0 W4e). It can only take calls away. It runs on
	// Run's goroutine and is read at call time.
	Refuse func(name string) string

	// Tool hooks run inside Tools (F0); the loop fires no hooks itself.
	// SessionID names the spill directory for oversized results.
	SessionID string
	// SpillDir overrides the spill base directory; "" uses the default
	// (ctxmgr.ToolResultsBaseDir). Only tests set it. recall_tool_result
	// reads from the default base, so with a custom SpillDir the recall id
	// in a capped result's marker does not resolve: the spill file is still
	// written and named, but only that path reaches it.
	SpillDir string
	// SpillCounter numbers spill files across Loops that share it (the chat
	// starts one Loop per turn, 2.0 F2d), so a call ID repeated in a later
	// run never overwrites an earlier spill. nil: counted per Loop.
	SpillCounter *atomic.Int64
	// contains filtered or unexported fields
}

Loop runs one conversation's tool loop. Configure the exported fields, then call Run (once per step; the Loop may be reused).

func (*Loop) Events

func (l *Loop) Events() <-chan Event

Events returns the event stream. Call it before Run. Sends are unbuffered: the loop waits for the consumer to take each event, so a consumer that has received event N+1 has finished handling event N, and once Run returns the consumer has handled everything before EventDone. A consumer must keep reading until EventDone. Without a call to Events the loop emits nothing.

func (*Loop) Run

func (l *Loop) Run(ctx context.Context, history []Message) (msgs []Message, res Result, err error)

Run drives turns until the model answers without tools, a limit or guard stops it, the provider fails, or ctx is cancelled (an interrupt). It returns a copy of history with the run's messages appended. Every declared tool call in the returned history has a result.

func (*Loop) Steer

func (l *Loop) Steer(msg string)

Steer queues a user message. It joins the conversation at the next tool boundary (before the next request). Safe from any goroutine.

func (*Loop) TakeSteers

func (l *Loop) TakeSteers() []string

TakeSteers removes and returns the steers no run has joined: typed after the last tool boundary of a run that then ended (an interrupt, a guard, a cap). The caller decides what to do with them; the chat sends them next.

type Message

type Message = tui.ChatMessage

Message is one chat message. The loop copies the history it is given and only appends to the copy.

func ReminderMessage

func ReminderMessage(r Reminder) Message

ReminderMessage is a reminder as the history holds it: a hidden user message (not shown in the chat, never checked by UserPromptSubmit) in a <system-reminder> block.

type Mode

type Mode int

Mode is the surface a run belongs to.

const (
	ModeChat    Mode = iota // the TUI
	ModeAgent               // celeste agent, subagents, orchestrator, /agent, MCP mode:"agent"
	ModeMCPChat             // MCP celeste mode:"chat"
)

func (Mode) String

func (m Mode) String() string

type NestedOptions

type NestedOptions struct {
	// Workspace is the child's workspace; "" means the parent's. A different
	// one (an isolated worktree) gets its own hooks, project context and
	// code graph.
	Workspace string
	// Warn receives the child's own setup warnings; nil means the parent's
	// sink. A shared hooks runner reports to the sink in the ctx of the run
	// that fired the hook (hooks.WithWarn), else to the parent's.
	Warn func(string)
	// Notice receives the child's timing notices (a git snapshot or
	// code-graph update that ran out of time); nil means Warn when Warn is
	// set, else the parent's.
	Notice func(string)
}

NestedOptions configures a child Env.

type Nester

type Nester interface {
	Nested(NestedOptions) (*Env, error)
}

Nester builds the Env for a runner started by another run: an *Env built by Setup, or a *Parent.

type Parent

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

Parent is the Env the nested runners of one owner share (the subagent manager's, or one orchestrator run's). The first Nested call builds it, and a later one rebuilds it when ConfigStamp(workspace) has changed, so a new deny rule, hook, trust approval, skill or MCP server applies from the next runner. A replaced Env closes when the last runner nested under it closes.

func NewParent

func NewParent(cfg *config.Config, workspace string, opts SetupOptions) *Parent

NewParent returns a Parent for workspace. Nothing is built until the first Nested call.

func (*Parent) Close

func (p *Parent) Close()

Close releases the current Env; runners still using it keep its shared parts open until they close. Later Nested calls fail.

func (*Parent) Nested

func (p *Parent) Nested(opts NestedOptions) (*Env, error)

Nested builds a child of the current Env (see Env.Nested), building or rebuilding that Env first as needed. It holds the Parent's lock throughout, so a rebuild never closes an Env between choosing it and nesting under it; concurrent callers wait for each other (Setup takes up to 15 s, a child's refresh up to nestedCodeGraphTimeout).

type PromptCheck

type PromptCheck func(ctx context.Context, msg Message) (Message, PromptVerdict, error)

PromptCheck is UserPromptSubmit for the loop. Run calls it before the first request for every user message not checked yet (MetaPromptHookDone unset, not hidden), and for every steer when it joins. It returns the message to send, with any hook context in Metadata[tui.MetaHookContext]; the loop marks it checked. An error (the run's ctx ended: an interrupt, not a verdict) leaves the message unchecked.

func HookPromptCheck

func HookPromptCheck(h *hooks.Runner) PromptCheck

HookPromptCheck is h's UserPromptSubmit as a Loop.CheckPrompt, or nil when h has no UserPromptSubmit hooks, so such a run takes the unchecked path.

type PromptOptions

type PromptOptions struct {
	// Contract is the agent operating contract (agent mode only).
	Contract string
	// Sliders overrides slider.json (a subagent's persona override).
	Sliders *config.SliderConfig
	// Session is one run's SessionStart context; it never enters the Env.
	Session string
	// Level is the persona level: "" is the mode's profile (chat full,
	// agent spine); prompts.PersonaOff for typed explore and review
	// subagents (2.0 W4e) and orchestrator lanes.
	Level prompts.PersonaLevel
	// Window is the run's model's context window; 0 uses the Env's.
	Window int
}

PromptOptions is what one system prompt adds to the Env's own inputs.

type PromptVerdict

type PromptVerdict struct {
	Blocked bool
	Reason  string // for the person, never the model
}

PromptVerdict is a PromptCheck's answer.

type Reminder

type Reminder struct {
	Source string // "rule:<name>" or "watchdog"
	Text   string
}

Reminder is text a stream rule or the watchdog adds to the conversation.

type Result

type Result struct {
	FinalText         string // last assistant text, verbatim
	ToolCallsLastTurn int
	NoToolTurns       int // consecutive tool-free turns at the end of this Run
	StopReason        StopReason
	Turns             int // turns started this Run (an overflow retry is the same turn)
	ToolCalls         int // tool calls executed this Run
	// HistoryEdited: the returned history holds an edit (a prune, or
	// provider blocks stripped after BlocksRejected) that no History
	// snapshot event carried, because the run ended first. A consumer that
	// mirrors the snapshots takes the returned history (2.0 F3).
	HistoryEdited bool
}

Result describes one Run. The completion gate is not here: the agent layer (and W3) read it.

type SetupOptions

type SetupOptions struct {
	SessionID string       // hooks' session_id and checkpoint session; "" = "<mode>-<pid>-<start nanos>"
	Warn      func(string) // setup and hook warnings; nil = stderr
	// Notice gets timing notices (a git snapshot or code-graph update that
	// ran out of time). They depend on the machine, not the configuration.
	// nil = Warn.
	Notice func(string)
	// Approve asks the person to trust a repo hook. Only ModeChat uses it
	// (non-interactive modes never approve, F0); nil there means a terminal
	// prompt when stdin and stderr are terminals.
	Approve hooks.ApproveFunc
	// GlobalMCPOnly skips the workspace's MCP configs in ModeChat too, as
	// every other mode does: a chat whose user cannot be asked before a
	// repo's server starts (ACP, where the editor passes its own servers).
	GlobalMCPOnly bool
}

SetupOptions carries what only the adopter knows.

type Steering

type Steering interface {
	// Request is called before each request. interrupt cuts that request
	// short; it is a no-op once the request has returned, and nil when the
	// turn has used its re-runs (Observe's answer is then ignored too).
	Request(turn int, interrupt func())
	// Observe sees every event before consumers do. True interrupts the
	// request in flight (only text deltas arrive during one).
	Observe(ev Event) bool
	// Calls sees a turn's complete tool calls before they are recorded or
	// run. True drops the turn's reply and re-runs it.
	Calls(turn int, calls []ToolCall) bool
	// Reminders removes and returns the reminders due at b.
	Reminders(b Boundary) []Reminder
}

Steering is stream rules and the watchdog as the loop sees them (W3). Its methods are called on Run's goroutine, except the interrupt function Request hands over, which is safe from any goroutine.

type StopReason

type StopReason string

StopReason says why Run returned.

const (
	StopDone        StopReason = "done"         // a turn with no tool calls
	StopCap         StopReason = "cap"          // Limits.MaxTurns
	StopIdentical   StopReason = "identical"    // identical-call guard
	StopProgress    StopReason = "progress"     // progress guard
	StopInterrupted StopReason = "interrupted"  // ctx cancelled
	StopInvalidArgs StopReason = "invalid_args" // Limits.MaxInvalidArgTurns
	StopError       StopReason = "error"        // the provider failed; Run returns the error
	StopBlocked     StopReason = "blocked"      // CheckPrompt blocked every new prompt; no request was sent
)

type StreamEnder

type StreamEnder interface {
	EndStream() bool
}

StreamEnder is an optional Steering method. EndStream is called when a request's stream has completed without an error, before the request returns: a matcher that batches its scans checks what it held back. True interrupts the request (the reply is dropped even though the provider sent all of it).

type ToolCall

type ToolCall struct {
	ID    string
	Name  string
	Input map[string]any
	// Key names this one invocation, unique in the process: a model's IDs
	// may repeat or be empty, so whatever binds a permission ask to the
	// call it shows (ACP) keys on Key, never on ID. It is also on the
	// call's context (tools.CallKeyFromContext). Empty outside runCalls.
	Key string
}

ToolCall is one call as hooks and events see it.

type TurnTimeoutError

type TurnTimeoutError struct {
	Timeout time.Duration
	Err     error
}

TurnTimeoutError keeps the provider's message unchanged and marks it as a per-turn timeout.

func (*TurnTimeoutError) Error

func (e *TurnTimeoutError) Error() string

func (*TurnTimeoutError) Is

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

func (*TurnTimeoutError) Unwrap

func (e *TurnTimeoutError) Unwrap() error

Jump to

Keyboard shortcuts

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