agent

package
v0.2.0-rc.2 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package agent defines the model backend interface and the types a run exchanges with it: the request, the result, the usage and the event log, plus the run budgets, the failure reasons, error classification and the environment a local CLI backend's child process receives.

Index

Constants

View Source
const (
	BackendClaude      = "claude"
	BackendCodex       = "codex"
	BackendAnthropic   = "anthropic"
	BackendCursorAgent = "cursor-agent"
)

Backend names, as config.Backend accepts them.

View Source
const (
	ReasonValidationRejected  = "validation rejected the document"
	ReasonProviderUnavailable = "the model provider was unavailable"
	// ReasonStalled ends a run whose harness stopped making progress twice:
	// see Budgets.StallTimeout.
	ReasonStalled = "the review agent stalled"
)

The failure reasons below are text hosts may store and match when read back, so it must not change.

View Source
const (
	StageOutline = "outline"
	StageDetails = "details"
	StageContext = "context"
)

The stages of a staged review (Generator.StagedReview). Each name also labels the stage's files in the run directory (Task.Label): schema-outline.json, rejected-details-1.json and so on.

View Source
const MaxDiagnostics = 512

MaxDiagnostics bounds the diagnostic events an Outcome keeps over a whole run.

View Source
const MaxFixSummaryBytes = 16 << 10

MaxFixSummaryBytes bounds the summary a Fixer returns.

View Source
const MaxRetainedDocumentBytes = 2 << 20

MaxRetainedDocumentBytes bounds the rejected document kept in an Outcome.

View Source
const OutsideRun = "<outside-run>"

OutsideRun is an Event's Path for a tool path outside the run directory.

View Source
const RunDirPrefix = "baum-agent-run-"

RunDirPrefix is the convention for the name of the temporary directory a local caller runs a backend in (os.MkdirTemp("", agent.RunDirPrefix+"*")). Nothing in this package creates it. Session discovery skips sessions recorded under such a directory as a backstop; the primary protection is that the backend persists no session and loads no hooks.

View Source
const SecretFinding = "" /* 198-byte string literal not displayed */

SecretFinding is the finding of a run rejected for a secret or e-mail address in a value a schema closes. It quotes nothing the model wrote.

Variables

View Source
var ErrSessionNotFound = errors.New("the agent session to continue was not found")

ErrSessionNotFound is a resume or fork whose session the CLI could not find: its files were deleted, or it was recorded under another working directory. The call made no model request, so nothing was spent.

Functions

func ChargedTokens

func ChargedTokens(input, output, cacheCreation int) int

ChargedTokens is what a run pays for in full: input, output and cache creation. Cache reads are not charged against a token budget.

func ErrorStatus

func ErrorStatus(message string) string

ErrorStatus classifies an error message into a fixed word.

func EventPath

func EventPath(directory, path string) string

EventPath reports a tool's path for an Event: relative to the run directory, or OutsideRun for one outside it, with control characters replaced and at most 256 bytes. A CLI names absolute paths from its own working directory, which the kernel reports with symlinks resolved: on macOS the run directory under $TMPDIR (/var/folders/...) reaches the CLI as /private/var/folders/..., so a path under the resolved directory is inside the run too.

func Exceeds

func Exceeds(used, limit int) bool

Exceeds reports whether used is over limit, a call or token budget where zero is no limit.

func GenerationBudgetExceededReason

func GenerationBudgetExceededReason(name string) string

GenerationBudgetExceededReason is the reason of a run that exhausted the named budget.

func HTTPErrorStatus

func HTTPErrorStatus(code int) string

HTTPErrorStatus classifies the HTTP status code of a failed provider response: 401 and 403 are a rejected key, 404 a model or route that does not exist, 426 a protocol the endpoint refuses, 429 a rate limit, 529 an overloaded provider, and any other 4xx but 408 a request the provider refused. Any other code, 408 and 5xx included, gives "".

func IsProviderErrorStatus

func IsProviderErrorStatus(status string) bool

IsProviderErrorStatus reports whether an ErrorStatus class is a provider failure, as opposed to a tool error. Each class is a fixed word, so a host may report it where the message it classifies may not be reported.

func IsRejectedStatus

func IsRejectedStatus(status string) bool

IsRejectedStatus reports whether a provider error class is a refusal a retry cannot change: a rejected key, schema or request, a missing model or route, or a refused protocol.

func LastErrorStatus

func LastErrorStatus(events []Event) string

LastErrorStatus is the classified status of an attempt's last error event, or empty when the backend recorded none.

func LastProviderErrorStatus

func LastProviderErrorStatus(events []Event) string

LastProviderErrorStatus is LastErrorStatus when the last error event's class was read from the provider (Event.FromProvider), and otherwise empty: a class guessed from free text may be a request ID or quoted content that reads like a status.

func LocalEnvironment

func LocalEnvironment(environ []string, extra ...string) []string

LocalEnvironment returns the variables of environ a local CLI backend may pass to its child: a fixed allowlist, plus the names in extra, which the calling backend trusts (for example CLAUDE_CONFIG_DIR). Provider credentials and configuration, cloud and GitHub tokens, Git settings and the SSH agent never pass, even when extra names them. Each element of environ is "NAME=value".

This covers environment variables only. HOME and the XDG directories stay, so the child can still read credentials kept in files under them.

