loop

package
v0.27.1 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

README

pkg/loop

pkg/loop defines immutable loop recipes and the public contracts for live loops. Concrete loop actors and their construction remain internal (see internal/loopruntime); consumers compose definitions into a pkg/rig and interact with the returned loop.Handle or loop.Controller values.

A loop is the agent: one goroutine owns its mutable state, drives one inference client through turns and steps, and emits events. The package holds the design-time types (Definition, Mode, Engine, CompactionPolicy), the live identity/mutation surface (Handle, Controller, ModeCatalog, ExternalToolInstaller), and the runtime-supporting types the actor and the consumer both depend on (Backend, Provenance, RuntimeContextProvider, AccessGate, ReadGuard).

What is loop?

  • Definition — an immutable loop recipe. Built with loop.Define and a stream of Option values: name, inference client, model, system prompt, tools, access gate, middlewares, modes, delegation, compaction policy, structured output. Define validates every invariant and freezes the result.
  • Handle — the read-only identity and inference view of a live loop (ID, Mode, Model).
  • Controller — the trusted mutation surface (SetMode, Change, Interrupt). Interrupt is subtree-scoped: it cancels this loop's current turn and every loop below it in the delegate subtree.
  • Backend — the narrow turn-engine contract Session drives. Both the native actor and a foreign loop satisfy it; it's the minimal subset the session uses (command submission, completion signal, committed-state snapshot).

How to use

Consumers don't build loops directly; they compose definitions into a rig:

operator, err := loop.Define(
    loop.WithName("operator"),
    loop.WithClient(inferenceClient),
    loop.WithModel(model.Model{ /* provider, name, sampling.effort */ }),
    loop.WithSystem("You are a careful coding agent."),
    loop.WithTools(/* tool.Definition values from looprig/tools */...),
    loop.WithAccessGate(gateEvaluator),
    loop.WithDelegation(loop.Delegation{Style: loop.DelegationManaged}),
    loop.WithDelegates("reviewer", "architect"),
    loop.WithModes(loop.Mode{Name: "plan", /* ... */}),
    loop.WithInitialMode("plan"),
    loop.WithCompaction(/* optional CompactionPolicy */),
    loop.WithOutputSchema(/* optional inference.OutputSchema */),
    loop.WithPolicyRevision("2026-07-21.1"),
)
if err != nil { return err }

r, err := rig.Define(rig.WithLoops(operator), /* ... */)

Driving a live loop happens through the Session returned by the rig:

handle := session.ActiveLoop()
handle.ID(); handle.Model(); handle.Mode()

ctl, ok := session.LoopController(handle.ID())
if ok {
    ctl.SetMode(ctx, "plan")
    ctl.Interrupt(ctx)  // subtree-scoped
}

Sibling packages

  • pkg/tooltool.Definition values passed to loop.WithTools.
  • pkg/gate — the gate.Evaluator bound via loop.WithAccessGate.
  • pkg/identityidentity.AgentName used by loop.WithName and loop.WithDelegates.
  • pkg/eventevent.TurnIndex and the events the loop emits.
  • pkg/command — the commands the actor accepts on its Commands channel.
  • pkg/foreignEngineForeignClaude / EngineForeignCodex select a foreign-loop backend.
  • pkg/hustle — the parallel background work subsystem a loop can invoke through the hustle tool.
  • github.com/looprig/inferenceinference.Client, model.Model, inference.OutputSchema, context counters.
  • github.com/looprig/foreignloops — the codex/claude backends behind Engine.

How it is designed

A Definition is an immutable recipe. The private internal/loopruntime package binds a definition into a live actor: one goroutine that owns mutable state, accepts commands on a channel, and spawns a fresh turn goroutine per accepted turn.

                Definition (immutable recipe)
                          │
                          │  rig binds it (Bind)
                          ▼
                internal/loopruntime.Loop
                          │
   ┌──────────────────────┴───────────────────────┐
   │ Loop actor goroutine                          │
   │  state: idle | running | shuttingDown          │
   │  commands chan  ◀── Submit/CancelQueued/...   │
   │  priorityCommands ◀── Interrupt/Shutdown      │
   │  gateReg ◀── pending gate registrations       │
   │  snapshots ◀── committed-state queries        │
   └──────┬───────────────────────────────────────┘
          │ per accepted turn
          ▼
   ┌──────────────────────────────────────────────┐
   │ Turn runner goroutine                         │
   │  turnState.msgs (staged; owned by turn)       │
   │  events chan (per-turn; owned by caller)     │
   │  for each step:                               │
   │    LLM stream  ─► emit TokenDelta              │
   │    tool batch ─► gate → sandbox → result      │
   │    commit group into loopState.msgs           │
   └──────────────────────────────────────────────┘

The full goroutine/channel/ack picture — including StartTurn, Interrupt, and Shutdown semantics, and the rationale for there being no turn queue in v1 — is in docs/architecture/agent-loop.md.

Engines

Engine selects the loop backend. The zero value is EngineNative (harness's own actor). EngineForeignClaude and EngineForeignCodex select a foreign-loop backend built by a foreign.Builder registered on the rig. A foreign loop satisfies the same Backend contract the session drives; restore recovers its foreign session id from the journal.

Modes and tool limits

A Mode is a predeclared alternative to a definition's base inference settings: model, effort, tools, tool limits, and an optional instructions override. The implicit base mode is the empty ModeName. Default tool limits are 25 iterations, 100 calls per turn, 8 parallel calls.

Compaction and context policy

A CompactionPolicy opts a loop into per-loop token accounting: when input occupancy crosses a threshold, the loop runs a hustle-style compaction that produces a summary tied to the exact context basis, then commits the compacted context. ContextObservationPolicy is the mutually-exclusive alternative — observe occupancy without compacting. Both require a contextcount.ContextCounter and a compatible inference.Client; Define validates the binding.

KeepRecentSegments and KeepRecentTokens bound the suffix protected from compaction. Segments are the newest complete user-anchored portions of the conversation. KeepRecentTokens uses a deterministic estimate (where selection needs one: ceil(JSON bytes / 4)) over the original, unprojected messages; it is best-effort. The newest complete segment is retained even when its estimate exceeds the token target, and the cut moves earlier when needed to keep a complete tool-use/result pair together. The retained suffix may therefore exceed either configured target.

Before invoking the compactor, the runtime counts the actual retained-tail context (including a live runtime tail when present) and checks it, the summary budget, and the request InputLimit. An over-limit retained tail publishes CompactionRejected with CompactRejectRetainedTailTooLarge without invoking the compactor LLM.

Concurrency contract

The actor's Commands channel is unbuffered — sends block until the actor is ready. Callers must never close it; stop the actor with Shutdown. The submit commands (UserInput, SubagentResult, CancelQueuedInput) are fire-and-forget: their outcomes are published as typed events, not replied on a per-command channel. Only the control commands (Interrupt, Shutdown) carry an Ack channel, and each must be non-nil and buffered(1) so the actor's send never stalls.

Documentation

Overview

Package loop defines immutable loop recipes and the public contracts for live loops. Concrete loop actors and their construction remain internal to the harness; consumers compose definitions into a rig and interact with returned Handle or Controller values.

Index

Constants

View Source
const ManagedInputQueueCapacity = 64

ManagedInputQueueCapacity is the maximum number of accepted managed inputs waiting behind active work. It is public because native and optional backends must share this observable boundary.

Variables

This section is empty.

Functions

func EffectiveSystem

func EffectiveSystem(system, instructions string) string

EffectiveSystem combines a base system prompt with a mode's instructions: the base alone when a mode adds no instructions, the instructions alone when there is no base, otherwise the two joined by a blank line. It is exported as the SINGLE source of this rule so the loop actor (which resolves the SELECTED mode's system per turn, in loopruntime) composes it byte-for-byte identically — restore fidelity depends on the live and folded system prompts matching exactly.

func GateApprover

func GateApprover() gate.Approver

GateApprover returns the gate.Approver a consumer passes to interactive evaluator construction (gate.NewInteractiveEvaluator). It resolves each combined prompt through the live loop's approval capability — the loop's own permission gate — installed on ctx by the runner for exactly one call, and fails closed when invoked outside a live loop call.

func IsReservedToolName

func IsReservedToolName(name string) bool

