Recover Codex sessions with missing rollouts
This commit is contained in:
1 parent
3af2502982
commit
46831520e3
3 files changed
+158
-9
No files matched your search
@@ -42,12 +42,13 @@ impl Translator {
|
||||
if line.get("id").is_some() && line.get("method").is_none() {
|
||||
return true;
|
||||
}
|
||||
if line.get("method").and_then(Value::as_str) == Some("thread/started")
|
||||
&& line
|
||||
.pointer("/params/thread/parentThreadId")
|
||||
.is_some_and(|parent| !parent.is_null())
|
||||
{
|
||||
return false;
|
||||
let kind = line.get("method").and_then(Value::as_str);
|
||||
let body = kind.and_then(|_| line.get("params")).unwrap_or(line);
|
||||
if let Some(root) = started_thread_is_root(kind, body) {
|
||||
// A clear or missing-rollout recovery replaces the root id inside this same
|
||||
// app-server. The new root cannot match the id this translator still holds; its null
|
||||
// parent is the authoritative distinction from a newly spawned subagent.
|
||||
return root;
|
||||
}
|
||||
match (self.thread_id.as_deref(), notification_thread(line)) {
|
||||
(Some(parent), Some(thread)) => parent == thread,
|
||||
@@ -80,10 +81,13 @@ impl Translator {
|
||||
self.remember_spawned_thread(kind, body);
|
||||
|
||||
let thread = notification_thread(line);
|
||||
let root_started = started_thread_is_root(kind, body) == Some(true);
|
||||
let child = thread.filter(|thread| {
|
||||
self.thread_id
|
||||
.as_deref()
|
||||
.is_some_and(|parent| parent != *thread)
|
||||
!root_started
|
||||
&& self
|
||||
.thread_id
|
||||
.as_deref()
|
||||
.is_some_and(|parent| parent != *thread)
|
||||
});
|
||||
if let Some(id) = child {
|
||||
self.ensure_child(id, "subagent", None);
|
||||
@@ -467,6 +471,13 @@ fn subagent_title(path: &str) -> String {
|
||||
.replace('_', " ")
|
||||
}
|
||||
|
||||
fn started_thread_is_root(kind: Option<&str>, body: &Value) -> Option<bool> {
|
||||
(kind == Some("thread/started")).then(|| {
|
||||
body.pointer("/thread/parentThreadId")
|
||||
.is_none_or(Value::is_null)
|
||||
})
|
||||
}
|
||||
|
||||
fn start_item(item: &Value) -> Vec<Event> {
|
||||
if matches!(
|
||||
item.get("type").and_then(Value::as_str),
|
||||
@@ -935,6 +946,25 @@ mod tests {
|
||||
assert!(translator.limited());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_replacement_root_is_not_mistaken_for_a_subagent() {
|
||||
let mut translator = Translator {
|
||||
thread_id: Some("old-root".to_string()),
|
||||
..Translator::default()
|
||||
};
|
||||
let replacement = line(
|
||||
r#"{"method":"thread/started","params":{"thread":{"id":"new-root","parentThreadId":null}}}"#,
|
||||
);
|
||||
assert!(translator.is_parent(&replacement));
|
||||
assert!(translator.translate(&replacement).is_empty());
|
||||
assert_eq!(translator.thread_id.as_deref(), Some("new-root"));
|
||||
|
||||
let child = line(
|
||||
r#"{"method":"thread/started","params":{"thread":{"id":"child","parentThreadId":"new-root"}}}"#,
|
||||
);
|
||||
assert!(!translator.is_parent(&child));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_translation_only_hides_the_known_bash_wrapper() {
|
||||
let legacy = tool(&line(
|
||||
|
||||
Reference in new issue
Block a user