func MissingSession

func MissingSession(output string) (string, bool)

MissingSession finds the line of a CLI's error output, every line of it, that says the session to continue does not exist: a CLI may print warnings first.

func ProviderFailure

func ProviderFailure(harness, status string, refused bool) error

ProviderFailure is the error of a harness call whose provider failed with the error class status. refused says the class was read from the provider's HTTP status or a structured field of its response, not guessed from free text: only then is a refusal a RejectedError, which the loop does not retry. Any other failure is ReasonProviderUnavailable.

func ProviderRejectedReason

func ProviderRejectedReason(status string) string

ProviderRejectedReason is the reason of a run the model provider refused in a way a retry cannot change. status is the fixed word of the refusal: authentication_error, invalid_request, refusal or context_window_exceeded.

func RedactOutput

func RedactOutput(ctx context.Context, validator *review.DocumentValidator, redactor *bundle.Redactor, document []byte, b *bundle.Bundle, checkout map[string]bool, outcome *Outcome) (redacted []byte, closed int, err error)

RedactOutput masks secret spans in everything a Generator returned, before any of it is stored, uploaded or shown: the document, a kept rejected document, the findings, the model and each diagnostic string. A host applies it to every outcome it records, whether the loop ran in its process or its result crossed a boundary. It records the masked spans on the outcome, apart from the bundle's count, and returns the document to keep. A document with nothing to mask is returned byte for byte. On failure the returned strings are dropped rather than kept unmasked.

checkout lists the repository paths of the run's checkout, either tree; a path in it may stay as it is. The masked spans include those of the loop's identifier pass, and closed counts the closed values either pass masked: a run with any must be rejected (RejectSecrets).

func RedactStagedOutput

func RedactStagedOutput(ctx context.Context, validator *review.DocumentValidator, redactor *bundle.Redactor, document []byte, b *bundle.Bundle, checkout map[string]bool, outcome *StagedOutcome) (redacted []byte, closed int, err error)

RedactStagedOutput is RedactOutput for a staged review: it masks the document and the run's outcome as RedactOutput does, then each stage's outcome the same way (its findings, its rejected part, its model and usage strings, its diagnostics), and each stage's reason. A host applies it, instead of RedactOutput, to every staged outcome before it stores or shows any of it. closed counts the closed values masked in the document, the run's and the stages' outcomes and the identifier pass, which every stage shares and is counted once: a run with any must be rejected (RejectSecrets). On failure every outcome's strings are dropped.

func RedactTaskOutput

func RedactTaskOutput(ctx context.Context, redactor *bundle.Redactor, document []byte, outcome *Outcome) ([]byte, error)

RedactTaskOutput masks secret spans in everything RunTask returned, as RedactOutput does for a review, before any of it is stored, uploaded or shown. A task's document has no review contract, so every string in it, keys included, is free text and none is a closed value.

func RejectSecrets

func RejectSecrets(outcome *Outcome, document []byte, closed int) (kept []byte, rejected bool)

RejectSecrets rejects a run whose output had a secret masked in a closed value, as RedactOutput counts them in closed. The masked document may still be valid, so the rejection is explicit: it sets ReasonValidationRejected, adds SecretFinding to the findings and keeps only the masked copy as the rejected document unless the run had already failed. It returns the document to keep, which is nil after a rejection, and whether it rejected. With closed zero it changes nothing.

func RepairText

func RepairText(backend string) (instruction, ending string)

RepairText returns the text that opens and closes a repair prompt for a backend. The ending is a format string taking the repair attempt number and the attempt limit.

A backend that submits through a StructuredOutput tool (claude, anthropic) is told to call it again. A backend that answers in its final message (codex, cursor-agent) is told to reply with the whole document there. Any other backend gets the StructuredOutput wording.

func RetainedDocument

func RetainedDocument(document []byte) json.RawMessage

RetainedDocument returns a copy of a rejected document for Outcome.RejectedDocument, or nil when it is empty, over MaxRetainedDocumentBytes or not JSON.

func SessionNotFound

func SessionNotFound(cli, line string) error

SessionNotFound is the error a backend returns for a resume or fork whose session is gone, with the CLI's own line.

func SignInRejected

func SignInRejected(err error) bool

SignInRejected reports whether err, or the text a host kept of it, is the provider refusing the harness's sign-in: none, expired or revoked. A host matches the text because the error's type does not survive its redaction.

func TaskSchemaFile

func TaskSchemaFile(label string) string

TaskSchemaFile is the name of the file RunTask writes a task's schema to in the run directory: schema.json, or schema-<label>.json for a labelled task. Its rejected replies go to rejected-N.json, or rejected-<label>-N.json.

func TitlePath

func TitlePath(directory, path string) string

TitlePath is a tool's path for its transcript title: relative to the run directory when it is inside it, otherwise as the agent named it.

func ValidSessionID

func ValidSessionID(id string) bool

ValidSessionID reports whether id is a session id a request may resume: a canonical UUID, as both CLIs report.

func WithUserEnv

func WithUserEnv(environ, user []string) []string

WithUserEnv is environ with the user's own agent variables (agent_env), each "NAME=value", set over it. They are the user's explicit choice, so unlike inherited variables they may name a Git setting, such as GIT_CONFIG_GLOBAL. Callers append the variables a run itself needs after them, which then win.

Types

