loopruntime

package
v0.31.0 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 38 Imported by: 0

Documentation

Overview

Package loopruntime implements the private loop actor, turn and step machinery used by the rig-owned session runtime. Public consumers use package loop contracts instead.

Index

Constants

View Source
const (
	ConfigMissingClient    = loop.ConfigMissingClient
	ConfigInvalidModel     = loop.ConfigInvalidModel
	ConfigMissingPublisher = loop.ConfigMissingPublisher
	CommitTurnCancelled    = loop.CommitTurnCancelled
	DelegationManaged      = loop.DelegationManaged
)
View Source
const InterruptedResponseNotice = "[interrupted: this reply was stopped before it completed; the content above is what had arrived]"

InterruptedResponseNotice is TruncatedResponseNotice's sibling for the OTHER way a reply is cut short: someone stopped it on purpose. A cancelled turn is not a failed turn, and telling a reader — a human scrolling back, or the model on the next request — that "the stream failed" would be a false report of a fault. It matters most to the model: "you were interrupted" is a resumable state it can act on, while "the connection broke" invites it to apologize for an outage that never happened.

It deliberately does not name WHO stopped the reply. From inside the turn a user's Ctrl-C, a parent agent's StopAgent, and a graceful shutdown are the same cancelled context; a notice that guessed would be wrong a third of the time. What is always true is that the reply was stopped and the text above is what had arrived by then — and unlike a stream failure, that text is exactly what the user saw, with nothing else in flight.

View Source
const TruncatedResponseNotice = "[truncated: the stream failed before this reply completed; the content above may be incomplete]"

TruncatedResponseNotice is the marker text block appended, as the LAST block, to an assistant message stored after a stream failed part-way through the model's reply. It is the transcript's own record that the turn was cut off: a reader who only ever sees committed history — a human scrolling back, an export, the model on the next request — must not mistake a partial reply for a complete one.

It is deliberately in-band rather than a side-channel flag. The alternatives all lose the signal at some boundary: a field on content.AIMessage would have to be carried by every codec, and an event-only marker is invisible to anything that reads the stored message graph (which is exactly what the next request is built from). A block travels with the content it qualifies. The wording claims only what the runtime can actually prove. A stream can fail after every content chunk arrived (a malformed terminal-metadata frame does exactly that), so the notice says the reply MAY be incomplete rather than asserting it was cut mid-word. What is always true is that the stream failed and the turn did not finish.

Variables

View Source
var (
	WithProvenance          = loop.WithProvenance
	ProvenanceFrom          = loop.ProvenanceFrom
	WithToolUseID           = loop.WithToolUseID
	ToolUseIDFrom           = loop.ToolUseIDFrom
	WithPreparedCall        = loop.WithPreparedCall
	PreparedCallFromContext = loop.PreparedCallFromContext
	WithUserInputRequester  = loop.WithUserInputRequester
	WithApprovalRequester   = loop.WithApprovalRequester
)

Functions

func EmitFromContext

func EmitFromContext(ctx context.Context) (func(event.Event), bool)

EmitFromContext returns the per-turn event-emit func the runner injected, and false when none is present (the tool is being run outside a turn). Event-emitting tools call this; it is the only sanctioned way for a tool in tools/ to emit an event without depending on the loop internals.

func ParseCompactionSummaryXML

func ParseCompactionSummaryXML(raw []byte) (*content.UserMessage, error)

ParseCompactionSummaryXML validates and wraps the exact replacement grammar. It preserves the original escaped XML bytes in the returned user text block.

func RequestUserInput

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

RequestUserInput is the loop-provided helper AskUser calls to open a user-input gate. It encapsulates all the gate plumbing so a tool never touches gateReg directly:

  1. Read emit, ToolExecutionID, gateReg from ctx — any missing → *GateContextError (fail-secure; calling this outside a turn is a bug).
  2. Register a gateUserInput gate synchronously and ctx-aware: send the registration, then wait for the ack (install-before-emit). Both selects escape on ctx.Done so a cancelled turn / departed actor never wedges.
  3. Emit UserInputRequested AFTER the ack — the gate is installed, so the matching ProvideUserInput cannot be dropped on a race.
  4. Block on the dedicated reply channel (buffered(1), runner is sole reader) or ctx.Done. ToolExecutionID is re-validated on receipt as cheap defence.

