Read a session's transcript as the file it is
The conversation on screen is a drawing of the record, and when the two disagree -- or when something in the record is what has gone wrong -- there was no way to see the record itself from the phone. View raw in the session settings dialog opens the file explorer on the transcript, which is its third caller and needed no new screen: the file name and path in the header, a back button, and the lines as code. The session says where the file is, because only the backend knows, and it names this backend's machine rather than the session's -- for a remote session those are two different filesystems. Back from the file lands in the session's own directory, where the log and the process record are. The paragraph under Reload goes, and both buttons move to a line of their own: two buttons and a measurement do not fit a phone's width.
This commit is contained in:
1 parent
049780fda6
commit
66b3403a71
8 files changed
+123
-12
No files matched your search
@@ -37,7 +37,10 @@
|
||||
//! PUT /machines/{id} rename {name?} and/or re-probe {rediscover?}
|
||||
//! DELETE /machines/{id} remove, refused while sessions use it
|
||||
//! GET /sessions list (id, provider, title, model, status, last activity)
|
||||
//! GET /sessions/{id} one session, for refetching after a change
|
||||
//! GET /sessions/{id} one session, for refetching after a change. Its
|
||||
//! `transcriptFile` is where the record itself is --
|
||||
//! {machine, machineName, path} -- so the explorer can be
|
||||
//! pointed at it
|
||||
//! POST /sessions spawn {machine, provider, title?, model?, cwd?, params?}
|
||||
//! POST /sessions/order {sessions} -- the list in the order it is drawn in,
|
||||
//! as the reader dragged it; ids left out keep their
|
||||
|
||||
@@ -316,6 +316,10 @@ pub struct SessionInfo {
|
||||
/// Absent until it reports one; absence is not a measured zero.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub background_tasks: Option<usize>,
|
||||
/// Where the transcript file itself is, for a reader who wants the record
|
||||
/// rather than the conversation drawn from it -- see [`TranscriptFile`].
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub transcript_file: Option<TranscriptFile>,
|
||||
/// How many subagents this session has started, from a directory
|
||||
/// listing rather than reading each one's status -- see
|
||||
/// `GET /sessions/{id}/subagents` for that. 0 when it has none, not
|
||||
@@ -323,6 +327,37 @@ pub struct SessionInfo {
|
||||
pub subagents: usize,
|
||||
}
|
||||
|
||||
/// Where a session's transcript file is, as somewhere the file explorer can
|
||||
/// be pointed at.
|
||||
///
|
||||
/// The machine as well as the path, and it is **this backend's** machine
|
||||
/// rather than the session's: the file is written here whichever machine the
|
||||
/// session runs on, and for a remote session those are two different
|
||||
/// filesystems. `None` where this backend's own machine is not in the config
|
||||
/// at all, which is the state in which nothing could read the file anyway --
|
||||
/// and the phone then offers no way in, rather than one that fails.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TranscriptFile {
|
||||
pub machine: String,
|
||||
pub machine_name: String,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
/// Where `id`'s transcript is, said the way the explorer takes it.
|
||||
fn transcript_file(config: &Config, data_dir: &Path, id: &str) -> Option<TranscriptFile> {
|
||||
let machine = config.machine(crate::config::LOCAL_MACHINE_ID)?;
|
||||
Some(TranscriptFile {
|
||||
machine: machine.id.clone(),
|
||||
machine_name: machine.name.clone(),
|
||||
path: data_dir
|
||||
.join(id)
|
||||
.join("transcript.jsonl")
|
||||
.to_string_lossy()
|
||||
.into_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
/// What is running a session at this moment, and `None` when nothing is.
|
||||
///
|
||||
/// Behind a lock because a session outlives its process: stopping one and
|
||||
@@ -673,6 +708,7 @@ impl LiveSession {
|
||||
current: &SessionConfig,
|
||||
imported: bool,
|
||||
kind: Option<DriverKind>,
|
||||
transcript_file: Option<TranscriptFile>,
|
||||
) -> SessionInfo {
|
||||
SessionInfo {
|
||||
id: self.meta.id.clone(),
|
||||
@@ -717,6 +753,7 @@ impl LiveSession {
|
||||
.driver()
|
||||
.and_then(|driver| driver.background_tasks())
|
||||
.map(|tasks| tasks.len()),
|
||||
transcript_file,
|
||||
subagents: subagent::count(self.dir()),
|
||||
}
|
||||
}
|
||||
@@ -1234,6 +1271,7 @@ impl SessionManager {
|
||||
meta,
|
||||
import::read_cursor(&self.data_dir.join(&meta.id)).is_some(),
|
||||
kind_of(&inner.config, &meta.machine, &meta.provider),
|
||||
transcript_file(&inner.config, &self.data_dir, &meta.id),
|
||||
),
|
||||
None => SessionInfo {
|
||||
id: meta.id.clone(),
|
||||
@@ -1274,6 +1312,7 @@ impl SessionManager {
|
||||
last_activity: meta.created,
|
||||
created: meta.created,
|
||||
background_tasks: None,
|
||||
transcript_file: transcript_file(&inner.config, &self.data_dir, &meta.id),
|
||||
subagents: subagent::count(&self.data_dir.join(&meta.id)),
|
||||
},
|
||||
})
|
||||
@@ -1491,6 +1530,7 @@ impl SessionManager {
|
||||
&session.meta,
|
||||
import::read_cursor(&self.data_dir.join(&id)).is_some(),
|
||||
Some(provider.kind),
|
||||
transcript_file(&inner.config, &self.data_dir, &id),
|
||||
);
|
||||
inner.live.insert(id, session);
|
||||
Ok(info)
|
||||
@@ -3487,7 +3527,7 @@ mod tests {
|
||||
// open to look one up on.
|
||||
assert_eq!(
|
||||
first.title,
|
||||
session.info("m", &session.meta, false, None).title
|
||||
session.info("m", &session.meta, false, None, None).title
|
||||
);
|
||||
|
||||
manager.set_session_notify(&info.id, false).expect("off");
|
||||
|
||||
Reference in new issue
Block a user