kcode-kennedy-sessions 0.1.1

Kennedy logical session lifecycle and agent orchestration
Documentation
# kcode-kennedy-sessions 0.1.1

`kcode-kennedy-sessions` owns the complete mechanical lifecycle of one Kennedy
logical session: Session History and Chatend projection, Kweb context and staged
writes, tool dispatch, object resolution, provider-loop hosting, recovery,
checkpoints, and final archive/commit mechanics. KennedyServer supplies a fully
composed prompt and selects roots, runtime, channel, and outer scheduling.

## Complete public API

```rust
pub use kcode_telegram_session_coordinator::validate_file_name
    as validate_delivery_file_name;

#[derive(Clone)]
pub struct Service { /* private shared capability handles */ }

pub struct Capabilities {
    pub kmap: kcode_kweb_manager::KwebManager,
    pub intelligence: kcode_intelligence_router::Intelligence,
    pub history: kcode_session_history::SessionHistory,
    pub speech_classifier: Arc<kcode_speech_classification::SpeechClassifier>,
    pub dev_tools: kcode_dev_tools::Service,
    pub agents: kcode_agent_runtime::AgentRuntime,
    pub telegram: kcode_telegram_session_coordinator::Service,
}

impl Service {
    pub fn new(capabilities: Capabilities) -> Self;
}

pub struct RuntimeModel {
    pub model: String,
    pub reasoning_effort: String,
    pub context_window_tokens: u64,
}

impl RuntimeModel {
    pub fn from_intelligence(
        runtime: kcode_intelligence_router::RuntimeModel,
    ) -> Self;
}

pub enum AgentMode {
    Conversation,
    FreeTime,
    Wakeup,
    Ingress { record_id: Option<String> },
}

pub struct SessionOptions {
    pub session_type: String,
    pub root_node_ids: Vec<String>,
    pub reference_root_node_ids: Vec<String>,
    pub channel: serde_json::Value,
    pub free_time: serde_json::Value,
    pub orchestration: serde_json::Value,
    pub provenance_id: Option<String>,
    pub mode: AgentMode,
    pub source_session_type: Option<String>,
    pub group_context: serde_json::Value,
    pub rust_lib_session_id: Option<String>,
}

impl SessionOptions {
    pub fn conversation(
        session_type: impl Into<String>,
        roots: Vec<String>,
    ) -> Self;
}

pub struct ResolvedObject {
    pub object_id: String,
    pub bytes: Vec<u8>,
    pub file_name: String,
    pub media_type: String,
    pub transport_kind: Option<String>,
}

pub struct Session {
    pub session_type: String,
    pub channel: serde_json::Value,
    pub free_time: serde_json::Value,
    pub orchestration: serde_json::Value,
    pub provenance_id: Option<String>,
    pub rust_lib_session_id: String,
    pub root_node_ids: Vec<String>,
    pub reference_root_node_ids: Vec<String>,
    pub started_at: String,
    pub transcript: Vec<serde_json::Value>,
    pub pending_turn: bool,
    pub pending_external_event_id: Option<String>,
    pub completed: bool,
    pub rounds_used: u64,
    // private runtime, journal, Kweb plan, mode, context, and commit state
}

impl Session {
    pub async fn new(
        service: Service,
        system_prompt: String,
        runtime: RuntimeModel,
        options: SessionOptions,
        restored: Option<&serde_json::Value>,
    ) -> anyhow::Result<Self>;

    pub fn append_final_user_message(
        &mut self,
        text: &str,
        metadata: &serde_json::Value,
    ) -> bool;

    pub fn stage_source_message(
        &mut self,
        kennedy: bool,
        text: &str,
        metadata: serde_json::Value,
    ) -> anyhow::Result<()>;

    pub fn answer_for_external_event(
        &self,
        id: &str,
    ) -> Option<&serde_json::Value>;

    pub fn responses_for_external_event(
        &self,
        id: &str,
    ) -> Vec<&serde_json::Value>;

    pub fn resolve_object(&mut self, object_id: &str)
        -> anyhow::Result<ResolvedObject>;

    pub fn requires_history_ingress(&self) -> bool;
    pub fn stage_free_time_opening(&mut self) -> bool;
    pub fn stage_wakeup_opening(&mut self) -> anyhow::Result<bool>;

    pub fn begin_user_turn(
        &mut self,
        text: &str,
        metadata: &serde_json::Value,
    ) -> bool;

    pub fn reset_exhausted_turn_rounds_for_retry(&mut self);
    pub fn interrupt_current_turn(&mut self) -> anyhow::Result<()>;

    pub async fn run_pending_turn<C, F>(
        &mut self,
        operation_id: uuid::Uuid,
        checkpoint: C,
    ) -> anyhow::Result<Option<String>>
    where
        C: FnMut(serde_json::Value) -> F + Send,
        F: Future<Output = anyhow::Result<()>> + Send;

    pub fn refresh_telegram_group_context(
        &mut self,
        group_context: &serde_json::Value,
        current_message_id: Option<&str>,
    ) -> anyhow::Result<()>;

    pub fn finalize_free_time(&mut self, reason: &str)
        -> anyhow::Result<()>;
    pub fn commit_current_write_session(&mut self)
        -> anyhow::Result<()>;
    pub fn snapshot(&self) -> anyhow::Result<serde_json::Value>;
    pub async fn release_managed_sources(&self);
}
```