Returns the raw answer; AskUser validates it against its choices.

func RunBatch

func RunBatch(
	ctx context.Context,
	calls []content.ToolUseBlock,
	ts ToolSet,
	runtime BatchRuntime,
) []result

RunBatch executes a batch of tool calls. It mints a ToolExecutionID per call via runtime.IDGen (fail-secure: a call whose ToolExecutionID cannot be minted is NOT executed and NO gate is opened for it), resolves tools + permissions sequentially (so a session grant on call N is visible to call N+1's Check), emits ALL ToolCallStarted before executing any call, runs the executable calls (serial batch drained first, then bounded-parallel with same-WriteTarget serialization), and emits one ToolCallCompleted per call. The returned []result is in call order, each result owning its slot by index.

RunBatch SERIALIZES its own event emission: it wraps emit in an internal mutex (safeEmit) and uses that for every Started and every (possibly concurrent) Completed. The caller's emit therefore need NOT be concurrent-safe.

Types

type AccessGate

type AccessGate = loop.AccessGate

type Backend

type Backend = loop.Backend

type BatchRuntime

type BatchRuntime struct {
	GateRegistrations chan<- gateRegistration
	IDGen             func() (uuid.UUID, error)
	Emit              func(event.Event)
	EmitContext       eventEmitter
	Hooks             *hook.Runner
	Coordinates       identity.Coordinates
	AgentName         identity.AgentName
	Cause             identity.Cause
	// contains filtered or unexported fields
}

BatchRuntime carries the runtime-owned services and attribution shared by one batch. EmitContext is preferred when the caller can preserve operation context; Emit remains the context-free compatibility seam used by focused tests. Zero values are safe: UUID generation defaults to uuid.New, event emission is discarded, and a nil hook runner is a no-op.

type CommitCancelReason

type CommitCancelReason = loop.CommitCancelReason

type CommitError

type CommitError = loop.CommitError

type CompactionCoordinationError

type CompactionCoordinationError struct {
	Kind  CompactionCoordinationErrorKind
	Cause error
}

CompactionCoordinationError is the typed internal hand-off used by later controller/finalizer tasks. Infrastructure failures are never converted into a false CompactionRejected or CompactWaiterRejected event.

func (*CompactionCoordinationError) Error

func (*CompactionCoordinationError) Unwrap

func (e *CompactionCoordinationError) Unwrap() error

type CompactionCoordinationErrorKind

type CompactionCoordinationErrorKind string

CompactionCoordinationErrorKind identifies infrastructure failures that prevent the control actor from creating or durably resolving a compaction obligation.

const (
	CompactionCoordinationAttemptID CompactionCoordinationErrorKind = "attempt_id"
	CompactionCoordinationOutcome   CompactionCoordinationErrorKind = "outcome"
	CompactionCoordinationBasis     CompactionCoordinationErrorKind = "basis"
)

type CompactionFinalizationError

type CompactionFinalizationError struct {
	Kind      CompactionFinalizationErrorKind
	AttemptID event.CompactAttemptID
	CommandID uuid.UUID
	Cause     error
}

CompactionFinalizationError preserves the failing transition and its cause so the session can fail closed on journal infrastructure errors.

func (*CompactionFinalizationError) Error

func (*CompactionFinalizationError) Unwrap

func (e *CompactionFinalizationError) Unwrap() error

type CompactionFinalizationErrorKind

type CompactionFinalizationErrorKind string

CompactionFinalizationErrorKind identifies the actor-owned transition that could not be completed. Journal failures remain infrastructure failures and are never rewritten as a false durable rejection.

const (
	CompactionFinalizationProposal       CompactionFinalizationErrorKind = "proposal"
	CompactionFinalizationTerminalMint   CompactionFinalizationErrorKind = "terminal_mint"
	CompactionFinalizationTerminalClone  CompactionFinalizationErrorKind = "terminal_clone"
	CompactionFinalizationTerminalAppend CompactionFinalizationErrorKind = "terminal_append"
	CompactionFinalizationWaiterMint     CompactionFinalizationErrorKind = "waiter_mint"
	CompactionFinalizationWaiterAppend   CompactionFinalizationErrorKind = "waiter_append"
)

type CompactionOutcome

type CompactionOutcome struct {
	Value *loop.CompactionOutput
	Err   error
}

CompactionOutcome carries exactly one typed adapter result or failure while the hustle controller still owns finalization.

func (CompactionOutcome) Validate

func (o CompactionOutcome) Validate() error

Validate enforces the exactly-one result contract.

type CompactionOutcomeError

type CompactionOutcomeError struct{}

CompactionOutcomeError reports a malformed adapter/finalizer handoff.

func (*CompactionOutcomeError) Error

func (*CompactionOutcomeError) Error() string

type Compactor

type Compactor interface {
	CompactAndFinalize(context.Context, loop.CompactionInput, func(context.Context, CompactionOutcome) error) error
}

Compactor is the only hustle capability visible to the loop actor. It cannot select arbitrary definitions or run a generic hustle.

type ConfigError

type ConfigError = loop.ConfigError

type ConfigErrorKind

type ConfigErrorKind = loop.ConfigErrorKind

type Delegation

type Delegation = loop.Delegation

type GateContextError

type GateContextError struct{ Missing GateContextMissing }

GateContextError is returned by RequestUserInput when the ctx is missing one of the runner-injected values. Callers can errors.As to inspect which value.

func (*GateContextError) Error

func (e *GateContextError) Error() string

type GateContextMissing

type GateContextMissing string

GateContextMissing identifies which injected ctx value RequestUserInput could not find. It is a fail-secure signal: a tool that calls RequestUserInput outside a turn (no emit / ToolExecutionID / gateReg in ctx) is a bug, so it errors rather than silently proceeding.

const (
	GateContextEmit    GateContextMissing = "emit"
	GateContextCallID  GateContextMissing = "callID"
	GateContextGateReg GateContextMissing = "gateReg"
)

type GateReplyMismatchError

type GateReplyMismatchError struct{ ToolExecutionID uuid.UUID }

GateReplyMismatchError is returned if the command delivered on a gateUserInput reply channel is not a ProvideUserInput for the expected ToolExecutionID. runLoop routes by ToolExecutionID + kind, so this is a defence-in-depth guard that should never fire in normal operation.

func (*GateReplyMismatchError) Error

func (e *GateReplyMismatchError) Error() string

type IDGenerationError

type IDGenerationError = loop.IDGenerationError

type Loop

type Loop struct {
	Commands chan<- command.Command
	Done     <-chan struct{}
	// contains filtered or unexported fields
}

Loop is the handle to a running agent loop for internal packages. Commands is unbuffered — sends block until the actor is ready. Callers must never close Commands; stop the actor with Shutdown. (Closing it would exit the actor through the `!ok` path, skipping terminal delivery and shutdown acks.) Done is closed when the actor has fully exited. Direct callers must honor the command contracts. The submit commands (UserInput/SubagentResult) and CancelQueuedInput are fire-and-forget: their outcomes are PUBLISHED as typed events onto the session fan-in, not replied on a per-command channel. Only the control commands carry a reply channel — Interrupt (Ack chan bool) and Shutdown (Ack chan error) — and each must be non-nil and buffered(1) so the actor's direct send never stalls.

func New

func New(loopCtx context.Context, sessionID, loopID uuid.UUID, parent loop.Provenance, events eventPublisher, bound loop.BoundDefinition) (*Loop, error)

New constructs a loop and starts its actor goroutine. loopCtx is the loop's lifetime (derived by the session from its sessionCtx); it is NOT a turn lifetime. sessionID is shared by every loop in the session; loopID is unique to this loop. parent is the provenance of the turn/step that spawned this loop (zero for the primary loop). events is the session-level event publisher the loop depends on (Dependency Inversion); it must be non-nil.

New spawns an EMPTY loop (no committed history, turnIndex 0). The restore path (NewRestored) seeds pre-built committed state instead; both funnel through newLoopWithSeed, which is identical save for that seed.

func NewInMode

func NewInMode(loopCtx context.Context, sessionID, loopID uuid.UUID, parent loop.Provenance, events eventPublisher, bound loop.BoundDefinition, initialMode loop.ModeName) (*Loop, error)

NewInMode is New with an explicit starting mode: an EMPTY initialMode uses the definition's initial mode (identical to New), while a non-empty name starts the loop directly in that predeclared mode's effective config — the delegation path's mode-selective spawn, so a child begins in the requested mode without a synthetic LoopModeChanged. An unknown mode name fails with the same typed BindError Bind uses.

func NewInModeWithCompactor

func NewInModeWithCompactor(
	loopCtx context.Context,
	sessionID, loopID uuid.UUID,
	parent loop.Provenance,
	events eventPublisher,
	bound loop.BoundDefinition,
	initialMode loop.ModeName,
	compactor Compactor,
	reviewContext *ReviewContext,
) (*Loop, error)

NewInModeWithCompactor is the focused native composition seam for a loop whose definition installs compaction. The caller supplies only the summary capability; loopruntime derives the executor from the bound loop's own counter, capabilities, and policy. Generic hustle selection and coordination remain private.

reviewContext mirrors compactor's shape: internal/sessionruntime is the only caller that ever supplies a non-nil value (whenever the session has permission classifiers registered — see Session.loopReviewContext), so it travels as a parameter here rather than through the bound loop.Definition, exactly like compactor does. nil leaves the resulting loop's turns with reviewContext == nil, identical to every caller before this addendum.

func NewInModeWithRuntime

func NewInModeWithRuntime(
	loopCtx context.Context,
	sessionID, loopID uuid.UUID,
	parent loop.Provenance,
	events eventPublisher,
	bound loop.BoundDefinition,
	initialMode loop.ModeName,
	deps RuntimeDependencies,
) (*Loop, error)

NewInModeWithRuntime constructs a native loop with its runtime-only dependencies while keeping the declarative definition immutable.

func NewRestored

func NewRestored(loopCtx context.Context, sessionID, loopID uuid.UUID, parent loop.Provenance, events eventPublisher, bound loop.BoundDefinition, seed RestoredState) (*Loop, error)

NewRestored constructs a loop SEEDED with pre-built committed state and starts its actor goroutine IDLE — the restore counterpart to New. New spawns an empty loop that commits its first message at the first submit; NewRestored seeds loopState.msgs + turnIndex from the journal fold so the resumed loop already holds its prior history and numbers its next turn correctly. Everything else is identical to New: the same config validation/defaulting, the same actor goroutine, the same idle status — the ONLY difference is the seeded initial state.

loopCtx, sessionID, loopID, events, and cfg mean exactly what they do in New. loopID MUST be the loop's ORIGINAL id (the session passes the root loop's recovered id) so identity is stable across restore. seed is the folded committed state; a zero RestoredState (empty Msgs, zero TurnIndex) yields a loop indistinguishable from a freshly New'd one.

