# kcode-kennedy-sessions 0.2.1
`kcode-kennedy-sessions` runs one Kennedy logical session. It owns lifecycle and mode authorization, context recovery, provider and subagent hosting, checkpoints, Kweb write staging and final commit, and service compatibility.
Deterministic rendering is delegated to `kcode-kennedy-session-presentation`; strict Ktool decoding and media/model/document validation to `kcode-kennedy-session-tool-contracts`; object resolution, authoritative descriptors, Telegram media staging, and delivery attachment construction to `kcode-kennedy-session-objects`; and durable input staging and transcript reconstruction to `kcode-kennedy-session-ingress`.
## Public API
```rust
pub use kcode_kennedy_session_objects::ResolvedObject;
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 load_fixed_connections: bool,
pub kmap: kcode_kweb_manager::KwebManager,
pub intelligence: kcode_intelligence_router::Intelligence,
pub history: kcode_session_history::SessionHistory,
pub speech_classifier: Arc<kcode_speaker_system::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 enum TurnDeadlineKind {
Telegram,
SelfTimeHardStop,
}
pub struct TurnDeadline {
pub kind: TurnDeadlineKind,
pub at: chrono::DateTime<chrono::Utc>,
}
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,
// remaining runtime, journal, Kweb-plan, mode, context, and commit state is private
}
impl Session {
pub async fn new(
service: Service,
system_prompt: String,
subagent_codex_prompt: String,
runtime: RuntimeModel,
started_at: String,
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,
turn_deadline: Option<TurnDeadline>,
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);
}
```
The application-selected fixed-loading flag is applied consistently to the primary Kweb context and every box-free subagent context. It defaults in the application CLI, not in session persistence.
Version 0.2.1 selects `kcode-kennedy-kweb-loader` 0.1.4, `kcode-kennedy-subagent-context` 0.2.1, and `kcode-kweb-context` 0.2.7. Primary and child Kweb projections therefore present each compatibility `short_description` value as a model-facing map marker and use connection-map-marker terminology. Rust fields, storage and wire names, Ktool argument keys, session state, and historical Chatend text remain unchanged.
Kennedy's explicit task-board Ktools are `CreateTaskCategory`,
`GetTaskCategory`, `RemoveTaskCategory`, `CreateTask`, `GetTask`, `UpdateTask`,
`RemoveTask`, and `GetTopTaskOrphan`. Each maps directly to its corresponding
`kcode-task-board` method on a blocking worker. Category lookup uses the
session's first user root and supports optional `offset` and `limit`; it does
not expose category or task enumeration to Kennedy. Task-board mutations are
available in ordinary conversations because users must work through Kennedy.
## Construction and lifecycle
`SessionOptions::conversation` creates an idle conversation with ordered writable roots. Reference roots are sorted, deduplicated, and removed when also writable. Callers may edit the other option fields before construction. `system_prompt` must already be fully composed, `subagent_codex_prompt` must be the exact Codex-only harness layer selected by orchestration, and `RuntimeModel` must contain the selected model, reasoning effort, and verified context window.
`Session::new` creates or reopens the durable Session History journal, restores
compatible state, loads the Kweb roots, and prepares history ingress when
requested. `started_at` must be RFC 3339 and must exactly match a restored
session's durable `startedAt`; orchestration uses that one value when composing
the stable system prompt and creating Session History metadata. `AgentMode`
controls completion: conversations may remain open; free-time, wakeup, and
ingress sessions finalize their staged Kweb transaction when their run ends.
Reopening an unsealed journal whose history ingress already started creates a
fresh provider-visible attempt: Chatend returns to its initial ingress
baseline, staged Kweb planning and round accounting reset, provider affinity
is cleared, and loaded roots are revalidated. A sealed journal continues its
existing idempotent commit recovery instead.
Checkpoint `stateVersion` 5 is the first version whose native affinity can be
restored across this boundary. A version-4 checkpoint remains otherwise
usable, but its `providerAffinity` is ignored so a pre-fix native continuation
cannot resume. No durable migration is run and no journal data is rewritten.
`begin_user_turn` rejects empty input or an already-pending turn. It durably stages text and attachments, records an optional `externalEventId`, resets round accounting, and opens a turn. Attachment object writes can succeed before later validation or box writes fail; such persistence failures become fatal to the next run rather than being silently retried. `append_final_user_message` stages a final source message without opening a turn. `stage_source_message` stages an explicitly user- or Kennedy-owned message. These operations apply normal context recovery.
`stage_free_time_opening` and `stage_wakeup_opening` run at most once while a turn is pending. Wakeup requires an RFC 3339 `channel.wakeupMarker`. `interrupt_current_turn` repairs unfinished tool records, records a user-stop notice, and returns the session to idle. `reset_exhausted_turn_rounds_for_retry` affects only an exhausted conversation.
`run_pending_turn` returns `Ok(None)` without work when no turn is pending.
Otherwise it runs the provider loop and invokes `checkpoint` with a complete
snapshot after each durable semantic transition; callback failure aborts the
run. `turn_deadline` is the workflow owner's already-enforced absolute outer
deadline. It is projected transiently but is never written as a box, event, or
transcript entry. Every provider input and tool continuation displays the live
250-call budget, provider-call time, the supplied enclosing deadline, and
self-time deadlines when applicable. A conversation returns `Ok(Some(answer))`
for a new terminal text answer. `Ok(None)` means an object/tool already
responded, the source terminated, or a non-conversation mode completed. The
supplied operation ID owns cancellation lineage for provider and descendant
work.
Ingress starts a transient 45-minute monotonic timer immediately before its
first provider inference. Expiry is checked at every actual provider-resume
boundary, including consecutive native tool results inside one outer round.
An already-started inference may finish, and a late `EndSession` returned by
that inference remains valid and completes without starting another inference.
The timer is not snapshotted. Ingress time is reported in rounded-down seconds
through sparse append-only Chatend markers: initially, alongside every
context-size marker, at the 20-minute threshold when no marker was shown after
30 minutes, and unconditionally at the 10-minute threshold. Ten-minute markers
retain the failure-and-lost-Kmap warning inside their brackets. Expiry before a
nonterminal resume escapes through the typed ingress-time error; agent-loop
round exhaustion remains an error rather than a request to commit partial
ingress work.
Before each primary call and every native provider resume, the session durably
reconciles sparse stale-box and context-size markers. Newly due marker events
are appended to Chatend exactly once. At a native resume, the structured
Session History preparation determines whether the journal remains
append-only. For append-only history, the exact new marker lines enter the
immediately following tool result before the live runtime-budget suffix,
provider synchronization advances as before, and native affinity is retained.
If structured preparation reports rewritten provider-visible history, the
exact reset reason and cleared affinity are checkpointed before the host
returns `RestartFresh`; the prior synchronization cursor is not advanced.
Agent Runtime does not call the old native continuation, drops rather than
replays the completed tool outcome, accounts the interrupted receipt without a
continuation, and starts the next outer round. That round has no continuation
or resume cursor, submits the complete current durable projection once, and
only then establishes new synchronization under the ordinary submission and
receipt rules. Receipt, checkpoint, or cold-start failure leaves affinity
cleared, the durable tool invocation/result intact, and synchronization
unadvanced; a failed receipt prevents the fresh round.
The first Codex call submits the full projection. A successful call checkpoints
its native thread, cumulative usage baseline, provider-material fingerprint,
and synchronized journal cursor; later rounds, user messages, and restored
sessions resume that thread with only later projected events. Rewritten
context, changed provider material, interruption, or ambiguous completion
clears affinity and records an explicit fresh-thread reason. The durable
provider-input event retains the full logical projection and separately records
the exact transport-delta hash and byte length. Structured logs report thread
action and stable provider thread ID rather than inferring cache hit/miss from
cumulative totals. Receipts contain checked per-call usage deltas. Snapshots
and archives continue to expose full submitted `chatendText` and structured
material.
Each subagent owns an ephemeral `kcode-kennedy-subagent-context` projection
and one fresh native provider turn, within which all tool calls run.
The selected child model is resolved before that projection is built. A Codex
child receives `subagent_codex_prompt` before Kennedy's selected node
descriptions; OpenAI API, Gemini, and every other provider receive no static
prompt. No other primary prompt layer enters the child.
`LoadNodes`, Kmap mutations, managed-source snapshots, and freeform managed
writes update that box-free projection and never create, revise, or dehydrate a
parent box. The agent runtime replaces superseded displayed state with its one
generic marker while retaining exact durable subagent audit facts.
`NoteToSelf` accepts exactly one nonblank `message` string without an arbitrary
length ceiling. In a primary session, its Kennedy-owned invocation box renders
as `[note to self: <message>]`, stays in canonical Chatend history, and creates
neither a separate result box nor a user-facing transcript or transport
message. In a subagent, the same rendering stays only in that child's box-free
context and never changes the parent Chatend. Existing context-capacity and box
representation operations remain authoritative for a primary note box.
Nested `RunSubagent`, `EndSession`, and all parent box controls are unavailable
inside a child. `EmitObject`, `SendTelegramDM`, and
`SendTelegramGroupMessage` remain delegable: object emission creates the same
ordinary user-visible Kennedy message and transcript record as a direct call,
while Telegram retains its ordinary cold-delivery, eligibility, attachment,
source-audit, and reply-bridge behavior. Their Ktool results remain child-only.
## Objects, tools, events, and completion
`resolve_object` accepts a session-pending object ID or canonical Kweb object ID and returns exact bytes plus authoritative delivery metadata. Resolving canonical objects does not stage or commit them. `validate_delivery_file_name` validates bounded, path-free recipient-visible filenames.
`TranscribeAudio` and `AnnotateMedia` require a prompt that is not blank after trimming. The exact prompt is passed to the intelligence boundary unchanged without a character or byte ceiling. `TranscribeAudio` currently rejects every optional field, including `temperature`, and forwards `None` to the intelligence boundary.
Media model, MIME-type, and document-format validation uses exact allowlists. Object resolution and size enforcement precede the operation-specific validations in their established order.
`answer_for_external_event` returns the newest terminal response for an exact event ID. `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 channel context, adds a controller update, and applies context recovery.
`requires_history_ingress` is true only for a conversation whose source has terminated. `finalize_free_time` accepts exactly `tool`, `deadline`, `hard-stop`, or `user-stop`. `commit_current_write_session` is valid only for free-time, wakeup, or ingress and atomically finalizes staged Kweb writes. `release_managed_sources` releases this session's managed-development leases.
`snapshot` is the complete opaque recovery checkpoint. Persist it as a unit and pass it back to `Session::new`; do not independently edit its journal, Kweb plan, boxes, or events.
## Authority and persistence
Session History owns the durable journal, staged objects, archive, and
structured native-resume reconciliation. Kweb Manager owns database reads and
writes; durable read projection and legacy node decoding are delegated to the
read-only Kweb loader. Agent Runtime owns provider execution and restart-fresh
control flow, `kcode-kennedy-subagent-context` owns child-only current-state
projection, and the Telegram coordinator owns delivery and Telegram context.
The application retains prompt composition, roots and runtime selection,
transport scheduling, retry policy, and checkpoint persistence. This crate
retains session policy, Kweb write staging, tool authorization and dispatch,
context recovery, affinity checkpoint policy, and final commit.
Version 0.2.0 selects Session History 0.1.13 for structured native-resume
preparation and Agent Runtime 0.3.0 for `RestartFresh`. Those dependency
changes are required together; the public Kennedy session API is otherwise
unchanged.