Use native Codex steering and transcript deletion
This commit is contained in:
1 parent
00538cc19b
commit
8c88a7e991
12 files changed
+1004
-390
No files matched your search
+71
-14
@@ -205,6 +205,9 @@ pub struct SessionInfo {
|
||||
/// Reported rather than worked out on the phone, because the phone has
|
||||
/// the provider's *name* and this is a property of its *kind*.
|
||||
pub keeps_own_transcript: bool,
|
||||
/// The CLI whose copy the delete dialog can optionally remove.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub own_transcript_name: Option<&'static str>,
|
||||
/// How much this session asks before acting. Reported so the phone can
|
||||
/// *show* the current mode rather than assume one -- a picker that
|
||||
/// guesses its own value is how you change something you thought you
|
||||
@@ -600,6 +603,7 @@ impl LiveSession {
|
||||
usage_provider: kind.and_then(DriverKind::usage_provider),
|
||||
imported,
|
||||
keeps_own_transcript: kind.is_some_and(DriverKind::keeps_own_transcript),
|
||||
own_transcript_name: kind.and_then(DriverKind::own_transcript_name),
|
||||
cwd: cwd.map(Path::to_path_buf),
|
||||
status: *self.shared.status.lock().unwrap(),
|
||||
last_activity: *self.shared.last_activity.lock().unwrap(),
|
||||
@@ -625,9 +629,8 @@ pub struct SessionManager {
|
||||
/// Where every session's pump reports what this layer does not act on --
|
||||
/// see [`Announcements`].
|
||||
announce: Announcements,
|
||||
/// Imports and deletes running against a machine's Claude Code
|
||||
/// sessions: like the notifications, state the phone reads but does not
|
||||
/// own.
|
||||
/// Provider-transcript operations running against a machine: like the
|
||||
/// notifications, state the phone reads but does not own.
|
||||
pending: Arc<pending::Registry>,
|
||||
/// What to mark sessions spawned here as -- see
|
||||
/// [`SessionManager::marking_new_sessions_throwaway`].
|
||||
@@ -640,6 +643,38 @@ pub struct SessionManager {
|
||||
inner: RwLock<Inner>,
|
||||
}
|
||||
|
||||
/// The CLI-owned copy optionally removed with an ai-app session.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ForeignTranscript {
|
||||
pub setup: String,
|
||||
pub id: String,
|
||||
kind: DriverKind,
|
||||
}
|
||||
|
||||
impl ForeignTranscript {
|
||||
/// Removes the provider's own durable record. The route remains generic:
|
||||
/// adding another transcript-owning driver extends this provider boundary.
|
||||
pub async fn delete(&self, transport: &Transport) -> Result<()> {
|
||||
match self.kind {
|
||||
DriverKind::ClaudeCli => import::delete(transport, std::slice::from_ref(&self.id))
|
||||
.await?
|
||||
.remove(&self.id)
|
||||
.unwrap_or_else(|| Err(format!("nothing was reported about {}", self.id)))
|
||||
.map_err(anyhow::Error::msg),
|
||||
DriverKind::CodexCli => codex::delete_transcript(transport, &self.id).await,
|
||||
DriverKind::Echo | DriverKind::LlamaCpp => {
|
||||
anyhow::bail!("this provider keeps no transcript of its own")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn owner_name(&self) -> &'static str {
|
||||
self.kind
|
||||
.own_transcript_name()
|
||||
.expect("a foreign transcript has an owner")
|
||||
}
|
||||
}
|
||||
|
||||
impl SessionManager {
|
||||
/// Loads the config and brings every persisted session back: its
|
||||
/// transcript, its pump, and the process it left running where it left
|
||||
@@ -1034,15 +1069,26 @@ impl SessionManager {
|
||||
Some((setup.ssh.clone()?, meta.cwd.clone()))
|
||||
}
|
||||
|
||||
pub fn foreign_transcript(&self, id: &str) -> Option<(String, String)> {
|
||||
pub fn foreign_transcript(&self, id: &str) -> Option<ForeignTranscript> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let meta = inner.config.sessions.iter().find(|meta| meta.id == id)?;
|
||||
let (followed, resuming) = foreign_ids(&self.data_dir.join(&meta.id));
|
||||
// The cursor first: an imported session follows a file that exists
|
||||
// whether or not a CLI has resumed it yet.
|
||||
followed
|
||||
.or(resuming)
|
||||
.map(|foreign| (meta.setup.clone(), foreign))
|
||||
let kind = kind_of(&inner.config, &meta.setup, &meta.provider)?;
|
||||
let session_dir = self.data_dir.join(&meta.id);
|
||||
let foreign = match kind {
|
||||
DriverKind::ClaudeCli => {
|
||||
let (followed, resuming) = foreign_ids(&session_dir);
|
||||
// The cursor first: an imported session follows a file that exists
|
||||
// whether or not a CLI has resumed it yet.
|
||||
followed.or(resuming)
|
||||
}
|
||||
DriverKind::CodexCli => codex::read_thread(&session_dir),
|
||||
DriverKind::Echo | DriverKind::LlamaCpp => None,
|
||||
}?;
|
||||
Some(ForeignTranscript {
|
||||
setup: meta.setup.clone(),
|
||||
id: foreign,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Every session, in config order, with live status joined in. A
|
||||
@@ -1088,6 +1134,8 @@ impl SessionManager {
|
||||
&meta.setup,
|
||||
&meta.provider,
|
||||
),
|
||||
own_transcript_name: kind_of(&inner.config, &meta.setup, &meta.provider)
|
||||
.and_then(DriverKind::own_transcript_name),
|
||||
cwd: meta.cwd.clone(),
|
||||
status: status_of_unlaunched(&self.data_dir.join(&meta.id)),
|
||||
last_activity: meta.created,
|
||||
@@ -2804,6 +2852,8 @@ mod tests {
|
||||
#[test]
|
||||
fn only_a_driver_that_keeps_its_own_record_survives_deletion() {
|
||||
assert!(DriverKind::ClaudeCli.keeps_own_transcript());
|
||||
assert!(DriverKind::CodexCli.keeps_own_transcript());
|
||||
assert_eq!(DriverKind::CodexCli.own_transcript_name(), Some("Codex"));
|
||||
assert!(!DriverKind::Echo.keeps_own_transcript());
|
||||
assert!(!DriverKind::LlamaCpp.keeps_own_transcript());
|
||||
}
|
||||
@@ -3268,14 +3318,17 @@ mod tests {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let config_path = dir.path().join("config.ron");
|
||||
let data_dir = dir.path().join("sessions");
|
||||
seed_echo_only(&config_path);
|
||||
let provider = seed_stand_in_cli(&config_path, dir.path());
|
||||
let manager = SessionManager::new(
|
||||
config_path.clone(),
|
||||
data_dir.clone(),
|
||||
data_dir.join("models"),
|
||||
)
|
||||
.expect("manager");
|
||||
let info = manager.spawn_session(echo_spec()).expect("spawn");
|
||||
.expect("manager")
|
||||
.marking_new_sessions_throwaway(true);
|
||||
let info = manager
|
||||
.spawn_session(stand_in_spec(&provider))
|
||||
.expect("spawn");
|
||||
|
||||
// Nothing recorded yet, so there is nothing a delete would reach.
|
||||
assert_eq!(manager.foreign_transcript(&info.id), None);
|
||||
@@ -3283,7 +3336,11 @@ mod tests {
|
||||
claude::write_resume_token(&data_dir.join(&info.id), "5ecf21da-d53f");
|
||||
assert_eq!(
|
||||
manager.foreign_transcript(&info.id),
|
||||
Some((info.setup.clone(), "5ecf21da-d53f".to_string()))
|
||||
Some(ForeignTranscript {
|
||||
setup: info.setup.clone(),
|
||||
id: "5ecf21da-d53f".to_string(),
|
||||
kind: DriverKind::ClaudeCli,
|
||||
})
|
||||
);
|
||||
|
||||
// A session that is not there has no transcript to name, rather
|
||||
|
||||
Reference in new issue
Block a user