type Accounting

type Accounting struct {
	Usage         Usage          `json:"usage"`
	Attempts      []UsageAttempt `json:"attempts"`
	ChargedTokens int            `json:"charged_tokens"`
}

Accounting is additive provenance. Its absence in older records means unknown, never zero. Usage.Unreported marks a partial or missing provider report.

func (*Accounting) Add

func (a *Accounting) Add(result Result, failed bool)

func (*Accounting) ClearStrings

func (a *Accounting) ClearStrings()

ClearStrings keeps exact numeric reports while withholding provider-controlled metadata until the host has redacted it, or after redaction fails.

func (*Accounting) Clone

func (a *Accounting) Clone() *Accounting

Clone detaches published accounting from a producer's subsequent updates.

type Activity

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

Activity is what an attempt has been doing, as the stall watchdog sees it: when it last made progress, the model requests it has open and the tools it is running. Backends report to the Activity of their Request; a nil *Activity ignores every report.

func NewActivity

func NewActivity() *Activity

func (*Activity) Progress

func (a *Activity) Progress()

Progress records that the attempt did something, such as an event the harness emitted.

func (*Activity) Running

func (a *Activity) Running() int

Running is the number of tools the attempt is running.

func (*Activity) ToolFinished

func (a *Activity) ToolFinished(id string)

ToolFinished records that the tool with this ID ended; an unknown ID is ignored.

func (*Activity) ToolStarted

func (a *Activity) ToolStarted(id, kind string)

ToolStarted records a tool the harness started, by its own item ID and kind (for example command_execution). A running tool is never idle, but it is bounded by the tool timeout.

type Backend

type Backend interface {
	Name() string
	Run(context.Context, Request) (Result, error)
}

Backend runs one model call loop over a checkout and returns the document the model submitted.

type Budgets

type Budgets struct {
	WallClock           time.Duration `json:"wall_clock"`
	MaxCalls            int           `json:"max_calls"`
	MaxTokens           int           `json:"max_tokens"`
	MaxProviderAttempts int           `json:"max_provider_attempts"`
	MaxRepairAttempts   int           `json:"max_repair_attempts"`
	// StallTimeout ends an attempt that has no model request open and no
	// tool running and has made no progress for this long; ToolTimeout ends
	// one whose single tool runs longer. Zero disables each. An attempt ended
	// either way is retried once, then the run fails with ReasonStalled.
	StallTimeout time.Duration `json:"stall_timeout,omitempty"`
	ToolTimeout  time.Duration `json:"tool_timeout,omitempty"`
}

Budgets bounds one run. MaxTokens counts tokens the run pays for in full (input, output and cache creation); cache reads repeat the same context on every call and are reported but not charged against it. A MaxCalls or MaxTokens of zero is no limit: the wall clock bounds the run.

type CompleteRequest

type CompleteRequest struct {
	System, User string
	// Schema is the reply's JSON Schema.
	Schema []byte
	// MaxOutputTokens caps the reply; zero is the backend's default.
	MaxOutputTokens int
}

CompleteRequest is one Completer call.

type Completer

type Completer interface {
	// Complete makes the call. On an error the Completion still carries the
	// model and the usage the provider reported, when the call reached it, so
	// the caller can account for what it may have been billed.
	Complete(context.Context, CompleteRequest) (Completion, error)
}

Completer is a backend that can make a single structured-output model call without tools, files or a working directory: the system prompt, one user message and the JSON Schema the reply must match. Decision capture uses it. The claude, codex and anthropic backends implement it; cursor-agent does not.

type Completion

type Completion struct {
	// Document is the reply object, expected to match the request's Schema.
	Document []byte
	// Model is the model the provider reported, else the configured one.
	Model string
	Usage Usage
	// Rejections counts the replies the harness rejected against the schema
	// (claude), and RejectionDetail is a bounded copy of the last one's
	// reason, which describes the reply's shape, for the private log.
	Rejections      int
	RejectionDetail string
}

Completion is a Completer's reply.

type ConcurrentCaller

type ConcurrentCaller interface {
	ConcurrentCalls() bool
}

ConcurrentCaller is a Backend that reports whether it may run several calls over one run directory at once, each with its own schema: one that writes nothing per call to a shared fixed path. A Backend that does not implement it, or reports false, gets one call at a time (Generator.StagedReview).

type Endpoint

type Endpoint struct {
	BaseURL string
	Key     string
}

Endpoint is a complete provider base URL and the key or token that authenticates to it.

type Event

type Event struct {
	Kind      string `json:"kind"`
	ElapsedMS int64  `json:"elapsed_ms"`
	ID        string `json:"id,omitempty"`
	Tool      string `json:"tool,omitempty"`
	Path      string `json:"path,omitempty"`
	Offset    int    `json:"offset,omitempty"`
	Limit     int    `json:"limit,omitempty"`
	Bytes     int    `json:"bytes,omitempty"`
	Status    string `json:"status,omitempty"`
	// FromProvider marks an error event whose Status was read from the
	// provider's HTTP status or a structured field of its response, not
	// guessed from free text.
	FromProvider bool `json:"from_provider,omitempty"`
	Input        int  `json:"input_tokens,omitempty"`
	Output       int  `json:"output_tokens,omitempty"`
	CacheRead    int  `json:"cache_read_tokens,omitempty"`
	CacheCreate  int  `json:"cache_creation_tokens,omitempty"`
}