func NewRestoredWithCompactor

func NewRestoredWithCompactor(
	loopCtx context.Context,
	sessionID, loopID uuid.UUID,
	parent loop.Provenance,
	events eventPublisher,
	bound loop.BoundDefinition,
	seed RestoredState,
	compactor Compactor,
	reviewContext *ReviewContext,
) (*Loop, error)

NewRestoredWithCompactor is the restored counterpart to NewInModeWithCompactor. It installs the focused executor while preserving the restore-folded mode and inference runtime. reviewContext mirrors NewInModeWithCompactor's parameter of the same name — see its doc comment.

func NewRestoredWithRuntime

func NewRestoredWithRuntime(
	loopCtx context.Context,
	sessionID, loopID uuid.UUID,
	parent loop.Provenance,
	events eventPublisher,
	bound loop.BoundDefinition,
	seed RestoredState,
	deps RuntimeDependencies,
) (*Loop, error)

NewRestoredWithRuntime is the restored counterpart to NewInModeWithRuntime.

func (*Loop) CommandSink

func (l *Loop) CommandSink() chan<- command.Command

CommandSink returns the actor's command input.

func (*Loop) DoneChan

func (l *Loop) DoneChan() <-chan struct{}

DoneChan closes when the actor exits.

func (*Loop) PriorityCommandSink

