Support Codex subagent transcripts
This commit is contained in:
1 parent
7d9df5d572
commit
83b113ef0f
6 files changed
+557
-38
No files matched your search
+24
-11
@@ -1,9 +1,11 @@
|
||||
//! Codex CLI sessions over its persistent app-server protocol.
|
||||
//!
|
||||
//! One app-server owns one conversation. Unlike `codex exec --json`, this
|
||||
//! surface can interrupt a turn without killing the connection and can steer
|
||||
//! an active turn through `turn/steer`. Its stdio is a fifo plus logs so both
|
||||
//! the process and an in-flight turn survive an ai-server restart.
|
||||
//! One app-server owns one conversation tree. Unlike `codex exec --json`,
|
||||
//! this surface can interrupt a turn without killing the connection and can
|
||||
//! steer an active turn through `turn/steer`. Child threads are multiplexed
|
||||
//! onto the same stdout and routed into subagent transcripts. Its stdio is a
|
||||
//! fifo plus logs so both the process and in-flight turns survive an
|
||||
//! ai-server restart.
|
||||
|
||||
mod translate;
|
||||
|
||||
@@ -22,6 +24,7 @@ use super::driver::{
|
||||
AttachmentRef, Driver, Event, EventSink, SessionStatus, Unqueued, store_image,
|
||||
};
|
||||
use super::process;
|
||||
use super::subagent::Subagents;
|
||||
use super::transport::{Launch, Streams, Transport};
|
||||
use crate::config::{ProviderConfig, SessionConfig};
|
||||
use translate::Translator;
|
||||
@@ -96,6 +99,7 @@ struct Inner {
|
||||
to_child: mpsc::UnboundedSender<String>,
|
||||
transport: Transport,
|
||||
session_dir: PathBuf,
|
||||
subagents: Arc<Subagents>,
|
||||
reading: AtomicBool,
|
||||
}
|
||||
|
||||
@@ -110,6 +114,7 @@ impl CodexDriver {
|
||||
transport: Transport,
|
||||
session_dir: &Path,
|
||||
sink: EventSink,
|
||||
subagents: Arc<Subagents>,
|
||||
) -> Result<Self> {
|
||||
let mut state = read_state(session_dir);
|
||||
let recorded = process::recorded(session_dir);
|
||||
@@ -170,6 +175,7 @@ impl CodexDriver {
|
||||
to_child,
|
||||
transport,
|
||||
session_dir: session_dir.to_path_buf(),
|
||||
subagents,
|
||||
reading: AtomicBool::new(true),
|
||||
});
|
||||
|
||||
@@ -577,7 +583,11 @@ fn spawn_follower(inner: Arc<Inner>, record: process::Record) {
|
||||
async fn follow(inner: Arc<Inner>, mut record: process::Record, mut offset: u64) {
|
||||
let stdout = inner.session_dir.join(STDOUT_LOG);
|
||||
let stderr = inner.session_dir.join(STDERR_LOG);
|
||||
let mut translator = Translator::default();
|
||||
let mut translator = Translator::new(
|
||||
Arc::clone(&inner.subagents),
|
||||
read_thread(&inner.session_dir),
|
||||
inner.state.lock().unwrap().active_turn.is_some(),
|
||||
);
|
||||
// `offset` is the durable boundary after the last complete record. `read_at` may move beyond
|
||||
// it while app-server is still writing one record. Image-bearing tool results can be several
|
||||
// megabytes long, and rereading their incomplete prefix every 50 ms made arrival over ssh
|
||||
@@ -664,7 +674,8 @@ fn handle_line(inner: &Arc<Inner>, translator: &mut Translator, line: &Value) {
|
||||
}
|
||||
let method = line.get("method").and_then(Value::as_str);
|
||||
let params = line.get("params").unwrap_or(&Value::Null);
|
||||
match method {
|
||||
let parent = translator.is_parent(line);
|
||||
match method.filter(|_| parent) {
|
||||
Some("thread/started") => {
|
||||
if let Some(thread) = params.pointer("/thread/id").and_then(Value::as_str) {
|
||||
write_thread(&inner.session_dir, thread);
|
||||
@@ -697,9 +708,6 @@ fn handle_line(inner: &Arc<Inner>, translator: &mut Translator, line: &Value) {
|
||||
if item.get("type").and_then(Value::as_str) == Some("userMessage") {
|
||||
announce_user(inner, item);
|
||||
}
|
||||
for event in image_events(inner, item) {
|
||||
let _ = inner.sink.send(event);
|
||||
}
|
||||
}
|
||||
Some("turn/completed") => {
|
||||
let mut state = inner.state.lock().unwrap();
|
||||
@@ -711,10 +719,15 @@ fn handle_line(inner: &Arc<Inner>, translator: &mut Translator, line: &Value) {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
for event in translator.translate(line) {
|
||||
let images = if method == Some("item/completed") {
|
||||
image_events(inner, ¶ms["item"])
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
for event in translator.translate_with_prefix(line, images) {
|
||||
let _ = inner.sink.send(event);
|
||||
}
|
||||
if method == Some("turn/completed") {
|
||||
if parent && method == Some("turn/completed") {
|
||||
dispatch_waiting(inner);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user