IsReservedToolName reports whether name is Harness's exact model-facing structured-output control name. It deliberately does not trim: surrounding whitespace is invalid ordinary tool metadata, but it is not the reserved name.

func OccupancyBasisPoints

func OccupancyBasisPoints(used, limit content.TokenCount) (event.BasisPoints, error)

OccupancyBasisPoints calculates floor(used*10_000/limit) with checked 128-bit multiplication and clamps over-limit display values at 100%.

func PreparedCallFromContext

func PreparedCallFromContext(ctx context.Context) (tool.PreparedCall, bool)

PreparedCallFromContext returns the prepared execution contract carried by ctx, and false when the tool is running outside a prepared call.

func RequestFingerprint

func RequestFingerprint(input RequestFingerprintInput) ([32]byte, error)

RequestFingerprint returns the deterministic SHA-256 identity of all secret-free request-shape inputs that affect counting.

func RequestUserInput

func RequestUserInput(ctx context.Context, question string, choices []string) (string, error)

RequestUserInput routes a tool request through the live loop capability in ctx.

func ToolUseIDFrom

func ToolUseIDFrom(ctx context.Context) (string, bool)

ToolUseIDFrom returns the provider tool-use id carried by ctx.

func WithApprovalRequester

func WithApprovalRequester(ctx context.Context, requester ApprovalRequestFunc) context.Context

WithApprovalRequester installs the live loop's combined-approval capability for a tool call. Only the runner wires this, per call, so a gate opened by the evaluator routes to the loop's own gate machinery.

func WithPreparedCall

func WithPreparedCall(ctx context.Context, prepared tool.PreparedCall) context.Context

WithPreparedCall carries the prepared execution contract for a tool invocation: the minted execution ID, the typed request, the tool's opaque per-call artifact, and the fresh grant tokens issued for this call. The runner installs it per call; the executing tool reads it back via PreparedCallFromContext. Tokens travel only inside this contract, never in a separate ambient grant carrier.

func WithProvenance

func WithProvenance(ctx context.Context, p Provenance) context.Context

WithProvenance returns a child ctx carrying the current loop/turn/step coordinates, so a tool (e.g. an agent collaboration tool) can learn its OWN provenance and pass it as the `parent` when spawning a sub-loop. The loop injects it at the tool-batch boundary, where all three ids are unambiguously the running step's.

func WithToolUseID

func WithToolUseID(ctx context.Context, id string) context.Context

WithToolUseID carries the provider tool-use id for a running tool call.

func WithUserInputRequester

func WithUserInputRequester(ctx context.Context, requester RequestUserInputFunc) context.Context

WithUserInputRequester installs the live loop's user-input capability for a tool call.

Types

type AccessGate

type AccessGate interface {
	Authorize(ctx context.Context, request tool.Request) (gate.Resolution, error)
}

AccessGate is the runner's view of the combined three-state access decision for one prepared request. It is satisfied by *gate.Evaluator (interactive or headless construction) or by a consumer-provided equivalent.

Authorize evaluates the complete typed request once, opens at most one combined approval (interactive construction only), and returns the fresh execution-bound grant tokens for the approved call. An unapproved Resolution with a nil error is a policy or user denial; any error is fail-closed. An implementation must be safe for concurrent calls.

type AgentHarnessName

type AgentHarnessName string

AgentHarnessName is the stable, model-facing alias of a child execution harness. It is intentionally not an executable name or a connector path.

type ApprovalContextError

type ApprovalContextError struct{}

ApprovalContextError reports that an approval was requested outside a live loop call (no runner-installed approval capability on ctx). It is fail-closed: no capability means no prompt and no approval.

func (*ApprovalContextError) Error

func (*ApprovalContextError) Error() string

type ApprovalRequestFunc

type ApprovalRequestFunc func(ctx context.Context, prompt gate.ApprovalPrompt) (gate.ApprovalAction, error)

ApprovalRequestFunc is the actor-supplied capability that opens ONE combined interactive approval gate for a prepared request and blocks until it is resolved to exactly one approval action.

type Backend

type Backend interface {
	CommandSink() chan<- command.Command
	DoneChan() <-chan struct{}
	Snapshot(ctx context.Context) (content.AgenticMessages, event.TurnIndex, error)
}

Backend is the narrow turn-engine contract Session drives. Both native and *foreignloop.Loop satisfy it. It is deliberately the minimal subset Session uses (command submission, completion signalling, committed-state snapshot) — it does NOT expose the native loop's internal gate/commit/drain seams, which a foreign loop has no analogue for. Snapshot's signature is exactly *Loop.Snapshot's.

type BindError

type BindError struct {
	Kind  BindErrorKind
	Name  string
	Index int
	Cause error
}

BindError reports a failure while creating one loop's runtime collaborators.

func (*BindError) Error

func (e *BindError) Error() string

func (*BindError) Unwrap

func (e *BindError) Unwrap() error

type BindErrorKind

type BindErrorKind string

BindErrorKind identifies a runtime-binding failure.

const (
	BindInvalidDefinition       BindErrorKind = "invalid_definition"
	BindInvalidContext          BindErrorKind = "invalid_context"
	BindDuplicateDefinitionName BindErrorKind = "duplicate_definition_name"
	BindDuplicateToolName       BindErrorKind = "duplicate_tool_name"
	BindInvalidToolInfo         BindErrorKind = "invalid_tool_info"
	BindInvalidAccessGate       BindErrorKind = "invalid_access_gate"
	BindInvalidRuntime          BindErrorKind = "invalid_runtime"
	BindInvalidSessionID        BindErrorKind = "invalid_session_id"
	BindInvalidLoopID           BindErrorKind = "invalid_loop_id"
)

type BoundDefinition

type BoundDefinition interface {
	Name() identity.AgentName
	DisplayName() string
	Description() string
	Engine() Engine
	RuntimeProfile() RuntimeProfileName
	RuntimeSource() RuntimeSourceName
	RuntimeSelectionKind() RuntimeSelectionKind
	RuntimeCatalogDigest() string
	RuntimeIdentity() RuntimeIdentity
	Client() inference.Client
	Model() model.Model
	Effort() model.Effort
	System() string
	EffectiveSystem() string
	Instructions() string
	Tools() []tool.InvokableTool
	ToolLimits() ToolLimits
	Modes() []BoundMode
	Mode(ModeName) (BoundMode, bool)
	InitialMode() ModeName
	Access() AccessGate
	Middlewares() []tool.ToolMiddleware
	DrainTimeout() time.Duration
	RuntimeContext() RuntimeContextProvider
	ContextCounter() contextcount.ContextCounter
	CounterCapability() (contextcount.CounterCapability, bool)
	InferenceCapability() (contextcount.InferenceCapability, bool)
	// ContextTransportCapability resolves the declared InferenceCapability for
	// model's transport, or (zero, false) if that transport is not a member of
	// this definition's declared ContextTransport set.
	ContextTransportCapability(model.Model) (contextcount.InferenceCapability, bool)
	ContextObservationPolicy() (ContextObservationPolicy, bool)
	CompactionPolicy() (CompactionPolicy, bool)
	OutputSchema() (*inference.OutputSchema, bool)
	ValidateContextModel(model.Model) error
	Delegation() Delegation
	Delegates() []identity.AgentName
	// contains filtered or unexported methods
}

BoundDefinition is the sealed read-only runtime view of one bound loop.

func OverrideBoundAccess

func OverrideBoundAccess(bound BoundDefinition, access AccessGate) (BoundDefinition, error)

OverrideBoundAccess returns a private bound view whose Access() resolves the given gate instead of the definition's own. It is the binding-time seam a composition root uses to give ONE bound loop a different combined access gate (for example a restricted evaluator for a reviewer role) without mutating the immutable definition.

Authority differences between loops are expressed by the CONSUMER passing different evaluators — there is no harness-side attenuation, and a bound loop without an override always resolves its own definition's gate, never another loop's. A nil gate is rejected: overriding to "no gate" would silently turn a gated loop into a fail-closed-only loop through a side door; configure the definition without WithAccessGate instead.

func OverrideBoundRuntime

func OverrideBoundRuntime(bound BoundDefinition, profile RuntimeProfileName, target model.Model, effort model.Effort) (BoundDefinition, error)