func (l *Loop) PriorityCommandSink() chan<- command.Command

PriorityCommandSink returns the bounded native Interrupt/Shutdown lane.

func (*Loop) Snapshot

Snapshot returns a consistent view of the loop's committed conversation and turn count by querying the actor (the sole owner of loopState), so the read never races a concurrent commit. It is the restore-verification primitive (the session proves a restored loop's history matches the original) and the hook a future dormant-snapshot writer reads from. It returns a typed *SnapshotError if the loop has exited (its actor is gone) or ctx is done before the actor replies — never a partial or zero view.

type PermissionReviewRequest

type PermissionReviewRequest struct {
	GateID          gatedomain.ID
	ToolExecutionID uuid.UUID
	Request         tool.Request
	ReviewContext   gatedomain.ReviewContext
}

PermissionReviewRequest is the live-only handoff the actor gives a session's review starter once a permission gate's GateOpened has committed and the runner has been acked (design §14.3). It carries exactly what a classifier needs to build a PermissionReviewSubject and nothing a durable record or event may not: no raw tool arguments beyond what the human-facing gate already displays, and no token or grant material. ReviewContext already carries the loop/session/turn/step coordinates plus the gate policy revision and security ceiling in effect when the batch was captured (internal/loopruntime/review_context.go); a zero ReviewContext (ContextRevision == "") means no live review was configured for this turn, and a review starter must treat that as "nothing to review" rather than guessing at defaults.