Event stores bounded metadata, never assistant text, tool output or search patterns.

type Fixer

type Fixer interface {
	Fix(context.Context, Request) (Result, error)
}

Fixer is a backend that can run an agent with write access to a checkout, for a fix a person asked for: the agent edits the files of Request.Directory and its final message, a short summary of what it did, is Result.Document. Request.Schema is not used. The agent gets no network access and no git credentials; the caller commits and pushes what it changed. Only the claude and codex backends implement it.

type Framing

type Framing int

Framing is how a backend frames a task's prompt.

const (
	// TaskFraming frames a generic task, without pull request review wording
	// (Request.Task).
	TaskFraming Framing = iota
	// BackendFraming keeps the backend's own framing, a pull request
	// review's (Generator.ReviewTask).
	BackendFraming
)

type Generator

type Generator struct {
	Backend Backend
	Prompts Prompts
	// Validator checks and completes what the model returns.
	Validator *review.DocumentValidator
	// Redactor masks the secrets the model writes into identifiers; nil loads
	// the default rules for every run.
	Redactor *bundle.Redactor
	// Logger receives progress and failures; nil logs nothing.
	Logger *slog.Logger
	// RetryDelay is the wait before provider attempt n+1 after attempt n
	// (counting from 0) failed; nil waits 1<<min(n,4) seconds.
	RetryDelay func(attempt int) time.Duration
}

Generator is the generate-and-repair loop: it asks the backend for a review document, validates it with the DocumentValidator and, while the validator has findings, sends them back for a repair, up to the run's repair budget. Provider failures are retried within the run's attempt budget; every call and token counts against the run's budgets. RunTask runs a generic task through the same loop.

A Generator is safe for concurrent use when its Backend, Prompts and Validator are.

func (*Generator) Continue

func (g *Generator) Continue(ctx context.Context, budgets Budgets, request Request, outcome *Outcome) (Result, error)

Continue runs one call of a conversation that keeps its agent session (Request.PersistSession, ResumeSession and ForkSession) through the loop's provider retries, stall watchdog and budgets, charging its calls and tokens to outcome. Unlike RunTask it neither validates nor repairs the reply: a repair has to continue the same session, so the caller checks the reply and calls Continue again with the session the result names. A resume or fork whose session the CLI cannot find fails at once, without retries, with an error wrapping ErrSessionNotFound.

func (*Generator) Generate

func (g *Generator) Generate(ctx context.Context, run Run) (outcome Outcome, document []byte, err error)

Generate runs the loop. The returned document is the validated and prepared one, or the last rejected one when the Outcome carries a Reason of ReasonValidationRejected. Failures that are not a validation rejection (a spent budget, an unavailable provider, a cancelled context) come back as an error alongside the Outcome of what was spent. The Outcome's text is not redacted: the host applies RedactOutput before it stores or shows it.

func (*Generator) ReviewTask

func (g *Generator) ReviewTask(ctx context.Context, run Run) (Task, error)

ReviewTask is the review of run as a task RunTask runs: it writes bundle.json into the run directory and returns the review's model schema, its prompts (the instructions, the bundle and the source excerpts, and the repair text) and its Accept, which keeps the offered tile kinds, preserves the first pass's risk omissions in a repair, masks the secrets written into identifiers and prepares the document with the validator. A secret masked in a closed value stops the repairs: the host rejects the run whatever a later pass returns.

func (*Generator) RunTask

func (g *Generator) RunTask(ctx context.Context, task Task) (outcome Outcome, document []byte, err error)

RunTask runs a task through the same loop as Generate: provider retries, the stall watchdog and the budgets of call, and a reply rejected by the schema or the task's Check (or its Accept) is written to the run directory and sent back for a repair, up to the repair budget or a Verdict that stops. The returned document is the accepted one, or the last rejected one when the Outcome carries a Reason of ReasonValidationRejected; any other failure comes back as an error with the Outcome of what was spent. The Outcome's text is not redacted: the host applies RedactTaskOutput before it stores or shows it. Nothing the model wrote is logged.

func (*Generator) StagedReview

func (g *Generator) StagedReview(ctx context.Context, run StagedRun) (outcome StagedOutcome, document []byte, err error)

StagedReview writes a review in stages, each a task RunTask runs in the run directory with its own schema and repairs:

  1. The outline: the overview and each section's id, title, kind, hunks and assessment. A run whose outline fails fails as Generate does.
  2. The section details, for every section the previous version cannot supply (review.ReusableSectionDetails), validated one section at a time; a repair resends only the failing sections, each with its own findings. A section still invalid after the repairs, or not written when the stage ends early, has failed details.
  3. The codebase context (summary.context), at the same time as the details when the backend may run calls at once (ConcurrentCaller), else after them. A run whose context fails succeeds without one.

A run given an Outline (StagedRun.Outline) skips the outline and the context and writes only the details its Reuse version cannot supply.

The returned document is the assembled authored document with its reading time (review.ReviewMinutes), prepared with review's staged contract: a host completes it with CompleteStaged, as it completes Generate's document with CompleteWithPrompt. It never holds pending details. The run fails, as Generate does, when the outline fails, when no section ends with its details (none written and none reused), when ctx is done, or when the assembled document is rejected. Budgets are split between the stages (see stageBudgets); every call counts against its stage's share, and the shares never add up to more than the run's budgets.