OverrideBoundRuntime returns a private bound view whose engine, runtime profile, model, and effort are replaced by an already-validated runtime selection. The caller MUST have resolved the selection through its parent-scoped RuntimeCatalog; this function does not re-consult policy. Every bound mode receives the same model and effort so a later mode selection cannot silently un-pin the selected runtime tuple.

func OverrideBoundRuntimeCatalog

func OverrideBoundRuntimeCatalog(bound BoundDefinition, catalog RuntimeCatalog) (BoundDefinition, error)

OverrideBoundRuntimeCatalog records the immutable catalog snapshot used to authorize a bound runtime. It changes only the runtime identity and leaves the selected runtime tuple and all loop behavior untouched.

func OverrideBoundRuntimeManaged

func OverrideBoundRuntimeManaged(bound BoundDefinition, profile RuntimeProfileName) (BoundDefinition, error)

OverrideBoundRuntimeManaged installs the typed seam used by a native ACP harness-managed selection. It intentionally leaves the definition's base model and modes untouched: those values satisfy the existing BoundDefinition invariant, while the adapter receives no concrete model identity from this runtime selection. Later ACP phases must consume RuntimeIdentity.Source and RuntimeIdentity.SelectionKind and omit model/effort overrides.

func OverrideBoundRuntimeSelection

func OverrideBoundRuntimeSelection(bound BoundDefinition, profile RuntimeProfileName, alias ModelAlias, target model.Model, effort model.Effort) (BoundDefinition, error)

OverrideBoundRuntimeSelection is the binding-time seam for a resolved runtime tuple. alias is optional only for compatibility with callers using OverrideBoundRuntime; non-empty aliases use the catalog's identifier rules.

func OverrideBoundRuntimeSelectionWithIdentity

func OverrideBoundRuntimeSelectionWithIdentity(bound BoundDefinition, profile RuntimeProfileName, alias ModelAlias, target model.Model, effort model.Effort, source RuntimeSourceName, selectionKind RuntimeSelectionKind) (BoundDefinition, error)

OverrideBoundRuntimeSelectionWithIdentity is the source-aware binding seam for a concrete catalogue selection. Empty source and selection kind preserve the legacy helper's identity shape for callers that predate source-aware runtime selection.

func SelectBoundMode

func SelectBoundMode(bound BoundDefinition, mode ModeName) (BoundDefinition, error)

SelectBoundMode returns a private bound view whose default accessors resolve the selected effective mode. It retains every declared mode for later trusted changes.

type BoundMode

type BoundMode struct {
	Name         ModeName
	Model        model.Model
	Effort       model.Effort
	Tools        []tool.InvokableTool
	ToolLimits   ToolLimits
	Instructions string
}

BoundMode is one immutable definition mode resolved to runtime tool instances. Values returned by BoundDefinition are defensive copies.

type Change

type Change interface {
	InferenceModel() (model.Model, bool)
	InferenceEffort() (model.Effort, bool)
	// contains filtered or unexported methods
}

Change is a sealed immutable loop-inference change. Runtime implementations inspect the two read-only projections and validate a whole batch atomically.

func ChangeEffort

func ChangeEffort(effort model.Effort) Change

ChangeEffort selects a new inference effort. Controllers validate it atomically.

func ChangeModel

func ChangeModel(model model.Model) Change

ChangeModel selects a new secret-free model descriptor. Controllers validate it atomically with the other changes before applying anything.

type ChangeError

type ChangeError struct {
	Kind  ChangeErrorKind
	Mode  ModeName
	Tool  string
	Cause error
}

ChangeError is the typed refusal returned by Controller.SetMode / Controller.Change. Mode carries the offending mode name for ChangeInvalidMode; Cause chains an underlying validation or persistence error where one exists. Callers errors.As it to distinguish a user error (invalid mode/model/effort) from a lifecycle refusal (shutting down / exited) from a persistence fault. Tool carries the offending model-facing tool name for ChangeExternalToolCollision and ChangeExternalBuildFailed, so a caller can report which tool refused the batch without parsing the message.

func (*ChangeError) Error

func (e *ChangeError) Error() string

func (*ChangeError) Unwrap

func (e *ChangeError) Unwrap() error

type ChangeErrorKind

type ChangeErrorKind string

ChangeErrorKind classifies why a SetMode or Change was refused. Every kind is a fail-secure refusal: the change is NOT applied (no partial apply) and no enduring event is emitted (except when the durable append itself is the failure, in which case the append faulted the session and the change is not applied).

const (
	// ChangeInvalidMode: the requested mode name is not a predeclared mode of the loop
	// definition (nor the base mode).
	ChangeInvalidMode ChangeErrorKind = "invalid_mode"
	// ChangeInvalidModel: the requested model descriptor failed structural validation.
	ChangeInvalidModel ChangeErrorKind = "invalid_model"
	// ChangeInvalidEffort: the requested effort is not a known effort level.
	ChangeInvalidEffort ChangeErrorKind = "invalid_effort"
	// ChangeNoChanges: a Change batch selected neither a model nor an effort.
	ChangeNoChanges ChangeErrorKind = "no_changes"
	// ChangeLoopShuttingDown: the loop is shutting down and admits no configuration change.
	ChangeLoopShuttingDown ChangeErrorKind = "loop_shutting_down"
	// ChangeLoopExited: the loop's actor has exited, so no change can be delivered.
	ChangeLoopExited ChangeErrorKind = "loop_exited"
	// ChangeContextDone: the caller's context was cancelled before the change committed.
	ChangeContextDone ChangeErrorKind = "context_done"
	// ChangeDurableAppendFailed: the enduring change event's required durable append
	// failed (the session faulted), so the change was NOT applied.
	ChangeDurableAppendFailed ChangeErrorKind = "durable_append_failed"
	// ChangeInvalidExternalSource: the external toolset's Source is empty, over-long,
	// or not a valid slot name.
	ChangeInvalidExternalSource ChangeErrorKind = "invalid_external_source"
	// ChangeInvalidExternalGeneration: the external toolset's Generation is empty or
	// over-long. A generation is required — an unidentified toolset cannot be audited.
	ChangeInvalidExternalGeneration ChangeErrorKind = "invalid_external_generation"
	// ChangeExternalBuildFailed: at least one external definition failed to Build (or
	// to describe itself). NOTHING was installed — the prior generation stays.
	ChangeExternalBuildFailed ChangeErrorKind = "external_build_failed"
	// ChangeExternalToolCollision: an external tool's model-facing name collides with a
	// declared tool of the loop definition, with another tool in the same replacement,
	// or with a tool installed by a different source. The whole replacement is refused
	// so an external tool can never shadow a declared one.
	ChangeExternalToolCollision ChangeErrorKind = "external_tool_collision"
	// ChangeExternalToolsUnsupported: this loop cannot host external tools (it is a
	// foreign loop whose toolset is owned by the foreign agent, so harness holds no
	// tool bindings for it).
	ChangeExternalToolsUnsupported ChangeErrorKind = "external_tools_unsupported"
)

type CommitCancelReason

type CommitCancelReason string

CommitCancelReason distinguishes why a per-step commit handshake did not reach the actor's commit point.

const (
	// CommitTurnCancelled means the turn context was cancelled (Interrupt/Shutdown)
	// before the actor committed the step. runTurn returns a TurnInterrupted and the
	// in-flight step is discarded; committed steps stay committed.
	CommitTurnCancelled CommitCancelReason = "turn cancelled"
)

type CommitError

type CommitError struct {
	Reason CommitCancelReason
	Cause  error
}

CommitError is returned by turnConfig.commit when the ctx-cancellable commit handshake cannot deliver a completed step to the actor. The turn goroutine stops and surfaces a terminal without wedging; the already-committed steps remain in loopState.msgs. Callers MAY errors.As to distinguish cancel reasons; today the only reason is CommitTurnCancelled and callers treat any commit error as an interrupt. Reason is reserved for a later phase (e.g. Shutdown-vs-Interrupt).

func (*CommitError) Error

func (e *CommitError) Error() string

func (*CommitError) Unwrap

func (e *CommitError) Unwrap() error

type CompactionInput

type CompactionInput struct {
	Basis              event.ContextBasis
	Model              model.ModelKey
	RequestFingerprint [32]byte
	Transcript         content.AgenticMessages
	MaxSummaryTokens   content.TokenCount
}

