(*--------------------------------------------------------------------------- Copyright (c) 2026 Anil Madhavapeddy . All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) (** Per-channel and per-DM Claude session management. Sessions maintain conversation context independently for each Zulip channel or DM thread, enabling multi-turn conversations with Claude. *) val src : Logs.Src.t (** Log source for session management. *) (** Role in a conversation turn *) type role = User | Assistant (** A conversation turn in a session *) type turn = { role : role; content : string; timestamp : float } (** Session scope - either a channel+topic or a DM with a user *) type scope = | Channel of { stream : string; topic : string } | Direct of { user_email : string; user_full_name : string } val scope_to_string : scope -> string (** [scope_to_string scope] returns a human-readable description of the scope. *) val scope_to_mention : scope -> string (** [scope_to_mention scope] returns a Zulip-compatible linked mention. For channels, returns [#**stream>topic**] format. For DMs, returns [DM with `email`] format. *) (** Session data *) type t val empty : now:float -> t (** [empty ~now] creates an empty session. *) val max_turns : int (** Maximum turns to keep in a session for context window management. *) val scope_of_message : Zulip_bot.Message.t -> scope (** [scope_of_message msg] extracts the session scope from a Zulip message. Channel messages use stream+topic as scope, DMs use sender email. *) val load : Zulip_bot.Storage.t -> scope:scope -> now:float -> t (** [load storage ~scope ~now] loads a session from storage. Returns an empty session if none exists or if the session has expired. *) val save : Zulip_bot.Storage.t -> scope:scope -> t -> unit (** [save storage ~scope session] persists the session to storage. *) val add_user_message : t -> content:string -> now:float -> t (** [add_user_message session ~content ~now] adds a user message to the session. *) val add_assistant_message : t -> content:string -> now:float -> t (** [add_assistant_message session ~content ~now] adds an assistant response. *) val clear : Zulip_bot.Storage.t -> scope:scope -> unit (** [clear storage ~scope] removes the session from storage. *) val build_context : t -> string option (** [build_context session] builds a context string from the session history for inclusion in Claude prompts. Returns [None] if the session is empty. *) val stats : t -> string (** [stats session] returns human-readable session statistics. *)