Documentation
¶
Overview ¶
Package session owns agent-session lifecycle.
It is the sole authority over session creation, resumption, kind/channel policy, main-session resolution, review candidate selection, and the conversion from a validated session record to a memory operation scope.
Info is a session-domain type, not an alias to memory.SessionInfo. Every use boundary fails closed: the persistence mapping (Record / InfoFromRecord) and the memory-scope conversion (MemoryScope) validate the session invariant and return an error rather than emit an unchecked value. The memory scope is produced only by MemoryScope; production code must not hand-build memory.Session values.
Index ¶
- Variables
- type Channel
- type ChannelRequest
- type Info
- type Kind
- type ListOptions
- type MainRequest
- type Registry
- func (r *Registry) Archive(ctx context.Context, scope Scope, id string) error
- func (r *Registry) Ensure(ctx context.Context, req Request) (Info, error)
- func (r *Registry) Get(ctx context.Context, scope Scope, id string) (Info, error)
- func (r *Registry) List(ctx context.Context, scope Scope, opts ListOptions) ([]Info, error)
- func (r *Registry) ListForReview(ctx context.Context, req ReviewRequest) ([]Info, error)
- func (r *Registry) MemoryScope(info Info) (memory.Session, error)
- func (r *Registry) ResolveChatChannel(ctx context.Context, req ChannelRequest) (Info, error)
- func (r *Registry) ResolveMain(ctx context.Context, req MainRequest) (Info, error)
- func (r *Registry) RotateChannel(ctx context.Context, req ChannelRequest) (Info, error)
- func (r *Registry) RotateMain(ctx context.Context, req MainRequest) (Info, error)
- type Request
- type ReviewPolicy
- type ReviewRequest
- type RotationOutcome
- type Scope
Constants ¶
This section is empty.
Variables ¶
var ErrArchived = errors.New("session is archived")
ErrArchived is returned when an attempt is made to write to an archived session.
var ErrForbidden = errors.New("session access forbidden")
ErrForbidden is returned when a caller attempts to access a session they do not own.
var ErrNotFound = errors.New("session not found")
ErrNotFound is returned when a session cannot be located.
var ErrRotationOutcomeUnknown = memory.ErrRotationOutcomeUnknown
ErrRotationOutcomeUnknown is returned when a rotation failed at commit acknowledgement and may or may not have been persisted. See memory.ErrRotationOutcomeUnknown.
var ErrStaleRotation = memory.ErrStaleRotation
ErrStaleRotation is returned when a rotation's expected session is no longer the active one. It is the store-level sentinel so callers can match a stale rotation with one errors.Is check regardless of which layer detected it.
var ErrWrongKind = errors.New("session kind mismatch")
ErrWrongKind is returned when a resumed session has a different kind than required.
Functions ¶
This section is empty.
Types ¶
type ChannelRequest ¶ added in v0.61.0
type ChannelRequest struct {
UserID string
AgentID string
// GroupID marks a group binding. When set it must equal UserID.
GroupID string
// Channel is the durable channel value the binding carries. It is part of the
// binding predicate for private chat channels and informational for groups.
Channel Channel
// LegacyID is the pre-binding key-as-ID session for this chat. On a binding
// miss it is resolved once and, when it is still a valid active session for
// this binding, adopted (binding fields persisted) instead of superseded by a
// new empty session. Empty disables the fallback.
LegacyID string
// ExpectedSessionID makes a rotation a compare-and-rotate: the binding's
// current session must still be this one, otherwise RotateChannel reports
// ErrStaleRotation and changes nothing. Ignored by ResolveChatChannel.
ExpectedSessionID string
}
ChannelRequest describes a durable chat-channel session binding: the identity a channel chat resolves against instead of pinning itself to one session id.
A group binds on its owning group (UserID == GroupID) plus agent and kind=chat, because a group's channel string varies with the platform reply channel it arrives through. A private chat channel binds on user + agent + kind=chat + Channel, which is the channel adapter's stable session key.
type Info ¶
type Info struct {
ID string
AgentID string
UserID string
GroupID string
Channel string
Kind string
ProjectID string
Title string
CreatedAt time.Time
LastActive time.Time
Archived bool
// LatestSeq is populated by review listings and is not session metadata.
LatestSeq int64
}
Info holds validated metadata about an agent session. It is a session-domain value distinct from the memory-layer persistence type memory.SessionInfo; callers cross the persistence boundary through Record and InfoFromRecord.
GroupID identifies the group a group session belongs to. It is now durable (ctx_conversation.group_id) and load/save round-trip it. The durable invariant is that a group session is owned by the group itself: UserID equals GroupID, and GroupID is the canonical ctx_group_state UUID. That equality is what makes group read, write, and compaction resolve one memory partition; it is enforced by Validate at every use boundary rather than left as an undeclared convention.
func InfoFromRecord ¶ added in v0.60.0
func InfoFromRecord(r memory.SessionInfo) (Info, error)
InfoFromRecord maps a memory-layer persistence record back to a session-domain Info. It is the persistence→domain half of the session store boundary and the only sanctioned way to turn an unvalidated memory.SessionInfo into an Info; it validates the resulting Info and fails closed on a record that violates the session invariant.
func (Info) MemoryScope ¶ added in v0.60.0
MemoryScope is the single canonical conversion from a validated session Info to a memory.Session operation scope. It fails closed: an Info that violates the session invariant yields an error, never a half-formed scope.
For a private session, read (Assemble), write (Append/Bootstrap), and compaction all derive their scope here and land on the same partition. A group session shares one durable canonical scope for read and write (partition keyed on session/user/agent, with GroupID selecting the group assembly/isolation path); group compaction is a separate matter and is unsupported (rejected by agent.Service.CompactSession), because group history is assembled from the group event log rather than the LCM conversation.
func (Info) Record ¶ added in v0.60.0
func (i Info) Record() (memory.SessionInfo, error)
Record maps a validated session-domain Info onto the memory-layer persistence type. It is the domain→persistence half of the session store boundary and fails closed on an invalid Info.
func (Info) Validate ¶ added in v0.60.0
Validate enforces the durable session-scope invariant. Every session needs an ID, an agent, and a user owner. A group session is owned by its group: UserID must equal GroupID and GroupID must be a canonical ctx_group_state UUID. This is the single fail-closed gate the persistence and memory-scope boundaries run.
type ListOptions ¶
type ListOptions struct {
// Kinds filters by session kind; empty = all kinds.
Kinds []Kind
// IncludeArchived includes archived sessions.
IncludeArchived bool
// ExcludeInternal omits task/delegate worker sessions from human lists.
ExcludeInternal bool
// ProjectID filters to one project when non-empty.
ProjectID string
Limit int
Offset int
}
ListOptions controls session listing.
type MainRequest ¶
type MainRequest struct {
UserID string
AgentID string
// ExpectedSessionID makes a rotation a compare-and-rotate: the current main
// must still be this session, otherwise RotateMain reports ErrStaleRotation
// and changes nothing. Empty means "rotate whatever is current"; it is
// ignored by ResolveMain.
ExpectedSessionID string
}
MainRequest describes a main-session resolution or rotation request.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is the sole owner of agent-session lifecycle. It creates, resumes, lists, and archives sessions; it also converts validated session records into memory operation scopes.
Registry has no knowledge of LLM execution, runners, or tools.
func NewRegistry ¶
NewRegistry creates a Registry backed by the given memory.Provider. mem must implement memory.SessionManager; if it does not, an error is returned. agentID may be empty for a cross-agent registry.
func NewRegistryWithStore ¶
NewRegistryWithStore creates a Registry with an explicit store (for testing).
func (*Registry) Ensure ¶
Ensure finds or creates a session according to the request policy.
- If req.ID is set and the session exists: validates kind and user, returns it. - If req.ID is set but missing and AllowExactIDCreate: creates with that ID. - If req.ID is empty and CreateIfMissing: generates a new ID and creates. - Otherwise: returns ErrNotFound.
func (*Registry) Get ¶
Get returns session metadata by ID. The session must belong to the given scope (userID + agentID match).
func (*Registry) ListForReview ¶
ListForReview returns sessions that are candidates for reflect review.
func (*Registry) MemoryScope ¶
MemoryScope converts a validated session Info into a memory.Session scope. This is the ONLY authorised way to produce memory.Session for production agent sessions; it delegates to the canonical Info.MemoryScope conversion. Private read, write, and compaction resolve one partition; group read and write share the durable canonical group scope (group compaction is unsupported, so MemoryScope makes no claim about it). It fails closed on an invalid Info.
func (*Registry) ResolveChatChannel ¶ added in v0.61.0
ResolveChatChannel returns the chat session a channel chat is currently bound to, creating one when the binding has none yet. Unlike Ensure it never uses a derived key as the session id, so the binding can rotate onto a successor while staying the same chat.
func (*Registry) ResolveMain ¶
ResolveMain returns the main session for a user+agent pair. It first looks for an existing main-kind session, then promotes the most recent candidate, and finally creates a new one if CreateIfMissing is set.
func (*Registry) RotateChannel ¶ added in v0.61.0
RotateChannel archives the chat channel's current session and returns its successor, so the next turn starts with an empty context while the old session stays searchable. The archive and the create are one store-level transaction, so the binding is never left without an active session.
When req.ExpectedSessionID is set it must still be the binding's current session; otherwise the rotation is stale (another `/new` already ran) and reports ErrStaleRotation without changing anything.
func (*Registry) RotateMain ¶ added in v0.61.0
RotateMain archives the user's current main session and returns its successor, so the next turn starts with an empty context while the old session stays searchable. The archive and the create are one store-level transaction: the binding is never left without an active main.
When req.ExpectedSessionID is set it must still be the current main; otherwise the rotation is stale (another /new already ran) and reports ErrStaleRotation without changing anything. Rotating when no main exists is just a create.
type Request ¶
type Request struct {
// ID is the exact session ID to resume. If empty, a new ID is generated.
ID string
// UserID and AgentID are required for user-scoped operations.
UserID string
AgentID string
// GroupID marks this as a group session owned by the group. When set it is
// the canonical ctx_group_state UUID and must equal UserID; it is persisted
// so the group identity survives reload. Empty for private sessions.
GroupID string
// ProjectID scopes the session to a project when non-empty.
ProjectID string
// Kind is the session kind to use when creating a new session.
// If the session already exists, it must match RequireKind when set.
Kind Kind
// Channel is the originating channel (e.g. ChannelWeb, ChannelDelegate).
Channel Channel
// Title is optional; pool_chat auto-derives it from the first message.
Title string
// CreateIfMissing creates a new session if none matches.
CreateIfMissing bool
// AllowExactIDCreate allows creating a new session with the explicit ID
// when that ID does not yet exist. Required for delegate session persistence.
AllowExactIDCreate bool
// RequireKind enforces that a resumed session must have this kind.
// Empty means any kind is acceptable.
RequireKind Kind
}
Request describes what session to find or create.
type ReviewPolicy ¶
type ReviewPolicy struct {
// ExcludeKinds lists session kinds to skip.
ExcludeKinds []Kind
// RequirePrivateMain, when true, includes only sessions on private user channels.
RequirePrivateMain bool
}
ReviewPolicy controls which sessions are included in reflect review.
func DefaultReviewPolicy ¶
func DefaultReviewPolicy() ReviewPolicy
DefaultReviewPolicy returns the policy used by the reflect system. Delegate, task, and scheduler sessions are excluded by default.
func (ReviewPolicy) Includes ¶
func (p ReviewPolicy) Includes(kind Kind) bool
Includes reports whether a session with the given kind passes this policy.
func (ReviewPolicy) IsZero ¶
func (p ReviewPolicy) IsZero() bool
IsZero reports whether no review policy was supplied.
type ReviewRequest ¶
type ReviewRequest struct {
AgentID string
// Policy controls inclusion/exclusion rules.
Policy ReviewPolicy
}
ReviewRequest describes which sessions are candidates for reflect review.
type RotationOutcome ¶ added in v0.61.0
type RotationOutcome int
RotationOutcome is what a re-read of the chat's session binding can say about a rotation whose commit acknowledgement was lost (ErrRotationOutcomeUnknown): the transaction may have landed, so the error alone is not an answer. The binding is the answer — it is what the rotation moves, and it never moves back.
const ( // RotationUncertain means the verification read itself failed, so nothing is // known. A caller must say so rather than invite a retry: retrying a // rotation that did commit archives the fresh context instead of the old. RotationUncertain RotationOutcome = iota // RotationCommitted means the binding has left the expected session, so the // rotation did happen and should be reported as success. RotationCommitted // RotationNotExecuted means the binding still points at the expected // session, so the transaction rolled back. Retrying is safe. RotationNotExecuted )
func VerifyRotation ¶ added in v0.61.0
func VerifyRotation(ctx context.Context, expectedSessionID string, resolve func(context.Context) (Info, error)) RotationOutcome
VerifyRotation classifies a lost-acknowledgement rotation by re-resolving the chat's session binding. expectedSessionID is the session the rotation was asked to replace; resolve reads the binding's current session.
A resolve that succeeds but names nothing is treated as uncertain: an empty id is not evidence that the binding moved.