Documentation
¶
Overview ¶
Package session exposes the live session data-plane and control-plane contracts. Session construction and restoration are owned exclusively by package rig.
Index ¶
- Constants
- type AcceptAllDecider
- type AgentNameMismatchError
- type ConfigMismatchError
- type DefaultPolicyDecider
- type GateError
- type GateErrorKind
- type GateHost
- type RestoreDecider
- type RestoreDecision
- type RestoreDiscoveryError
- type RestoreDiscoveryErrorKind
- type RestoreError
- type RestoreErrorKind
- type RestoreRejectedError
- type RestoreRuntimeMismatchError
- type RuntimeRestoreRequest
- type RuntimeRestoreResolver
- type Session
- type SessionController
- type SessionError
- type SessionErrorKind
- type TurnRejectedError
- type WorkspaceNotConfiguredError
- type WorkspaceRecoveryError
- type WorkspaceRootBusyError
- type WorkspaceRootLeaseLostError
Constants ¶
const ( RestoreRuntimeMissing = "missing_runtime" RestoreRuntimeTargetMismatch = "target_mismatch" RestoreRuntimeCredentialMismatch = "credential_mismatch" // #nosec G101 -- closed error-category label, not a credential RestoreRuntimeEffortMismatch = "effort_mismatch" )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AcceptAllDecider ¶
type AcceptAllDecider struct{}
AcceptAllDecider accepts every assessment. It backs the deprecated WithAllowConfigMismatch shim (wired in a later task).
func (AcceptAllDecider) DecideRestore ¶
func (AcceptAllDecider) DecideRestore(_ context.Context, _ event.DriftAssessment) (RestoreDecision, error)
type AgentNameMismatchError ¶
func (*AgentNameMismatchError) Error ¶
func (e *AgentNameMismatchError) Error() string
type ConfigMismatchError ¶
type ConfigMismatchError struct{ Persisted, Live event.ConfigFingerprint }
ConfigMismatchError is the legacy config-drift restore error. For a manifest-carrying session it is superseded by RestoreRejectedError, which carries a typed drift assessment; the legacy fingerprint path (a session with no ConfigManifest configured) still returns it during the deprecation window. Its formal deprecation and removal path is open question 9 in docs/plans/2026-07-16-session-versioning-migration-design.md; it is not marked Deprecated here because internal code still depends on it.
func (*ConfigMismatchError) Error ¶
func (e *ConfigMismatchError) Error() string
type DefaultPolicyDecider ¶
type DefaultPolicyDecider struct{}
DefaultPolicyDecider is the fail-secure default: accept when every change is Info, reject when any change is Warn.
func (DefaultPolicyDecider) DecideRestore ¶
func (DefaultPolicyDecider) DecideRestore(_ context.Context, a event.DriftAssessment) (RestoreDecision, error)
type GateError ¶
type GateError struct {
GateID gate.ID
Kind GateErrorKind
Cause error
}
func (*GateError) GateErrorKind ¶
type GateErrorKind ¶
type GateErrorKind string
const ( GateNotFound GateErrorKind = "not_found" GateNotReady GateErrorKind = "not_ready" GateKindMismatch GateErrorKind = "kind_mismatch" GateActionInvalid GateErrorKind = "action_invalid" GateCapacity GateErrorKind = "capacity" GateAppendFailed GateErrorKind = "append_failed" )
type GateHost ¶
type GateHost interface {
// OpenHostGate opens g and returns its id. The gate is public and answerable
// when it returns. The caller MUST then either AwaitGateAnswer or CloseGate;
// abandoning it without either leaks the answer slot for the session's life.
OpenHostGate(context.Context, uuid.UUID, gate.Gate, gate.Payload) (gate.ID, error)
// AwaitGateAnswer blocks until the gate is answered and returns the validated
// answer, including the form values that are absent from every durable record.
// An answer is delivered exactly once. Cancelling the context abandons the
// wait and frees the slot but does NOT close the gate — the gate is durable
// state and the context is the caller's, so an opener that gives up must
// CloseGate.
AwaitGateAnswer(context.Context, gate.ID) (gate.Answer, error)
// CloseGate withdraws a gate without answering it, waking any awaiter with a
// *GateError{GateNotFound}. It is how an opener cleans up after a cancelled or
// timed-out request.
CloseGate(context.Context, gate.ID, gate.CloseReason) error
}
GateHost is the capability to raise a HOST-OWNED gate: to put a structured question or an out-of-band action to a human and receive the answer directly.
It is a SEPARATE contract rather than three more methods on SessionController, for two independent reasons.
The first is segregation. Opening a gate is not part of running a session: almost every consumer of a SessionController — the TUI, the CLI, a test — submits work, watches events, and answers gates, and none of them raise one. Widening SessionController would force every implementation to grow three methods that only an integration host calls, which is the exact coupling the interface rules forbid.
The second is that the two contracts have different holders. A SessionController is the session's operator. A GateHost is whatever opened a particular gate and is blocked on its answer — an MCP binding servicing an elicitation, say. Those are the two ends of the same gate, and RespondGate (on Session) is the other end: a client answers, the host receives. Keeping them separate keeps that asymmetry visible instead of collapsing both roles into one god-interface.
A live session implements it, so a host obtains one by asserting on the controller rig returns:
host, ok := controller.(session.GateHost)
The contract is host-owned gates ONLY (gate.KindForm and gate.KindOpenURL with gate.ResolverSession). There is deliberately no way to open a permission or ask-user gate through it: those are answered by resuming a parked loop, and a host that could mint one could park — or forge an approval against — a loop that is not its own. An implementation MUST refuse anything else at open time rather than at answer time, so a caller learns its request was invalid before a human is shown a prompt that can never be delivered.
type RestoreDecider ¶
type RestoreDecider interface {
DecideRestore(ctx context.Context, assessment event.DriftAssessment) (RestoreDecision, error)
}
RestoreDecider answers a restore drift assessment. It runs while the restore lease is held; ctx carries the restore deadline, and a timeout is a rejection.
An ACCEPTING RestoreDecision must honor the RestoreDecision contract above: a valid Source (empty defaults to policy) and bounded Actor/Message (truncated, not rejected). A decider therefore cannot brick a restore with a malformed decision — the constructor normalizes an accepting decision before it becomes a durable ConfigurationAdopted.
type RestoreDecision ¶
type RestoreDecision struct {
Accept bool
Source event.DecisionSource // user | policy | operator (migration reserved for Harness)
Actor string
Message string
}
RestoreDecision is an application's answer to a drift assessment. Source, Actor, and Message are recorded durably on the resulting ConfigurationAdopted.
Contract for an ACCEPTING decision (Accept == true): the valid decider Sources are user | policy | operator. `migration` is RESERVED for Harness itself (a Phase-2 migration) and must never be stamped by a decider. An empty OR migration Source on accept is normalized to policy by the restore constructor, so a decider can neither omit a source nor forge a migration adoption. Actor and Message are BOUNDED audit fields: the restore constructor truncates them (to MaxConfigActorLen / MaxConfigMessageLen bytes) before writing the durable adoption, so an over-long value is silently shortened rather than bricking the restore — never rely on their full length surviving.
type RestoreDiscoveryError ¶
type RestoreDiscoveryError struct {
Kind RestoreDiscoveryErrorKind
SessionID uuid.UUID
}
func (*RestoreDiscoveryError) Error ¶
func (e *RestoreDiscoveryError) Error() string
type RestoreDiscoveryErrorKind ¶
type RestoreDiscoveryErrorKind string
const ( RestoreNoSessionStarted RestoreDiscoveryErrorKind = "no_session_started" RestoreNoPrimerLoop RestoreDiscoveryErrorKind = "no_primer_loop" )
type RestoreError ¶
type RestoreError struct {
Kind RestoreErrorKind
Cause error
}
func (*RestoreError) Error ¶
func (e *RestoreError) Error() string
func (*RestoreError) Unwrap ¶
func (e *RestoreError) Unwrap() error
type RestoreErrorKind ¶
type RestoreErrorKind string
const ( RestoreLeaseFailed RestoreErrorKind = "lease_failed" RestoreJournalFailed RestoreErrorKind = "journal_failed" RestoreReplayFailed RestoreErrorKind = "replay_failed" RestoreAppendFailed RestoreErrorKind = "append_failed" // RestoreAdoptionInvalid names the specific failure of building/validating the // durable ConfigurationAdopted (event.ValidateEvent rejected it), distinct from // RestoreAppendFailed (an actual journal Append failure — lost lease, storage // error). It lets a caller tell "the decision produced a malformed adoption" // apart from "the journal write failed". RestoreAdoptionInvalid RestoreErrorKind = "adoption_invalid" RestoreLoopFailed RestoreErrorKind = "loop_failed" RestoreContextDone RestoreErrorKind = "context_done" RestoreIDGenerationFailed RestoreErrorKind = "id_generation_failed" RestoreForeignSIDMissing RestoreErrorKind = "foreign_sid_missing" RestoreForeignBuilderMissing RestoreErrorKind = "foreign_builder_missing" RestoreMaterializeFailed RestoreErrorKind = "materialize_failed" )
type RestoreRejectedError ¶
type RestoreRejectedError struct {
Assessment event.DriftAssessment
Source event.DecisionSource
Cause error
}
RestoreRejectedError reports a restore refused by the configured RestoreDecider (or by default policy). It carries the full typed assessment so callers and operators see exactly which fields drifted and how severely. Cause is set only when the rejection was caused by the decider ITSELF failing (a returned error or a timeout — a timeout is a rejection): it stays inspectable via Unwrap so callers can errors.As/errors.Is through to the underlying cause (e.g. context.DeadlineExceeded). A plain policy rejection leaves Cause nil.
func (*RestoreRejectedError) Error ¶
func (e *RestoreRejectedError) Error() string
func (*RestoreRejectedError) Unwrap ¶
func (e *RestoreRejectedError) Unwrap() error
Unwrap exposes the underlying decider failure (nil for a plain policy rejection) so errors.As/errors.Is reach it.
type RestoreRuntimeMismatchError ¶
type RestoreRuntimeMismatchError struct {
Kind string
Harness loop.AgentHarnessName
Cause error
}
RestoreRuntimeMismatchError reports a fail-closed adapter restore decision. Its public text is deliberately category-only: journal selectors, model keys, credentials, and catalog/provider details never reach model-facing errors.
func (*RestoreRuntimeMismatchError) Error ¶
func (e *RestoreRuntimeMismatchError) Error() string
func (*RestoreRuntimeMismatchError) Unwrap ¶
func (e *RestoreRuntimeMismatchError) Unwrap() error
type RuntimeRestoreRequest ¶ added in v0.27.0
type RuntimeRestoreRequest struct {
AgentName identity.AgentName
Harness loop.AgentHarnessName
Profile loop.RuntimeProfileName
Source loop.RuntimeSourceName
Credential loop.CredentialMode
Target model.ModelKey
Effort model.Effort
SelectionKind loop.RuntimeSelectionKind
SmallModelAlias loop.ModelAlias
Mismatch string
Catalog loop.RuntimeCatalog
}
RuntimeRestoreRequest is the bounded, secret-free runtime selection context a composition may use when exact durable runtime reconstruction fails.
type RuntimeRestoreResolver ¶ added in v0.27.0
type RuntimeRestoreResolver interface {
ResolveRuntimeRestore(context.Context, RuntimeRestoreRequest) (loop.Resolved, error)
}
RuntimeRestoreResolver lets the composition layer authorize a current runtime selection for a durable loop. Omitting it keeps exact, fail-closed reconstruction.
type Session ¶
type Session interface {
SessionID() uuid.UUID
ActiveLoop() loop.Handle
Loop(uuid.UUID) (loop.Handle, bool)
Submit(context.Context, []content.Block) (uuid.UUID, error)
SubmitToLoop(context.Context, uuid.UUID, []content.Block) (uuid.UUID, error)
Compact(context.Context) (uuid.UUID, error)
CompactToLoop(context.Context, uuid.UUID) (uuid.UUID, error)
SubscribeEvents(event.EventFilter) (event.Subscription, error)
RespondGate(context.Context, gate.GateResponse) error
Interrupt(context.Context) (bool, error)
}
Session is the ordinary data-plane view of one live rig execution.
type SessionController ¶
type SessionController interface {
Session
SetActiveLoop(context.Context, uuid.UUID) error
LoopController(uuid.UUID) (loop.Controller, bool)
CheckpointWorkspace(context.Context) (workspacestore.Ref, error)
RestoreWorkspace(context.Context, workspacestore.Ref) error
Shutdown(context.Context) error
}
SessionController is the trusted policy and lifecycle view of a Session.
type SessionError ¶
type SessionError struct {
Kind SessionErrorKind
Cause error
}
func (*SessionError) Error ¶
func (e *SessionError) Error() string
func (*SessionError) Unwrap ¶
func (e *SessionError) Unwrap() error
type SessionErrorKind ¶
type SessionErrorKind string
const ( SessionIDGenerationFailed SessionErrorKind = "id_generation_failed" SessionLoopIDGenerationFailed SessionErrorKind = "loop_id_generation_failed" SessionLoopExited SessionErrorKind = "loop_exited" SessionLoopNotFound SessionErrorKind = "loop_not_found" SessionEventChannelClosed SessionErrorKind = "event_channel_closed" SessionContextDone SessionErrorKind = "context_done" SessionClosing SessionErrorKind = "session_closing" SessionFaulted SessionErrorKind = "session_faulted" SessionLoopDepthExceeded SessionErrorKind = "loop_depth_exceeded" SessionLoopQuotaExceeded SessionErrorKind = "loop_quota_exceeded" SessionForeignBuilderMissing SessionErrorKind = "foreign_builder_missing" SessionCompactionUnsupported SessionErrorKind = "compaction_unsupported" SessionDelegateIntentAppendFailed SessionErrorKind = "delegate_intent_append_failed" SessionDelegateAdmissionCommitFailed SessionErrorKind = "delegate_admission_commit_failed" )
type TurnRejectedError ¶
type TurnRejectedError struct{ Reason event.RejectReason }
func (*TurnRejectedError) Error ¶
func (e *TurnRejectedError) Error() string
type WorkspaceNotConfiguredError ¶
type WorkspaceNotConfiguredError struct{}
func (*WorkspaceNotConfiguredError) Error ¶
func (*WorkspaceNotConfiguredError) Error() string
type WorkspaceRecoveryError ¶
WorkspaceRecoveryError reports that a per-session workspace destination could not be established or recovered safely. Path identifies the refused filesystem object, Reason is a stable diagnostic, and Cause preserves any syscall failure.
func (*WorkspaceRecoveryError) Error ¶
func (e *WorkspaceRecoveryError) Error() string
func (*WorkspaceRecoveryError) Unwrap ¶
func (e *WorkspaceRecoveryError) Unwrap() error
type WorkspaceRootBusyError ¶
WorkspaceRootBusyError reports that an exclusive workspace root is already leased by another session. HolderEpoch is copied from the storage refusal so callers never need an internal runtime type to diagnose contention.
func (*WorkspaceRootBusyError) Error ¶
func (e *WorkspaceRootBusyError) Error() string
func (*WorkspaceRootBusyError) Unwrap ¶
func (e *WorkspaceRootBusyError) Unwrap() error
type WorkspaceRootLeaseLostError ¶
type WorkspaceRootLeaseLostError struct{}
WorkspaceRootLeaseLostError reports that an exclusive workspace lease ended while its session was live. It is the public leaf chained by SessionFaulted.
func (*WorkspaceRootLeaseLostError) Error ¶
func (*WorkspaceRootLeaseLostError) Error() string