wire/answer/sessions.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! The stretches of one room, newest first: where each began, how, its
7//! runs and its last line (`crates/wire/spec/Answer/Sessions.lean` §8-71).
8
9use kernel::{Address, Effort, Origin, Seq, TimeMs};
10use serde::{Deserialize, Serialize};
11
12use crate::command::Carry;
13
14/// How many stretches one answer lists. A bound on the size of an answer
15/// on the wire, not a machine reading: the page that reads it shows a
16/// room's recent stretches, and `earlier` says how many more there are.
17pub const SESSIONS_MAX: usize = 64;
18
19/// How many characters of a stretch's last reply [`SessionLine::preview`]
20/// carries. A bound on the answer, not on what a page shows: the page cuts
21/// again to the width it has (`crates/wire/spec/Answer/Sessions.lean` D27).
22pub const SESSION_PREVIEW_MAX: usize = 240;
23
24/// One room's stretches, newest first, and how many older ones the answer
25/// leaves out. `room` echoes the question, which the wire carries no id
26/// for.
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
29pub struct SessionsAnswer {
30 pub room: Address,
31 pub sessions: Vec<SessionLine>,
32 pub earlier: u64,
33}
34
35/// One stretch of a room, each field read off a line the Ledger files
36/// under the room's address.
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
39pub struct SessionLine {
40 /// The stretch's first line: its `session_opened`, or the room's first
41 /// `run_started` when dispatching opened the room.
42 pub began: Seq,
43 pub start: SessionStart,
44 /// The runs started in this stretch.
45 pub runs: u64,
46 /// The last line of this stretch filed under the room's address, and
47 /// its time: the room's slice ends there (`crates/storage/Spec.lean` §8-24).
48 pub last: Seq,
49 pub at: TimeMs,
50 /// The name the last `session_named` for this stretch gave it; `None`
51 /// when it has none, and a page shows the room's address.
52 #[serde(default)]
53 pub name: Option<String>,
54 /// The model the stretch's last run called.
55 #[serde(default)]
56 pub model: Option<String>,
57 /// The effort the stretch's last run froze; `None` when it left the
58 /// provider to choose, and on lines written before the key existed.
59 #[serde(default)]
60 pub effort: Option<Effort>,
61 /// The worktree the stretch's last run was lent.
62 #[serde(default)]
63 pub workspace: Option<String>,
64 /// The start of the stretch's last reply, at most
65 /// [`SESSION_PREVIEW_MAX`] characters.
66 #[serde(default)]
67 pub preview: Option<String>,
68}
69
70/// How a stretch began.
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(rename_all = "snake_case")]
73#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
74pub enum SessionStart {
75 /// Dispatching into the room opened it; no `session_opened` was
76 /// written. `by` is who dispatched, as that first `run_started`
77 /// records it; `None` for a line written before the key existed (D40).
78 Dispatched {
79 #[cfg_attr(feature = "schema", schemars(with = "Option<String>"))]
80 by: Option<kernel::event::Who>,
81 },
82 /// `/new`: what crossed from the stretch before - `Handoff` when the
83 /// previous stretch's handoff travelled - and the run line it branched
84 /// from, when it is a branch.
85 Opened { carry: Carry, from: Option<Origin> },
86}