pub struct Session {
pub meta: SessionMeta,
pub path: PathBuf,
}Fields§
§meta: SessionMeta§path: PathBufImplementations§
Source§impl Session
impl Session
Sourcepub fn default_dir() -> Result<PathBuf>
pub fn default_dir() -> Result<PathBuf>
Where transcripts live: ~/.mecha/sessions, or $MECHA_SESSION_DIR.
pub fn create(dir: &Path, meta: SessionMeta) -> Result<Self>
pub fn new_id() -> String
pub fn append(&self, record: &Record) -> Result<()>
pub fn append_messages(&self, messages: &[Message]) -> Result<()>
Sourcepub fn record_run(&self, before: &[Message], convo: &Conversation) -> Result<()>
pub fn record_run(&self, before: &[Message], convo: &Conversation) -> Result<()>
Record what a run did to the conversation, given the messages it started from.
before must be what the file already holds — every front-end has
appended the opening user message (and, resumed, the loaded history)
before the run starts. The walk visits every state the run’s rewrites
replaced (Conversation::rewritten) and then the final one, so a
run long enough to compact itself still gets its whole head into
the file: each pre-rewrite snapshot extends the previous recorded
state append-only (its cheap tail append), and each post-rewrite
state lands as the Record::Rewrite the next transition writes.
The signature takes the conversation rather than a message slice so a
caller cannot record the destination while skipping the journey.
Sourcepub fn record_outcome(&self, outcome: &RunOutcome) -> Result<()>
pub fn record_outcome(&self, outcome: &RunOutcome) -> Result<()>
Record how the run went, beside what it said.
Separate from record_run rather than folded into it, because the
two answer to different failures: a run that errored mid-flight still
has messages worth keeping and no outcome to describe, and a caller
that has an outcome always has it after the transcript is safe.
Deliberately takes the whole outcome rather than the fields, so a new
counter reaches every front-end by upgrading rather than by
remembering to thread it through six call sites.
Sourcepub fn outcomes_attributed(
path: &Path,
) -> Result<Vec<(String, String, RunStats)>>
pub fn outcomes_attributed( path: &Path, ) -> Result<Vec<(String, String, RunStats)>>
Every outcome recorded in a transcript, in order, with the model and provider that were in effect when it was written.
Not the session header: the TUI can switch model mid-session and
records a Config when it does, so attributing every run to the
header would credit the second model’s work to the first — and defeat
the per-model split in exactly the case where blending actually
happens. Falls back to the header when no Config precedes the row,
which is what an older transcript looks like.
Sourcepub fn outcomes(path: &Path) -> Result<Vec<RunStats>>
pub fn outcomes(path: &Path) -> Result<Vec<RunStats>>
Every outcome recorded in a transcript, in order.
One per run, so a resumed session has several. Malformed lines are skipped rather than fatal, like every other reader here: a torn line is the store’s problem and must not cost the rows around it.
Sourcepub fn load(path: &Path) -> Result<(SessionMeta, Conversation)>
pub fn load(path: &Path) -> Result<(SessionMeta, Conversation)>
Read a transcript back, taint included.
Unparseable lines are skipped rather than failing the load — a truncated final line is the normal result of a killed process.
Sourcepub fn messages_ever(transcript: &str) -> Vec<Message>
pub fn messages_ever(transcript: &str) -> Vec<Message>
Every message the conversation ever contained, in first-seen order.
Message records are the append-only common case. A Rewrite record is a
compaction (or eviction, or thinning) replacing the list in place — for
loading a session the replacement is the truth, but for a reader asking what the conversation ever held the whole
point is what the replacement dropped, so its messages are unioned in
rather than substituted: anything new (the summary, an edited result)
joins the corpus, anything already seen is skipped. Malformed lines are
skipped exactly as crate::session::Session::load skips them — a
truncated final line is the normal residue of a killed process.
Sourcepub fn taint_timeline(path: &Path) -> Result<TaintTimeline>
pub fn taint_timeline(path: &Path) -> Result<TaintTimeline>
The taint checkpoints of a transcript, positioned against its messages.
Every front-end appends a Record::Taint checkpoint after the
messages of the run it describes, so the checkpoint that covers a
message is the first one written after it — and by then the taint of
everything earlier in that run, hostile fetches included, has merged
in. That ordering is what makes TaintTimeline::covering safe to
gate on: it can over-taint a message (a fetch later in the same run
counts against it), never under-taint one.
Sourcepub fn run_configs(path: &Path) -> Result<Vec<RunConfig>>
pub fn run_configs(path: &Path) -> Result<Vec<RunConfig>>
Every run configuration in a transcript, in the order the runs happened.
A replay driver needs this per run rather than per session: resuming under different flags is a normal thing to do, and the turns before and after are not comparable. An empty result means a transcript written before this was recorded — which cannot be replayed faithfully, because the system prompt and tool list that shaped it are gone.
Sourcepub fn peek_meta(path: &Path) -> Option<SessionMeta>
pub fn peek_meta(path: &Path) -> Option<SessionMeta>
The header alone, without parsing the rest of the file.
Listing goes through this rather than Session::load so mecha sessions stays O(number of sessions) instead of O(total transcript
bytes) — with reflect-on-close recording every interaction, the full
parse re-read the whole store to print one line per file. The header
is the first record create writes; a file whose first record is
anything else is not a session this process wrote, and is skipped
exactly as load’s no-header error skipped it.