`Service` and `Capabilities` are public aliases of private-module concrete
types; callers do not receive the individual internal adapter methods. The
service is cheaply cloneable and shares the supplied typed capability handles.

## Construction and restoration

`SessionOptions::conversation` creates an idle conversation with the supplied
session type and ordered writable roots. Callers then set channel data,
reference roots, mode, free-time state, group context, provenance, or a stable
managed-source session ID as needed. Reference roots are sorted, deduplicated,
and removed when also present as writable roots.

`Session::new` creates or reopens the Session History journal, establishes the
system-prompt and Kweb projection, restores accepted state from `restored`, and
prepares history ingress when requested. `system_prompt` must already contain
the complete application-selected prompt; this crate does not read prompt
files or choose identity/channel layers. `RuntimeModel` carries the exact model,
reasoning effort, and verified context window used mechanically by the session.

`AgentMode` controls completion policy. Conversation sessions may remain open
and later require history ingress. Free-time, wakeup, and ingress sessions own
write-session finalization and become completed after their pending turn.

## Input and turn lifecycle

`begin_user_turn` rejects an already-pending turn or empty input, resets the
cumulative round count, records an optional `externalEventId`, and durably
stages text and attachments into Chatend. `append_final_user_message` stages a
last source message without opening a new model turn. `stage_source_message`
adds an explicitly user- or Kennedy-owned source message and runs ordinary
context recovery.

`stage_free_time_opening` and `stage_wakeup_opening` create the mode-specific
opening exactly once. Wakeup requires the acquired RFC 3339 `wakeupMarker` in
`channel`. `interrupt_current_turn` repairs unfinished tool records, adds the
durable user-stop notice, clears the pending turn, and returns orchestration to
idle. `reset_exhausted_turn_rounds_for_retry` resets only an exhausted ordinary
conversation.

`run_pending_turn` does nothing and returns `Ok(None)` when no turn is pending.
Otherwise it runs the `kcode-agent-runtime` primary-session loop and calls the
checkpoint callback with a complete `snapshot` after every durable semantic
transition. Callback failure aborts the run. A conversation returns
`Ok(Some(answer))` for a new terminal assistant answer; `Ok(None)` means no new
answer was required because an object/tool already supplied the response, the
source terminated, or a non-conversation mode completed. A conversation that
ends without any recoverable response is an error. The caller's operation ID
owns provider and descendant cancellation lineage.

## Objects, external events, and Telegram context

`resolve_object` accepts a pending Session History object ID or canonical Kweb
object ID. It returns exact decoded bytes and authoritative filename, media
type, and native transport hint; resolving canonical objects does not stage or
commit them. `validate_delivery_file_name` validates the same bounded,
path-free recipient-visible filename contract used by Telegram delivery.

`TranscribeAudio` requires `prompt` to be a string that is not blank after
trimming. The accepted prompt is passed onward unchanged, and this crate does
not impose an independent character ceiling; provider or context constraints
report their own real limits.

`answer_for_external_event` returns the newest terminal response for an exact
external event. `responses_for_external_event` returns all Kennedy/system
responses for that event in transcript order. `refresh_telegram_group_context`
is a no-op outside Telegram-group sessions; in a group it replaces current
channel context, adds one controller update box, and applies context recovery.

## Completion and snapshots

`requires_history_ingress` is true only for an ordinary conversation whose
source has terminated. `finalize_free_time` accepts exactly `tool`, `deadline`,
`hard-stop`, or `user-stop`, records the slice result, and clears the turn.
`commit_current_write_session` is valid only for free-time, wakeup, or ingress;
it atomically finalizes the session's staged Kweb transaction and marks the
session completed. `release_managed_sources` releases the session-scoped leases
held by managed development tools.

`snapshot` returns the complete versioned recovery/presentation value. It
contains format and state versions, session and Chatend metadata, channel and
orchestration state, roots, transcript and turn state, round count, completion
and commit facts, Kweb plan, boxes/events, the structured projection, exact
`chatendText`, and current session status. Callers persist this value as an
opaque complete checkpoint and pass it back to `Session::new`; they should not
independently edit the journal, Kweb plan, boxes, or events.

## Ownership boundary

This crate owns session mechanics, tool authorization/dispatch, Kweb staging
and revalidation, context recovery, accounting projection, and final commit.
Session History remains the durable journal/object/archive owner; Kweb Manager
remains the database owner; agent-runtime remains the provider-loop owner; and
the Telegram coordinator remains the delivery/context owner. KennedyServer
owns prompt composition, root/runtime selection, outer transport scheduling,
job retry policy, and persistence of the opaque snapshots supplied to its
checkpoint callback.