type IdentifierRedaction

type IdentifierRedaction struct {
	Masked       int `json:"masked"`
	MaskedClosed int `json:"masked_closed"`
}

IdentifierRedaction counts the spans the identifier pass masked, and the values they were in. Every value it masks held a secret, so MaskedClosed is not zero whenever Masked is not, and the run is then rejected.

func (*IdentifierRedaction) Reported

func (r *IdentifierRedaction) Reported() bool

Reported reports whether the producer ran the identifier pass and says so with counts that can be true.

type Outcome

type Outcome struct {
	Accounting     *Accounting `json:"accounting,omitempty"`
	Model          string      `json:"model,omitempty"`
	SchemaRevision string      `json:"schema_revision"`
	// Reason is why the run failed, one of the fixed reasons (ReasonValidationRejected
	// and the budget and provider reasons). Empty means the run produced a
	// valid document.
	Reason   string `json:"reason,omitempty"`
	Findings string `json:"findings,omitempty"`
	// RejectedDocument is the last document the validator rejected when the
	// run failed, kept next to the findings so a maintainer can read both.
	RejectedDocument           json.RawMessage `json:"rejected_document,omitempty"`
	Diagnostics                []Event         `json:"diagnostics,omitempty"`
	Calls                      int             `json:"calls"`
	Tokens                     int             `json:"tokens"`
	ProviderAttempts           int             `json:"provider_attempts"`
	ErroredAttempts            int             `json:"errored_attempts"`
	ValidationRepairs          int             `json:"validation_repairs"`
	Usage                      Usage           `json:"usage"`
	StructuredOutputRejections int             `json:"structured_output_rejections"`
	// OutputMaskedSpans counts the secret spans RedactOutput masked in the
	// returned output, apart from the bundle's own count.
	OutputMaskedSpans int `json:"output_masked_spans"`
	// IdentifierRedaction is what the loop's identifier pass masked over every
	// pass of the run. The loop sets it on every outcome it builds; nil is a
	// producer that predates the pass.
	IdentifierRedaction *IdentifierRedaction `json:"identifier_redaction,omitempty"`
	// SessionID is the backend session of the last successful call, set only
	// when the run asked to persist its session (Run.PersistSession). It is
	// not part of the diagnostics.
	SessionID string `json:"-"`
	// Sessions is every session id the run's calls reported, retries and
	// repairs included, so a host can delete the CLI's files of the ones it
	// does not keep. Set only with Run.PersistSession.
	Sessions []string `json:"-"`
}

Outcome is what a generation run reports besides the document: what the loop did and what it spent. A host embeds it in its own outcome type; the field names are the wire format of the hosted agent, so they must not change.

type Prompt

type Prompt struct {
	// Version and Digest identify the instructions in the document's generator
	// block; Digest is the sha256 of Body. The JSON tags are the hosted run
	// header's wire format.
	Version string `json:"version"`
	Body    string `json:"body"`
	Digest  string `json:"digest"`
	// Repair and RepairEnding replace RepairText(backend) when they are not
	// empty: the text that opens and closes a validation repair prompt. A host
	// whose instructions name tools differently sets both. RepairEnding takes
	// two %d verbs, the attempt number and the attempt limit. They
	// are not part of the wire format.
	Repair       string `json:"-"`
	RepairEnding string `json:"-"`
}

Prompt is one immutable set of review instructions for one backend.

type Prompts

type Prompts interface {
	// Instructions returns the review instructions for a backend. The loop
	// ignores Version and Digest: the host records the version in the
	// document's generator.prompt_version and keeps the digest. The loop
	// appends the offered tile guidance (tilecatalogue.Instructions) to the
	// body itself.
	Instructions(ctx context.Context, backend string) (Prompt, error)
}

Prompts supplies the review instructions to the loop.

type RejectedError

type RejectedError struct {
	// Status is the fixed word of the refusal.
	Status string
	Err    error
}

RejectedError is a backend failure the loop does not retry: a rejected key, a request the provider refused, a declined prompt or a conversation over the model's context window. Its text starts with ProviderRejectedReason.

func (*RejectedError) Error

func (e *RejectedError) Error() string

func (*RejectedError) Unwrap

func (e *RejectedError) Unwrap() error

type Request