CompactionInput is the exact context identity and transcript summarized by one compaction hustle invocation.

func (CompactionInput) Validate

func (i CompactionInput) Validate() error

Validate checks the typed domain boundary before the adapter serializes it.

type CompactionInputError

type CompactionInputError struct {
	Field CompactionInputField
	Cause error
}

CompactionInputError reports malformed typed input without rendering transcript or prompt bytes.

func (*CompactionInputError) Error

func (e *CompactionInputError) Error() string

func (*CompactionInputError) Unwrap

func (e *CompactionInputError) Unwrap() error

type CompactionInputField

type CompactionInputField string

CompactionInputField identifies an invalid domain input component.

const (
	CompactionInputFieldBasis              CompactionInputField = "basis"
	CompactionInputFieldModel              CompactionInputField = "model"
	CompactionInputFieldRequestFingerprint CompactionInputField = "request_fingerprint"
	CompactionInputFieldTranscript         CompactionInputField = "transcript"
	CompactionInputFieldMaxSummaryTokens   CompactionInputField = "max_summary_tokens"
)

type CompactionOutput

type CompactionOutput struct {
	Basis              event.ContextBasis
	Model              model.ModelKey
	RequestFingerprint [32]byte
	Summary            *content.UserMessage
}

CompactionOutput is a validated summary tied to the exact input identity.

func (CompactionOutput) Validate

func (o CompactionOutput) Validate() error

Validate checks the output's identity and single-user-text replacement shape. The strict XML grammar is enforced by the internal adapter before constructing this value.

type CompactionPolicy

type CompactionPolicy struct {
	Automatic          bool
	CounterPolicy      CounterPolicy
	CompactAt          event.BasisPoints
	RearmBelow         event.BasisPoints
	KeepRecentSegments int
	KeepRecentTokens   content.TokenCount
	ReservedOutput     content.TokenCount
	SafetyMargin       content.TokenCount
	MaxSummaryTokens   content.TokenCount
	CountTimeout       time.Duration
	Hustle             hustle.Name
}

CompactionPolicy is the complete explicit policy installed by WithCompaction. Harness supplies no timeout or threshold defaults.

func (CompactionPolicy) Validate

func (p CompactionPolicy) Validate(capability contextcount.CounterCapability) error

Validate checks the policy against already-validated, I/O-free counter metadata. It never calls CountContext.

type CompactionPolicyError

type CompactionPolicyError struct {
	Field CompactionPolicyField
	Cause error
}

CompactionPolicyError reports invalid explicit compaction configuration.

func (*CompactionPolicyError) Error

func (e *CompactionPolicyError) Error() string

func (*CompactionPolicyError) Unwrap

func (e *CompactionPolicyError) Unwrap() error

type CompactionPolicyField

type CompactionPolicyField string

CompactionPolicyField identifies one rejected policy dimension.

const (
	CompactionFieldCounterPolicy      CompactionPolicyField = "CounterPolicy"
	CompactionFieldCompactAt          CompactionPolicyField = "CompactAt"
	CompactionFieldRearmBelow         CompactionPolicyField = "RearmBelow"
	CompactionFieldKeepRecentSegments CompactionPolicyField = "KeepRecentSegments"
	CompactionFieldKeepRecentTokens   CompactionPolicyField = "KeepRecentTokens"
	CompactionFieldReservedOutput     CompactionPolicyField = "ReservedOutput"
	CompactionFieldSafetyMargin       CompactionPolicyField = "SafetyMargin"
	CompactionFieldMaxSummaryTokens   CompactionPolicyField = "MaxSummaryTokens"
	CompactionFieldCountTimeout       CompactionPolicyField = "CountTimeout"
	CompactionFieldHustle             CompactionPolicyField = "Hustle"
)

type CompactionWireVersion

type CompactionWireVersion uint8

CompactionWireVersion identifies the concrete adapter JSON contract.

const (
	CompactionWireVersionUnknown CompactionWireVersion = iota
	CompactionWireV1
)

func (CompactionWireVersion) Valid

func (v CompactionWireVersion) Valid() bool

Valid reports whether the wire version is implemented.

type ConfigError

type ConfigError struct {
	Kind  ConfigErrorKind
	Cause error
}

ConfigError reports an invalid resolved definition or missing actor-runtime collaborator discovered during internal binding. Public callers normally encounter DefinitionError from Define or BindError from Definition.Bind before this seam.

func (*ConfigError) Error

func (e *ConfigError) Error() string

func (*ConfigError) Unwrap

func (e *ConfigError) Unwrap() error

type ConfigErrorKind

type ConfigErrorKind string
const (
	ConfigMissingClient    ConfigErrorKind = "missing_client"
	ConfigInvalidModel     ConfigErrorKind = "invalid_model"
	ConfigMissingPublisher ConfigErrorKind = "missing_publisher"
)

type ContextLimitError

type ContextLimitError struct {
	Measurement event.ContextMeasurement
}

ContextLimitError reports that an authoritative candidate-request measurement reached or exceeded its resolved hard input limit.

func (*ContextLimitError) Error

func (e *ContextLimitError) Error() string

type ContextLimitUnknownError

type ContextLimitUnknownError struct {
	Model model.ModelKey
	Cause error
}

ContextLimitUnknownError reports that model metadata and policy do not yield a safe non-zero input denominator.

func (*ContextLimitUnknownError) Error

func (e *ContextLimitUnknownError) Error() string

func (*ContextLimitUnknownError) Unwrap

func (e *ContextLimitUnknownError) Unwrap() error

type ContextObservationPolicy

type ContextObservationPolicy struct {
	ReservedOutput content.TokenCount
	SafetyMargin   content.TokenCount
	CountTimeout   time.Duration
}

ContextObservationPolicy owns hard-admission settings for a non-compacting loop. Every value is explicit; harness supplies no timeout or limit defaults.

func (ContextObservationPolicy) Validate

Validate checks policy values against already-validated counter metadata.

type ContextObservationPolicyError

type ContextObservationPolicyError struct {
	Field ContextObservationPolicyField
}

ContextObservationPolicyError reports invalid explicit observation policy.

func (*ContextObservationPolicyError) Error

type ContextObservationPolicyField

type ContextObservationPolicyField string

ContextObservationPolicyField identifies one rejected observation setting.

const (
	ContextObservationFieldReservedOutput ContextObservationPolicyField = "ReservedOutput"
	ContextObservationFieldSafetyMargin   ContextObservationPolicyField = "SafetyMargin"
	ContextObservationFieldCountTimeout   ContextObservationPolicyField = "CountTimeout"
)

type ContextTransport

type ContextTransport struct {
	Provider   model.ProviderName
	APIFormat  model.APIFormat
	BaseURL    string
	Capability contextcount.InferenceCapability
}

ContextTransport is one admitted (wire transport -> trust posture) pair a loop definition allows a live model switch to move to.

type ContextTransportNotDeclaredError

type ContextTransportNotDeclaredError struct {
	Provider  model.ProviderName
	APIFormat model.APIFormat
	BaseURL   string
}

ContextTransportNotDeclaredError reports a candidate model whose transport is not a member of a loop definition's declared ContextTransport set.

func (*ContextTransportNotDeclaredError) Error

type Controller

type Controller interface {
	Handle
	SetMode(context.Context, ModeName) error
	Change(context.Context, ...Change) error
	// Interrupt cancels this loop's current turn AND every loop below it in the delegate
	// subtree, marking the whole subtree interrupt-pending so a parent whose interrupted
	// delegate wait resolves cannot open a fresh delegate step. It is the subtree-scoped
	// counterpart to the session-wide Session.Interrupt and the single-child agent
	// interrupt. The runtime holds an admission barrier over the subtree until it is idle.
	Interrupt(context.Context) error
}

Controller is the trusted mutation surface of a live loop. Changes are applied by the actor at a turn boundary; Task 9 provides that behavior.

type CounterPolicy

type CounterPolicy uint8

CounterPolicy selects the count qualities automatic compaction may trust.

const (
	CounterPolicyUnknown CounterPolicy = iota
	CounterPolicyRequireExact
	CounterPolicyAllowConservative
)

type CredentialMode

type CredentialMode string

CredentialMode identifies who supplies the credential used by a child harness: the product gateway or the harness's own login state.