type Provenance

type Provenance = loop.Provenance

type ReadGuard

type ReadGuard = loop.ReadGuard

type RestoreTransportMismatchError

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

RestoreTransportMismatchError reports a durably-folded model runtime whose transport is no longer a member of the current bound definition's declared ContextTransport set. Restore fails unconditionally on this error — there is no coherent "resume anyway" answer for a resolved model whose trust tier can no longer be determined, so it does not route through RestoreDecider/WithAllowConfigMismatch (see the design doc's "New restore-time hard validation" section).

func (*RestoreTransportMismatchError) Error

type RestoredState

type RestoredState struct {
	Msgs      content.AgenticMessages
	TurnIndex event.TurnIndex

	// DerivedPrefix counts the leading messages in Msgs that are a
	// compaction-generated summary rather than genuine human-authored
	// conversation (folded from the same CompactionCommitted event that
	// replaces Msgs entirely — sessionruntime's foldLoop). It seeds
	// loopState.msgsDerivedPrefix (see that field's doc comment for the full
	// rationale): without it, a restored loop's next turn would clone Msgs
	// into cfg.base with no marker at all, and capturePermissionReviewContext
	// would credit a model-generated compaction summary with
	// gate.ReviewContextOriginUser — genuine human authorization.
	DerivedPrefix int

	// Mode is the loop's LAST durably-selected mode (folded from LoopModeChanged, last write
	// wins); HasMode distinguishes "the loop changed mode" (reapply Mode, which may be the
	// base "") from "the loop never changed mode" (come up under the definition's initial
	// mode). NewRestored re-resolves the mode's model/effort/tools/instructions from the
	// fresh bound definition, so only the NAME is carried across restore.
	Mode    loop.ModeName
	HasMode bool

	// Runtime is the latest durable resolved runtime, whether selected by start,
	// mode change, or direct inference change. The live bound model supplies the
	// transport fields while this durable payload restores identity, limits, and effort.
	Runtime    event.ModelRuntime
	HasRuntime bool

	Context    event.ContextMeasurement
	HasContext bool

	Basis    event.ContextBasis
	HasBasis bool

	AutomaticBasis    event.ContextBasis
	HasAutomaticBasis bool

	// PendingProcessNotifications are Task 24C's undelivered process
	// completion notifications reconstructed at restore: the session replays
	// this loop's durable ProcessNotification commands and subtracts any
	// whose CommandID already appears as the cause of one of this loop's
	// durable Enduring events (already consumed before the crash). NewRestored
	// seeds them directly into the actor's live de-dup guard — the SAME
	// representation a live delivery populates — so restore never re-dispatches
	// them through Loop.Commands (there is no live sender at restore time) and
	// never re-appends them (they are already durable).
	PendingProcessNotifications []tool.ProcessCompletionNotification
}

