Documentation
¶
Index ¶
- Constants
- Variables
- func IsActiveRunStatus(status string) bool
- func SteerRunAvailable(run *CurrentRunView) bool
- type ActiveRunUpdate
- type Admission
- type AdmitInput
- type Backend
- type Command
- type CommandSubscription
- type CurrentRunPatch
- type CurrentRunView
- type Cursor
- type DecisionFenceActivator
- type DecisionOutputLimits
- type DecisionOutputPage
- type DecisionOutputRef
- type DecisionOutputState
- type DecisionOutputStore
- type DecisionResponse
- type DecisionResponseResult
- type DecisionStore
- type DecisionTarget
- type DistributedBackend
- type Event
- type Execution
- type FenceActivator
- type FollowUpClaimRef
- type FollowUpItem
- type FollowUpItemID
- type FollowUpPendingRef
- type HistoryResetBackend
- type HistoryResetHandler
- type Key
- type LeaseCandidate
- type LiveQueueBackend
- type LivenessBackend
- type Manager
- func (m *Manager) Abort(ctx context.Context, botID, sessionID, runID string) (bool, error)
- func (m *Manager) AbortControl(ctx context.Context, botID, sessionID, runID, controlID string) (bool, error)
- func (m *Manager) AbortRun(ctx context.Context, handle RunHandle) (bool, error)
- func (m *Manager) Admit(ctx context.Context, in AdmitInput) (Admission, error)
- func (m *Manager) ApplyFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
- func (m *Manager) ApplySteer(ctx context.Context, key Key, ref SteerClaimRef) error
- func (m *Manager) BeginBotHistoryReset(ctx context.Context, botID string) (context.Context, func(), error)
- func (m *Manager) BeginSessionHistoryReset(ctx context.Context, botID, sessionID string) (context.Context, func(), error)
- func (m *Manager) CancelFollowUp(ctx context.Context, key Key, itemID FollowUpItemID) error
- func (m *Manager) CancelSteer(ctx context.Context, key Key, itemID SteerItemID) error
- func (m *Manager) ClaimNextFollowUp(ctx context.Context, key Key, triggerRunID string) (FollowUpItem, FollowUpClaimRef, bool, error)
- func (m *Manager) ClaimNextSteer(ctx context.Context, handle RunHandle, sealIfEmpty bool) (SteerItem, SteerClaimRef, bool, error)
- func (m *Manager) Close() error
- func (m *Manager) CloseContext(ctx context.Context) error
- func (m *Manager) CloseSteerRun(ctx context.Context, key Key, runID string) error
- func (m *Manager) ContinuationStepIndex(handle RunHandle) (int, error)
- func (m *Manager) DecisionContinuationContext(cmd Command) (context.Context, context.CancelFunc, RunHandle, error)
- func (m *Manager) DisableSteer(ctx context.Context, handle RunHandle) error
- func (m *Manager) EnableSteer(ctx context.Context, handle RunHandle) error
- func (m *Manager) EnqueueFollowUp(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (FollowUpItem, error)
- func (m *Manager) EnqueueSteer(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (SteerItem, error)
- func (m *Manager) FinishRun(ctx context.Context, handle RunHandle, status, message string) error
- func (m *Manager) FinishRunWithErrorCode(ctx context.Context, handle RunHandle, status, errorCode string) error
- func (m *Manager) HandleAgentEvent(ctx context.Context, handle RunHandle, event native.StreamEvent) ([]chatview.UIMessage, error)
- func (m *Manager) HandleAgentEventWithStatus(ctx context.Context, handle RunHandle, event native.StreamEvent) ([]chatview.UIMessage, string, error)
- func (m *Manager) IsDistributed() bool
- func (m *Manager) LivenessGeneration(ctx context.Context) (string, error)
- func (m *Manager) MarkInlineDecisionRun(botID, sessionID, runID string)
- func (m *Manager) OwnerID() string
- func (m *Manager) PendingQueues(ctx context.Context, key Key, limit int) ([]SteerItem, []FollowUpItem, error)
- func (m *Manager) PromoteFollowUpToSteer(ctx context.Context, key Key, ref FollowUpPendingRef) (PromoteFollowUpResult, error)
- func (m *Manager) PublishDecisionOutput(ctx context.Context, command Command, seq int64, payload json.RawMessage) error
- func (m *Manager) PublishQueueUserTurns(ctx context.Context, handle RunHandle, update QueueUserTurnUpdate) error
- func (m *Manager) ReleaseFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
- func (m *Manager) ReleaseSteer(ctx context.Context, key Key, ref SteerClaimRef) error
- func (m *Manager) ReorderFollowUp(ctx context.Context, key Key, item, before FollowUpPendingRef) ([]FollowUpItem, error)
- func (m *Manager) ReorderSteer(ctx context.Context, key Key, item, before SteerPendingRef) ([]SteerItem, error)
- func (m *Manager) RouteDecisionResponse(ctx context.Context, response DecisionResponse) (DecisionResponseResult, error)
- func (m *Manager) RunRef(ctx context.Context, botID, sessionID, runID string) (RunRef, bool, error)
- func (m *Manager) SetAdmissionObserver(observer func(botID, sessionID string))
- func (m *Manager) SetCommandHandler(handler func(context.Context, Command) error)
- func (m *Manager) SetDecisionFinalizer(finalizer func(context.Context, RunHandle) error)
- func (m *Manager) SetDecisionStore(store DecisionStore)
- func (m *Manager) SetHistoryResetHandler(handler HistoryResetHandler)
- func (m *Manager) SetLostRunDecisionCanceller(canceller func(context.Context, string, string, string, int64, string) error)
- func (m *Manager) SetTerminalObserver(observer func(context.Context, TerminalRun))
- func (m *Manager) SetTerminalReconciler(reconciler func(context.Context) error)
- func (m *Manager) Snapshot(ctx context.Context, botID, sessionID string) (Snapshot, error)
- func (m *Manager) Start(ctx context.Context) error
- func (m *Manager) SteerWake(handle RunHandle) <-chan struct{}
- func (m *Manager) StreamDecisionResponse(ctx context.Context, response DecisionResponse, output chan<- json.RawMessage) (DecisionResponseResult, error)
- func (m *Manager) Subscribe(ctx context.Context, botID, sessionID string) (Subscription, error)
- func (m *Manager) UpdateFollowUp(ctx context.Context, key Key, itemID FollowUpItemID, payload []byte) (FollowUpItem, error)
- func (m *Manager) UpdateSteer(ctx context.Context, key Key, itemID SteerItemID, payload []byte) (SteerItem, error)
- func (m *Manager) ValidateRunOwnership(ctx context.Context, handle RunHandle) error
- func (m *Manager) WaitDecisionContinuationReady(ctx context.Context, cmd Command) error
- func (m *Manager) WaitForHistoryReset(ctx context.Context, botID, sessionID string) error
- type MemoryBackend
- func (b *MemoryBackend) AcquireHistoryReset(ctx context.Context, scope ResetScope, token string, ttl time.Duration) (ResetLease, bool, error)
- func (*MemoryBackend) AcquireLeaderLease(context.Context, string, time.Duration) (bool, error)
- func (b *MemoryBackend) AppendDecisionOutput(ctx context.Context, ref DecisionOutputRef, seq int64, payload json.RawMessage, ...) (DecisionOutputState, error)
- func (b *MemoryBackend) ApplyFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
- func (b *MemoryBackend) ApplySteer(ctx context.Context, key Key, ref SteerClaimRef) error
- func (b *MemoryBackend) CancelFollowUp(ctx context.Context, key Key, itemID FollowUpItemID) error
- func (b *MemoryBackend) CancelSteer(ctx context.Context, key Key, itemID SteerItemID) error
- func (b *MemoryBackend) ClaimDecisionOutput(ctx context.Context, ref DecisionOutputRef) (bool, error)
- func (b *MemoryBackend) ClaimNextFollowUp(ctx context.Context, key Key, triggerRunID string) (FollowUpItem, FollowUpClaimRef, bool, error)
- func (b *MemoryBackend) ClaimNextSteer(ctx context.Context, handle RunHandle, sealIfEmpty bool) (SteerItem, SteerClaimRef, bool, error)
- func (b *MemoryBackend) Close() error
- func (b *MemoryBackend) CloseSteerRun(ctx context.Context, key Key, runID string) error
- func (b *MemoryBackend) EffectiveHistoryReset(ctx context.Context, scope ResetScope) (ResetLease, bool, error)
- func (b *MemoryBackend) EnqueueFollowUp(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (FollowUpItem, error)
- func (b *MemoryBackend) EnqueueSteer(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (SteerItem, error)
- func (*MemoryBackend) ExpiredLeaseCandidates(context.Context, int64) ([]LeaseCandidate, error)
- func (b *MemoryBackend) LivenessGeneration(context.Context) (string, error)
- func (b *MemoryBackend) Load(ctx context.Context, key Key) (Snapshot, bool, error)
- func (*MemoryBackend) Now(ctx context.Context) (time.Time, error)
- func (b *MemoryBackend) PendingQueues(ctx context.Context, key Key, limit int) ([]SteerItem, []FollowUpItem, error)
- func (b *MemoryBackend) PromoteFollowUpToSteer(ctx context.Context, key Key, ref FollowUpPendingRef) (PromoteFollowUpResult, error)
- func (b *MemoryBackend) Publish(ctx context.Context, event Event) error
- func (b *MemoryBackend) ReadDecisionOutput(ctx context.Context, ref DecisionOutputRef, from int) (DecisionOutputPage, error)
- func (b *MemoryBackend) ReleaseDecisionOutput(ctx context.Context, ref DecisionOutputRef) error
- func (b *MemoryBackend) ReleaseFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
- func (b *MemoryBackend) ReleaseHistoryReset(ctx context.Context, lease ResetLease) (bool, error)
- func (*MemoryBackend) ReleaseLeaderLease(context.Context, string) error
- func (*MemoryBackend) ReleaseLeaseCandidate(context.Context, LeaseCandidate) (bool, error)
- func (b *MemoryBackend) ReleaseSteer(ctx context.Context, key Key, ref SteerClaimRef) error
- func (b *MemoryBackend) RenewHistoryReset(ctx context.Context, lease ResetLease, ttl time.Duration) (ResetLease, bool, error)
- func (b *MemoryBackend) ReorderFollowUp(ctx context.Context, key Key, item, before FollowUpPendingRef) ([]FollowUpItem, error)
- func (b *MemoryBackend) ReorderSteer(ctx context.Context, key Key, item, before SteerPendingRef) ([]SteerItem, error)
- func (b *MemoryBackend) StartRunIfNoHistoryReset(ctx context.Context, key Key, update SnapshotUpdate) (Snapshot, bool, error)
- func (b *MemoryBackend) Subscribe(ctx context.Context, key Key) (Subscription, error)
- func (b *MemoryBackend) Update(ctx context.Context, key Key, update SnapshotUpdate) (Snapshot, bool, error)
- func (b *MemoryBackend) UpdateFollowUp(ctx context.Context, key Key, itemID FollowUpItemID, payload []byte) (FollowUpItem, error)
- func (b *MemoryBackend) UpdateSteer(ctx context.Context, key Key, itemID SteerItemID, payload []byte) (SteerItem, error)
- type Options
- type PromoteFollowUpResult
- type QueueStatus
- type QueueUserTurnUpdate
- type Reaper
- func (r *Reaper) Close(ctx context.Context) error
- func (r *Reaper) SetLostRunDecisionCanceller(canceller func(context.Context, string, string, string, int64, string) error)
- func (r *Reaper) SetTerminalObserver(observer func(context.Context, TerminalRun))
- func (r *Reaper) SetTerminalReconciler(reconciler func(context.Context) error)
- func (r *Reaper) SetWaitingDecisionRecoverer(recoverer func(context.Context, LeaseCandidate) (bool, error))
- func (r *Reaper) Start(ctx context.Context) error
- type RedisBackend
- func (b *RedisBackend) AcquireHistoryReset(ctx context.Context, scope ResetScope, token string, ttl time.Duration) (ResetLease, bool, error)
- func (b *RedisBackend) AcquireLeaderLease(ctx context.Context, ownerID string, ttl time.Duration) (bool, error)
- func (b *RedisBackend) AppendDecisionOutput(ctx context.Context, ref DecisionOutputRef, seq int64, payload json.RawMessage, ...) (DecisionOutputState, error)
- func (b *RedisBackend) ApplyFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
- func (b *RedisBackend) ApplySteer(ctx context.Context, key Key, ref SteerClaimRef) error
- func (b *RedisBackend) CancelFollowUp(ctx context.Context, key Key, itemID FollowUpItemID) error
- func (b *RedisBackend) CancelSteer(ctx context.Context, key Key, itemID SteerItemID) error
- func (b *RedisBackend) CheckHealth(ctx context.Context) error
- func (b *RedisBackend) ClaimDecisionOutput(ctx context.Context, ref DecisionOutputRef) (bool, error)
- func (b *RedisBackend) ClaimNextFollowUp(ctx context.Context, key Key, triggerRunID string) (FollowUpItem, FollowUpClaimRef, bool, error)
- func (b *RedisBackend) ClaimNextSteer(ctx context.Context, handle RunHandle, sealIfEmpty bool) (SteerItem, SteerClaimRef, bool, error)
- func (b *RedisBackend) Close() error
- func (b *RedisBackend) CloseSteerRun(ctx context.Context, key Key, runID string) error
- func (b *RedisBackend) DeleteRunRef(ctx context.Context, ref RunRef) (bool, error)
- func (b *RedisBackend) EffectiveHistoryReset(ctx context.Context, scope ResetScope) (ResetLease, bool, error)
- func (b *RedisBackend) EnqueueFollowUp(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (FollowUpItem, error)
- func (b *RedisBackend) EnqueueSteer(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (SteerItem, error)
- func (b *RedisBackend) ExpiredLeaseCandidates(ctx context.Context, limit int64) ([]LeaseCandidate, error)
- func (b *RedisBackend) LivenessGeneration(ctx context.Context) (string, error)
- func (b *RedisBackend) Load(ctx context.Context, key Key) (Snapshot, bool, error)
- func (b *RedisBackend) LoadCommandResult(ctx context.Context, commandID string) (Command, bool, error)
- func (b *RedisBackend) LoadRunRef(ctx context.Context, key Key, runID string) (RunRef, bool, error)
- func (b *RedisBackend) Now(ctx context.Context) (time.Time, error)
- func (b *RedisBackend) PendingQueues(ctx context.Context, key Key, limit int) ([]SteerItem, []FollowUpItem, error)
- func (b *RedisBackend) PromoteFollowUpToSteer(ctx context.Context, key Key, ref FollowUpPendingRef) (PromoteFollowUpResult, error)
- func (b *RedisBackend) Publish(ctx context.Context, event Event) error
- func (b *RedisBackend) PublishCommand(ctx context.Context, ownerID string, command Command) error
- func (b *RedisBackend) ReadDecisionOutput(ctx context.Context, ref DecisionOutputRef, from int) (DecisionOutputPage, error)
- func (b *RedisBackend) ReconcileTerminalRun(ctx context.Context, key Key, ref RunRef, update ActiveRunUpdate) (Snapshot, bool, error)
- func (b *RedisBackend) ReleaseDecisionOutput(ctx context.Context, ref DecisionOutputRef) error
- func (b *RedisBackend) ReleaseFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
- func (b *RedisBackend) ReleaseHistoryReset(ctx context.Context, lease ResetLease) (bool, error)
- func (b *RedisBackend) ReleaseLeaderLease(ctx context.Context, ownerID string) error
- func (b *RedisBackend) ReleaseLeaseCandidate(ctx context.Context, candidate LeaseCandidate) (bool, error)
- func (b *RedisBackend) ReleaseRun(ctx context.Context, key Key, ref RunRef, update ActiveRunUpdate) (Snapshot, bool, error)
- func (b *RedisBackend) ReleaseSteer(ctx context.Context, key Key, ref SteerClaimRef) error
- func (b *RedisBackend) RenewHistoryReset(ctx context.Context, lease ResetLease, ttl time.Duration) (ResetLease, bool, error)
- func (b *RedisBackend) RenewLease(ctx context.Context, key Key, runID, ownerID, generation string, ...) error
- func (b *RedisBackend) ReorderFollowUp(ctx context.Context, key Key, item, before FollowUpPendingRef) ([]FollowUpItem, error)
- func (b *RedisBackend) ReorderSteer(ctx context.Context, key Key, item, before SteerPendingRef) ([]SteerItem, error)
- func (b *RedisBackend) StartRun(ctx context.Context, key Key, ref RunRef, update SnapshotUpdate) (Snapshot, bool, error)
- func (b *RedisBackend) StoreCommandResult(ctx context.Context, result Command, ttl time.Duration) error
- func (b *RedisBackend) Subscribe(ctx context.Context, key Key) (Subscription, error)
- func (b *RedisBackend) SubscribeCommands(ctx context.Context, ownerID string) (CommandSubscription, error)
- func (b *RedisBackend) Update(ctx context.Context, key Key, update SnapshotUpdate) (Snapshot, bool, error)
- func (b *RedisBackend) UpdateActiveRun(ctx context.Context, key Key, runID, generation string, update ActiveRunUpdate) (Snapshot, bool, error)
- func (b *RedisBackend) UpdateFollowUp(ctx context.Context, key Key, itemID FollowUpItemID, payload []byte) (FollowUpItem, error)
- func (b *RedisBackend) UpdateSteer(ctx context.Context, key Key, itemID SteerItemID, payload []byte) (SteerItem, error)
- func (b *RedisBackend) ValidateRunOwnership(ctx context.Context, key Key, ref RunRef) error
- type RedisOptions
- type ResetLease
- type ResetScope
- type RunAdmissionView
- type RunHandle
- type RunOperationView
- type RunRef
- type RuntimeDelta
- type RuntimeMessageAppend
- type RuntimeProgressAppend
- type Snapshot
- type SnapshotUpdate
- type SteerClaimRef
- type SteerItem
- type SteerItemID
- type SteerPendingRef
- type SteerTurnView
- type Subscription
- type TerminalRun
Constants ¶
const ( EventRuntimeSnapshot = "runtime_snapshot" EventRuntimeDelta = "runtime_delta" EventRuntimeDropped = "runtime_dropped" // EventDecisionOutput wakes a channel continuation reader. It carries no // payload: the reader always resumes from its cursor in the output log. EventDecisionOutput = "decision_output" RunStatusRunning = "running" RunStatusAdmitting = "admitting" // RunStatusWaitingDecision keeps the admitted run active while its native // execution is parked on a durable approval or ask_user decision. RunStatusWaitingDecision = "waiting_decision" RunStatusAborting = "aborting" RunStatusFinishing = "finishing" RunStatusCompleted = "completed" RunStatusAborted = "aborted" RunStatusErrored = "errored" RunStatusLost = "lost" RunOperationRetry = "retry" RunOperationEdit = "edit" CommandAbort = "abort" CommandSteerWake = "steer_wake" CommandToolApprovalResponse = "tool_approval_response" CommandUserInputResponse = "user_input_response" CommandHistoryReset = "history_reset" CommandResult = "command_result" ResetScopeSession = "session" ResetScopeBot = "bot" )
const ( // MaxPendingQueueItems bounds accepted items per queue and session. Queue // state is one serialized document per queue, so an unbounded pending set // would grow every mutation's read and write linearly. MaxPendingQueueItems = 64 )
const ( // QueueErrorTargetRunNotActive marks a steer whose target run reached a // terminal state before the steer entered a model step. QueueErrorTargetRunNotActive = "queue_target_run_not_active" )
Stable error codes recorded on rejected queue items. They are runtime vocabulary, not transport codes: the item stays readable through the queue API until compaction drops it.
Variables ¶
var ( // ErrSessionBusy means the session already has an active run and this is a // different invocation. SR-OWN-001 offers two ways to honour single-session // execution — durable queueing or a stable retryable busy answer — and this // runtime answers busy. It is an alias rather than a second sentinel so that // one errors.Is works whether the caller holds the ledger error or this one. // // The consequence is deliberate: the session runtime is not a general task // queue. Channel delivery and schedules already own a retry // mechanism, and reusing those is strictly better than teaching the runtime // to hold work for them, which would cost a queued state, a queue index, // promotion on every terminal transition, queued aborts, and a queue // projection on the client. ErrSessionBusy = ledger.ErrSessionBusy // ErrInvocationConflict means one invocation_id was submitted twice with // different canonical input. Per SR-ADM-001 that is a stable, identifiable // conflict rather than a new run: silently admitting it would give the // caller two runs for one retry. // // Both sentinels reach clients as apperror catalog codes — session_busy is // retryable, invocation_conflict is not. Domain code keeps the sentinel; // the HTTP boundary owns that mapping. ErrInvocationConflict = errors.New("runtime invocation conflicts with an earlier submission") // built without a ledger. Admission is durable-first by definition, so // there is no in-memory fallback to degrade to. ErrLedgerUnavailable = errors.New("session runtime ledger is not configured") // fence that claims depend on. Claiming without it would hand a run // ownership that PostgreSQL does not enforce, so the superseded owner would // keep writing history — the failure SR-OWN-002 exists to rule out. ErrFenceUnavailable = errors.New("session runtime persistence fence is not configured") // ErrSessionRuntimeSplit means the deployment declared more than one // instance but the live backend cannot arbitrate ownership between them. ErrSessionRuntimeSplit = errors.New("session runtime cluster mode requires a distributed backend") )
var ( ErrQueueSteerUnsupported = errors.New("queue: active run has no steer consumer") ErrQueueNoActiveRun = errors.New("queue: no active run") ErrQueueInvalidReference = errors.New("queue: invalid claim reference") ErrQueueNotPending = errors.New("queue: item is not an accepted pending item") ErrQueueInvocationConflict = errors.New("queue: invocation payload conflicts with an existing item") ErrQueueCapacityExceeded = errors.New("queue: pending capacity exceeded") ErrQueueAdmissionOverloaded = errors.New("queue: admission overloaded") )
var ( ErrCommandTargetNotActive = errors.New("runtime command target is not active") ErrCommandTargetMismatch = errors.New("run does not belong to this session") ErrCommandExpired = errors.New("runtime command expired before acknowledgement") ErrCommandBusy = errors.New("runtime command executor is busy") ErrCommandPayloadConflict = errors.New("runtime command payload conflicts with an earlier request") ErrDecisionNotFound = errors.New("runtime decision was not found") ErrManagerClosed = errors.New("session runtime manager is closed") ErrRunOwnershipLost = errors.New("runtime run ownership was lost") ErrHistoryResetInProgress = errors.New("session history reset is in progress") ErrHistoryResetLeaseLost = runtimefence.ErrResetLeaseLost )
var ErrDecisionOutputSequenceGap = errors.New("decision output sequence gap")
ErrDecisionOutputSequenceGap reports an append whose seq skips uncommitted entries. The producer treats it as a failed checkpoint write.
Functions ¶
func IsActiveRunStatus ¶ added in v0.20.0
IsActiveRunStatus reports whether a projected run status still occupies the session: accepted, running, or waiting for a decision.
func SteerRunAvailable ¶ added in v0.20.0
func SteerRunAvailable(run *CurrentRunView) bool
SteerRunAvailable reports whether the current executor accepts new step inputs.
Types ¶
type ActiveRunUpdate ¶
type Admission ¶
type Admission struct {
RunID string
// TurnID and TurnPosition are decided at admission so the eventual terminal
// write targets a turn that already exists on paper.
TurnID string
TurnPosition int64
State ledger.State
// Replay is true when this invocation was already admitted. The rest of the
// struct then describes the original run, whatever state it has reached.
Replay bool
// Started is true when this call took ownership and began execution. It is
// false for a replay of a run someone else owns and for a replay of a
// finished run.
Started bool
// Cursor is the position at which this run became observable. A caller that
// both starts runs and subscribes to the session uses it to place the run in
// the stream it is already reading, rather than guessing whether a snapshot
// it holds predates its own submission (SR-OBS-002).
//
// It is set only when Started. A replay reserved nothing, so this call
// produced no position; where the session stands now is a different fact,
// and the subscription's snapshot is the authoritative answer to it.
Cursor Cursor
// Handle names the run this process now owns, including the fencing token
// its durable writes must carry. It is meaningful only when Started.
Handle RunHandle
}
Admission is the durable answer to one submission. It is safe to return to a caller as confirmation to stop retrying: everything in it is committed before the call returns.
type AdmitInput ¶
type AdmitInput struct {
BotID string
SessionID string
// InvocationID is the caller's retry identity. Callers that cannot mint one
// must derive it deterministically from their source (for example a channel
// message id), because a fresh id per attempt turns a retry into a second
// run.
InvocationID string
// Payload is the canonically encoded submission. Its fingerprint decides
// whether a repeated invocation_id is the same submit or a conflict, so the
// encoding must be stable for equal inputs — same key order, no timestamps,
// no request ids.
Payload []byte
// Execution is the owner-side plumbing used only if this call wins the
// claim. A replay that finds the run already owned never touches it.
Execution Execution
}
AdmitInput is one submission from a public entry point: HTTP, a channel adapter or a schedule. Every caller supplies the same three identities, which is what lets one admission path serve all of them.
type Backend ¶
type Backend interface {
Now(ctx context.Context) (time.Time, error)
// Load returns a snapshot the caller owns and may freely mutate.
Load(ctx context.Context, key Key) (Snapshot, bool, error)
Update(ctx context.Context, key Key, update SnapshotUpdate) (Snapshot, bool, error)
Publish(ctx context.Context, event Event) error
Subscribe(ctx context.Context, key Key) (Subscription, error)
DecisionOutputStore
Close() error
}
type Command ¶
type Command struct {
Type string `json:"type"`
ID string `json:"id,omitempty"`
ReplyOwnerID string `json:"reply_owner_id,omitempty"`
BotID string `json:"bot_id"`
SessionID string `json:"session_id"`
RunID string `json:"run_id"`
Generation string `json:"generation"`
FencingToken int64 `json:"fencing_token,omitempty"`
TargetID string `json:"target_id,omitempty"`
// DecisionResolved means the command target was resolved from PostgreSQL
// before routing. Owner-side execution must not consult the live UI
// projection again: it is derived state and may lag the durable decision.
DecisionResolved bool `json:"decision_resolved,omitempty"`
Payload json.RawMessage `json:"payload,omitempty"`
PayloadHash string `json:"payload_hash,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
Error string `json:"error,omitempty"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
// StreamOutput is fixed at admission and travels to the owner with the command.
// It must not depend on subscriber liveness: disconnecting cannot change a run.
StreamOutput bool `json:"stream_output,omitempty"`
}
type CommandSubscription ¶
type CommandSubscription struct {
C <-chan Command
Close func()
}
type CurrentRunPatch ¶
type CurrentRunPatch struct {
RunID string `json:"run_id"`
Status *string `json:"status,omitempty"`
ErrorCode *string `json:"error_code,omitempty"`
Error *string `json:"error,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
OwnerLeaseExpiresAt *time.Time `json:"owner_lease_expires_at,omitempty"`
}
type CurrentRunView ¶
type CurrentRunView struct {
// ConfigurationOnly holds the session lock but does not generate a reply.
ConfigurationOnly bool `json:"configuration_only,omitempty"`
RunID string `json:"run_id" validate:"required" format:"uuid"`
// TurnID is the durable turn this run writes into, allocated at admission.
// It is part of the observable view because SR-OBS-003 requires every
// subscriber to agree on the run's turn, and a subscriber that only learns
// the run id cannot line the run up against persisted history.
TurnID string `json:"turn_id" validate:"required" format:"uuid"`
// TurnPosition is the immutable turn-level sequence admission drew for
// TurnID from bot_sessions.next_turn_position. It rides the live view for
// the same reason TurnID does: a subscriber that learns only the turn's name
// still cannot order it against persisted history, and SR-TURN-001 forbids
// falling back to timestamps to decide where a turn belongs.
TurnPosition int64 `json:"turn_position,omitempty"`
// InvocationID is the caller-supplied intent identity recorded at admission
// (session_runs.invocation_id). It rides the live view so the client that
// originated the send can match projection frames to its optimistic turn by
// reading the frame, instead of inferring the pairing from arrival timing
// while waiting for the acceptance that names the two. Subscribers that did
// not originate the run see an id unknown to them and treat the turn as
// foreign — which is the correct standalone rendering for cross-device runs.
InvocationID string `json:"invocation_id,omitempty"`
Generation string `json:"generation"`
Status string `json:"status"`
OwnerID string `json:"owner_id,omitempty"`
OwnerLeaseExpiresAt *time.Time `json:"owner_lease_expires_at,omitempty"`
StartedAt time.Time `json:"started_at"`
UpdatedAt time.Time `json:"updated_at"`
Messages []chatview.UIMessage `json:"messages"`
// UserTurns is the authoritative ordered set of user inputs already
// admitted into this run, including the original input and applied steers.
// The legacy request_user_turn is derived only at the JSON boundary.
UserTurns []chatview.UITurn `json:"user_turns,omitempty"`
// SteerSupported is published only by an installed step-boundary consumer.
// Missing on old owners and on runtimes without that execution capability.
SteerSupported bool `json:"steer_supported,omitempty"`
// The snapshot outlives the lease key and retains the exact persistence
// fence needed to reconcile a durable terminal after owner expiry.
FencingToken int64 `json:"fencing_token,omitempty"`
// SteerTurns locates live queue inputs inside the run's assistant message
// stream. Claimed entries are provisional runtime state; applied entries
// point at the history turn written by the application.
SteerTurns []SteerTurnView `json:"steer_turns,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
Error string `json:"error,omitempty"`
ProposedTerminalStatus string `json:"proposed_terminal_status,omitempty"`
FinishProposedAt *time.Time `json:"finish_proposed_at,omitempty"`
Operation *RunOperationView `json:"operation,omitempty"`
}
func (CurrentRunView) MarshalJSON ¶ added in v0.20.0
func (run CurrentRunView) MarshalJSON() ([]byte, error)
func (*CurrentRunView) UnmarshalJSON ¶ added in v0.20.0
func (run *CurrentRunView) UnmarshalJSON(data []byte) error
type Cursor ¶
Cursor is a position in one session's observable event stream. Epoch and Seq travel as a pair because Seq restarts whenever the epoch does: a live backend that loses its state hands the session a new epoch, so comparing sequence numbers across epochs would order two unrelated streams against each other.
This is the position subscribers dedupe and recover on (SR-OBS-002). It is a different thing from ledger.Cursor, which is a keyset position in the reaper's sweep over durable rows.
type DecisionFenceActivator ¶
type DecisionFenceActivator interface {
ReclaimWaitingDecision(
ctx context.Context,
botID, sessionID, runID, ownerID, liveGeneration string,
previousToken, newToken int64,
decisions []runtimefence.PreservedDecision,
) error
}
DecisionFenceActivator advances a parked run's persistence fence while preserving every pending decision that can still resume it — a turn may park on several approvals and user inputs at once. Implementations must update the decision rows to the new token in the same transaction that activates the session fence.
type DecisionOutputLimits ¶ added in v0.20.0
DecisionOutputLimits bounds one log. Exceeding them marks the log failed rather than silently truncating it; the producer reports the overflow.
type DecisionOutputPage ¶ added in v0.20.0
type DecisionOutputPage struct {
DecisionOutputState
Events []json.RawMessage
}
DecisionOutputPage is a read from a cursor to the current end of the log.
type DecisionOutputRef ¶ added in v0.20.0
DecisionOutputRef identifies the raw output log of one accepted decision command. Logs are keyed per command, not per session: one run can park on a second question without ending, and successive answers must not share a cursor.
func (DecisionOutputRef) String ¶ added in v0.20.0
func (r DecisionOutputRef) String() string
type DecisionOutputState ¶ added in v0.20.0
type DecisionOutputState struct {
Exists bool
Length int
Bytes int
Done bool
Failed bool
Claimed bool
// Applied reports whether this append changed the log. Replays of an
// already-committed seq and writes after a terminal marker are no-ops.
Applied bool
// Exceeded reports that this append tripped the limits and failed the log.
Exceeded bool
}
DecisionOutputState is the log's committed position after an append or read.
type DecisionOutputStore ¶ added in v0.20.0
type DecisionOutputStore interface {
AppendDecisionOutput(ctx context.Context, ref DecisionOutputRef, seq int64, payload json.RawMessage, limits DecisionOutputLimits) (DecisionOutputState, error)
ReadDecisionOutput(ctx context.Context, ref DecisionOutputRef, from int) (DecisionOutputPage, error)
ClaimDecisionOutput(ctx context.Context, ref DecisionOutputRef) (bool, error)
ReleaseDecisionOutput(ctx context.Context, ref DecisionOutputRef) error
}
DecisionOutputStore is an append-only raw event log with the same lifetime/TTL as live state. It is separate from Snapshot so session state keeps one meaning and each append writes one entry, not the whole log.
Append is idempotent by seq: seq must be Length+1 to apply; seq <= Length is a replay and returns the current state; a larger seq is a gap and an error. A nil payload closes the log (Done). Claim hands exclusive forwarding rights to one caller across processes. Release drops the stored entries once they are delivered but keeps the Done/Failed/Claimed markers until the TTL: a retry that arrives after delivery must still lose the claim, never replay the run's output to the channel a second time.
type DecisionResponse ¶
type DecisionResponse struct {
ControlID string
Type string
DecisionID string
BotID string
SessionID string
RunID string
Payload json.RawMessage
// contains filtered or unexported fields
}
DecisionResponse is one transport-neutral answer. ControlID is minted by the caller and remains the command identity even after the addressed run leaves live state.
type DecisionResponseResult ¶
type DecisionResponseResult struct {
SessionID string
Generation string
RunID string
Handled bool
Applied bool
// Replayed acknowledges an earlier submission without rerunning its output.
Replayed bool
}
DecisionResponseResult separates "this is a runtime decision" from "the answer changed it". A resolved terminal decision is handled but not applied; an unfenced ACP/MCP request is not handled and follows its existing path.
type DecisionStore ¶
type DecisionStore interface {
ResolveRuntimeDecision(ctx context.Context, commandType, decisionID string) (DecisionTarget, error)
PendingRuntimeDecisions(ctx context.Context, runID string) ([]DecisionTarget, error)
}
DecisionStore is implemented by the application layer over the PostgreSQL decision tables. RouteDecisionResponse uses ResolveRuntimeDecision for every transport; recovery uses PendingRuntimeDecisions to preserve every decision that parked a run while advancing its fencing token.
type DecisionTarget ¶
type DecisionTarget struct {
Type string
ID string
BotID string
SessionID string
RunID string
TurnID string
Status string
FencingToken int64
ControlID string
PayloadHash string
// SessionRuntime is the session's runtime type. Recovery needs it to
// tell a native parked run (resumable: the decision continuation is
// rebuilt from the database) from an inline waiter run (codex, claude,
// ACP), whose blocked turn died with its owner and cannot be resumed.
SessionRuntime string
}
DecisionTarget is the durable identity of one approval or user-input request. It is resolved from PostgreSQL and is the correctness boundary for decision routing; CurrentRunView.Messages is only a subscriber projection.
type DistributedBackend ¶
type DistributedBackend interface {
Backend
UpdateActiveRun(ctx context.Context, key Key, runID, generation string, update ActiveRunUpdate) (Snapshot, bool, error)
StartRun(ctx context.Context, key Key, ref RunRef, update SnapshotUpdate) (Snapshot, bool, error)
ReleaseRun(ctx context.Context, key Key, ref RunRef, update ActiveRunUpdate) (Snapshot, bool, error)
// ReconcileTerminalRun applies an authoritative durable terminal outcome to
// the matching live reservation even after its lease expired. The fencing
// token is mandatory so a stale reaper cannot release a successor.
ReconcileTerminalRun(ctx context.Context, key Key, ref RunRef, update ActiveRunUpdate) (Snapshot, bool, error)
RenewLease(ctx context.Context, key Key, runID, ownerID, generation string, renewedAt, expiresAt time.Time) error
ValidateRunOwnership(ctx context.Context, key Key, ref RunRef) error
LoadRunRef(ctx context.Context, key Key, runID string) (RunRef, bool, error)
DeleteRunRef(ctx context.Context, ref RunRef) (bool, error)
PublishCommand(ctx context.Context, ownerID string, command Command) error
SubscribeCommands(ctx context.Context, ownerID string) (CommandSubscription, error)
StoreCommandResult(ctx context.Context, result Command, ttl time.Duration) error
LoadCommandResult(ctx context.Context, commandID string) (Command, bool, error)
}
DistributedBackend adds cross-process run ownership and command routing. MemoryBackend intentionally does not implement this interface.
type Event ¶
type Event struct {
Type string `json:"type"`
BotID string `json:"bot_id"`
SessionID string `json:"session_id"`
Epoch string `json:"epoch,omitempty"`
RunID string `json:"run_id,omitempty"`
Seq int64 `json:"seq"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
Snapshot *Snapshot `json:"snapshot,omitempty"`
Delta *RuntimeDelta `json:"delta,omitempty"`
Message string `json:"message,omitempty"`
}
type Execution ¶
type Execution struct {
// ConfigurationOnly keeps the execution lock without presenting an assistant turn.
ConfigurationOnly bool
// Admission persists the run's user turn and any replacement operation, and
// returns the view subscribers should see when the run leaves admitting. It
// runs after ownership is established, so its writes can be fenced with
// RunHandle.FencingToken.
Admission func(ctx context.Context, handle RunHandle) (RunAdmissionView, error)
// AbortCh and Cancel are how a routed abort reaches this run. A run without
// them is durably abortable but cannot be interrupted mid-turn.
AbortCh chan<- struct{}
Cancel context.CancelFunc
// InjectCh receives steering messages; nil means this caller does not
// support steering. The caller owns channel closure; the runtime only stops
// sending during teardown.
InjectCh chan<- turn.InjectMessage
// OwnershipCancel revokes the caller's execution context when the lease is
// lost, so a superseded owner stops producing output rather than racing the
// fence it can no longer pass.
OwnershipCancel context.CancelCauseFunc
}
Execution is what the winner of a claim needs in order to actually run the turn. It is part of the admit call rather than a second step because the two cannot be separated safely: live state reserved without an executor is a run holding a session's only slot with nothing driving it to a terminal state.
type FenceActivator ¶
type FenceActivator interface {
Activate(ctx context.Context, botID, sessionID string, token int64) error
}
FenceActivator hands durable persistence ownership to one run's fencing token. It is the second half of a claim: the ledger decides who owns the run, and this decides whose writes PostgreSQL will still accept (SR-OWN-002).
The token is the same one the claim wrote to session_runs, which is the whole point of drawing it from a shared monotonic sequence. Two independent orderings — one for run ownership, one for persistence — could disagree, and the disagreement would surface as a superseded owner still writing history.
It is expressed over (bot, session, token) rather than over a store handle so the runtime never learns that PostgreSQL exists; runtimefence.NewActivator satisfies it.
type FollowUpClaimRef ¶ added in v0.20.0
type FollowUpClaimRef struct {
ItemID FollowUpItemID
TriggerRunID string
ClaimToken string
}
type FollowUpItem ¶ added in v0.20.0
type FollowUpItem struct {
ID FollowUpItemID
BotID, SessionID string
EnqueuedDuringRunID string
InvocationID string
Payload []byte
Status QueueStatus
Position int64
Claim *FollowUpClaimRef
// ErrorCode is set when Status is rejected.
ErrorCode string
CreatedAt time.Time
}
type FollowUpItemID ¶ added in v0.20.0
type FollowUpItemID string
type FollowUpPendingRef ¶ added in v0.20.0
type FollowUpPendingRef struct {
ItemID FollowUpItemID `json:"item_id"`
}
type HistoryResetBackend ¶
type HistoryResetBackend interface {
AcquireHistoryReset(ctx context.Context, scope ResetScope, token string, ttl time.Duration) (ResetLease, bool, error)
RenewHistoryReset(ctx context.Context, lease ResetLease, ttl time.Duration) (ResetLease, bool, error)
ReleaseHistoryReset(ctx context.Context, lease ResetLease) (bool, error)
EffectiveHistoryReset(ctx context.Context, scope ResetScope) (ResetLease, bool, error)
}
HistoryResetBackend provides a tokenized, expiring live gate. Redis uses key TTLs; MemoryBackend uses the same contract under its process mutex.
type HistoryResetHandler ¶
type HistoryResetHandler func(context.Context, ResetScope) error
HistoryResetHandler performs owner-local runtime teardown. Returning is the acknowledgement boundary: the ACP pool must not return until Session.Close and the owned process Close operation have completed.
type LeaseCandidate ¶
type LeaseCandidate struct {
Key Key
RunID string
FencingToken int64
// ExpiresAt is the deadline recorded in the index, sampled on the backend's
// clock rather than the reaper's.
ExpiresAt time.Time
}
LeaseCandidate is one entry of the live backend's lease index whose deadline has passed. It carries the fencing token that was current when the lease was written, which is what lets the reaper apply a fenced transition without trusting anything else about the vanished owner.
type LiveQueueBackend ¶ added in v0.20.0
type LiveQueueBackend interface {
EnqueueSteer(context.Context, Key, string, string, []byte) (SteerItem, error)
EnqueueFollowUp(context.Context, Key, string, string, []byte) (FollowUpItem, error)
PendingQueues(context.Context, Key, int) ([]SteerItem, []FollowUpItem, error)
ReorderSteer(context.Context, Key, SteerPendingRef, SteerPendingRef) ([]SteerItem, error)
ReorderFollowUp(context.Context, Key, FollowUpPendingRef, FollowUpPendingRef) ([]FollowUpItem, error)
UpdateSteer(context.Context, Key, SteerItemID, []byte) (SteerItem, error)
UpdateFollowUp(context.Context, Key, FollowUpItemID, []byte) (FollowUpItem, error)
CancelSteer(context.Context, Key, SteerItemID) error
CancelFollowUp(context.Context, Key, FollowUpItemID) error
PromoteFollowUpToSteer(context.Context, Key, FollowUpPendingRef) (PromoteFollowUpResult, error)
ClaimNextSteer(context.Context, RunHandle, bool) (SteerItem, SteerClaimRef, bool, error)
ApplySteer(context.Context, Key, SteerClaimRef) error
ReleaseSteer(context.Context, Key, SteerClaimRef) error
// CloseSteerRun rejects every accepted or claimed steer that targets the
// given run and seals the run against later steer admission. It is called
// once the run is durably terminal; a steer is bound to its run and has no
// meaning for any later run of the session.
CloseSteerRun(context.Context, Key, string) error
ClaimNextFollowUp(context.Context, Key, string) (FollowUpItem, FollowUpClaimRef, bool, error)
ApplyFollowUp(context.Context, Key, FollowUpClaimRef) error
ReleaseFollowUp(context.Context, Key, FollowUpClaimRef) error
}
LiveQueueBackend is transient session coordination. Implementations must serialize each operation with the live run state for the same session.
type LivenessBackend ¶
type LivenessBackend interface {
// LivenessGeneration identifies this incarnation of the live backend. It is
// created once and read by everyone, so a value different from the one
// stamped on a run means the backend that claimed that run is gone and its
// in-flight state cannot be recovered.
LivenessGeneration(ctx context.Context) (string, error)
// ExpiredLeaseCandidates returns up to limit entries whose lease deadline
// has passed. The backend samples its own clock: a reaper with a skewed
// clock must not be able to declare a healthy owner dead. Candidates are
// returned, not claimed — the leader is single, so a visibility timeout
// would add a second failure mode without removing one.
ExpiredLeaseCandidates(ctx context.Context, limit int64) ([]LeaseCandidate, error)
// ReleaseLeaseCandidate removes an index entry, but only while the entry
// still carries the token the candidate was read with. A run reclaimed
// between read and release keeps its new entry.
ReleaseLeaseCandidate(ctx context.Context, candidate LeaseCandidate) (bool, error)
// AcquireLeaderLease elects the single cluster-wide reaper. Renewal is the
// same call: an existing holder re-acquires its own lease. Failover is safe
// because every reaper duty is an idempotent fenced transition, so a new
// leader repeating work the old one had started changes nothing.
AcquireLeaderLease(ctx context.Context, ownerID string, ttl time.Duration) (bool, error)
// ReleaseLeaderLease hands leadership back on graceful shutdown so a peer
// takes over in one tick instead of one lease period.
ReleaseLeaderLease(ctx context.Context, ownerID string) error
}
LivenessBackend is the half of the runtime backend that answers "is an owner still alive". It is separate from Backend and DistributedBackend because liveness is the one thing PostgreSQL deliberately does not know: the ledger records ownership changes, never lease ticks, so only this interface can tell the reaper which runs to give up on.
Both backends implement it. On a memory backend there is a single process and therefore no lease to expire, but there is still an incarnation to compare against and orphans to repair, which is exactly how a single-instance install recovers after a crash.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
func NewManager ¶
func NewManagerFromConfig ¶
func NewManagerFromConfig(log *slog.Logger, cfg config.SessionRuntimeConfig, runs ledger.Store, fence FenceActivator) (*Manager, error)
NewManagerFromConfig is the single construction site where the two configured values turn into the full set of derived timings. runs is the durable ledger and fence is the persistence-ownership cutover that claims apply; they go together, and both may be nil only in tests that never admit a run.
func (*Manager) AbortControl ¶
func (m *Manager) AbortControl(ctx context.Context, botID, sessionID, runID, controlID string) (bool, error)
AbortControl aborts a run on behalf of a client that named the request with its own control id. Two things follow from that id and neither is available to plain Abort:
The abort becomes idempotent across instances. A client that retries — or that reconnects to a different server and retries there — gets the answer the first attempt produced rather than a second execution, because the control id keys the shared command result. Without it a retry after the run terminalized would report "not active" and contradict the ack the client already holds.
The intent is recorded durably before anything is routed, so a run that is aborted survives as aborted-on-purpose rather than as an unexplained cancellation, even if the owner never answers (SR-CTL-001).
func (*Manager) Admit ¶
Admit is the one durable entry point for starting a session run. It commits the admission before returning, so a caller that receives an Admission can stop retrying even if this server dies immediately afterwards (SR-ADM-001, SR-DUR-001).
The order is deliberate and not reorderable: persist, then claim, then execute. Reserving live state first would create a run that exists only in a process, which is the failure the ledger exists to prevent.
A caller that gets Started sees a run that is owned, fenced and reserved, and receives the handle its own durable writes must be fenced with. A caller that gets an Admission without Started is looking at a run someone else owns or one that already finished, and must not execute anything.
A session runs one turn at a time, so a different invocation arriving while one is active gets ErrSessionBusy and is expected to retry (SR-OWN-001). The runtime holds nothing on the caller's behalf.
func (*Manager) ApplyFollowUp ¶ added in v0.20.0
func (*Manager) ApplySteer ¶ added in v0.20.0
func (*Manager) BeginBotHistoryReset ¶
func (*Manager) BeginSessionHistoryReset ¶
func (*Manager) CancelFollowUp ¶ added in v0.20.0
func (*Manager) CancelSteer ¶ added in v0.20.0
func (*Manager) ClaimNextFollowUp ¶ added in v0.20.0
func (m *Manager) ClaimNextFollowUp(ctx context.Context, key Key, triggerRunID string) (FollowUpItem, FollowUpClaimRef, bool, error)
func (*Manager) ClaimNextSteer ¶ added in v0.20.0
func (*Manager) CloseSteerRun ¶ added in v0.20.0
func (*Manager) ContinuationStepIndex ¶ added in v0.20.0
ContinuationStepIndex resumes the owner-local step cursor after a parked decision. A recovered owner starts a new generation/cursor; it is not a durable replay offset and must never be inferred from timestamps or messages.
func (*Manager) DecisionContinuationContext ¶
func (m *Manager) DecisionContinuationContext(cmd Command) (context.Context, context.CancelFunc, RunHandle, error)
DecisionContinuationContext detaches the model continuation from the short command acknowledgement deadline while keeping it tied to the run owner's lifecycle and persistence fence.
func (*Manager) DisableSteer ¶ added in v0.20.0
func (*Manager) EnableSteer ¶ added in v0.20.0
EnableSteer advertises an actual execution consumer, not just an allocated channel. Other instances therefore reject queues aimed at old/unsupported owners.
func (*Manager) EnqueueFollowUp ¶ added in v0.20.0
func (*Manager) EnqueueSteer ¶ added in v0.20.0
func (*Manager) FinishRunWithErrorCode ¶
func (m *Manager) FinishRunWithErrorCode(ctx context.Context, handle RunHandle, status, errorCode string) error
FinishRunWithErrorCode records a stable public failure code without treating that code as a display message or persisting private provider diagnostics.
func (*Manager) HandleAgentEvent ¶
func (*Manager) HandleAgentEventWithStatus ¶
func (m *Manager) HandleAgentEventWithStatus(ctx context.Context, handle RunHandle, event native.StreamEvent) ([]chatview.UIMessage, string, error)
HandleAgentEventWithStatus applies an agent event and returns the run status from that same serialized mutation. Terminal callers use this instead of a second snapshot read so a routed control and terminal event have one winner even when a later backend read would fail.
func (*Manager) IsDistributed ¶
IsDistributed reports whether this manager coordinates owners through a cross-process backend. Memory managers intentionally return false.
func (*Manager) LivenessGeneration ¶ added in v0.20.0
LivenessGeneration returns the current live-backend incarnation for application-owned recovery code. It is read-only; ownership still changes only through the durable fenced claim.
func (*Manager) MarkInlineDecisionRun ¶ added in v0.20.0
MarkInlineDecisionRun declares, before the runtime starts prompting, that this run's runtime blocks inline on decisions: terminal decision statuses resume the run directly. Runs without the declaration keep the native park semantics (only EventAgentStart resumes), so a decision answered faster than the parking FinishRun cannot resume — and then complete — the run underneath the native re-entry. Scope-keyed (run IDs are unique); the caller runs before any decision event, so no generation check is needed.
func (*Manager) OwnerID ¶ added in v0.20.0
OwnerID returns this manager's stable execution-owner identity.
func (*Manager) PendingQueues ¶ added in v0.20.0
func (*Manager) PromoteFollowUpToSteer ¶ added in v0.20.0
func (m *Manager) PromoteFollowUpToSteer(ctx context.Context, key Key, ref FollowUpPendingRef) (PromoteFollowUpResult, error)
func (*Manager) PublishDecisionOutput ¶ added in v0.20.0
func (m *Manager) PublishDecisionOutput(ctx context.Context, command Command, seq int64, payload json.RawMessage) error
PublishDecisionOutput appends one raw event (or closes the log with a nil payload) and then wakes readers. Commit before notify, exactly as runtime UI deltas do; a lost wakeup costs latency, never data.
func (*Manager) PublishQueueUserTurns ¶ added in v0.20.0
func (m *Manager) PublishQueueUserTurns(ctx context.Context, handle RunHandle, update QueueUserTurnUpdate) error
PublishQueueUserTurns projects one committed queue step into the live run. The update is atomic so applying one steer and claiming the next cannot briefly render them out of order. A claimed steer is shown only after the coordinator committed its claim and execution accepted the resulting input.
func (*Manager) ReleaseFollowUp ¶ added in v0.20.0
func (*Manager) ReleaseSteer ¶ added in v0.20.0
func (*Manager) ReorderFollowUp ¶ added in v0.20.0
func (m *Manager) ReorderFollowUp(ctx context.Context, key Key, item, before FollowUpPendingRef) ([]FollowUpItem, error)
func (*Manager) ReorderSteer ¶ added in v0.20.0
func (*Manager) RouteDecisionResponse ¶
func (m *Manager) RouteDecisionResponse(ctx context.Context, response DecisionResponse) (DecisionResponseResult, error)
RouteDecisionResponse is the single decision entry point for WebSocket, HTTP, and gRPC callers. PostgreSQL resolves the decision before live ownership is consulted, while the client control id is checked first so a successful command remains replayable after the run terminalizes.
func (*Manager) SetAdmissionObserver ¶ added in v0.20.0
SetAdmissionObserver installs the lightweight activity invalidation sink. The composition root owns its transport; Runtime does not depend on the bot activity hub or carry conversation contents through that channel.
func (*Manager) SetCommandHandler ¶
SetCommandHandler installs the owner-local executor for routed runtime commands whose domain behavior lives outside the sessionruntime package.
func (*Manager) SetDecisionFinalizer ¶ added in v0.20.0
SetDecisionFinalizer closes durable decisions before a run becomes terminal. A failure retains ownership so the normal finish retry can complete cleanup.
func (*Manager) SetDecisionStore ¶
func (m *Manager) SetDecisionStore(store DecisionStore)
SetDecisionStore installs the PostgreSQL-backed decision authority used by every response transport and by waiting-decision recovery.
func (*Manager) SetHistoryResetHandler ¶
func (m *Manager) SetHistoryResetHandler(handler HistoryResetHandler)
SetHistoryResetHandler installs the owner-local operation-boundary closer. SessionPool supplies it without sessionruntime depending on the ACP package.
func (*Manager) SetLostRunDecisionCanceller ¶ added in v0.20.0
func (m *Manager) SetLostRunDecisionCanceller(canceller func(context.Context, string, string, string, int64, string) error)
SetLostRunDecisionCanceller installs run-scoped cleanup for decisions parked by a run that the reaper has durably marked lost.
func (*Manager) SetTerminalObserver ¶
func (m *Manager) SetTerminalObserver(observer func(context.Context, TerminalRun))
SetTerminalObserver installs the application-owned sink for authoritative durable run outcomes. The callback is invoked synchronously without holding the manager lock, and may be called more than once for the same run when a fenced terminal transition is replayed.
func (*Manager) SetTerminalReconciler ¶
SetTerminalReconciler installs a bounded application-owned repair pass for terminal runs whose observation was interrupted after the ledger commit. The elected reaper invokes it once per tick after its own terminal duties.
func (*Manager) Snapshot ¶
Snapshot returns the session's authoritative runtime view. The live backend answers first because it is the only place a run in flight exists; the ledger fallback below covers the case where it cannot answer at all.
func (*Manager) SteerWake ¶ added in v0.20.0
SteerWake is an owner-local, coalesced notification. The queue remains the source of truth; neither duplicate notifications nor a stale wake apply input.
func (*Manager) StreamDecisionResponse ¶ added in v0.20.0
func (m *Manager) StreamDecisionResponse(ctx context.Context, response DecisionResponse, output chan<- json.RawMessage) (DecisionResponseResult, error)
func (*Manager) UpdateFollowUp ¶ added in v0.20.0
func (m *Manager) UpdateFollowUp(ctx context.Context, key Key, itemID FollowUpItemID, payload []byte) (FollowUpItem, error)
func (*Manager) UpdateSteer ¶ added in v0.20.0
func (*Manager) ValidateRunOwnership ¶
ValidateRunOwnership fails closed before durable side effects when this process no longer owns the active runtime run.
func (*Manager) WaitDecisionContinuationReady ¶
WaitDecisionContinuationReady holds the resumed model call until the stream that produced the deferred decision has finished its terminal persistence. The decision itself is already committed and acknowledged; this barrier only prevents its tool result from racing the assistant tool-call write.
type MemoryBackend ¶
type MemoryBackend struct {
// contains filtered or unexported fields
}
func NewMemoryBackend ¶
func NewMemoryBackend() *MemoryBackend
func NewMemoryBackendWithTTL ¶
func NewMemoryBackendWithTTL(stateTTL time.Duration) *MemoryBackend
func (*MemoryBackend) AcquireHistoryReset ¶
func (b *MemoryBackend) AcquireHistoryReset(ctx context.Context, scope ResetScope, token string, ttl time.Duration) (ResetLease, bool, error)
func (*MemoryBackend) AcquireLeaderLease ¶
AcquireLeaderLease always succeeds: a single process is trivially the leader.
func (*MemoryBackend) AppendDecisionOutput ¶ added in v0.20.0
func (b *MemoryBackend) AppendDecisionOutput(ctx context.Context, ref DecisionOutputRef, seq int64, payload json.RawMessage, limits DecisionOutputLimits) (DecisionOutputState, error)
func (*MemoryBackend) ApplyFollowUp ¶ added in v0.20.0
func (b *MemoryBackend) ApplyFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
func (*MemoryBackend) ApplySteer ¶ added in v0.20.0
func (b *MemoryBackend) ApplySteer(ctx context.Context, key Key, ref SteerClaimRef) error
func (*MemoryBackend) CancelFollowUp ¶ added in v0.20.0
func (b *MemoryBackend) CancelFollowUp(ctx context.Context, key Key, itemID FollowUpItemID) error
func (*MemoryBackend) CancelSteer ¶ added in v0.20.0
func (b *MemoryBackend) CancelSteer(ctx context.Context, key Key, itemID SteerItemID) error
func (*MemoryBackend) ClaimDecisionOutput ¶ added in v0.20.0
func (b *MemoryBackend) ClaimDecisionOutput(ctx context.Context, ref DecisionOutputRef) (bool, error)
func (*MemoryBackend) ClaimNextFollowUp ¶ added in v0.20.0
func (b *MemoryBackend) ClaimNextFollowUp(ctx context.Context, key Key, triggerRunID string) (FollowUpItem, FollowUpClaimRef, bool, error)
func (*MemoryBackend) ClaimNextSteer ¶ added in v0.20.0
func (b *MemoryBackend) ClaimNextSteer(ctx context.Context, handle RunHandle, sealIfEmpty bool) (SteerItem, SteerClaimRef, bool, error)
func (*MemoryBackend) Close ¶
func (b *MemoryBackend) Close() error
func (*MemoryBackend) CloseSteerRun ¶ added in v0.20.0
func (*MemoryBackend) EffectiveHistoryReset ¶
func (b *MemoryBackend) EffectiveHistoryReset(ctx context.Context, scope ResetScope) (ResetLease, bool, error)
func (*MemoryBackend) EnqueueFollowUp ¶ added in v0.20.0
func (b *MemoryBackend) EnqueueFollowUp(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (FollowUpItem, error)
func (*MemoryBackend) EnqueueSteer ¶ added in v0.20.0
func (*MemoryBackend) ExpiredLeaseCandidates ¶
func (*MemoryBackend) ExpiredLeaseCandidates(context.Context, int64) ([]LeaseCandidate, error)
ExpiredLeaseCandidates is always empty for a memory backend: one process owns every run it admitted, so a lease would only ever expire together with the process that was watching it.
func (*MemoryBackend) LivenessGeneration ¶
func (b *MemoryBackend) LivenessGeneration(context.Context) (string, error)
LivenessGeneration returns this process's incarnation. A memory backend keeps live state in the heap, so its incarnation ends with the process: after a restart every run the ledger still shows as active belongs to a generation that no longer exists, which is precisely the signal the recovery sweep needs.
func (*MemoryBackend) PendingQueues ¶ added in v0.20.0
func (b *MemoryBackend) PendingQueues(ctx context.Context, key Key, limit int) ([]SteerItem, []FollowUpItem, error)
func (*MemoryBackend) PromoteFollowUpToSteer ¶ added in v0.20.0
func (b *MemoryBackend) PromoteFollowUpToSteer(ctx context.Context, key Key, ref FollowUpPendingRef) (PromoteFollowUpResult, error)
func (*MemoryBackend) Publish ¶
func (b *MemoryBackend) Publish(ctx context.Context, event Event) error
func (*MemoryBackend) ReadDecisionOutput ¶ added in v0.20.0
func (b *MemoryBackend) ReadDecisionOutput(ctx context.Context, ref DecisionOutputRef, from int) (DecisionOutputPage, error)
func (*MemoryBackend) ReleaseDecisionOutput ¶ added in v0.20.0
func (b *MemoryBackend) ReleaseDecisionOutput(ctx context.Context, ref DecisionOutputRef) error
func (*MemoryBackend) ReleaseFollowUp ¶ added in v0.20.0
func (b *MemoryBackend) ReleaseFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
func (*MemoryBackend) ReleaseHistoryReset ¶
func (b *MemoryBackend) ReleaseHistoryReset(ctx context.Context, lease ResetLease) (bool, error)
func (*MemoryBackend) ReleaseLeaderLease ¶
func (*MemoryBackend) ReleaseLeaderLease(context.Context, string) error
func (*MemoryBackend) ReleaseLeaseCandidate ¶
func (*MemoryBackend) ReleaseLeaseCandidate(context.Context, LeaseCandidate) (bool, error)
func (*MemoryBackend) ReleaseSteer ¶ added in v0.20.0
func (b *MemoryBackend) ReleaseSteer(ctx context.Context, key Key, ref SteerClaimRef) error
func (*MemoryBackend) RenewHistoryReset ¶
func (b *MemoryBackend) RenewHistoryReset(ctx context.Context, lease ResetLease, ttl time.Duration) (ResetLease, bool, error)
func (*MemoryBackend) ReorderFollowUp ¶ added in v0.20.0
func (b *MemoryBackend) ReorderFollowUp(ctx context.Context, key Key, item, before FollowUpPendingRef) ([]FollowUpItem, error)
func (*MemoryBackend) ReorderSteer ¶ added in v0.20.0
func (b *MemoryBackend) ReorderSteer(ctx context.Context, key Key, item, before SteerPendingRef) ([]SteerItem, error)
func (*MemoryBackend) StartRunIfNoHistoryReset ¶
func (b *MemoryBackend) StartRunIfNoHistoryReset(ctx context.Context, key Key, update SnapshotUpdate) (Snapshot, bool, error)
func (*MemoryBackend) Subscribe ¶
func (b *MemoryBackend) Subscribe(ctx context.Context, key Key) (Subscription, error)
func (*MemoryBackend) Update ¶
func (b *MemoryBackend) Update(ctx context.Context, key Key, update SnapshotUpdate) (Snapshot, bool, error)
func (*MemoryBackend) UpdateFollowUp ¶ added in v0.20.0
func (b *MemoryBackend) UpdateFollowUp(ctx context.Context, key Key, itemID FollowUpItemID, payload []byte) (FollowUpItem, error)
func (*MemoryBackend) UpdateSteer ¶ added in v0.20.0
func (b *MemoryBackend) UpdateSteer(ctx context.Context, key Key, itemID SteerItemID, payload []byte) (SteerItem, error)
type Options ¶
type Options struct {
OwnerID string
StateTTL time.Duration
OwnerLeaseTTL time.Duration
// BackendLossGrace comes from config because a Redis restart budget depends
// on the deployment. Everything else below OwnerLeaseTTL is derived.
BackendLossGrace time.Duration
// Cluster mirrors session_runtime.cluster.
Cluster bool
// Ledger is the durable session run store.
Ledger ledger.Store
// Fence applies the persistence-ownership cutover for a claimed run. It is
// required together with Ledger.
Fence FenceActivator
CommandAckTTL time.Duration
CommandWorkerLimit int
Logger *slog.Logger
EpochGenerator func() string
RunGenerationGenerator func() string
// ScanBatchSize and MaxScanBatchesPerTick exist so tests can watch recovery
// page rather than to be tuned in production; both default to package
// constants.
ScanBatchSize int32
MaxScanBatchesPerTick int
// contains filtered or unexported fields
}
type PromoteFollowUpResult ¶ added in v0.20.0
type PromoteFollowUpResult struct {
FollowUp FollowUpPendingRef
Steer SteerItem
}
type QueueStatus ¶ added in v0.20.0
type QueueStatus string
const ( QueueAccepted QueueStatus = "accepted" QueueClaimed QueueStatus = "claimed" QueueApplied QueueStatus = "applied" QueueRejected QueueStatus = "rejected" QueueExpired QueueStatus = "expired" QueueCanceled QueueStatus = "canceled" )
type QueueUserTurnUpdate ¶ added in v0.20.0
type QueueUserTurnUpdate struct {
PersistedTurns []chatview.UITurn
AppliedSteerItemID string
AppliedSteerTurn *chatview.UITurn
ClaimedSteerItemID string
ClaimedSteerText string
ClaimedSteerTimestamp time.Time
// ClaimedSteerTurn* is the turn slot drawn for this input at claim time. It
// is the same identity the step commit files the user row under, so the live
// bubble and the settled one are one turn from the start.
ClaimedSteerTurnID string
ClaimedSteerTurnPosition int64
// AfterStepIndex, when set, is the durable step whose output must already
// be in the live projection before a claimed steer is anchored. The commit
// barrier runs on the model loop while agent events are consumed on
// another goroutine, so without this wait the anchor could be computed
// from a projection still missing the tail of the step, and the steer
// would render above output that preceded it.
AfterStepIndex *int
}
type Reaper ¶
type Reaper struct {
// contains filtered or unexported fields
}
Reaper is the single cluster-wide janitor for the session run ledger. Exactly one instance acts at a time, elected through a TTL lease in the live backend.
One leader rather than many workers is a deliberate simplification. The three duties below are all idempotent fenced transitions, so the only thing extra reapers would add is duplicate PostgreSQL traffic and a second way for a candidate to be consumed and then dropped by a crash. Failover costs at most one lease period and repeats at most one transition, which changes nothing.
The reaper is the only component that can decide a run is unrecoverable, because it is the only one that reads liveness and durable state together.
func NewReaper ¶
func NewReaper(runs ledger.Store, liveness LivenessBackend, tune tuning, ownerID string, logger *slog.Logger) *Reaper
NewReaper builds a reaper for one manager. It shares the manager's derived tuning so the leader lease and the tick that renews it cannot drift apart.
func (*Reaper) Close ¶
Close stops ticking and hands leadership back so a peer takes over in one tick instead of waiting out the lease.
func (*Reaper) SetLostRunDecisionCanceller ¶ added in v0.20.0
func (r *Reaper) SetLostRunDecisionCanceller(canceller func(context.Context, string, string, string, int64, string) error)
SetLostRunDecisionCanceller installs the application-owned cleanup for decisions created by a run that is durably marked lost. It is deliberately run-scoped: canceling a whole session could expire a newer run's prompt.
func (*Reaper) SetTerminalObserver ¶
func (r *Reaper) SetTerminalObserver(observer func(context.Context, TerminalRun))
SetTerminalObserver installs the sink for authoritative durable outcomes.
func (*Reaper) SetTerminalReconciler ¶
SetTerminalReconciler installs the bounded repair pass run by the elected reaper after its ordinary ledger duties.
func (*Reaper) SetWaitingDecisionRecoverer ¶
func (r *Reaper) SetWaitingDecisionRecoverer(recoverer func(context.Context, LeaseCandidate) (bool, error))
SetWaitingDecisionRecoverer installs the owner-local half of parked-run recovery. It is set before Start, so the reaper never observes a partially configured callback.
type RedisBackend ¶
type RedisBackend struct {
// contains filtered or unexported fields
}
func NewRedisBackend ¶
func NewRedisBackend(ctx context.Context, opts RedisOptions) (*RedisBackend, error)
func (*RedisBackend) AcquireHistoryReset ¶
func (b *RedisBackend) AcquireHistoryReset(ctx context.Context, scope ResetScope, token string, ttl time.Duration) (ResetLease, bool, error)
func (*RedisBackend) AcquireLeaderLease ¶
func (*RedisBackend) AppendDecisionOutput ¶ added in v0.20.0
func (b *RedisBackend) AppendDecisionOutput(ctx context.Context, ref DecisionOutputRef, seq int64, payload json.RawMessage, limits DecisionOutputLimits) (DecisionOutputState, error)
func (*RedisBackend) ApplyFollowUp ¶ added in v0.20.0
func (b *RedisBackend) ApplyFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
func (*RedisBackend) ApplySteer ¶ added in v0.20.0
func (b *RedisBackend) ApplySteer(ctx context.Context, key Key, ref SteerClaimRef) error
func (*RedisBackend) CancelFollowUp ¶ added in v0.20.0
func (b *RedisBackend) CancelFollowUp(ctx context.Context, key Key, itemID FollowUpItemID) error
func (*RedisBackend) CancelSteer ¶ added in v0.20.0
func (b *RedisBackend) CancelSteer(ctx context.Context, key Key, itemID SteerItemID) error
func (*RedisBackend) CheckHealth ¶
func (b *RedisBackend) CheckHealth(ctx context.Context) error
func (*RedisBackend) ClaimDecisionOutput ¶ added in v0.20.0
func (b *RedisBackend) ClaimDecisionOutput(ctx context.Context, ref DecisionOutputRef) (bool, error)
func (*RedisBackend) ClaimNextFollowUp ¶ added in v0.20.0
func (b *RedisBackend) ClaimNextFollowUp(ctx context.Context, key Key, triggerRunID string) (FollowUpItem, FollowUpClaimRef, bool, error)
func (*RedisBackend) ClaimNextSteer ¶ added in v0.20.0
func (b *RedisBackend) ClaimNextSteer(ctx context.Context, handle RunHandle, sealIfEmpty bool) (SteerItem, SteerClaimRef, bool, error)
func (*RedisBackend) Close ¶
func (b *RedisBackend) Close() error
func (*RedisBackend) CloseSteerRun ¶ added in v0.20.0
func (*RedisBackend) DeleteRunRef ¶
func (*RedisBackend) EffectiveHistoryReset ¶
func (b *RedisBackend) EffectiveHistoryReset(ctx context.Context, scope ResetScope) (ResetLease, bool, error)
func (*RedisBackend) EnqueueFollowUp ¶ added in v0.20.0
func (b *RedisBackend) EnqueueFollowUp(ctx context.Context, key Key, itemID, invocationID string, payload []byte) (FollowUpItem, error)
func (*RedisBackend) EnqueueSteer ¶ added in v0.20.0
func (*RedisBackend) ExpiredLeaseCandidates ¶
func (b *RedisBackend) ExpiredLeaseCandidates(ctx context.Context, limit int64) ([]LeaseCandidate, error)
func (*RedisBackend) LivenessGeneration ¶
func (b *RedisBackend) LivenessGeneration(ctx context.Context) (string, error)
LivenessGeneration reads the shared incarnation marker, creating it with SET NX when absent so concurrently starting processes converge on one value instead of each minting its own. The marker lives and dies with the Redis dataset: a flushed or non-persistent Redis comes back without it, the next SET NX writes a new value, and every run stamped with the old one is unrecoverable by definition.
func (*RedisBackend) LoadCommandResult ¶
func (*RedisBackend) LoadRunRef ¶
func (*RedisBackend) PendingQueues ¶ added in v0.20.0
func (b *RedisBackend) PendingQueues(ctx context.Context, key Key, limit int) ([]SteerItem, []FollowUpItem, error)
func (*RedisBackend) PromoteFollowUpToSteer ¶ added in v0.20.0
func (b *RedisBackend) PromoteFollowUpToSteer(ctx context.Context, key Key, ref FollowUpPendingRef) (PromoteFollowUpResult, error)
func (*RedisBackend) Publish ¶
func (b *RedisBackend) Publish(ctx context.Context, event Event) error
func (*RedisBackend) PublishCommand ¶
func (*RedisBackend) ReadDecisionOutput ¶ added in v0.20.0
func (b *RedisBackend) ReadDecisionOutput(ctx context.Context, ref DecisionOutputRef, from int) (DecisionOutputPage, error)
func (*RedisBackend) ReconcileTerminalRun ¶ added in v0.20.0
func (b *RedisBackend) ReconcileTerminalRun(ctx context.Context, key Key, ref RunRef, update ActiveRunUpdate) (Snapshot, bool, error)
func (*RedisBackend) ReleaseDecisionOutput ¶ added in v0.20.0
func (b *RedisBackend) ReleaseDecisionOutput(ctx context.Context, ref DecisionOutputRef) error
func (*RedisBackend) ReleaseFollowUp ¶ added in v0.20.0
func (b *RedisBackend) ReleaseFollowUp(ctx context.Context, key Key, ref FollowUpClaimRef) error
func (*RedisBackend) ReleaseHistoryReset ¶
func (b *RedisBackend) ReleaseHistoryReset(ctx context.Context, lease ResetLease) (bool, error)
func (*RedisBackend) ReleaseLeaderLease ¶
func (b *RedisBackend) ReleaseLeaderLease(ctx context.Context, ownerID string) error
func (*RedisBackend) ReleaseLeaseCandidate ¶
func (b *RedisBackend) ReleaseLeaseCandidate(ctx context.Context, candidate LeaseCandidate) (bool, error)
ReleaseLeaseCandidate drops the index member only while it still carries the token it was read with. Because the token is part of the member string, the compare is free: a run reclaimed between read and release has a different member and this ZREM removes nothing.
func (*RedisBackend) ReleaseRun ¶
func (b *RedisBackend) ReleaseRun(ctx context.Context, key Key, ref RunRef, update ActiveRunUpdate) (Snapshot, bool, error)
func (*RedisBackend) ReleaseSteer ¶ added in v0.20.0
func (b *RedisBackend) ReleaseSteer(ctx context.Context, key Key, ref SteerClaimRef) error
func (*RedisBackend) RenewHistoryReset ¶
func (b *RedisBackend) RenewHistoryReset(ctx context.Context, lease ResetLease, ttl time.Duration) (ResetLease, bool, error)
func (*RedisBackend) RenewLease ¶
func (*RedisBackend) ReorderFollowUp ¶ added in v0.20.0
func (b *RedisBackend) ReorderFollowUp(ctx context.Context, key Key, item, before FollowUpPendingRef) ([]FollowUpItem, error)
func (*RedisBackend) ReorderSteer ¶ added in v0.20.0
func (b *RedisBackend) ReorderSteer(ctx context.Context, key Key, item, before SteerPendingRef) ([]SteerItem, error)
func (*RedisBackend) StartRun ¶
func (b *RedisBackend) StartRun(ctx context.Context, key Key, ref RunRef, update SnapshotUpdate) (Snapshot, bool, error)
func (*RedisBackend) StoreCommandResult ¶
func (*RedisBackend) Subscribe ¶
func (b *RedisBackend) Subscribe(ctx context.Context, key Key) (Subscription, error)
func (*RedisBackend) SubscribeCommands ¶
func (b *RedisBackend) SubscribeCommands(ctx context.Context, ownerID string) (CommandSubscription, error)
func (*RedisBackend) Update ¶
func (b *RedisBackend) Update(ctx context.Context, key Key, update SnapshotUpdate) (Snapshot, bool, error)
func (*RedisBackend) UpdateActiveRun ¶
func (b *RedisBackend) UpdateActiveRun(ctx context.Context, key Key, runID, generation string, update ActiveRunUpdate) (Snapshot, bool, error)
func (*RedisBackend) UpdateFollowUp ¶ added in v0.20.0
func (b *RedisBackend) UpdateFollowUp(ctx context.Context, key Key, itemID FollowUpItemID, payload []byte) (FollowUpItem, error)
func (*RedisBackend) UpdateSteer ¶ added in v0.20.0
func (b *RedisBackend) UpdateSteer(ctx context.Context, key Key, itemID SteerItemID, payload []byte) (SteerItem, error)
func (*RedisBackend) ValidateRunOwnership ¶
type ResetLease ¶
type ResetLease struct {
Scope ResetScope `json:"scope"`
Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"`
}
ResetLease is the live-backend half of the reset fence. The same token is used in PostgreSQL so renewal and release are successor-safe on both sides.
type ResetScope ¶
type ResetScope struct {
BotID string `json:"bot_id"`
SessionID string `json:"session_id,omitempty"`
}
ResetScope identifies the canonical history protected by a reset lease. SessionID is empty for a bot-wide reset.
type RunAdmissionView ¶
type RunAdmissionView struct {
RequestUserTurn *chatview.UITurn
Operation *RunOperationView
}
RunAdmissionView is the canonical state published when a reserved run becomes active. RequestUserTurn is intentionally runtime state rather than a durable history row; ordinary sends still persist user + assistant together when the run reaches a terminal result.
type RunHandle ¶
type RunHandle struct {
BotID string
SessionID string
RunID string
// OwnerID identifies the live execution owner for queue claims. It is
// intentionally carried with the handle so claim CAS checks use the same
// owner identity as the session runtime.
OwnerID string
TurnID string
Generation string
// FencingToken is the ledger ownership token for this run. Callers need it
// to fence their own durable writes, which is why it travels with the
// handle rather than staying inside the runtime. It is zero for runs
// created by backend-only reservation tests.
FencingToken int64
}
RunHandle identifies one admitted run. A run id can be reused by a client that replays an old reservation, so owner-side mutations also carry the generation.
type RunOperationView ¶
type RunRef ¶
type RunRef struct {
BotID string `json:"bot_id"`
SessionID string `json:"session_id"`
RunID string `json:"run_id"`
OwnerID string `json:"owner_id"`
Generation string `json:"generation"`
// FencingToken is the durable ownership token this reservation was made
// with. It is stored with the ref so the lease index entry can be written
// and removed from the backend's own copy, without a caller having to
// reconstruct a token it may no longer hold. Zero means the run has no
// ledger identity and therefore nothing for the reaper to transition.
FencingToken int64 `json:"fencing_token,omitempty"`
}
type RuntimeDelta ¶
type RuntimeDelta struct {
CurrentRunView *CurrentRunView `json:"current_run_view,omitempty"`
Run *CurrentRunPatch `json:"run,omitempty"`
UserTurnUpserts []chatview.UITurn `json:"user_turn_upserts,omitempty"`
SteerTurnUpserts []SteerTurnView `json:"steer_turn_upserts,omitempty"`
SteerTurnRemovals []string `json:"steer_turn_removals,omitempty"`
MessageAppends []RuntimeMessageAppend `json:"message_appends,omitempty"`
ProgressAppends []RuntimeProgressAppend `json:"progress_appends,omitempty"`
MessageUpserts []chatview.UIMessage `json:"message_upserts,omitempty"`
ResetMessages bool `json:"reset_messages,omitempty"`
}
RuntimeDelta carries only the state changed by one committed runtime transition. Full snapshots are reserved for hydration and gap recovery.
type RuntimeMessageAppend ¶
type RuntimeMessageAppend struct {
ID int `json:"id"`
Type chatview.UIMessageType `json:"type"`
Content string `json:"content"`
}
type RuntimeProgressAppend ¶
type Snapshot ¶
type Snapshot struct {
BotID string `json:"bot_id"`
SessionID string `json:"session_id"`
Epoch string `json:"epoch"`
Seq int64 `json:"seq"`
CurrentRunView *CurrentRunView `json:"current_run_view,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
}
Snapshot is the authoritative live view of one session. It holds at most one run: admission answers busy rather than queueing, so there is no pending list to project and a subscriber never has to reason about work it cannot see yet.
func EmptySnapshot ¶
EmptySnapshot returns the canonical empty runtime snapshot for a session.
type SnapshotUpdate ¶
type SteerClaimRef ¶ added in v0.20.0
type SteerItem ¶ added in v0.20.0
type SteerItem struct {
ID SteerItemID
BotID, SessionID, TargetRunID string
InvocationID string
Payload []byte
Status QueueStatus
Position int64
Claim *SteerClaimRef
// ErrorCode is set when Status is rejected.
ErrorCode string
CreatedAt time.Time
}
type SteerItemID ¶ added in v0.20.0
type SteerItemID string
type SteerPendingRef ¶ added in v0.20.0
type SteerPendingRef struct {
ItemID SteerItemID `json:"item_id"`
}
type SteerTurnView ¶ added in v0.20.0
type SteerTurnView struct {
ItemID string `json:"item_id" validate:"required" format:"uuid"`
Status string `json:"status" validate:"required" enums:"claimed,applied"`
Text string `json:"text" validate:"required"`
// TurnID and TurnPosition are drawn when the steer is claimed, not when its
// step commits: a subscriber that only learns the queue item id cannot line
// the input up against history, which left the live bubble and the settled
// one rendering side by side until the commit published the durable name.
TurnID string `json:"turn_id,omitempty" format:"uuid"`
TurnPosition int64 `json:"turn_position,omitempty"`
AfterMessageID int `json:"after_message_id"`
Timestamp time.Time `json:"timestamp" validate:"required" format:"date-time"`
}
type Subscription ¶
type Subscription struct {
C <-chan Event
Close func()
}
type TerminalRun ¶
type TerminalRun struct {
RunID string
BotID string
SessionID string
FencingToken int64
State string
ErrorCode string
ErrorMessage string
}
TerminalRun is the authoritative durable outcome of one admitted run. It is emitted only after the fenced session_runs transition has applied, or when a replay observes that the same run is already terminal. State uses the durable ledger vocabulary: completed, aborted, failed, or lost.
Source Files
¶
- activity.go
- admit.go
- commands.go
- config.go
- control.go
- decision_output.go
- fence.go
- finalize.go
- live_queue.go
- live_queue_manager.go
- live_queue_transitions.go
- liveness.go
- manager.go
- memory.go
- memory_decision_output.go
- memory_live_queue.go
- reaper.go
- recovery.go
- redis.go
- redis_decision_output.go
- redis_live_queue.go
- redis_liveness.go
- reset.go
- run_view_codec.go
- state.go
- tuning.go
- types.go
- user_turns.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ledger is the durable half of the session runtime: the record of which runs were admitted, who owns them, and how they ended.
|
Package ledger is the durable half of the session runtime: the record of which runs were admitted, who owns them, and how they ended. |