const (
	// CredentialGatewayBacked routes the child through the product gateway.
	CredentialGatewayBacked CredentialMode = "gateway-backed"
	// CredentialNativeAuth leaves authentication to the child harness.
	CredentialNativeAuth CredentialMode = "native-auth"
)

type Definition

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

Definition is a concrete immutable loop definition. Its zero value is invalid.

func Define

func Define(opts ...Option) (Definition, error)

Define validates and freezes one loop definition.

func (Definition) Bind

func (d Definition) Bind(ctx context.Context, bindings tool.Bindings) (BoundDefinition, error)

Bind creates fresh session-specific collaborators and resolves every declared mode.

func (Definition) CompactionPolicy

func (d Definition) CompactionPolicy() (CompactionPolicy, bool)

CompactionPolicy returns the frozen policy when configured.

func (Definition) ContextObservationPolicy

func (d Definition) ContextObservationPolicy() (ContextObservationPolicy, bool)

ContextObservationPolicy returns the frozen observe-only policy when configured.

func (Definition) Delegates

func (d Definition) Delegates() []identity.AgentName

Delegates returns a defensive copy of the definition's allowed delegate names.

func (Definition) Delegation

func (d Definition) Delegation() Delegation

Delegation returns the immutable delegation policy.

func (Definition) Description

func (d Definition) Description() string

Description returns the immutable user-facing guidance attached to this definition. It is used when compiling parent-scoped agent capabilities.

func (Definition) Engine

func (d Definition) Engine() Engine

Engine returns the immutable backend selected for this definition. Composition roots use this bind-free view to reject unsupported capability combinations before any tool factory can run.

func (Definition) FingerprintInitial

func (d Definition) FingerprintInitial() InitialFingerprint

FingerprintInitial resolves the definition's selected initial mode without building tools, permissions, runtime context, or any other session-specific collaborator.

func (Definition) InitialMode

func (d Definition) InitialMode() ModeName

InitialMode returns the explicitly selected mode, or empty for the base mode.

func (Definition) Modes

func (d Definition) Modes() []Mode

Modes returns defensive copies of the predeclared modes. The implicit base mode is available after Bind and is not included here.

func (Definition) Name

func (d Definition) Name() identity.AgentName

Name returns the immutable attribution name used to register this definition.

func (Definition) PolicyRevision

func (d Definition) PolicyRevision() string

PolicyRevision returns a deterministic, secret-free digest of immutable loop behavior used by a rig topology fingerprint. Opaque function-valued collaborators require WithPolicyRevision, whose caller-supplied identity is included here. Adding, removing, or changing a hashed field here shifts this digest for every existing consumer once, surfacing as an ordinary one-time Info-severity DriftTopology on their next restore — expected, not a bug.

func (Definition) ToolRequirements

func (d Definition) ToolRequirements() tool.Requirements

ToolRequirements returns the union of every configured tool's Requirements across the base tool set and all declared modes. rig uses it to reject a workspace-requiring tool definition when no workspace placement is configured (the RequiresWorkspace binding could never be satisfied). It reads immutable design-time state, so it needs no runtime binding.

func (Definition) ValidateContextModel

func (d Definition) ValidateContextModel(model model.Model) error

ValidateContextModel checks structural validity and that model's transport identity (Provider/APIFormat/BaseURL) is a member of this definition's declared ContextTransport set (see WithContextTransports).

type DefinitionError

type DefinitionError struct {
	Kind  DefinitionErrorKind
	Field string
	Value string
	Cause error
}

DefinitionError reports an invalid immutable definition.

func (*DefinitionError) Error

func (e *DefinitionError) Error() string

func (*DefinitionError) Unwrap

func (e *DefinitionError) Unwrap() error

type DefinitionErrorKind

type DefinitionErrorKind string

DefinitionErrorKind identifies a declarative loop-definition failure.

const (
	DefinitionMissingName                DefinitionErrorKind = "missing_name"
	DefinitionInvalidClient              DefinitionErrorKind = "invalid_client"
	DefinitionInvalidModel               DefinitionErrorKind = "invalid_model"
	DefinitionNilOption                  DefinitionErrorKind = "nil_option"
	DefinitionDuplicateOption            DefinitionErrorKind = "duplicate_option"
	DefinitionInvalidTool                DefinitionErrorKind = "invalid_tool"
	DefinitionInvalidToolLimits          DefinitionErrorKind = "invalid_tool_limits"
	DefinitionInvalidDrainTimeout        DefinitionErrorKind = "invalid_drain_timeout"
	DefinitionInvalidMiddleware          DefinitionErrorKind = "invalid_middleware"
	DefinitionInvalidAccessGate          DefinitionErrorKind = "invalid_access_gate"
	DefinitionInvalidEngine              DefinitionErrorKind = "invalid_engine"
	DefinitionInvalidRuntimeContext      DefinitionErrorKind = "invalid_runtime_context"
	DefinitionInvalidDelegate            DefinitionErrorKind = "invalid_delegate"
	DefinitionInvalidDelegation          DefinitionErrorKind = "invalid_delegation"
	DefinitionInvalidMode                DefinitionErrorKind = "invalid_mode"
	DefinitionDuplicateMode              DefinitionErrorKind = "duplicate_mode"
	DefinitionMissingInitialMode         DefinitionErrorKind = "missing_initial_mode"
	DefinitionInvalidInitialMode         DefinitionErrorKind = "invalid_initial_mode"
	DefinitionMissingPolicyRevision      DefinitionErrorKind = "missing_policy_revision"
	DefinitionInvalidPolicyRevision      DefinitionErrorKind = "invalid_policy_revision"
	DefinitionMissingContextCounter      DefinitionErrorKind = "missing_context_counter"
	DefinitionInvalidContextCounter      DefinitionErrorKind = "invalid_context_counter"
	DefinitionMissingInferenceCapability DefinitionErrorKind = "missing_inference_capability"
	DefinitionInvalidInferenceCapability DefinitionErrorKind = "invalid_inference_capability"
	DefinitionIncompatibleContextCounter DefinitionErrorKind = "incompatible_context_counter"
	DefinitionMissingContextPolicy       DefinitionErrorKind = "missing_context_policy"
	DefinitionConflictingContextPolicy   DefinitionErrorKind = "conflicting_context_policy"
	DefinitionInvalidContextObservation  DefinitionErrorKind = "invalid_context_observation"
	DefinitionInvalidCompaction          DefinitionErrorKind = "invalid_compaction"
	DefinitionInvalidModeBinding         DefinitionErrorKind = "invalid_mode_binding"
	DefinitionInvalidOutputSchema        DefinitionErrorKind = "invalid_output_schema"
	DefinitionReservedToolName           DefinitionErrorKind = "reserved_tool_name"
	DefinitionDuplicateContextTransport  DefinitionErrorKind = "duplicate_context_transport"
	DefinitionInvalidContextTransport    DefinitionErrorKind = "invalid_context_transport"
)

type Delegation

type Delegation struct{ Style DelegationStyle }

Delegation is the immutable delegation policy copied into a Definition.

type DelegationStyle

type DelegationStyle uint8

DelegationStyle selects the model-facing delegation action set.

const (
	DelegationSyncOnly DelegationStyle = iota
	DelegationManaged
)

type Engine

type Engine uint8

Engine selects the loop backend. The zero value is native.

const (
	EngineNative Engine = iota
	EngineForeignClaude
	EngineForeignCodex
	EngineAdapter
)

type ExternalToolInstaller

type ExternalToolInstaller interface {
	ReplaceExternalTools(context.Context, ExternalToolset) error
}

ExternalToolInstaller is the OPTIONAL trusted surface for replacing a loop's external toolset at a turn boundary. It is deliberately separate from Controller (like ModeCatalog) rather than folded into it: only a composition root wiring an external tool source needs it, and every existing Controller implementation must keep compiling. Callers type-assert for it and fail closed when it is absent.

The replacement is atomic and applies at the loop's NEXT turn boundary — a turn in flight keeps the toolset it started under. Every refusal (an unbuildable definition, a name colliding with a declared tool, a shutting-down loop) leaves the prior generation installed and changes nothing.

type ExternalToolset

type ExternalToolset struct {
	Source      string
	Generation  string
	Definitions []tool.Definition
}