RestoredState is the pre-built committed state a restored loop comes up with: the folded message history and the turn count from the durable journal. It is the loop half of the Restore constructor's payoff — the session folds a loop's Enduring events (foldLoop) into these two values and seeds a fresh actor with them so the resumed loop's history is byte-for-byte what it committed before teardown.

Msgs is the committed conversation ONLY — it does NOT carry a SystemMessage. The loop never stores the system prompt in loopState.msgs; the prompt rides runtimeConfig.System and is sent on every request, so a restored loop "re-seeds" the system prompt simply by carrying the same runtimeConfig. TurnIndex is the count of turns already started, so the next live turn numbers from TurnIndex+1 (installActiveTurn increments it), continuing the loop's numbering without a gap.

type ReviewContext

type ReviewContext struct {
	WorkspaceRoot      string
	WorkingDirectory   string
	RetryReason        string
	SecurityCeiling    string
	GatePolicyRevision string
	Policy             gate.ReviewContextPolicy
}

ReviewContext is the Harness-internal (Go-exported, but still inside the internal/ boundary — unreachable from outside this module) input that turns on live permission-review context capture for every turn a constructed Loop runs. internal/sessionruntime is the only caller: it builds one whenever a session has permission classifiers registered at all (see Session.loopReviewContext), auto-deriving what Harness already knows and sourcing the rest from the session's already-registered review policy. A nil *ReviewContext (the default, threaded by every pre-existing caller) leaves capture off, byte-identical to every Loop built before this addendum. Ordinary tools never see this type: it flows only from sessionruntime, through NewInModeWithCompactor/NewRestoredWithCompactor, into the loop's private runtimeConfig/turnConfig.reviewContext.

type RuntimeContextProvider

type RuntimeContextProvider = loop.RuntimeContextProvider

type RuntimeDependencies

type RuntimeDependencies struct {
	Compactor Compactor
	Hooks     *hook.Runner
	// ReviewContext mirrors Compactor's shape: internal/sessionruntime is the
	// only caller that ever supplies a non-nil value (whenever the session
	// has permission classifiers registered — see Session.loopReviewContext).
	// nil leaves the resulting loop's turns with reviewContext == nil.
	ReviewContext *ReviewContext

	// ToolResultObjects wires durable tool-result retention. Like Compactor and
	// ReviewContext it is optional: nil leaves the loop with no retention, which
	// is the behaviour of every caller that predates the capture pipeline.
	ToolResultObjects ToolResultObjectStore

	// ToolResultSpills wires the session-scoped spill directory. It is meaningful
	// only alongside ToolResultObjects: with no store there is nothing to upload
	// a spill to, and the runner keeps every tool on the materialized path.
	ToolResultSpills *ToolResultSpillDirectory
}

RuntimeDependencies carries native runtime collaborators that are not part of a loop's durable declarative definition.

type SnapshotError

type SnapshotError struct {
	Reason SnapshotErrorReason
	Cause  error
}

SnapshotError is returned by Snapshot when it cannot obtain a consistent view of the loop's committed state. Cause chains the underlying ctx error when present.

func (*SnapshotError) Error

func (e *SnapshotError) Error() string

