Close a session (terminal). Sets Status = 'Closed' + ClosedAt = now + CloseReason, then
disconnects all of its channel rows. Idempotent: closing an already-Closed session is a
no-op that returns true (and never overwrites the original CloseReason). A
missing/unloadable session returns false.
Why the session is being closed. Defaults to 'Explicit' so existing
call sites (the user-initiated close mutations) stamp the common case without modification;
background callers (janitor sweeps, shutdown drain, error teardowns) must pass their reason
explicitly. NULL rows remain only for legacy data closed before the column existed.
OptionalpreloadedChannels: MJAIAgentSessionChannelEntity[]Authorize and create a new session. Flow:
CanRun check on the agent — denied → SessionAuthorizationError, no row written.AIAgentSession as Active, stamped with this host and LastActiveAt = now.the saved MJAIAgentSessionEntity.
Record activity on a session. Coalesces persisted writes so at most one LastActiveAt update
lands per session per HEARTBEAT_MIN_WRITE_INTERVAL_MS; intervening calls return true
without touching the DB. Reactivates an Idle session to Active. A Closed session is not
reactivated (returns false).
Batch-loads active channels for many sessions at once and groups them by AgentSessionID
(lowercased), so a sweep can disconnect a whole page of sessions with ONE query instead of
one-per-session. Sessions with no active channels are absent from the map (callers default
to []). Pass each session's slice to CloseSession's preloadedChannels.
Transition an Active session to Idle (e.g. when its last channel disconnects). No-op when
the session is already Idle or Closed. Returns true on success / no-op-needed.
Server-side lifecycle manager for
AIAgentSessionrecords (not the media/socket transport — that arrives in P5). Stateless with respect to the caller: every method takes the request'scontextUserand request-scopedIMetadataProviderso all entity operations run under the correct identity/provider and never pin the process-global provider.The only in-memory state it holds is a small per-session last-heartbeat-write timestamp map used purely to coalesce Heartbeat writes; it carries no user or provider state across calls.
Responsibilities:
CanRun), optionally mint a Conversation, persist anActivesession.LastActiveAtbump; reactivatesIdle → Active.Active → Idletransition when the last channel goes quiet.Remarks
P5 will extend CloseSession to also abort any in-flight
AIAgentRun(via its cancellation token) and callClientToolRequestManager.ClearSession(...); those resources don't exist yet at this layer, so close here only reconciles the durable record + channel rows.