ExternalToolset is one atomic replacement of a loop's external tool slot. Source namespaces the slot (e.g. "mcp"): a replacement REPLACES that source's whole generation and never touches another source's, nor the loop definition's immutable declared tools. Generation is an opaque caller-computed identity digest recorded durably so an operator can tell which catalog a turn ran under; it is never interpreted by the runtime. Definitions are live factories built with the loop's own bindings — they are never serialized.

An empty Definitions is legal and meaningful: it clears the source's slot.

type Handle

type Handle interface {
	ID() uuid.UUID
	Mode() ModeName
	Model() model.Model
}

Handle is the read-only identity and inference view of a live loop.

type IDGenerationError

type IDGenerationError struct{ Cause error }

IDGenerationError is the typed cause logged when the actor cannot mint a TurnID from crypto/rand while starting a turn from an accepted submit. The turn is not started; the submit's outcome is a published event.TurnRejected{RejectInternal} (or, for a never-rejected agent hand-back, event.InputCancelled{CancelTurnFailed}). It remains a distinct typed error so the failure is greppable/testable, even though it is only logged — no event carries the error cause itself.

func (*IDGenerationError) Error

func (e *IDGenerationError) Error() string

func (*IDGenerationError) Unwrap

func (e *IDGenerationError) Unwrap() error

type InitialFingerprint

type InitialFingerprint struct {
	Model           model.Model
	EffectiveSystem string
	ToolNames       []string
}

InitialFingerprint is the immutable, bind-free view needed by a rig to stamp and compare compatibility before any runtime factories execute.

type InputRejectedError

type InputRejectedError struct {
	Reason event.RejectReason
	Cause  error
}

InputRejectedError is the actor's point-to-point admission refusal for a managed delegate input. The matching TurnRejected remains the durable/event-stream result; this typed error prevents the synchronous acceptance waiter from returning a handle.

func (*InputRejectedError) Error

func (e *InputRejectedError) Error() string

func (*InputRejectedError) Unwrap

func (e *InputRejectedError) Unwrap() error

type InvalidSummaryError

type InvalidSummaryError struct {
	Reason InvalidSummaryReason
	Cause  error
}

InvalidSummaryError reports a bounded failure without rendering untrusted transcript or model output bytes.

func (*InvalidSummaryError) Error

func (e *InvalidSummaryError) Error() string

func (*InvalidSummaryError) Unwrap

func (e *InvalidSummaryError) Unwrap() error

type InvalidSummaryReason

type InvalidSummaryReason string

InvalidSummaryReason is the closed, security-safe summary rejection category.

const (
	InvalidSummaryWire         InvalidSummaryReason = "wire"
	InvalidSummaryIdentity     InvalidSummaryReason = "identity"
	InvalidSummaryOutputShape  InvalidSummaryReason = "output_shape"
	InvalidSummaryByteLimit    InvalidSummaryReason = "byte_limit"
	InvalidSummaryTokenUsage   InvalidSummaryReason = "token_usage"
	InvalidSummaryTokenLimit   InvalidSummaryReason = "token_limit"
	InvalidSummaryXMLSyntax    InvalidSummaryReason = "xml_syntax"
	InvalidSummaryXMLRoot      InvalidSummaryReason = "xml_root"
	InvalidSummaryXMLStructure InvalidSummaryReason = "xml_structure"
	InvalidSummaryXMLContent   InvalidSummaryReason = "xml_content"
)

func (InvalidSummaryReason) Valid

func (r InvalidSummaryReason) Valid() bool

Valid reports whether the reason is recognized.

type Mode

type Mode struct {
	Name         ModeName
	Model        model.Model
	Effort       model.Effort
	Tools        []tool.Definition
	ToolLimits   ToolLimits
	Instructions string
}

Mode declares a validated alternative to a definition's base inference settings. Define defensively copies Tools and the model's sampling values.

type ModeCatalog

type ModeCatalog interface {
	Modes() []ModeName
}

ModeCatalog is the optional read-only selectable-mode view of a live loop. The empty ModeName identifies the base mode. Implementations return a defensive copy so callers cannot mutate the bound definition.

type ModeName

type ModeName string

ModeName identifies a predeclared loop mode. The empty name identifies the base mode.

type ModelAlias

type ModelAlias string

ModelAlias is the stable, harness-facing alias of a cataloged model target. It is not a provider model id and carries no credential or endpoint data.

type OccupancyError

type OccupancyError struct{ Limit content.TokenCount }

OccupancyError reports an invalid zero denominator.

func (*OccupancyError) Error

func (e *OccupancyError) Error() string

type Option

type Option func(*definitionOptions) error

Option contributes immutable loop-definition data.

func WithAccessGate

func WithAccessGate(access AccessGate) Option

WithAccessGate installs the combined prepared-access decision gate for every tool call this loop runs. Without one, every tool call fails closed: the runner denies unauthorized execution rather than running ungated. The gate is an opaque policy collaborator, so configuring it requires WithPolicyRevision.

func WithCompaction

func WithCompaction(policy CompactionPolicy) Option

WithCompaction installs explicit manual and optional automatic policy.

func WithContextCounter

func WithContextCounter(counter contextcount.ContextCounter) Option

WithContextCounter installs one fixed complete-request counter.

func WithContextObservation

func WithContextObservation(policy ContextObservationPolicy) Option

WithContextObservation installs explicit hard-admission policy without enabling conversation compaction.

func WithContextTransports

func WithContextTransports(transports ...ContextTransport) Option

WithContextTransports declares the complete set of (wire transport -> trust posture) pairs a live model switch or predeclared mode is allowed to move to. Omitting it synthesizes a one-element set from the base WithInference model and WithInferenceCapability value, byte-identical to today's single-transport behavior. Requires WithContextCounter; see validateContextDefinition for the full set of validations applied to a declared set.

func WithDelegates

func WithDelegates(names ...identity.AgentName) Option

func WithDelegation

func WithDelegation(policy Delegation) Option

func WithDescription

func WithDescription(desc string) Option

WithDescription sets the loop's user-facing description. It remains excluded from PolicyRevision because it is not execution policy, but topology compatibility fingerprints include it so injected agent guidance stays bound to the definition that produced it.

func WithDisplayName

func WithDisplayName(name string) Option

WithDisplayName sets the loop's user-facing presentation label. Purely presentational; empty means "no explicit label" and consumers fall back to the agent name. It is excluded from PolicyRevision so relabeling never breaks restore config-drift detection.

func WithDrainTimeout

func WithDrainTimeout(timeout time.Duration) Option

func WithEngine

func WithEngine(engine Engine) Option

func WithInference

func WithInference(client inference.Client, model model.Model) Option

func WithInferenceCapability

func WithInferenceCapability(capability contextcount.InferenceCapability) Option

WithInferenceCapability declares the fixed inference transport posture.

func WithInitialMode

func WithInitialMode(name ModeName) Option

func WithModes

func WithModes(modes ...Mode) Option

func WithName

func WithName(name identity.AgentName) Option

func WithOutputSchema

func WithOutputSchema(output inference.OutputSchema) Option

WithOutputSchema freezes one optional provider-neutral final-output policy. The option owns a clone immediately, and clones again whenever it is applied.

func WithPolicyRevision

func WithPolicyRevision(revision string) Option

WithPolicyRevision supplies stable loop-scoped identity for opaque policy collaborators.

func WithRuntimeContext

func WithRuntimeContext(provider RuntimeContextProvider) Option

func WithSystem

func WithSystem(system string) Option

func WithToolLimits

func WithToolLimits(limits ToolLimits) Option

func WithToolMiddlewares

func WithToolMiddlewares(middlewares ...tool.ToolMiddleware) Option

func WithTools

func WithTools(defs ...tool.Definition) Option

type PolicyRevisionMarshalError

type PolicyRevisionMarshalError struct{ Cause error }

PolicyRevisionMarshalError is the programmer-error panic value PolicyRevision raises if the fully-owned, total projection it builds cannot be JSON-marshaled. The projection is composed only of marshalable types, so a failure is a code defect (an unmarshalable field was introduced), never a runtime or input condition. PolicyRevision panics with it rather than returning a nil-collapsed digest, because a silent sha256(nil) would defeat the restore config-mismatch drift detection that consumes the digest.

func (*PolicyRevisionMarshalError) Error

func (*PolicyRevisionMarshalError) Unwrap