func (*SnapshotError) Unwrap

func (e *SnapshotError) Unwrap() error

type SnapshotErrorReason

type SnapshotErrorReason string

SnapshotErrorReason classifies why a Snapshot could not return a consistent view.

const (
	// SnapshotLoopExited means the actor goroutine has exited (Loop.Done closed), so
	// there is no live state to read.
	SnapshotLoopExited SnapshotErrorReason = "loop_exited"
	// SnapshotContextDone means the caller's context was cancelled before the actor
	// replied.
	SnapshotContextDone SnapshotErrorReason = "context_done"
)

type StaleCompactionError

type StaleCompactionError struct {
	ExpectedBasis              event.ContextBasis
	ActualBasis                event.ContextBasis
	ExpectedModel              model.ModelKey
	ActualModel                model.ModelKey
	ExpectedRequestFingerprint [32]byte
	ActualRequestFingerprint   [32]byte
}

StaleCompactionError reports the complete measurement identity that failed the actor's compare-and-swap. A stale proposal never mutates live state.

func (*StaleCompactionError) Error

func (*StaleCompactionError) Error() string

type StepIndex

type StepIndex uint64

StepIndex is the turn-local index of a step. Each turn numbers its own steps from 0; it is not unique across turns.

type ToolResultObjectStat added in v0.31.0

type ToolResultObjectStat = loop.ToolResultObjectStat

ToolResultObjectStat, ToolResultObjectStore and ToolResultObjectStreamStore are ALIASES of the public declarations in pkg/loop, not distinct types. The seam is public because a composition root outside this module has to name the store it wires, and internal/ types cannot be named from outside github.com/looprig/harness; it is aliased rather than re-declared so there is exactly one type in each case and no conversion — or divergence — at the boundary.

type ToolResultObjectStore added in v0.31.0

type ToolResultObjectStore = loop.ToolResultObjectStore

ToolResultObjectStat, ToolResultObjectStore and ToolResultObjectStreamStore are ALIASES of the public declarations in pkg/loop, not distinct types. The seam is public because a composition root outside this module has to name the store it wires, and internal/ types cannot be named from outside github.com/looprig/harness; it is aliased rather than re-declared so there is exactly one type in each case and no conversion — or divergence — at the boundary.

type ToolResultObjectStreamStore added in v0.31.0

type ToolResultObjectStreamStore = loop.ToolResultObjectStreamStore

ToolResultObjectStat, ToolResultObjectStore and ToolResultObjectStreamStore are ALIASES of the public declarations in pkg/loop, not distinct types. The seam is public because a composition root outside this module has to name the store it wires, and internal/ types cannot be named from outside github.com/looprig/harness; it is aliased rather than re-declared so there is exactly one type in each case and no conversion — or divergence — at the boundary.

type ToolResultRetentionError added in v0.31.0

type ToolResultRetentionError struct {
	ToolExecutionID uuid.UUID
	ToolUseID       string
	Stage           ToolResultRetentionStage
	Cause           error
}

ToolResultRetentionError is the typed terminal cause when a tool ran but its complete result could not be retained durably. The loop commits the step — including a model-visible notice in place of the result — and then ends the turn on it, rather than continuing to another inference with a shaped preview whose elided bytes no longer exist anywhere.

func (*ToolResultRetentionError) Error added in v0.31.0

func (e *ToolResultRetentionError) Error() string

func (*ToolResultRetentionError) Unwrap added in v0.31.0

func (e *ToolResultRetentionError) Unwrap() error

type ToolResultRetentionStage added in v0.31.0

type ToolResultRetentionStage string

ToolResultRetentionStage names the step of the retention pipeline that failed. Each stage is reached by exactly one check, so the stage on a ToolResultRetentionError identifies the failure rather than merely grouping it.