type Request struct {
	Logger            *slog.Logger
	Directory, Prompt string
	Schema            []byte
	// MaxCalls and MaxTokens bound the run; zero is no limit (Exceeds).
	MaxCalls, MaxTokens int
	// Endpoint replaces the backend's default provider address and key. Nil
	// leaves the backend's own.
	Endpoint *Endpoint
	// Activity receives the backend's progress, tools and turns for the
	// stall watchdog; nil when the run has none.
	Activity *Activity
	// Task marks a generic task (Generator.RunTask) rather than a review, so
	// the backend frames it without pull request review wording.
	Task bool
	// Scratch, for a Fixer, is a private directory the agent's commands may
	// write to besides Directory: their TMPDIR. Empty leaves the backend's
	// own temporary directory.
	Scratch string
	// Transcript, when set, receives each step of the run as the person
	// watching sees it (see TranscriptEntry); nil when none is kept.
	Transcript func(TranscriptEntry)
	// Usage, when set, receives the run's usage so far each time a model call
	// reports some, as the attempts Result.UsageAttempts will hold: a call
	// still running is Unreported with what it reported so far. Each report
	// replaces the previous one. It is called on the run's goroutine; a
	// backend that reports usage only at the end of a run never calls it.
	Usage func([]UsageAttempt)
	// Stage and Pass name a task's call (Generator.RunTask): the task's
	// Label, empty for a one-pass review, and 1 for its first pass, more for
	// each validation repair. Zero outside a task.
	Stage string
	Pass  int
	// PersistSession keeps the CLI's session instead of discarding it, so the
	// result's SessionID can be resumed later. Only a local Claude Code or
	// Codex run supports it; the hosted profiles, Cursor and the Anthropic API
	// backend refuse it. A ResumeSession implies it.
	PersistSession bool
	// ResumeSession is the session (Claude Code) or thread (Codex) id to
	// continue, a canonical UUID. The session is kept, as with PersistSession.
	// Run with the same Directory as the session it continues: Claude Code
	// stores sessions per working directory and Codex filters by it too. A
	// resumed Claude Code session keeps the system prompt it recorded, so put
	// per-turn framing in the user prompt.
	ResumeSession string
	// ForkSession, with ResumeSession, continues in a new session and leaves
	// the original untouched; Result.SessionID is then the new one. Without
	// ResumeSession it is an error.
	ForkSession bool
}

func (Request) Emit

func (r Request) Emit(entry TranscriptEntry)

Emit sends an entry to Transcript, stamped with the time unless it has one. It does nothing when there is no Transcript.

func (Request) KeepsSession

func (r Request) KeepsSession() bool

KeepsSession reports whether the CLI must keep its session: a persisted, resumed or forked run.

func (Request) ValidateSession

func (r Request) ValidateSession() error

ValidateSession rejects an inconsistent combination of session options.

func (Request) WantsSession

func (r Request) WantsSession() bool

WantsSession reports whether the request uses any session option.

type Result

type Result struct {
	Events                     []Event
	Document                   []byte
	Model                      string
	Calls, Tokens              int
	UsageAttempts              []UsageAttempt
	ModelSource                string
	Usage                      Usage
	StructuredOutputRejections int
	// LastRejection is a bounded copy of the last rejection's detail, which
	// describes the document's shape, not repository content.
	LastRejection string
	// SessionID is the session (Claude Code) or thread (Codex) id the CLI
	// reported for this call; with Request.ForkSession, the new session's id.
	// Empty when the CLI reported none.
	SessionID string
}

type Run

type Run struct {
	Bundle *bundle.Bundle
	// EncodedBundle is the bundle's JSON, as given to the model.
	EncodedBundle []byte
	// Directory is the run directory: the checkout's head and merge-base trees
	// and the attachments are unpacked in it. The Generator adds bundle.json,
	// schema.json and the rejected-N.json files of the repairs, and the backend
	// runs there.
	Directory string
	// TileKinds is the tile allow-list: the only kinds the run offers the model
	// and keeps from what it returns.
	TileKinds []string
	// TileEntries, when set, are the catalogue entries offered in the prompt
	// instead of the shipped entries of TileKinds (tilecatalogue.WithCandidates).
	// Evaluation uses it to try selection guidance; none (nil or empty) keeps the shipped ones.
	TileEntries []tilecatalogue.Entry
	Budgets     Budgets
	// Endpoint is how the backend reaches the model; nil leaves its own.
	Endpoint *Endpoint
	// Decorate, when set, adds to every document the model returns what the
	// host decides and the model never writes, such as a group review's
	// stack and its sections' members, before the document is masked and
	// validated. It must not fail: a document it cannot read is returned
	// as it came, for the validator to report.
	Decorate func([]byte) []byte
	// ModelSchema, when set, adjusts the model schema the run offers, such
	// as a group review's, which asks the model to name the group. What it
	// asks for must still prepare into a document the validator accepts,
	// with Decorate's help.
	ModelSchema func([]byte) ([]byte, error)
	// PersistSession keeps the backend's session of every call, so the
	// Outcome's SessionID can be resumed later (Request.PersistSession).
	PersistSession bool
}

Run is one generation: the bundle, its encoding and the directory holding the unpacked checkout, which the Generator writes the model's inputs into.

type StageOutcome

type StageOutcome struct {
	Outcome
	// Written, Reused and Failed name, for the details stage, the sections
	// whose details the model wrote, those taken from the previous version,
	// and those left without details.
	Written []string `json:"written,omitempty"`
	Reused  []string `json:"reused,omitempty"`
	Failed  []string `json:"failed,omitempty"`
}

StageOutcome is what one stage of a staged review did and spent. Its Outcome's Reason says why the stage failed, also when the run went on without it; its SessionID is the session that produced what the stage contributed (for the details stage, its first pass).

type StagedOutcome

type StagedOutcome struct {
	Outcome
	Outline StageOutcome
	Details StageOutcome
	Context StageOutcome
}