func (e *PolicyRevisionMarshalError) Unwrap() error

type Provenance

type Provenance struct {
	LoopID uuid.UUID
	TurnID uuid.UUID
	StepID uuid.UUID
}

Provenance identifies the parent turn/step that spawned a loop.

func ProvenanceFrom

func ProvenanceFrom(ctx context.Context) (Provenance, bool)

ProvenanceFrom returns the Provenance set by WithProvenance, and whether it was present. An absent key yields the zero Provenance and false — fail-safe: a tool run outside a turn (no provenance injected) treats it as root/unknown rather than panicking.

type ReadGuard

type ReadGuard interface {
	// DeniedRead reports whether reading absPath is denied by policy (e.g. the
	// §5.3 secret deny-reads such as "**/.env*", or a zerotrust restricted-read).
	//
	// CANONICAL-PATH CONTRACT (fail-secure): absPath MUST be an ABSOLUTE,
	// filepath.Clean'ed, SYMLINK-RESOLVED path. The guard is purely LEXICAL — it
	// matches the string it is handed and performs NO filesystem resolution of its
	// own. Resolving symlinks (and, on a case-insensitive volume such as default
	// macOS/APFS, canonicalising case) BEFORE the call is the CALLER's (the tool's)
	// responsibility: a guard fed a non-canonical path can be bypassed by a symlink
	// or a case variant that resolves to the denied file. The native read tools
	// honour this — ReadFile passes the containedPath-resolved abs, Grep/Glob pass
	// the EvalSymlinks'd path via denyFilteredRel. This mirrors the sandbox Resolve
	// contract, so the confinement adapter and the native tools must both feed canonical
	// paths or a deny is trivially evaded.
	DeniedRead(absPath string) bool
	// MaxReadBytes is the per-file read cap (bytes) ReadFile/Grep apply via
	// io.LimitReader.
	MaxReadBytes() int64
}

ReadGuard is the narrow read-side policy the read tools enforce themselves (Interface Segregation: read tools depend only on this, not the full gate). DeniedRead filters denied paths during Glob/Grep traversal and results; MaxReadBytes is the per-file cap ReadFile/Grep apply via io.LimitReader.

This is the read-adaptation SEAM: it is deliberately stdlib-typed (no import of any sandbox package) so a consumer can build one ReadGuard from its sandbox profile's read rules and bind the native ReadFile/Grep/Glob tools IDENTICALLY to a sandboxed `sh -c cat` — a single source of truth, with no drift between the in-process guards and OS enforcement.

type RequestFingerprintError

type RequestFingerprintError struct {
	Field string
	Cause error
}

RequestFingerprintError reports an invalid projection or marshal defect.

func (*RequestFingerprintError) Error

func (e *RequestFingerprintError) Error() string

func (*RequestFingerprintError) Unwrap

func (e *RequestFingerprintError) Unwrap() error

type RequestFingerprintInput

type RequestFingerprintInput struct {
	SystemRevision         string
	ToolPolicyRevision     string
	Model                  model.Model
	Basis                  event.ContextBasis
	RuntimeContextRevision string
	CounterCapability      contextcount.CounterCapability
	InferenceCapability    contextcount.InferenceCapability
}

RequestFingerprintInput is the complete secret-free request-shape projection used to identify one measurement. Revisions identify opaque prompt/tool/runtime context producers; model and capabilities are included in full.

type RequestUserInputFunc

type RequestUserInputFunc func(context.Context, string, []string) (string, error)

RequestUserInputFunc is the actor-supplied implementation behind RequestUserInput.

type Resolved

type Resolved struct {
	AgentType     identity.AgentName
	AgentHarness  AgentHarnessName
	Profile       RuntimeProfileName
	Source        RuntimeSourceName
	Credential    CredentialMode
	SelectionKind RuntimeSelectionKind
	// ModelAlias is the stable model-facing selector accepted by StartAgent.
	ModelAlias ModelAlias
	// TargetAlias is the concrete alias sent to a gateway or ACP launcher. It
	// is derived from the selected effort for gateway-backed runtimes and is
	// always bare for native-auth runtimes.
	TargetAlias      ModelAlias
	NativeSmallModel string
	SmallModel       ModelAlias
	Target           model.Model
	Effort           model.Effort
}

Resolved is the immutable runtime tuple selected from a RuntimeCatalog. Target is a defensive copy of the cataloged model descriptor.

type ResolvedContextLimits

type ResolvedContextLimits struct {
	ReservedOutput content.TokenCount
	RawInputLimit  content.TokenCount
	InputLimit     content.TokenCount
}

ResolvedContextLimits is the checked result of applying one loop policy to model metadata. SafetyMargin is reflected only in InputLimit and is always subtracted after the raw minimum is selected.

func ResolveContextLimits

func ResolveContextLimits(model model.ModelKey, limits model.ContextLimits, reservedOutput, safetyMargin content.TokenCount) (ResolvedContextLimits, error)

ResolveContextLimits applies explicit output reservation and safety margin to known model limits without inventing values for unknown fields.

type RuntimeCatalog

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

RuntimeCatalog is an immutable, parent-scoped set of permitted child runtimes. Its fields are intentionally private; all returned nested values are defensive copies.

func NewRuntimeCatalog

func NewRuntimeCatalog(entries []RuntimeCatalogEntry) (RuntimeCatalog, error)

NewRuntimeCatalog validates, sorts, and defensively copies entries into an immutable catalog. An empty catalog is valid and represents a parent with no optional runtime entries.

func (RuntimeCatalog) Digest

func (c RuntimeCatalog) Digest() string

Digest returns the deterministic SHA-256 identity of the catalog. The canonical projection includes only stable catalog and model identity data; raw endpoints and all credential-bearing material are deliberately omitted.

func (RuntimeCatalog) EntriesFor

func (c RuntimeCatalog) EntriesFor(agent identity.AgentName) []RuntimeCatalogEntry

EntriesFor returns the sorted entries admitted for agent. The returned entries and all nested values are independent copies. Unknown agents return nil rather than a partial or global catalog.

func (RuntimeCatalog) HasEntries

func (c RuntimeCatalog) HasEntries() bool

HasEntries reports whether this parent has any optional runtime choices. It is intentionally a narrow query so native/no-choice parents can preserve the absence of an adapter runtime without exposing the catalog backing slice.

func (RuntimeCatalog) Resolve

func (c RuntimeCatalog) Resolve(agent identity.AgentName, harness AgentHarnessName, alias ModelAlias, effort model.Effort) (Resolved, error)

Resolve selects a runtime tuple for agent. Empty harness, alias, and effort selectors use the deterministic default at that level. Explicit selectors are checked only within the already-selected parent-scoped entry; no global or format fallback is ever attempted. Its zero effort retains the legacy meaning of an omitted effort selector.

func (RuntimeCatalog) ResolveTargetAlias

func (c RuntimeCatalog) ResolveTargetAlias(agent identity.AgentName, harness AgentHarnessName, targetAlias ModelAlias, effort model.Effort) (Resolved, error)

ResolveTargetAlias resolves a durable or trusted runtime target alias back to its model-facing catalog selector. New gateway-backed records use the concrete per-effort alias; the bare model alias is also accepted so legacy records remain restorable. This method is intentionally not used by model-facing agent preparation or controller validation.

func (RuntimeCatalog) ResolveTargetAliasWithSource

func (c RuntimeCatalog) ResolveTargetAliasWithSource(agent identity.AgentName, harness AgentHarnessName, source RuntimeSourceName, targetAlias ModelAlias, effort model.Effort) (Resolved, error)

ResolveTargetAliasWithSource is the source-aware restore counterpart to ResolveTargetAlias. Empty source retains legacy target-alias behavior.

func (RuntimeCatalog) ResolveWithExplicitEffort

func (c RuntimeCatalog) ResolveWithExplicitEffort(agent identity.AgentName, harness AgentHarnessName, alias ModelAlias, effort model.Effort, explicitEffort bool) (Resolved, error)

ResolveWithExplicitEffort selects a runtime tuple while preserving whether the caller supplied the effort selector. This distinction matters because EffortNone is model.Effort's zero value: omitted effort uses DefaultEffort, while explicit none is valid only when the model advertises none.

func (RuntimeCatalog) ResolveWithExplicitSource