const (
	// ToolResultRetentionStageSpill means the local session spill could not be
	// established or could not hold what the producer supplied — it was never
	// opened, or its backing failed or short-wrote part way through. Nothing is
	// uploaded for such a capture: the counts and the digest the sink recorded no
	// longer describe anything that exists, so a Put would store bytes the
	// verification stages would then reject for the wrong reason.
	ToolResultRetentionStageSpill ToolResultRetentionStage = "spill"
	// ToolResultRetentionStagePut means the object write itself failed.
	ToolResultRetentionStagePut ToolResultRetentionStage = "put"
	// ToolResultRetentionStageStat means the object was written but could not be
	// read back for verification.
	ToolResultRetentionStageStat ToolResultRetentionStage = "stat"
	// ToolResultRetentionStageSize means verification found a different stored
	// byte count than the sink captured.
	ToolResultRetentionStageSize ToolResultRetentionStage = "size_mismatch"
	// ToolResultRetentionStageDigest means verification found different stored
	// content than the sink captured.
	ToolResultRetentionStageDigest ToolResultRetentionStage = "digest_mismatch"
)

type ToolResultSpillDirectory added in v0.31.0

type ToolResultSpillDirectory = captureSpillDirectory

ToolResultSpillDirectory is the exported name for one session's spill root, so internal/sessionruntime can hold and release it. It is an alias rather than a wrapper: there is one type, and its only exported method is Release, so no package outside this one can open a spill.

func NewToolResultSpillDirectory added in v0.31.0

func NewToolResultSpillDirectory(base string, sessionID uuid.UUID) (*ToolResultSpillDirectory, error)

NewToolResultSpillDirectory establishes a session's spill root under base.

func UnavailableToolResultSpillDirectory added in v0.31.0

func UnavailableToolResultSpillDirectory(cause error) *ToolResultSpillDirectory

UnavailableToolResultSpillDirectory is the directory a session holds when its spill root could not be established. Every openSink reports cause, so a loop wired for retention fails its turn at the spill stage rather than quietly retaining nothing — the composition asked for durable retention, and this is the honest report that it is unavailable.

type ToolResultSpillError added in v0.31.0

type ToolResultSpillError = captureSpillError

ToolResultSpillError is the exported name for the typed cause above. Release is the one spill operation whose error crosses a package boundary — internal/sessionruntime folds a failed removal into the shutdown failure list every other teardown phase reports through — and that caller must be able to match it by TYPE rather than by message text.

type ToolSet

type ToolSet struct {
	Access      loop.AccessGate
	Registry    []tool.InvokableTool
	Middlewares []tool.ToolMiddleware

	MaxToolIterations    int
	MaxToolCallsPerTurn  int
	MaxParallelToolCalls int
	MaxToolResultBytes   int

	// MaxToolResultCaptureBytes is the durable retention ceiling per tool result,
	// resolved from loop.ToolLimits.CaptureBytes. It is independent of
	// MaxToolResultBytes: see loop.DefaultToolResultCaptureBytes for why the two
	// must not share a knob.
	MaxToolResultCaptureBytes int

	// MaxMaterializedToolResultBytes is the hard maximum the runtime DECLARES for
	// a legacy materialized tool — one that hands back a fully built ToolResult
	// instead of streaming into a capture sink. Its bytes are already resident
	// when the loop sees them, so this bound is what a pooled Host budgets per
	// concurrent materialized call; it is not settable from a loop definition,
	// because it describes the runtime's memory contract rather than an agent's
	// policy. It bounds retention as well as accounting, so a producer that
	// exceeds it still records a durable, observable truncation rather than
	// silently losing its tail.
	MaxMaterializedToolResultBytes int
}

ToolSet is the actor-private resolved tool bundle. Access is the combined prepared-access decision gate; a nil Access denies every tool call (fail closed) rather than running ungated.

type TurnStartCapability

type TurnStartCapability interface {
	PublishTurnStarted(context.Context, event.TurnStarted) (committed bool, err error)
	Release()
}

TurnStartCapability is the actor's opaque, one-shot authority to publish one admitted turn's exact opening TurnStarted and later release its first-step reader. Its committed result names the primary TurnStarted append; a later derived-session transition may therefore return committed=true with a non-nil error.

Jump to

Keyboard shortcuts

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