StagedOutcome is what a staged review reports besides the document. Its Outcome is the run's: the calls, tokens, attempts, repairs, usage, accounting, diagnostics and sessions of every stage summed, the failure reason and findings when the run failed, and SessionID, the session of the details stage's first pass (the one that wrote most of the document; the outline stage's when no section was sent to the model). Each stage's own outcome follows, with its reason when it failed.

type StagedProgress

type StagedProgress struct {
	// Stage is StageOutline, StageDetails or StageContext.
	Stage string
	// Section is the section whose details landed, for StageDetails.
	Section string
	// Reused marks details taken from the previous version; Failed details
	// or a context that could not be written.
	Reused, Failed bool
	// Document is the authored document so far (review.AssembleStaged): the
	// outline, the sections written, every other section without its
	// details (pending, or failed once they failed), and the context once
	// written. It is not redacted: a host masks it before it shows it, and
	// completes it with review.DocumentValidator.CompleteStaged.
	Document []byte
	// MaskedClosed says the identifier pass has masked a secret in a closed
	// value: the host rejects the run (RejectSecrets), so it shows no draft.
	MaskedClosed bool
}

StagedProgress is one stage result of a staged review.

type StagedPrompt

type StagedPrompt struct {
	Version, Digest string
	Shared          string
	Outline         string
	Details         string
	Context         string
}

StagedPrompt is one immutable set of staged review instructions for one backend. Every stage's prompt starts with the same prefix, Shared followed by the input bundle and the source excerpts; the stage's own instructions (Outline, Details or Context, each ending with the backend's submit text) and what it is given follow. Within a stage, its repairs reuse the cached prefix. Across stages a provider probably caches nothing, since each stage's schema (a StructuredOutput tool or --json-schema) comes before the prompt in what it caches; the evaluation measures it. Digest identifies all four texts together.

type StagedPrompts

type StagedPrompts interface {
	// StagedInstructions returns the staged review instructions for a
	// backend. The loop ignores Version and Digest, as it does a Prompt's.
	StagedInstructions(ctx context.Context, backend string) (StagedPrompt, error)
}

StagedPrompts supplies the instructions of a staged review (Generator.StagedReview).

type StagedRun

type StagedRun struct {
	Run
	Prompts StagedPrompts
	// Progress, when set, is told of each stage result as it lands: the
	// outline accepted, each section's details written, reused or failed, and
	// the context written or failed, with the authored document so far.
	// Calls come from the stages' goroutines, one at a time, and hold up the
	// stage that made them, so it must return quickly.
	Progress func(StagedProgress)
	// Outline, when set, continues a review whose outline was accepted
	// before, such as a saved version some of whose sections failed: it is
	// prepared as an outline reply (PrepareOutline) instead of being written,
	// and the run writes no context. Only the details of the sections Reuse
	// cannot supply are written, with every call and token of the budgets.
	// Context is the context the review keeps, prepared with the outline
	// (PrepareContext); nil, or one that no longer validates, leaves the
	// document without one.
	Outline, Context []byte
	// Reuse is the version whose complete section details are reused
	// (review.ReusableSectionDetails); nil is Run.Bundle.Previous. A host
	// that continues a saved version passes that version here and the
	// version before it as the bundle's, which completion reconciles with.
	Reuse *bundle.PreviousVersion
}

StagedRun is one staged generation: a Run, written in stages with the instructions of Prompts. Run.Decorate is not supported: a review group's combined review is written in one pass (Generate).

type StaticPrompts

type StaticPrompts map[string]Prompt

StaticPrompts is a fixed Prompts for tests and simple hosts: the prompt for a backend by name, with the prompt under the empty name serving every other backend.

func (StaticPrompts) Instructions

func (s StaticPrompts) Instructions(_ context.Context, backend string) (Prompt, error)

Instructions returns the prompt named for the backend, else the one under the empty name, else an error.

type Task

type Task struct {
	// Directory is the run directory: the checkout and the task's input
	// files. RunTask adds schema.json and the rejected-N.json files of the
	// repairs (named by Label when set), and the backend runs there.
	Directory string
	Prompt    string
	// Schema is the JSON Schema of the reply, given to the backend as the
	// output schema and checked against every reply.
	Schema []byte
	// Check is the workflow's own check of a reply the schema accepts; it
	// returns findings, empty when the reply is valid. Nil checks nothing more.
	Check    func(document []byte) (findings string)
	Budgets  Budgets
	Endpoint *Endpoint
	// PassPrompt, when set, is the prompt of each pass instead of Prompt and,
	// on a repair, the standard repair text.
	PassPrompt func(TaskPass) (string, error)
	// Accept, when set, checks each reply instead of the schema and Check:
	// it returns the document the reply stands for, the findings that send it
	// back for a repair, or Stop to reject it without another repair.
	Accept func(ctx context.Context, pass TaskPass, document []byte, outcome *Outcome) (Verdict, error)
	// Framing is how the backend frames the task; the zero value frames a
	// generic task.
	Framing Framing
	// PersistSession keeps the backend's session of every call, so the
	// Outcome's SessionID can be resumed later (Request.PersistSession).
	PersistSession bool
	// Label, when set, names the task's files in the run directory
	// (TaskSchemaFile), so that tasks running in one directory at once do not
	// overwrite each other's: lowercase letters, digits and dashes. Empty
	// keeps schema.json and rejected-N.json.
	Label string
}

Task is a generic run besides a review: a prompt over a run directory whose reply is a JSON document of the task's schema. Only the Generator's Backend, Logger and RetryDelay take part in it.

type TaskPass

type TaskPass struct {
	Number, Repairs int
	Rejected        []byte
	Findings        string
}

TaskPass is one pass of a task: Number 1 is the first, each later one a repair of the Rejected document with the Findings of the pass before it. Repairs is the repair budget.

type TranscriptEntry

type TranscriptEntry struct {
	Time time.Time `json:"time"`
	// Kind is "message", "reasoning", "tool", "error", or "call": the host's
	// mark of one model call of a run that makes several, by ID like a tool,
	// with its name in Title.
	Kind string `json:"kind"`
	// ID, Tool, Title and Status are a tool entry's: the harness's id, the
	// tool ("command", "file_change", "Read", ...), its command line or name
	// and main argument, and "running", "completed" or "failed".
	ID     string `json:"id,omitempty"`
	Tool   string `json:"tool,omitempty"`
	Title  string `json:"title,omitempty"`
	Status string `json:"status,omitempty"`
	// Text is a message's or reasoning's text, or a finished tool's output.
	Text string `json:"text,omitempty"`
	// Call, set by the host, names the call of a run of several the entry
	// belongs to, as its "call" entry's Title does.
	Call string `json:"call,omitempty"`
}

TranscriptEntry is one step of a run as the person watching it sees it: local only, never sent anywhere, and never part of an Event. Entries are append-only: a tool that starts is a "tool" entry with status "running"; when it ends, another with the same ID, status "completed" or "failed" and its output in Text. A consumer merges them by ID.

type Usage

type Usage struct {
	Input, Output, CacheRead, CacheCreate int64
	// CacheCreate1h is the part of CacheCreate written with the 1-hour TTL and
	// Speed the speed ("standard" or "fast") every call ran at. Nil means the
	// agent did not report it, or its calls differed (a run spans many calls).
	CacheCreate1h *int64
	Speed         *string
	// MinPrompt and MaxPrompt bound the prompt (input plus cache reads and
	// writes) of the single model calls the usage covers, which decides each
	// call's long-prompt tier. They are set only when every call of the run
	// reported usage; nil means unknown, and the total is then priced with no
	// range. Codex's agent reports no per-call usage, so its range is unknown.
	MinPrompt, MaxPrompt *int64
	// Unreported means the backend could not report usage, so the zero buckets
	// are unknown rather than free.
	Unreported bool
}

Usage uses disjoint buckets, as Anthropic reports them: Input excludes the cache reads and writes counted separately.

func (*Usage) Add

func (u *Usage) Add(other Usage)

Add accumulates another attempt's usage: buckets sum. The 1-hour part is known only while every attempt that wrote to the cache reported a split; otherwise it is nil rather than an undercount. The speed survives only while every attempt that used tokens agrees on it. An attempt with no usage decides neither.

func (Usage) IsZero

func (u Usage) IsZero() bool

IsZero reports whether nothing was reported: no tokens, no TTL split, no speed and no Unreported mark.

type UsageAttempt

type UsageAttempt struct {
	ModelSource   string `json:"model_source,omitempty"`
	Model         string `json:"model"`
	Usage         Usage  `json:"usage"`
	Calls         int    `json:"calls"`
	ChargedTokens int    `json:"charged_tokens"`
	Failed        bool   `json:"failed"`
}

UsageAttempt preserves the model and provider-reported buckets of one backend report, including unsuccessful retries and validation repairs.

type Verdict

type Verdict struct {
	Document []byte
	Findings string
	Stop     bool
}

Verdict is a task's Accept on a reply: the Document it stands for, with Findings when it is rejected; Stop rejects it without another repair.

Directories

Path Synopsis
Package anthropic is an agent.Backend that calls the Anthropic Messages API directly: a model call loop over a run directory with read-only file tools (Read, Glob, Grep) confined to that directory, whose final answer is the document the model submits through a StructuredOutput tool.
Package anthropic is an agent.Backend that calls the Anthropic Messages API directly: a model call loop over a run directory with read-only file tools (Read, Glob, Grep) confined to that directory, whose final answer is the document the model submits through a StructuredOutput tool.
anthropictest
Package anthropictest serves Messages API responses as the event streams the anthropic backend reads, for stub servers in tests.
Package anthropictest serves Messages API responses as the event streams the anthropic backend reads, for stub servers in tests.
Package claude runs the Claude Code CLI as an agent.Backend: a read-only model call loop over a directory whose final answer is a JSON document.
Package claude runs the Claude Code CLI as an agent.Backend: a read-only model call loop over a directory whose final answer is a JSON document.
Package codex runs the Codex CLI as an agent.Backend and derives the strict structured-output schema it passes to OpenAI.
Package codex runs the Codex CLI as an agent.Backend and derives the strict structured-output schema it passes to OpenAI.
Package cursor runs the Cursor Agent CLI (cursor-agent) as an agent.Backend: a model call loop over a directory, in Cursor's read-only ask mode with its sandbox enabled, whose final message is a JSON document.
Package cursor runs the Cursor Agent CLI (cursor-agent) as an agent.Backend: a model call loop over a directory, in Cursor's read-only ask mode with its sandbox enabled, whose final message is a JSON document.
Package prompt is the review prompt this module ships: the instructions the generate-and-repair loop (agent.Generator) gives a model backend.
Package prompt is the review prompt this module ships: the instructions the generate-and-repair loop (agent.Generator) gives a model backend.

Jump to

Keyboard shortcuts

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