Documentation
¶
Overview ¶
Package agent is the engine that turns one awake ant into work: the explicit state-machine loop that drives model turns, schedules tool calls, compacts the context when the window fills, and recovers from provider errors (doc 03).
The loop is one for-loop over one State struct, and every retry, compaction, recovery, and fallback is a named transition that reassigns the state and continues the loop, never a recursion (D6). Given the same model responses and the same tool outputs, the same transitions fire in the same order, so a recorded session replays byte-identically for the eval harness (D23).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AntBody ¶
type AntBody struct {
Text string `json:"text,omitempty"`
Thinking string `json:"thinking,omitempty"`
Calls []provider.ToolCall `json:"calls,omitempty"`
}
AntBody is the session body of one assistant message.
type CompactBody ¶
type CompactBody struct {
Trigger string `json:"trigger"`
PreTokens int `json:"pre_tokens"`
Summarized int `json:"summarized"`
}
CompactBody is the session body of a compaction boundary (D9).
type DecideFunc ¶
type DecideFunc func(ctx context.Context, t tool.Tool, input json.RawMessage, callID string) Verdict
DecideFunc is the permission pipeline seam. Nil means every call is allowed, which only the tests use; the ant always wires the pipeline.
type HookResult ¶
type HookResult struct {
Block bool // a hook blocked (exit 2)
Message string // block or warning text, fed to the model on a block
Context string // additional context to append to the result
}
HookResult is the loop-facing outcome of a hook fire.
type Hooks ¶
type Hooks interface {
// PostTool runs the post-tool hooks after the tool returns. A blocked
// result feeds Message back to the model; otherwise Context is appended to
// the tool result. isErr routes to the post-tool-failure event.
PostTool(ctx context.Context, tool string, input json.RawMessage, result string, isErr bool) HookResult
// PromptSubmit runs UserPromptSubmit as a new human prompt enters the run.
// A block rejects the prompt; Context is injected before the model runs.
PromptSubmit(ctx context.Context, prompt string) HookResult
// SessionStart runs once at the start of a session and again after a
// compaction; reason is "startup" or "compact". Context is injected.
SessionStart(ctx context.Context, reason string) HookResult
// SessionEnd runs when the run reaches its terminal reason. It cannot
// change the outcome; it is for cleanup and notification side effects.
SessionEnd(ctx context.Context, reason string)
// Stop runs when the model finishes with no tool calls. A block asks the
// loop to keep working; the loop bounds the number of blocks so a Stop
// hook that always blocks cannot spin the loop.
Stop(ctx context.Context) HookResult
}
Hooks is the loop's seam to the lifecycle and post-tool hooks. The ant wires it to the trusted hook dispatcher; nil means no hooks, which is the case for an untrusted workspace and for tests. The loop stays free of the hook package: it knows only these calls and the small result they return.
PreToolUse does not live here: it steers the permission decision and so runs inside the pipeline seam (DecideFunc), whose Verdict carries any context a PreToolUse hook contributed.
type Limits ¶
type Limits struct {
// MaxTurns is the model-turn ceiling. Zero means the foreground
// default of 100.
MaxTurns int
// MaxToolConcurrency bounds a parallel batch. Zero means 10, the
// Claude Code default (doc 03 section 7).
MaxToolConcurrency int
// Window is the model's context size in tokens. Zero means 200000.
Window int
// MaxOut is the output-token cap sent with each request. Zero lets
// the provider default stand.
MaxOut int
// KeepToolResults is how many recent tool results rung one of the
// compaction ladder preserves. Zero means 5 (doc 03 section 18
// leaves the exact count to tuning; this is the seam).
KeepToolResults int
// AutoCompactAt and BlockingAt override the derived thresholds when
// nonzero, so a test triggers compaction without a huge transcript.
AutoCompactAt int
BlockingAt int
// Sleep is the retry backoff sleeper. Nil means time.Sleep; tests
// inject a recorder.
Sleep func(time.Duration)
}
Limits is the per-run configuration: ceilings, budgets, and the injected clock and sleep for deterministic tests.
type Loop ¶
type Loop struct {
Provider provider.Provider
Model string // resolved model id
Fallback string // fallback model id, "" means none
System []provider.Block
// Prefix is block two of the cache-aligned prompt (D14): the pinned
// index and project memory as one synthetic system-reminder user
// message, prepended before the task tail on every request. The ant
// builds it once per session so it stays byte-stable across turns;
// its last block carries the second cache breakpoint.
Prefix []provider.Message
Tools *tool.Registry
TC *tool.ToolContext
Decide DecideFunc
// Hooks is the tool-adjacent hook seam. Nil means no hooks, which is the
// case for an untrusted workspace and for tests; the ant wires it to the
// trusted dispatcher only when a workspace has hooks that pass the trust
// gate (doc 05 section 12).
Hooks Hooks
// Emit publishes one event on the stream; the colony's TurnHandle
// satisfies it. Nil drops events, for isolated transition tests.
Emit func(t event.Type, payload any) error
// Append writes one transcript entry. Nil drops them.
Append func(e session.Entry) error
// Record accounts one completed model turn. Nil skips the meter.
Record func(r ledger.Row)
// OverBudget is the between-turn budget gate (D5). Nil means no
// budget. Checked after each ledger flush, never mid-turn.
OverBudget func() bool
Session string
Turn string // the user turn id events are stamped with
Tier string // for the ledger row
Limits Limits
// Now is injected for deterministic tests. Nil means time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
Loop drives one ant through model turns to a terminal reason. The fields are the run's fixed dependencies; everything that changes across iterations lives in State.
func (*Loop) Run ¶
Run drives the loop from one user prompt to a terminal reason. It never recurses and never returns without a TermReason (D6). A non-nil error is a genuine bug, never a turn that ended badly; every model and tool failure is a reason, not an error (doc 03 section 13).
The conversation survives Run: a second Run on the same Loop appends its prompt to the transcript the first one built, because the ant owns one context window for the whole session (doc 01 section 2.2). Per-run counters and recovery guards reset; msgs, the boundary, the part counter, and the compaction count carry over.
type Outcome ¶
type Outcome struct {
Reason TermReason
Turns int
}
Outcome is what a run hands back: the terminal reason and how many model turns it took. Tokens live in the ledger, not here.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State is the entire cross-iteration state of one run. Handlers mutate it in place and set next; nothing important lives in a handler-local variable, because a local is invisible to the journal and dies at the continue (doc 03 section 4).
type TermReason ¶
type TermReason string
TermReason is the single typed reason a loop run ends with. Every Run returns exactly one; there is no untyped exit (doc 03 section 2).
const ( // TermCompleted is the model finishing with no tool calls. The only // success reason. TermCompleted TermReason = "completed" // TermMaxTurns is the per-run turn ceiling. Foreground runs set a // high one; worker ants get a tight one from their card (M3). TermMaxTurns TermReason = "max_turns" // TermBudgetExhausted is the ledger refusing another model call // because the budget is spent (D5). A between-turn gate, never a // mid-turn kill. TermBudgetExhausted TermReason = "budget_exhausted" // TermCanceled is cancellation arriving between turns: nothing was // running, nothing was lost. TermCanceled TermReason = "canceled" // TermToolsCanceled is cancellation arriving while tools were in // flight; kept distinct so the transcript can show which results // landed and which were abandoned. TermToolsCanceled TermReason = "tools_canceled" // TermPromptTooLong is a prompt the full compaction ladder could not // shrink under the blocking limit. TermPromptTooLong TermReason = "prompt_too_long" // TermModelError is an unrecoverable provider error after retries // and fallback were exhausted. TermModelError TermReason = "model_error" // TermCompactionFailed is the circuit breaker opening after // consecutive compaction failures. Separate from TermPromptTooLong // so the journal shows the breaker, not the symptom. TermCompactionFailed TermReason = "compaction_failed" )
type Thresholds ¶
type Thresholds struct {
AutoCompact int // auto-compaction fires when live tokens cross this
Blocking int // crossing this with recovery spent is TermPromptTooLong
}
Thresholds are the absolute-token trigger points, derived from the window, never percentages: a fixed buffer in tokens is what actually protects the output budget (doc 03 section 11).
type ToolBody ¶
type ToolBody struct {
Call string `json:"call"`
Tool string `json:"tool"`
Content string `json:"content"`
IsErr bool `json:"is_err,omitempty"`
}
ToolBody is the session body of one tool result.
type Verdict ¶
type Verdict struct {
Allow bool
UpdatedInput json.RawMessage // non-nil replaces the call's input
Reason string // model-facing sentence when not allowed
Context string // a PreToolUse hook's note to surface with the result
}
Verdict is the permission seam's answer for one tool call. The ant (slice 9) adapts the doc 05 pipeline to this; the loop only needs to know whether to run the call and with what input.