func (c RuntimeCatalog) ResolveWithExplicitSource(agent identity.AgentName, harness AgentHarnessName, source RuntimeSourceName, alias ModelAlias, effort model.Effort, explicitEffort bool) (Resolved, error)

ResolveWithExplicitSource selects a runtime tuple while optionally pinning the stable source identity. An omitted source preserves legacy default behavior; a supplied source disambiguates choices that share one harness.

type RuntimeCatalogEntry

type RuntimeCatalogEntry struct {
	AgentType    identity.AgentName
	AgentHarness AgentHarnessName
	Profile      RuntimeProfileName
	// Description is bounded, secret-free presentation guidance for this
	// harness. Empty means no guidance is available.
	Description string
	// Source is the stable catalogue source identity. Empty preserves the
	// legacy shape and is derived from Credential during normalization.
	Source     RuntimeSourceName
	Credential CredentialMode
	// SelectionKind is explicit by default. Harness-managed entries must be
	// native-auth and contain no model rows or model aliases.
	SelectionKind   RuntimeSelectionKind
	Default         bool
	DefaultModel    ModelAlias
	SmallModel      ModelAlias
	NeedsSmallModel bool
	Models          []RuntimeModelOption
}

RuntimeCatalogEntry describes one role/harness runtime combination.

type RuntimeCatalogError

type RuntimeCatalogError struct {
	Kind  RuntimeCatalogErrorKind
	Field string
}

RuntimeCatalogError reports a closed, deterministic catalog failure.

func (*RuntimeCatalogError) Error

func (e *RuntimeCatalogError) Error() string

type RuntimeCatalogErrorKind

type RuntimeCatalogErrorKind string

RuntimeCatalogErrorKind identifies a catalog construction or resolution failure. Error messages contain categories and fields, not untrusted values.

const (
	RuntimeCatalogInvalidCredential    RuntimeCatalogErrorKind = "invalid_credential" // #nosec G101 -- bounded error category, not a credential
	RuntimeCatalogInvalidSource        RuntimeCatalogErrorKind = "invalid_source"
	RuntimeCatalogInvalidSelectionKind RuntimeCatalogErrorKind = "invalid_selection_kind"
	RuntimeCatalogInvalidIdentifier    RuntimeCatalogErrorKind = "invalid_identifier"
	RuntimeCatalogInvalidDescription   RuntimeCatalogErrorKind = "invalid_description"
	RuntimeCatalogInvalidModel         RuntimeCatalogErrorKind = "invalid_model"
	RuntimeCatalogMissingDefaultModel  RuntimeCatalogErrorKind = "missing_default_model"
	RuntimeCatalogInvalidDefaultModel  RuntimeCatalogErrorKind = "invalid_default_model"
	RuntimeCatalogDuplicateAlias       RuntimeCatalogErrorKind = "duplicate_alias"
	RuntimeCatalogDuplicateHarness     RuntimeCatalogErrorKind = "duplicate_harness"
	RuntimeCatalogDefaultHarnessCount  RuntimeCatalogErrorKind = "default_harness_count"
	RuntimeCatalogInvalidEffort        RuntimeCatalogErrorKind = "invalid_effort"
	RuntimeCatalogDuplicateEffort      RuntimeCatalogErrorKind = "duplicate_effort"
	RuntimeCatalogInvalidDefaultEffort RuntimeCatalogErrorKind = "invalid_default_effort"
	RuntimeCatalogInvalidSmallModel    RuntimeCatalogErrorKind = "invalid_small_model"
	RuntimeCatalogNativeAliasConflict  RuntimeCatalogErrorKind = "native_alias_conflict"
	RuntimeCatalogDerivedAliasConflict RuntimeCatalogErrorKind = "derived_alias_conflict"
	RuntimeCatalogUnknownAgent         RuntimeCatalogErrorKind = "unknown_agent"
	RuntimeCatalogUnknownHarness       RuntimeCatalogErrorKind = "unknown_harness"
	RuntimeCatalogUnknownSource        RuntimeCatalogErrorKind = "unknown_source"
	RuntimeCatalogUnknownModel         RuntimeCatalogErrorKind = "unknown_model"
	RuntimeCatalogIncompatibleEffort   RuntimeCatalogErrorKind = "incompatible_effort"
)

type RuntimeContextProvider

type RuntimeContextProvider interface {
	Blocks(ctx context.Context) []content.Block
}

RuntimeContextProvider yields the volatile per-turn context blocks (date/cwd/git) the loop appends at the turn tail. Implementations must be cheap and non-fatal: a failure degrades (fewer or no blocks), never errors the turn. The returned slice may be empty (or nil) — the loop appends nothing in that case.

The interface lives in the engine-generic loop package so the loop can depend on it without importing any concrete provider; a default implementation is wired at the product composition root, keeping this package free of os/exec.

type RuntimeIdentity

type RuntimeIdentity struct {
	Profile        RuntimeProfileName
	CatalogDigest  string
	Source         RuntimeSourceName
	SelectionKind  RuntimeSelectionKind
	ModelAlias     ModelAlias
	TargetProvider model.ProviderName
	TargetModel    string
	Effort         model.Effort
}

RuntimeIdentity is the secret-free runtime portion of a bound loop's configuration identity. The catalog digest is supplied by the composition root from its immutable RuntimeCatalog snapshot; raw endpoints, credentials, and non-identity model behavior are intentionally absent.

func (RuntimeIdentity) Digest

func (i RuntimeIdentity) Digest() string

Digest returns a stable SHA-256 identity for the runtime selection. The zero identity returns empty so native callers retain the additive legacy shape. The composition root's session fingerprint builder is the integration point: it should carry this opaque digest as its runtime-identity revision rather than hashing a model descriptor, endpoint, or credential.

type RuntimeModelOption

type RuntimeModelOption struct {
	Alias ModelAlias
	// Description is bounded, secret-free presentation guidance for selecting
	// this model. Empty means no guidance is available.
	Description string
	// Source optionally overrides the entry source for this model. It is the
	// stable catalogue discriminator when gateway and native choices share one
	// harness.
	Source RuntimeSourceName
	// Credential optionally overrides the entry credential for this model.
	// It lets one harness expose its own native-auth catalogue alongside
	// product-owned gateway targets while each resolved child still has one
	// immutable credential mode. Empty inherits RuntimeCatalogEntry.Credential.
	Credential CredentialMode
	// NativeSmallModel is the connector-native small-model identifier used by
	// native-auth runtimes. It is intentionally bounded and secret-free.
	NativeSmallModel string
	Target           model.Model
	DefaultEffort    model.Effort
	Efforts          []model.Effort
}

RuntimeModelOption describes one model alias admitted by one catalog entry.

type RuntimeProfileName

type RuntimeProfileName string

RuntimeProfileName is the stable, secret-free key used by a backend builder to select the concrete runtime implementation.

type RuntimeSelectionKind

type RuntimeSelectionKind string

RuntimeSelectionKind identifies whether the product composition root selected or delegated model selection to the child harness.

const (
	RuntimeSelectionExplicit       RuntimeSelectionKind = "explicit"
	RuntimeSelectionHarnessManaged RuntimeSelectionKind = "harness-managed"
)

type RuntimeSourceName

type RuntimeSourceName string

RuntimeSourceName identifies the owner of a child runtime selection. It is deliberately narrower than CredentialMode: source is a stable catalogue identity, while credential mode describes the authentication contract used to launch the child.

const (
	RuntimeSourceGateway RuntimeSourceName = "gateway"
	RuntimeSourceNative  RuntimeSourceName = "native"
)

type SummaryTooLargeError

type SummaryTooLargeError struct {
	Measurement event.ContextMeasurement
}

SummaryTooLargeError is reserved for the Task 26 complete-request count after replacement. Isolated hustle output budgeting uses InvalidSummaryTokenLimit.

func (*SummaryTooLargeError) Error

func (*SummaryTooLargeError) Error() string

type ToolLimits

type ToolLimits struct {
	Iterations  int
	Calls       int
	Parallel    int
	ResultBytes int
}

ToolLimits bounds tool activity during one turn.

type UserInputContextError

type UserInputContextError struct{}

UserInputContextError reports that a tool requested user input outside a live loop call.

func (*UserInputContextError) Error

func (*UserInputContextError) Error() string

Jump to

Keyboard shortcuts

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