Documentation
¶
Overview ¶
Package nativeturn is the serve-level survival layer for native ACP chain turns: an in-flight turn runs on a serve-rooted Registry, so a dropped connection detaches a viewer without cancelling the turn. Every exported type is safe for concurrent use.
Index ¶
- Constants
- Variables
- type Config
- type Event
- type Registry
- func (r *Registry) AttachIfRunning(ctx context.Context, sid libacp.SessionID, viewer Viewer) (*Turn, bool, error)
- func (r *Registry) Cancel(sid libacp.SessionID) bool
- func (r *Registry) Close() error
- func (r *Registry) Get(sid libacp.SessionID) (TurnStatus, bool)
- func (r *Registry) List() []TurnStatus
- func (r *Registry) ReapIdle(_ context.Context) error
- func (r *Registry) Start(sid libacp.SessionID, fn TurnFunc, viewer Viewer) (*Turn, bool, error)
- func (r *Registry) Stop(sid libacp.SessionID) bool
- type Result
- type Turn
- type TurnFunc
- type TurnStatus
- type Viewer
Constants ¶
const ( // DefaultTurnDeadline is the fallback hard turn ceiling (belt 2). DefaultTurnDeadline = 60 * time.Minute // DefaultGraceWindow is the fallback last-viewer-detach survival window (belt 1). DefaultGraceWindow = 15 * time.Minute // DefaultJournalSize bounds the per-session replay journal (belt 3). DefaultJournalSize = 512 )
const ( // StateRunning: the turn's chain is executing and at least one viewer is attached. StateRunning = "running" // StateGrace: the chain is still executing but no viewer is attached, with a grace // timer counting down (belt 1); a reattach returns it to running, expiry cancels it. StateGrace = "grace" // StateFinished: the chain has ended and the session awaits viewer // detach / reaper cleanup. StateFinished = "finished" // StateSuspended: the chain suspended on a pending human approval and checkpointed // durably; lifecycle-wise a finished turn whose distinct state tells the operator // to wait for the approval, not "done". StateSuspended = "suspended" )
Turn states, the vocabulary of TurnStatus.State.
Variables ¶
var ErrClosed = errors.New("nativeturn: registry is closed")
ErrClosed is returned by Start once the Registry has been Closed; a sentinel for errors.Is(err, ErrClosed).
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// TurnDeadline is the hard wall-clock ceiling on a single turn (belt 2),
// terminating a still-running turn with a context deadline; must be > 0.
TurnDeadline time.Duration
// GraceWindow is how long an in-flight turn survives with no viewer attached
// before it is cancelled (belt 1); a reattach inside the window keeps it
// alive, and it must be > 0.
GraceWindow time.Duration
// JournalSize bounds the per-session replay journal (belt 3) to the most
// recent events, oldest dropped first; zero or negative resolves to
// DefaultJournalSize in New.
JournalSize int
}
Config holds the survival-layer tunables; TurnDeadline and GraceWindow are operator-facing (env-backed, see ParseEnv), JournalSize is overridable only in-process (tests).
type Event ¶
type Event struct {
// Seq is the 1-based monotonic id of this event within its session's turn.
Seq uint64
// Update is the session/update notification as emitted by the turn, before any
// per-viewer normalization.
Update libacp.SessionNotification
}
Event is one captured session/update tagged with a per-session monotonic sequence number, for a future SSE transport's Last-Event-ID replay.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry owns every native session's in-flight turn, off any single connection, keyed by ACP session id; every method is safe for concurrent use.
func New ¶
New returns a Registry rooted on a fresh serve context (call Close at shutdown); a non-positive JournalSize, TurnDeadline, or GraceWindow is floored to its default.
func (*Registry) AttachIfRunning ¶
func (r *Registry) AttachIfRunning(ctx context.Context, sid libacp.SessionID, viewer Viewer) (*Turn, bool, error)
AttachIfRunning attaches viewer to sid's in-flight turn if one exists, replaying the journal and joining the live fan-out; returns (nil, false, nil) when none exists (a finished turn is deliberately not attachable here).
func (*Registry) Cancel ¶
Cancel cancels sid's in-flight turn and tears it down (session/cancel), reporting whether one was present (no-op false if not); any viewer awaiting the turn unblocks with its cancelled result.
func (*Registry) Close ¶
Close tears every in-flight turn down and cancels the Registry's root context, after which Start returns ErrClosed. Idempotent.
func (*Registry) Get ¶
func (r *Registry) Get(sid libacp.SessionID) (TurnStatus, bool)
Get returns the status of sid's active turn, or ok=false when none is active (never started, or already reaped).
func (*Registry) List ¶
func (r *Registry) List() []TurnStatus
List returns a snapshot of every active turn, sorted by session id; a point-in-time report, not a transaction.
func (*Registry) ReapIdle ¶
ReapIdle sweeps every session and tears down the ones that must not linger (belt 4): finished with no viewers, unwatched past its grace deadline, or past its hard deadline plus one grace window; always returns nil.
type Result ¶
type Result struct {
// StopReason is the ACP stop reason the connected client's prompt resolves with.
StopReason libacp.StopReason
// Err is a genuine execution failure the client must see as a JSON-RPC error; nil
// for a clean end or a cancellation (which resolves as StopReasonCancelled with no
// error).
Err error
// Suspended marks a turn whose chain parked on a pending human approval:
// not a failure (Err is nil), and status reads StateSuspended instead of
// StateFinished until reaped.
Suspended bool
// ApprovalID is the durable approval a Suspended turn is parked on, communicated to
// the client since StopReason cannot express it; empty unless Suspended.
ApprovalID string
// DroppedContentKinds are the prompt content kinds the turn could not forward to
// the model, ridden on Result so a reattaching client still sees them.
DroppedContentKinds []string
}
Result is the outcome of one turn, returned by a TurnFunc and read back by the connected viewer to resolve its libacp session/prompt RPC.
type Turn ¶
type Turn struct {
// contains filtered or unexported fields
}
Turn is a caller's handle to one session's turn: how a connected viewer awaits completion, detaches on connection drop, and (for session/cancel) cancels.
func (*Turn) Await ¶
Await blocks until the turn completes (Result, true) or ctx is done first (zero Result, false, without disturbing the turn); a convenience over selecting on Done.
func (*Turn) Cancel ¶
func (t *Turn) Cancel()
Cancel ends this turn now and tears it down (the explicit session/cancel); any viewer awaiting completion unblocks with the cancelled result. Idempotent.
func (*Turn) Detach ¶
Detach removes viewerID from this turn's fan-out, never cancelling the turn while another viewer remains; detaching the last one starts the grace window (Belt 1). Idempotent.
func (*Turn) Done ¶
func (t *Turn) Done() <-chan struct{}
Done is closed when the turn's chain ends; Result is then readable.
type TurnFunc ¶
type TurnFunc func(ctx context.Context, emit func(ctx context.Context, n libacp.SessionNotification)) Result
TurnFunc runs one turn's work: ctx is the serve-rooted, hard-deadline-bounded turn context (belt 2), outliving the client that started it, and emit journals a session/update and fans it out to every attached viewer exactly once and in order.
type TurnStatus ¶
type TurnStatus struct {
SessionID libacp.SessionID `json:"sessionId"`
StartedAt time.Time `json:"startedAt"`
// Deadline is the hard wall-clock time this turn is terminated at (belt 2).
Deadline time.Time `json:"deadline"`
// Viewers is how many viewers are attached right now (0 in StateGrace).
Viewers int `json:"viewers"`
// State is one of StateRunning / StateGrace / StateFinished.
State string `json:"state"`
}
TurnStatus is a point-in-time snapshot of one active turn (Registry.List/Get); a value copy, mutating it never affects the live turn.
type Viewer ¶
type Viewer interface {
// ID uniquely identifies this viewer within a session; two viewers on one session
// must not share an ID.
ID() string
// Deliver receives one turn event, in order (replayed backlog then live); it must
// not block since it runs under the session lock, and its returned error is
// advisory only.
Deliver(ctx context.Context, ev Event) error
}
Viewer is a consumer attached to one session's live (and replayed) turn stream.