Skip to main content

Session

Struct Session 

Source
pub struct Session {
    pub meta: SessionMeta,
    pub path: PathBuf,
}

Fields§

§meta: SessionMeta§path: PathBuf

Implementations§

Source§

impl Session

Source

pub fn default_dir() -> Result<PathBuf>

Where transcripts live: ~/.mecha/sessions, or $MECHA_SESSION_DIR.

Source

pub fn create(dir: &Path, meta: SessionMeta) -> Result<Self>

Source

pub fn new_id() -> String

Source

pub fn append(&self, record: &Record) -> Result<()>

Source

pub fn append_messages(&self, messages: &[Message]) -> Result<()>

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn episode_stats(path: &Path) -> Result<Option<RunStats>>

The most recent outcome, without parsing the transcript that precedes it.

outcomes reads every line because it answers “how did each run on this session go” — right for the corpus, and wrong for a display asking only where a session stands now. A transcript is mostly messages and an outcome is appended last, so scanning backwards finds it in one parse instead of thousands.

Ok(None) means the transcript held no outcome at all, which is a third answer and not a failure: a run that never got as far as recording one, or a session written before the record existed. Callers must not fold it into either success or failure.

Still one reader of the record format — this lives beside outcomes rather than in a caller, so a change to Record cannot leave a second, private parser behind. Every outcome a session recorded, folded into the episode it describes.

last_outcome answers a different question — how the session ended — and using it as an episode’s stats is a unit mismatch: a resumed chat records one row per run, while anything replaying the session drives every recorded user turn and folds all of them.

Source

pub fn last_outcome(path: &Path) -> Result<Option<RunStats>>

Source

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.

Source

pub fn read(path: &Path) -> Result<Transcript>

Everything a reader can want from a transcript, in one pass.

load, run_configs and episode_stats each open the file and walk every line, so a caller that wants all three pays three reads and three parses of the same JSONL. That is fine for a one-off and is not fine for harness_probe’s pool, which considers four times the wanted episode count on every nightly — sixty-four transcripts, hundreds of KB apiece, read three times each to answer questions one walk can answer together.

The three keep their own entry points, because most callers want one thing and a caller that wants one thing should not have to hold a header it has no use for. This is the seam for the caller that wants all of them.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn usage_totals(path: &Path) -> Result<(Usage, u32)>

The run summaries of a transcript, summed: total usage and turns across every run the file records. Zero for a transcript that predates the summary record or died before writing one — an honest under-count, never a guess.

Source

pub fn list(dir: &Path) -> Result<Vec<(SessionMeta, PathBuf)>>

Sessions in dir, newest first.

A transcript whose header cannot be read is skipped so the walk stays best-effort — but skipped is not forgotten: callers that report on the store should use Session::list_counting, because a store rotting one file at a time is otherwise invisible from every reader at once (“an unreadable store is a finding, not an empty queue” — the outbox gets outbox_unreadable for exactly this, and the session store got nothing).

Source

pub fn list_counting(dir: &Path) -> Result<(Vec<(SessionMeta, PathBuf)>, usize)>

Session::list, plus how many .jsonl files were skipped because no header could be read from them — a torn write, a corrupt file, a permissions hole. The count is the reader’s to surface; the walk itself stays best-effort either way.

Source

pub fn find(dir: &Path, id_prefix: &str) -> Result<PathBuf>

Find a session by full id or unique prefix.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more