diff --git a/docs/agent-resume.md b/docs/agent-resume.md index 0d7953d..3190895 100644 --- a/docs/agent-resume.md +++ b/docs/agent-resume.md @@ -105,7 +105,7 @@ Live validation on 2026-09-28 was performed with the official macOS arm64 Codex The [recorded terminal fixtures](../tests/corpus/codex_0158_terminal/README.md) preserve resumed-history and clipboard output from those runs. Offline replay checks the recorded display and OSC 52 forwarding; it does not run the notifier or execute save/load. The separate `tests/daemon_resume.rs` integration tests exercise session resume with stub agents. -Live validation on 2026-10-04 was performed with codex-cli 0.160.0 over nine TUI sessions. Three thread IDs were reported from one process: the unsaved title thread, a sub-agent, and the root. The title thread's notification arrived before the root's in three sessions and after it in six, and no rollout appeared for it even minutes later. For a root or sub-agent turn, the thread's rollout header was on disk at least 2.5 s before its notification, and `task_complete` was recorded 21–256 ms before it. When resuming the title thread, "No saved session found" was reported; for the sub-agent, "cannot resume an unloaded multi-agent v2 sub-agent through its parent" was reported. With the notify override alone, `⚠ 1 warning` was displayed in the footer; with both overrides, none was displayed. With an unknown `features.*` key, startup completed with an unrecognized-setting warning. No Codex version predating `daemon_auto_start` was tested. On the same date, 493 local rollout headers from Codex 0.135.0 through 0.160.0 were examined: `id` and `session_id` were present in each. In Codex 0.140.0 and 0.141.0, a sub-agent's own ID was stored as its `session_id`; that format is rejected. +Live validation on 2026-10-04 was performed with codex-cli 0.160.0 over nine TUI sessions. Three thread IDs were reported from one process: the unsaved title thread, a sub-agent, and the root. The title thread's notification arrived before the root's in three sessions and after it in six, and no rollout appeared for it even minutes later. For a root or sub-agent turn, the thread's rollout header was on disk at least 2.5 s before its notification, and `task_complete` was recorded 21–256 ms before it. When resuming the title thread, "No saved session found" was reported; for the sub-agent, "cannot resume an unloaded multi-agent v2 sub-agent through its parent" was reported. With the notify override alone, `⚠ 1 warning` was displayed in the footer; with both overrides, none was displayed. With an unknown `features.*` key, startup completed with an unrecognized-setting warning. No Codex version predating `daemon_auto_start` was tested. ### `grok` @@ -129,7 +129,6 @@ At `agent_end`, after every turn, report two changes without dedicated events: s As a result: - For a fresh `omp` task, no ID is captured until the end of the first turn. If you save before then, no ID is stored, so you start fresh on reload. -- Before omp 18.3.2, `ctx.agent` is unavailable, so no session ID is captured: an `omp` task is always saved without an ID. - After switching or branching to a session without a file on disk, continue using the previous ID until the end of the next turn. Write each report to a temporary file beside the capture file, then rename it over the destination to avoid reads of partial payloads. Capture is best-effort: return immediately for an empty capture path and ignore write errors. diff --git a/docs/sessions.md b/docs/sessions.md index da303b1..2459064 100644 --- a/docs/sessions.md +++ b/docs/sessions.md @@ -46,7 +46,7 @@ A session file is a JSON object with three fields: `version` for the format vers - Values are ordered lists, loaded in order. Each task is started in its own PTY under the specified directory. - Directories are serialized alphabetically, with entry order preserved within each directory. -The `version` field must be an integer from 1 through the newest format supported by the running `fleetcom`. A missing field means version 1. Invalid or unsupported versions are refused on load, with the file's value and supported version in the error. A version 2 file read by an older `fleetcom` is refused by that same gate. +The `version` field must be an integer from 1 through the newest format supported by the running `fleetcom`. A missing field means version 1. A version 1 file loads with every entry literal, including `claude` and `claude --resume ''`, and they are written back as literals on save; to track such a task's conversation, launch it from the Agent page and save again. Invalid or unsupported versions are refused on load, with the file's value and supported version in the error. A version 2 file read by an older `fleetcom` is refused by that same gate. The schema is identified by shape, independently of the version. With an object-valued `dirs`, the wrapped form above is used. A flat map is also accepted, with directories as top-level keys and entry arrays as values. In that form, an array-valued key named `dirs` remains a directory entry, but a top-level `version` member is always the format version, never a directory. Flat-map files are listed by filename stem because no name is stored. On save, the wrapped form is written, without a stored-name collision check. @@ -60,20 +60,6 @@ After validation, group and display names are stripped of control characters and Use the string form for commands with neither a group nor a name. String and object entries can appear in the same directory array. -### Loading version 1 files - -In format 1, instrumented tasks were stored as command strings: a bare program word, or the word followed by its resume selector and a single-quoted UUID. When loading a file with `version` 1 or no version field, convert exactly those two forms to managed entries: - -| Version 1 entry | Loads as | -| -- | -- | -| `claude`, `codex`, `grok`, or `omp` alone | managed, fresh conversation | -| `claude --resume ''`, `codex resume ''`, `grok --resume ''`, or `omp --resume ''`, with or without the quotes | managed, resuming that ID | -| anything else | literal, unchanged | - -Tabs and repeated spaces between tokens are still accepted. Commands with paths (`/usr/local/bin/claude`, `~/bin/claude`) are loaded as literals to preserve your chosen binary instead of substituting one found on `PATH`; managed resume tracking is then unavailable. Commands with extra arguments, prompts, other resume spellings, or shell syntax remain literal, as before. No conversion is applied to version 2 files: a literal `claude` stays literal. - -Recovery snapshots are rewritten continually in the new format; named sessions are converted on the next save. This conversion will be removed one release after format 2 ships. After that, all entries in unconverted version 1 files will be loaded as literals. - ## Recovery The current task set is automatically snapshotted under `recovery/` inside the session directory. A separate file is written for each daemon (or `--foreground` core), named for its start time and process ID. After each write, the new file and files whose process IDs are still live are protected; among the remaining files, the nine newest names are retained. A shared recovery directory can therefore contain more than ten snapshots while multiple writers are live. diff --git a/src/harness/assets.rs b/src/harness/assets.rs index b89db3f..d46ee35 100644 --- a/src/harness/assets.rs +++ b/src/harness/assets.rs @@ -35,10 +35,10 @@ //! replace `$FLEETCOM_CAPTURE_FILE` with `{reason, sessionId, sessionFile, //! cwd}` JSON. Write `..tmp` beside the capture file and //! rename it over the destination to avoid reads of partial payloads. -//! Report only the top-level session (`ctx.agent.kind` is `"main"`) and -//! only when `sessionFile` exists on disk. On omp older than 18.3.2, -//! `ctx.agent` is absent, so write nothing. Skip unset or empty capture -//! paths and ignore all errors. +//! Report only the top-level session (`ctx.agent.kind` is `"main"`), since +//! sub-agents share the handlers and their IDs are not resumable, and only +//! when `sessionFile` exists on disk. Skip unset or empty capture paths and +//! ignore all errors. use std::{ fs, io, @@ -89,9 +89,8 @@ exec "$@" /// Report only an ID usable with `omp --resume`: /// /// - Only the top-level session's ID. The same handlers are registered for -/// sub-agent sessions, with the child's ID in their session manager. -/// - None on an omp older than 18.3.2. `ctx.agent` is absent there, so the -/// top-level session cannot be told from a sub-agent. +/// sub-agent sessions, with the child's ID in their session manager, and a +/// sub-agent's ID is not resumable. /// - Only after the session file exists on disk. For a fresh session, first /// report at `agent_end`. /// @@ -106,7 +105,7 @@ function write(ctx, reason) { const path = process.env.FLEETCOM_CAPTURE_FILE; if (!path) return; // These handlers are also registered for sub-agent sessions, which cannot - // be resumed by ID. Without `ctx.agent` (before 18.3.2), report nothing. + // be resumed by ID: report only the top-level session. if (ctx.agent?.kind !== "main") return; // The session file is created after the first assistant message. Until // then, the ID cannot be found through `omp --resume`. @@ -350,9 +349,8 @@ mod tests { /// Stand-in for omp's extension host. Load the module, collect its /// registrations, and fire one event with a context built from the /// arguments ` `. An empty - /// kind means no `ctx.agent`, as before omp 18.3.2; an empty file means - /// `undefined` from `getSessionFile()`. Print the registered event names - /// in registration order. + /// file means `undefined` from `getSessionFile()`. Print the registered + /// event names in registration order. const OMP_HOST_DRIVER: &str = r#"import register from "./omp-capture.mjs"; const handlers = {}; @@ -360,12 +358,12 @@ register({ on: (event, handler) => (handlers[event] = handler) }); const [event, kind, sessionId, sessionFile] = process.argv.slice(2); const ctx = { cwd: "/work/proj", + agent: { kind }, sessionManager: { getSessionId: () => sessionId, getSessionFile: () => sessionFile || undefined, }, }; -if (kind) ctx.agent = { kind }; handlers[event]({ type: event }, ctx); console.log(Object.keys(handlers).join(" ")); "#; @@ -928,15 +926,9 @@ console.log(Object.keys(handlers).join(" ")); } assert!(!orphan.parent().unwrap().exists()); - // Use an existing file for the sub-agent and absent-`ctx.agent` cases - // to test rejection by agent kind alone. For the top-level cases, - // omit the file on disk. - for (kind, file) in [ - ("sub", session.as_str()), - ("", session.as_str()), - ("main", unwritten), - ("main", ""), - ] { + // Use an existing file for the sub-agent case to test rejection by + // agent kind alone. For the top-level cases, omit the file on disk. + for (kind, file) in [("sub", session.as_str()), ("main", unwritten), ("main", "")] { for event in ["session_start", "agent_end"] { fire(event, kind, ID, file, Some(&cap)); assert!(!cap.exists(), "{event} {kind:?} {file:?} must not report"); diff --git a/src/harness/codex.rs b/src/harness/codex.rs index 06a7294..839b2c1 100644 --- a/src/harness/codex.rs +++ b/src/harness/codex.rs @@ -177,8 +177,9 @@ fn replace_slot(capture: &Path, root: &str) -> io::Result<()> { /// `source` object. /// /// Return `None` unless exactly one rollout is named for `thread` and its -/// header is in one of those two formats. No rollout is saved for the title -/// thread. Examine only directory listings and the first line. +/// header's `session_id` is `thread` itself or, under a `subagent` source, the +/// root. No rollout is saved +/// for the title thread. Examine only directory listings and the first line. fn root_thread(home: &Path, thread: &str) -> Option { let mut rollouts = Vec::new(); collect_rollouts( @@ -197,9 +198,8 @@ fn root_thread(home: &Path, thread: &str) -> Option { return None; } let root = capture_id(meta, "session_id")?; - // Require exactly one condition. In codex 0.140.0 and 0.141.0, a - // sub-agent's own ID was stored as `session_id`, so its root is unknown. - ((root == thread) != meta["source"].has_key("subagent")).then_some(root) + // A foreign root is trusted only from a sub-agent's header. + (root == thread || meta["source"].has_key("subagent")).then_some(root) } /// Push every entry with a name ending in `suffix`, exactly `depth` directory @@ -1027,26 +1027,6 @@ mod tests { assert_eq!(resolve(&home, CHILD).as_deref(), Some(ID)); } - /// In codex 0.140.0 and 0.141.0, a sub-agent's own ID was stored as - /// `session_id`. Reject `id == session_id` with a `subagent` source: - /// the root is unknown, and the sub-agent's ID is not resumable. - #[test] - fn arrival_root_refuses_a_sub_agent_that_names_itself_as_root() { - let home = temp("codex_capture_self_rooted"); - for source in [ - thread_spawn(ID, 1), - r#"{"subagent":{"other":"guardian"}}"#.to_string(), - r#"{"subagent":"review"}"#.to_string(), - ] { - install_codex_rollout( - &home, - CHILD, - codex_session_meta(&child_meta(CHILD, ID, CHILD, &source)), - ); - assert_eq!(resolve(&home, CHILD), None, "{source}"); - } - } - /// No rollout is saved for the title thread. Reject its notification /// with an absent store, an empty store, or rollouts for other threads. #[test] diff --git a/src/harness/omp.rs b/src/harness/omp.rs index 9e46d9a..d228e10 100644 --- a/src/harness/omp.rs +++ b/src/harness/omp.rs @@ -7,8 +7,6 @@ //! the same handlers are registered for them, but their IDs are not resumable. //! Wait until the session file is created after the first assistant message. //! For a fresh launch, no ID is captured until the end of the first turn. -//! Capture is unavailable before omp 18.3.2: without the agent kind in the -//! extension context, top-level sessions cannot be distinguished from sub-agents. //! //! omp's IDs are `UUIDv7`. [`is_uuid`](super::is_uuid) validates the 8-4-4-4-12 //! lowercase-hex shape and not the version field, so they pass unchanged. diff --git a/src/harness/summary.rs b/src/harness/summary.rs index e2ab5d1..81cb57a 100644 --- a/src/harness/summary.rs +++ b/src/harness/summary.rs @@ -44,8 +44,6 @@ pub const AWAITING_APPROVAL: &str = "awaiting approval"; /// Classify each emitted anchor rule ID as working or idle. Return `None` for IDs no /// adapter emits. List every emitted ID so the corpus test covers each classification. -/// Treat `grok:still-running` as idle because background subagents may remain active -/// after the turn ends. pub fn rule_reports_working(rule: &str) -> Option { match rule { "claude:spinner" | "claude:action-row" | "codex:working" | "grok:spinner" => Some(true), @@ -53,8 +51,8 @@ pub fn rule_reports_working(rule: &str) -> Option { | "claude:waiting" | "codex:approval-menu" | "codex:ran" - | "grok:worked" - | "grok:still-running" => Some(false), + | "codex:worked" + | "grok:worked" => Some(false), _ => None, } } @@ -141,14 +139,14 @@ impl SummaryAdapter for ClaudeSummary { claude_welcome_label(rows) } - /// Set `working` for braille and quadrant-circle title frames. Treat a - /// [`CLAUDE_SPINNER`] glyph as idle. We observed only `✳` from this family in titles, - /// and only while idle. Return `None` for other shapes or empty payloads. + /// Set `working` for quadrant-circle title frames: 2.1.296 emits `◐ ` and + /// `◑ ` while working. Treat a [`CLAUDE_SPINNER`] glyph as idle: the same + /// release emits `✳ Claude Code`, then `✳ `, at rest. Braille frames were + /// an older claude's title animation (2.1.209) and are rejected. Return `None` for + /// other shapes or empty payloads. fn normalize_title(&self, title: &str) -> Option<(String, bool)> { let rest = after_frame(title, |c| { - CLAUDE_SPINNER.contains(&c) - || braille_frame(c) - || ('\u{25D0}'..='\u{25D3}').contains(&c) + CLAUDE_SPINNER.contains(&c) || ('\u{25D0}'..='\u{25D3}').contains(&c) })?; // An empty payload cannot produce a usable preview. (!rest.is_empty()).then(|| (rest.to_string(), !title.starts_with(CLAUDE_SPINNER))) @@ -297,31 +295,37 @@ fn claude_approval(rows: &[String]) -> Option<(String, &'static str)> { .then(|| (AWAITING_APPROVAL.to_string(), "claude:approval-menu")) } -/// Complete effort values accepted before a welcome-box ellipsis. +/// Complete effort values accepted before a welcome-header ellipsis. const CLAUDE_EFFORT: &[&str] = &["low", "medium", "high", "xhigh", "max"]; -/// `Fable 5 with high effort` from the welcome box → `Fable 5 (high)`. The -/// welcome box is the stable source; user-configurable statusline rows are not -/// parsed. When the box scrolls away, the label is unavailable. +/// `Fable 5.1 with high effort` from the welcome header → `Fable 5.1 (high)`. +/// claude 2.1.296 paints a three-row logo header, logo glyphs left of the text: +/// +/// ```text +/// ▐▛███▛█ Claude Code v2.1.296 +/// ▝▜██████▀ Fable 5.1 with high effort · Claude Max +/// ▝▝ ▝▝ /…/scratchpad/claude-work · /rc +/// ``` +/// +/// The model row is the row after `Claude Code v`, found within the first +/// four rows; its first ` · ` segment is the model text. The header is the +/// stable source; user-configurable statusline rows are not parsed. With the +/// header scrolled out of view, no label is available. fn claude_welcome_label(rows: &[String]) -> Option { - let start = rows + let version = rows .iter() .take(4) - .position(|r| r.trim_start().starts_with("╭─── Claude Code"))?; - for row in &rows[start + 1..] { - if row.trim_start().starts_with('╰') { - break; - } - // First cell of the box row: the welcome pane, left of the divider. - let Some(cell) = row.split('│').nth(1) else { - continue; - }; - let head = cell.trim().split(" · ").next().unwrap_or(""); - if let Some(label) = claude_model_effort(head) { - return Some(label); - } - } - None + .position(|r| claude_header_text(r).starts_with("Claude Code v"))?; + let head = claude_header_text(rows.get(version + 1)?) + .split(" · ") + .next()?; + claude_model_effort(head) +} + +/// The text of a welcome-header row: strip the leading logo glyphs (block +/// elements U+2580 to U+259F) and whitespace. +fn claude_header_text(row: &str) -> &str { + row.trim_start_matches(|c: char| c.is_whitespace() || ('\u{2580}'..='\u{259F}').contains(&c)) } /// Normalize ` with effort` and its ellipsis form to ` @@ -382,17 +386,42 @@ impl SummaryAdapter for CodexSummary { codex_model_label(rows) } - /// Set `working` for a title led by a braille frame. Replace `[ . ] ` with `[ ! ] ` - /// to show the blocked blink's canonical phase. Return `None` for other title shapes - /// and for an empty frame payload. + /// Codex 0.162.1 titles the terminal `[head ]summary | cwd`, where `cwd` is + /// the working directory's basename and `head` is a braille frame or the + /// blocked blink `[ . ] `/`[ ! ] `. Across one turn the title reads + /// `codex-work` at startup, `⠴ ⠴ | codex-work` for the first seconds of + /// the turn (the summary is a frame placeholder until the turn is named), + /// `⠙ Count to twenty slowly | codex-work` while working, and `Count to + /// twenty slowly | codex-work` after. Return the summary, split at the last + /// ` | `, with `working` set for a frame head; fold the blink to its + /// `[ ! ] ` phase. Return `None` for the bare cwd (rendered verbatim on the + /// alternate screen) + /// and for a placeholder or empty summary: the working signal is lost for + /// the placeholder seconds rather than a frame rendering as text. A headed + /// title without ` | ` is all summary. fn normalize_title(&self, title: &str) -> Option<(String, bool)> { - if let Some(rest) = title.strip_prefix("[ . ] ") { - return Some((format!("[ ! ] {rest}"), false)); + let frame = after_frame(title, braille_frame); + let blink = title + .strip_prefix("[ . ] ") + .or_else(|| title.strip_prefix("[ ! ] ")); + let rest = frame.or(blink).unwrap_or(title); + let summary = match rest.rsplit_once(" | ") { + Some((summary, _)) => summary, + None if frame.is_some() || blink.is_some() => rest, + None => return None, + }; + let mut chars = summary.chars(); + match chars.next() { + None => return None, + Some(c) if braille_frame(c) && chars.next().is_none() => return None, + Some(_) => {} } - // Do not normalize a frame with an empty payload; Claude and OMP use the same rule. - after_frame(title, braille_frame) - .filter(|rest| !rest.is_empty()) - .map(|rest| (rest.to_string(), true)) + let text = if blink.is_some() { + format!("[ ! ] {summary}") + } else { + summary.to_string() + }; + Some((text, frame.is_some())) } } @@ -509,11 +538,9 @@ fn codex_warning_shortcut(hint: &str) -> bool { for modifier in ["ctrl+", "shift+"] { key = key.strip_prefix(modifier).unwrap_or(key); } - // Since Codex 0.160.0, `⌥w` is displayed instead of `⌥+w`. - // Treat a trailing `+` as the key itself. + // `⌥` is glued to its key (`⌥w`), unlike the `+`-joined modifiers. key = key .strip_prefix("alt+") - .or_else(|| key.strip_prefix("⌥+").filter(|rest| !rest.is_empty())) .or_else(|| key.strip_prefix('⌥')) .unwrap_or(key); (key.len() == 1 && !key.starts_with(char::is_whitespace)) @@ -575,11 +602,12 @@ fn codex_composer(rows: &[String]) -> Option { /// Walk up from the composer through the status region: blanks and indented rows /// (tool-output attachments like `└ ok`, wrapped continuations) are skipped, /// [`CODEX_QUEUED_HEADS`] are walked past, then inspect the first other column-0 row. -/// Extract only [`codex_status_head`] or `• Ran ` shapes; stop at any other column-0 -/// row (a reply bullet, a `⚠` notice, a turn separator): `• Ran` rows from prior turns -/// are retained in scrollback, and skipping an unknown row to reach one would resurface -/// stale work as live status. `• Ran ` is tested first because the status head matches -/// on structure, not on a literal verb. +/// Extract only [`codex_worked`], [`codex_status_head`], or `• Ran ` shapes; stop at +/// any other column-0 row (a reply bullet, a `⚠` notice, a turn separator): `• Ran` +/// rows from prior turns are retained in scrollback; skip past an unknown row to +/// reach one and stale work is reported as live status. The completion row is +/// indented, so it is tested before the indentation skip. `• Ran ` is tested before +/// the status head, which is matched on structure, not on a literal verb. fn codex_status(rows: &[String], composer: usize) -> Option<(String, &'static str)> { // Indented rows crossed since the last column-0 row. Exclude rows below a queued // head from the count to avoid exhausting the window on a deep queue. @@ -588,6 +616,9 @@ fn codex_status(rows: &[String], composer: usize) -> Option<(String, &'static st if row.is_empty() { continue; } + if let Some(text) = codex_worked(row) { + return (indented <= CODEX_STATUS_WINDOW).then_some((text, "codex:worked")); + } if row.starts_with(' ') { indented += 1; continue; @@ -612,6 +643,17 @@ fn codex_status(rows: &[String], composer: usize) -> Option<(String, &'static st None } +/// ` Worked for 1s • 4:19 PM` → `Worked for 1s`: the completion row codex +/// 0.162.1 paints under the last reply, indented like an attachment. Require +/// the ` • ` before the clock time: `Worked for` alone is conversation prose. +fn codex_worked(row: &str) -> Option { + let (duration, _) = row + .trim_start() + .strip_prefix("Worked for ")? + .split_once(" • ")?; + (!duration.is_empty()).then(|| format!("Worked for {duration}")) +} + /// Split a live status row into its header and the text after the opening /// parenthesis. The optional activity prefix is `• ` or `◦ `; the header must /// begin alphanumeric. [`codex_interrupt_paren`] supplies the fixed structure @@ -684,13 +726,13 @@ fn codex_working(header: &str, after_paren: &str) -> String { // ------------------------------------------------------------------ grok -- /// grok (alt screen). Use the bordered input box as the structural anchor. Read -/// the status from the first painted row above the box's top border: a braille -/// spinner while working, `Worked for {n}s` after a turn, or `◎ … still running` -/// / `◎ waiting` while background work is live. Limit this tier to working and -/// completion rows. Handle the three blocked states (permission prompt, plan -/// approval, question) through title normalization: expect a `┃`-fenced dialog -/// in place of the box for permission prompts and questions, and `◆` in the -/// probe position for plan approval. See [`SummaryAdapter::normalize_title`]. +/// the status from the first painted row above the box's top border, skipping +/// the Tasks panel: a braille spinner while working or `Worked for {n}s` after +/// a turn. Limit this tier to working and completion rows. Handle the three +/// blocked states (permission prompt, plan approval, question) through title +/// normalization: expect a `┃`-fenced dialog in place of the box for +/// permission prompts and questions, and `◆` in the probe position for plan +/// approval. See [`SummaryAdapter::normalize_title`]. pub struct GrokSummary; impl SummaryAdapter for GrokSummary { @@ -699,18 +741,35 @@ impl SummaryAdapter for GrokSummary { // One probe row: the first painted row above the box. The splash // panel's hints and the session header land here in non-working // states and match neither shape. - let probe = rows[..top].iter().rev().find(|r| !r.is_empty())?; + let first = rows[..top].iter().rposition(|r| !r.is_empty())?; + // While a command sent to the background with Ctrl+B runs, grok paints + // a Tasks panel between the status row and the box: + // + // ⠹ Waiting for response… 0.1s 9.5s ⇣19.2k [stop] + // + // ▾ Tasks 1 + // ⸬ Run Sleep for 40 seconds 0s + // + // ╭───…───╮ + // + // The panel is the `▾ Tasks {n}` header and the painted rows under + // it; the task glyph is animated (`⸬`, `⁙`, `⋅`, `:`), so the rows + // are not matched by shape. When the first painted row is in that + // run, the probe is the first painted row above the header: still + // one row, never a scan. + let end = (0..=first) + .rev() + .take_while(|&i| !rows[i].is_empty()) + .find(|&i| grok_tasks_header(&rows[i])) + .unwrap_or(first + 1); + let probe = rows[..end].iter().rev().find(|r| !r.is_empty())?; let t = probe.trim_start(); // Keep the label through its first ellipsis. Wrapped tail rows have no // spinner prefix, so they fail the frame check and fall through. if let Some(text) = spinner_text(t, braille_frame) { return Some((text, "grok:spinner")); } - // Still-running is the same probe, never a scan: a closer spinner - // or Worked-for row already returned above. - grok_worked(t) - .then(|| (t.to_string(), "grok:worked")) - .or_else(|| grok_still_running(t).map(|text| (text, "grok:still-running"))) + grok_worked(t).then(|| (t.to_string(), "grok:worked")) } fn model_label(&self, rows: &[String]) -> Option { @@ -785,29 +844,14 @@ fn grok_worked(t: &str) -> bool { }) } -/// Background-task chrome grok paints above the box while the main turn -/// looks idle. The `◎` head and the ` · send a message to interrupt` hint -/// drop; `waiting` and `{count} still running` stay. Scrollback such as -/// `Subagent running:` has no `◎` and never matches. -fn grok_still_running(t: &str) -> Option { - let rest = t.strip_prefix("◎ ")?; - let rest = rest - .strip_suffix(" · send a message to interrupt") - .unwrap_or(rest); - if rest == "waiting" { - return Some(rest.to_string()); - } - let body = rest.strip_suffix(" still running")?; - body.split(" · ") - .all(grok_still_running_count) - .then(|| rest.to_string()) -} - -/// One count segment: ASCII digits, a space, then one to three words. -fn grok_still_running_count(seg: &str) -> bool { - after_digits(seg) - .and_then(|r| r.strip_prefix(' ')) - .is_some_and(|words| (1..=3).contains(&words.split_whitespace().count())) +/// The Tasks panel header grok 1.0.50 paints above the box while a +/// backgrounded command runs: `▾ Tasks {n}` with an ASCII-digit count and +/// nothing after it. +fn grok_tasks_header(row: &str) -> bool { + row.trim() + .strip_prefix("▾ Tasks ") + .and_then(after_digits) + .is_some_and(str::is_empty) } /// `╰──── Grok 4.5 (xhigh) · always-approve ─╯` → `Grok 4.5 (xhigh)`: the text grok diff --git a/src/harness/summary_tests.rs b/src/harness/summary_tests.rs index fc5ae7c..1cced9d 100644 --- a/src/harness/summary_tests.rs +++ b/src/harness/summary_tests.rs @@ -298,8 +298,8 @@ fn claude_waiting_family_matches_the_skeleton_and_never_probes() { } /// Normalize every recognized frame to the same bare title text. Classify the resting -/// family as idle and braille and quadrant frames as working; reject unsupported shapes -/// and empty payloads. +/// family as idle and quadrant frames as working; reject braille frames (an older +/// claude's animation), unsupported shapes, and empty payloads. #[test] fn claude_title_frames_canonicalize_to_constant_text() { for frame in CLAUDE_SPINNER { @@ -309,30 +309,32 @@ fn claude_title_frames_canonicalize_to_constant_text() { "{frame:?}" ); } + // 2.1.296 names the session once the first turn starts, with the frame still `✳`. + assert_eq!( + ClaudeSummary.normalize_title("✳ Directory listing with ls -la"), + Some(("Directory listing with ls -la".to_string(), false)) + ); // Normalize every working frame to the same title and flag. - let rendered: std::collections::BTreeSet> = BRAILLE_FRAMES - .iter() - .copied() - .chain('\u{25D0}'..='\u{25D3}') + let rendered: std::collections::BTreeSet> = ('\u{25D0}'..='\u{25D3}') .map(|frame| ClaudeSummary.normalize_title(&format!("{frame} Run sleep command"))) .collect(); assert_eq!( rendered, std::collections::BTreeSet::from([Some(("Run sleep command".to_string(), true))]), - "braille and quadrant frames must render one string and report working" + "quadrant frames must render one string and report working" ); - // A braille frame plus the session summary. - assert_eq!( - ClaudeSummary.normalize_title("⠐ Review fleetcom preview design document"), - Some(("Review fleetcom preview design document".to_string(), true)) - ); - assert_eq!( - ClaudeSummary.normalize_title("⠴ Review fleetcom preview design document"), - Some(("Review fleetcom preview design document".to_string(), true)), - "mid-block braille frame" - ); + // Braille frames are the 2.1.209 title animation, which 2.1.296 no longer + // emits: refuse rather than guess a state. + for frame in BRAILLE_FRAMES.iter().chain(['⠐'].iter()) { + assert_eq!( + ClaudeSummary + .normalize_title(&format!("{frame} Review fleetcom preview design document")), + None, + "{frame:?}" + ); + } assert_eq!(ClaudeSummary.normalize_title("zellij: main"), None); assert_eq!(ClaudeSummary.normalize_title("✻"), None, "frame alone"); @@ -522,42 +524,74 @@ fn claude_approval_requires_the_dialog_shape() { assert_eq!(ClaudeSummary.live_preview(&lone), None); } -/// The welcome-box label accepts complete and ellipsis forms but rejects a -/// partial effort value. No box means no label. +/// The welcome-header label accepts complete and ellipsis forms but rejects a +/// partial effort value. The model row is the one after `Claude Code v`; no +/// header within the first four rows means no label. #[test] -fn claude_label_reads_the_welcome_box() { - let boxed = |cell: &str| { +fn claude_label_reads_the_logo_header() { + let header = |model_row: &str| { rs(&[ - "╭─── Claude Code v2.1.233 ────────────╮", - cell, - "╰──────────────────────────────────────╯", + "", + " ▐▛███▛█ Claude Code v2.1.296", + model_row, + " ▝▝ ▝▝ ~/Projects/fixture-sources · /rc", ]) }; - let fixed_pane = "│ Opus 5 (1M context) with high… · Claude Max · │ Added opt-in memory cgroup support for Bas… │"; - for (cell, want) in [ + for (model_row, want) in [ ( - "│ Fable 5 with high effort · Claude Max · │ notes │", - Some("Fable 5 (high)"), + "▝▜██████▀ Fable 5.1 with high effort · Claude Max", + Some("Fable 5.1 (high)"), ), // Parentheses in the model name do not change the output shape. ( - "│ Opus 5 (1M context) with high effort · Claude Max · │ notes │", + "▝▜██████▀ Opus 5 (1M context) with high effort · Claude Max", + Some("Opus 5 (1M context) (high)"), + ), + // At a narrow width the CLI truncates the row with its own ellipsis. + ( + "▝▜██████▀ Opus 5 (1M context) with high…", Some("Opus 5 (1M context) (high)"), ), - (fixed_pane, Some("Opus 5 (1M context) (high)")), // Partial and empty effort values are invalid. - ("│ Opus 5 (1M context) with hi… │ notes │", None), - ("│ Opus 5 (1M context) with … │ notes │", None), + ("▝▜██████▀ Opus 5 (1M context) with hi…", None), + ("▝▜██████▀ Opus 5 (1M context) with …", None), // Neither accepted suffix is present. - ("│ Some Model with high │ notes │", None), + ("▝▜██████▀ Some Model with high", None), ] { assert_eq!( - ClaudeSummary.model_label(&boxed(cell)), + ClaudeSummary.model_label(&header(model_row)), want.map(str::to_string), - "{cell:?}" + "{model_row:?}" ); } - assert_eq!(ClaudeSummary.model_label(&rs(&["no box here"])), None); + + // The model row is the one directly after the version row. + let gapped = rs(&[ + " ▐▛███▛█ Claude Code v2.1.296", + "", + "▝▜██████▀ Fable 5.1 with high effort · Claude Max", + ]); + assert_eq!(ClaudeSummary.model_label(&gapped), None); + + // A header below the fourth row is body text, not the header. + let scrolled = rs(&[ + "", + "", + "", + "", + " ▐▛███▛█ Claude Code v2.1.296", + "▝▜██████▀ Fable 5.1 with high effort · Claude Max", + ]); + assert_eq!(ClaudeSummary.model_label(&scrolled), None); + + // The 2.1.215 welcome box is gone; its grammar is not read. + let boxed = rs(&[ + "╭─── Claude Code v2.1.215 ─────────────────────╮", + "│ Fable 5 with high effort · Claude Max · │ notes │", + "╰──────────────────────────────────────────────╯", + ]); + assert_eq!(ClaudeSummary.model_label(&boxed), None); + assert_eq!(ClaudeSummary.model_label(&rs(&["no header here"])), None); } /// Working-row normalization: parenthetical dropped (unclosed included), @@ -658,19 +692,16 @@ fn codex_label_separates_right_aligned_warning_badges() { "⚠ 1 warning · /warnings to view", "⚠ 1 · f2", "⚠ 2 · shift+←", - "⚠ 3 · ⌥+w", "⚠ 4 · alt+w", "⚠ 5 · ctrl+x ctrl+w", "⚠ 6 · /warnings", - "⚠ 1 warning · ctrl+shift+⌥+f24 to view", - // Since Codex 0.160.0, `⌥` is displayed without `+` before the key. + // `⌥` is glued to its key. "⚠ 3 · ⌥w", "⚠ 2 warnings · ⌥w to view", "⚠ 1 warning · ctrl+shift+⌥f24 to view", "⚠ 5 · ctrl+x ⌥w", - // The `+` key itself, in the 0.160 and 0.159 spellings. + // The `+` key itself. "⚠ 1 · ⌥+", - "⚠ 1 · ⌥++", "⚠ 1 · fwd del", "⚠ 1 · ctrl+x ctrl+fwd del", "⚠ 1", @@ -738,40 +769,70 @@ fn codex_label_rejects_warning_prose_and_incomplete_footers() { assert_eq!(CodexSummary.model_label(&rows), None); } -/// Normalize braille frames to the bare title and mark them working. Canonicalize the -/// blocked-on-user blink to one phase; pass idle and foreign titles through. +/// The 0.162.1 title grammar is `[frame ]summary | cwd`: keep the summary, +/// drop the cwd, set `working` for a frame, and render every frame to the same +/// string. Return `None` for the bare cwd and the frame placeholder; fold the +/// blocked blink to one phase. #[test] -fn codex_title_animations_canonicalize_to_constant_text() { +fn codex_title_grammar_yields_the_summary() { + let t = |title: &str| CodexSummary.normalize_title(title); + let named = Some(("Count to twenty slowly".to_string(), true)); + let idle = Some(("Count to twenty slowly".to_string(), false)); + + // The four title forms of one turn, in order. + assert_eq!(t("codex-work"), None, "the cwd basename is not a summary"); + assert_eq!( + t("⠴ ⠴ | codex-work"), + None, + "the placeholder is a frame, not text" + ); + assert_eq!(t("⠙ Count to twenty slowly | codex-work"), named); + assert_eq!(t("Count to twenty slowly | codex-work"), idle); + for frame in BRAILLE_FRAMES { assert_eq!( - CodexSummary.normalize_title(&format!("{frame} fleetcom")), - Some(("fleetcom".to_string(), true)), + t(&format!("{frame} Count to twenty slowly | codex-work")), + named, + "{frame:?}" + ); + assert_eq!( + t(&format!("{frame} {frame} | codex-work")), + None, "{frame:?}" ); + assert_eq!( + t(&format!("{frame} fleetcom")), + Some(("fleetcom".to_string(), true)), + "{frame:?}: a frame-led title without a cwd is all summary" + ); } - // `[ . ]` folds to `[ ! ]`; `[ ! ]` already has the canonical text. + // The cwd is split off at the last ` | `; any earlier ` | ` is part of the summary. assert_eq!( - CodexSummary.normalize_title("[ . ] Action Required | fleetcom"), - Some(("[ ! ] Action Required | fleetcom".to_string(), false)) - ); - assert_eq!( - CodexSummary.normalize_title("[ ! ] Action Required | fleetcom"), - None, - "the frozen phase needs no rewrite" + t("⠙ Fix a | b | codex-work"), + Some(("Fix a | b".to_string(), true)) ); - // Bare, foreign, frame-only, and empty titles do not match Codex's title - // grammar. - assert_eq!(CodexSummary.normalize_title("fleetcom"), None); - assert_eq!(CodexSummary.normalize_title("zellij: main"), None); - assert_eq!(CodexSummary.normalize_title("⠹"), None, "frame alone"); - assert_eq!( - CodexSummary.normalize_title("⠹ "), - None, - "frame with an empty rest" - ); - assert_eq!(CodexSummary.normalize_title(""), None, "empty title"); + // Both blink phases are rendered as the `[ ! ]` phase, with and without a cwd. + for title in [ + "[ . ] Action Required | fleetcom", + "[ ! ] Action Required | fleetcom", + "[ . ] Action Required", + ] { + assert_eq!( + t(title), + Some(("[ ! ] Action Required".to_string(), false)), + "{title:?}" + ); + } + + // No match for foreign, frame-only, empty-summary, and empty titles. + assert_eq!(t("zellij: main"), None); + assert_eq!(t("⠹"), None, "frame alone"); + assert_eq!(t("⠹ "), None, "frame with an empty rest"); + assert_eq!(t(" | codex-work"), None, "empty summary"); + assert_eq!(t("⠹ | codex-work"), None, "empty summary behind a frame"); + assert_eq!(t(""), None, "empty title"); } /// The status row anchors on its parenthetical, not on a literal verb. The @@ -1004,6 +1065,53 @@ fn codex_ran_stops_at_foreign_rows() { assert_eq!(CodexSummary.live_preview(&behind_reply), None); } +/// The completion row `Worked for {duration} • {time}` is indented, so it is +/// read before the indentation skip and cut before the clock time. Without +/// the ` • ` it is prose and skipped like any attachment; behind a reply +/// bullet or beyond the indentation window it is stale. +#[test] +fn codex_worked_row_ends_the_turn() { + let tail = [ + "", + "› Ask Codex to do anything", + "", + " GPT-6-Luna max · 12.8K used · 20.7K in · 5 out", + ]; + let probe = |above: &[&str]| { + let mut rows = above.to_vec(); + rows.extend(tail); + CodexSummary.live_preview(&rs(&rows)) + }; + let worked = Some(("Worked for 1s".to_string(), "codex:worked")); + assert_eq!(probe(&["• ok", "", " Worked for 1s • 4:19 PM"]), worked); + assert_eq!( + probe(&[" Worked for 1m 05s • 4:19 PM"]), + Some(("Worked for 1m 05s".to_string(), "codex:worked")) + ); + for row in [ + " Worked for 1s", + " Worked for • 4:19 PM", + " Worked for 1s •4:19 PM", + ] { + assert_eq!(probe(&["• ok", "", row]), None, "{row:?}"); + } + assert_eq!( + probe(&[" Worked for 1s • 4:19 PM", "", "• ok"]), + None, + "a prior turn's completion row sits behind the reply bullet" + ); + + let mut deep = vec![" Worked for 1s • 4:19 PM"]; + deep.extend([" └ output"; 10]); + assert_eq!( + probe(&deep), + worked, + "ten indented rows are within the window" + ); + deep.push(" └ output"); + assert_eq!(probe(&deep), None, "eleven exhaust it"); +} + /// A hint row may follow the composer without a status line. The anchor /// still fires, without a model prefix. #[test] @@ -1115,7 +1223,8 @@ fn codex_requires_the_composer_pin() { } /// Spinner label cut at its `…`; the completion row kept verbatim, -/// longer durations included; free text above the box refuses. +/// longer durations included; free text above the box is refused. The Tasks +/// panel is skipped: the probe is the one row above its header. #[test] fn grok_status_shapes() { let probe = |status: &str| GrokSummary.live_preview(&grok_screen(&[status, ""])); @@ -1141,70 +1250,66 @@ fn grok_status_shapes() { None ); + // The Tasks panel of a backgrounded command: the probe is the one row + // above its header, whatever that row is. + let panel = |above: &str| { + GrokSummary.live_preview(&grok_screen(&[ + above, + "", + " ▾ Tasks 2", + " ⸬ Run Sleep for 40 seconds 0s", + " ⁙ Run cargo build 3s", + "", + ])) + }; assert_eq!( - GrokSummary.model_label(&grok_screen(&[" ⠋ Thinking… 0.2s", ""])), - Some("Grok 4.5 (xhigh)".to_string()) - ); - // A plain border carries no label. - let plain = rs(&[" ⠋ Thinking… 0.2s", "", "╭────╮", "│ ❯ │", "╰────╯"]); - assert_eq!(GrokSummary.model_label(&plain), None); -} - -/// Still-running chrome: `◎` plus a count phrase or `waiting`. Drop the interrupt hint; -/// body-shaped lookalikes and pre-0.2.109 wording do not match. Prefer a closer -/// Worked-for row: no upward scan. -#[test] -fn grok_still_running_shapes() { - let probe = |status: &str| GrokSummary.live_preview(&grok_screen(&[status, ""])); - assert_eq!( - probe(" ◎ 1 subagent still running"), - Some(("1 subagent still running".to_string(), "grok:still-running")) - ); - assert_eq!( - probe(" ◎ 1 command · 2 monitors · 1 loop · 1 subagent still running"), - Some(( - "1 command · 2 monitors · 1 loop · 1 subagent still running".to_string(), - "grok:still-running" - )) - ); - assert_eq!( - probe(" ◎ 1 command still running · send a message to interrupt"), - Some(("1 command still running".to_string(), "grok:still-running")) - ); - assert_eq!( - probe(" ◎ waiting · send a message to interrupt"), - Some(("waiting".to_string(), "grok:still-running")) - ); - assert_eq!( - probe(" ◎ waiting"), - Some(("waiting".to_string(), "grok:still-running")) + panel(" ⠹ Waiting for response… 0.1s 9.5s ⇣19.2k [stop]"), + Some(("Waiting for response…".to_string(), "grok:spinner")) ); - - for row in [ - " 1 subagent still running", - " watching · 1 subagent", - " Subagent running: \"do the thing\"", - " ◎ still running", - " ◎ 1 to finish", - " ◎ 1 command still running · leftover", - " ◎ 1 still running", - " ◎ command still running", - " ◎ 1 a b c d still running", + assert_eq!(panel(" Done."), None); + // A header-shaped row with a blank row below it is not a panel; a header + // with trailing text is not a header. + for (rows, want) in [ + ( + vec![ + " ⠋ Thinking… 0.2s", + "", + " ▾ Tasks 1", + "", + " Done.", + "", + ], + None, + ), + ( + vec![ + " ⠋ Thinking… 0.2s", + "", + " ▾ Tasks 1 pending", + " Done.", + "", + ], + None, + ), + ( + vec![" ⠋ Thinking… 0.2s", "", " ▾ Tasks 1", " Done.", ""], + Some(("Thinking…".to_string(), "grok:spinner")), + ), ] { - assert_eq!(probe(row), None, "{row:?}"); + assert_eq!( + GrokSummary.live_preview(&grok_screen(&rows)), + want, + "{rows:?}" + ); } - // The probe is a single row: prefer Worked-for closer to the box. - let rows = grok_screen(&[ - " ◎ 1 subagent still running", - "", - " Worked for 8.7s", - "", - ]); assert_eq!( - GrokSummary.live_preview(&rows), - Some(("Worked for 8.7s".to_string(), "grok:worked")) + GrokSummary.model_label(&grok_screen(&[" ⠋ Thinking… 0.2s", ""])), + Some("Grok 4.5 (xhigh)".to_string()) ); + // No label from a plain border. + let plain = rs(&[" ⠋ Thinking… 0.2s", "", "╭────╮", "│ ❯ │", "╰────╯"]); + assert_eq!(GrokSummary.model_label(&plain), None); } /// Check each observed ` - `-joined title form from grok 1.0.50. Expect the session @@ -1344,162 +1449,96 @@ fn codex_0158_checkpoint(bytes: &[u8], offset: usize, alternate: bool) -> Vec) -> Option { } /// Session format version written by `to_json` and accepted by `from_json`. Managed entries -/// were introduced in version 2. A missing version means 1; `from_json` loads a v1 file as -/// literals, then rewrites the recipe through [`migrate_v1`]. Reject newer versions. +/// were introduced in version 2. A missing version means 1; `from_json` loads a v1 file +/// verbatim, every entry literal. Through 0.15.0 `from_json` rewrote a v1 file's bare agent +/// words and canonical resume commands into managed entries, because v1 had no managed form; +/// that rewrite was retired in the release after 0.15.0, so an unconverted v1 file loads without +/// managed resume tracking, and `to_json` writes its entries back as literals. Reject newer +/// versions. const FORMAT_VERSION: u64 = 2; /// Build the recipe's `dirs` object. @@ -140,52 +144,6 @@ fn dirs_json(cfg: &SessionConfig) -> jzon::JsonValue { dirs } -/// Rewrite a v1 recipe's literal entries in place to the corresponding managed form; labels -/// are untouched. In v1, bare program words and canonical resume commands were -/// instrumented; preserving them as literals would silently disable conversation tracking. -/// Convert exactly those two forms, accepting v1's whitespace between tokens and -/// single-quoted IDs. Require an exact registered word as the first token. With a path -/// (`/usr/local/bin/claude`, `~/bin/claude`), preserve the user's chosen binary as a literal -/// rather than substitute the first binary found on `PATH`; managed resume tracking is then -/// unavailable. Leave all other commands literal. -/// -/// Phase 5, one release after format v2 ships, deletes this function, its one call in -/// `from_json`, and its tests (`v1_literals_migrate_exactly_the_two_instrumented_shapes`, -/// `v1_recovery_snapshots_migrate_on_load`). A v1 file still loads afterward, with every -/// entry literal: the file is accepted, managed resume tracking is not. -fn migrate_v1(cfg: &mut SessionConfig) { - /// The managed form of one v1 literal, or `None` to leave it literal. - fn managed_form(cmd: &str) -> Option { - let mut words = cmd.split([' ', '\t']).filter(|w| !w.is_empty()); - let (agent, selector) = words.next().and_then(harness::registered)?.shape(); - let resume = match (words.next(), words.next(), words.next()) { - (None, _, _) => None, - (Some(sel), Some(id), None) if sel == selector => { - let id = id - .strip_prefix('\'') - .and_then(|t| t.strip_suffix('\'')) - .unwrap_or(id); - if !harness::is_uuid(id) { - return None; - } - Some(id.to_string()) - } - _ => return None, - }; - Some(EntryKind::Managed { - agent: agent.to_string(), - resume, - }) - } - for entry in cfg.values_mut().flatten() { - if let EntryKind::Literal(cmd) = &entry.kind - && let Some(kind) = managed_form(cmd) - { - entry.kind = kind; - } - } -} - /// Serialize the versioned wrapped schema. Store the name to distinguish names /// sanitized to the same filename. fn to_json(name: &str, cfg: &SessionConfig) -> String { @@ -208,17 +166,20 @@ pub fn fingerprint_json(cfg: &SessionConfig) -> String { /// through [`FORMAT_VERSION`]. Treat a missing version as 1. Accept entry forms regardless /// of version: strings and `{"cmd"}` objects for literals, `{"agent"}` objects for managed /// agents. Reject the whole file for objects with both keys, unregistered agents, or resume -/// IDs that are not strict UUIDs. Pass a v1 recipe through [`migrate_v1`] once it is built. +/// IDs that are not strict UUIDs. Never rewrite an entry: a v1 file loads verbatim, every +/// entry literal. The rewrite that converted v1 agent commands to managed entries (v1 had no +/// managed form) shipped in 0.15.0 and was retired in the release after it. fn from_json(text: &str) -> io::Result<(Option, SessionConfig)> { let invalid = |message| io::Error::new(io::ErrorKind::InvalidData, message); let parsed = jzon::parse(text).map_err(|e| invalid(e.to_string()))?; if !parsed.is_object() { return Err(invalid("session root: expected an object".into())); } - // Validate version metadata before detecting the schema shape. - let version = if parsed.has_key("version") { + // Validate version metadata before detecting the schema shape. Only the range + // is checked: every accepted version has the same entry grammar. + if parsed.has_key("version") { match parsed["version"].as_u64() { - Some(n) if (1..=FORMAT_VERSION).contains(&n) => n, + Some(n) if (1..=FORMAT_VERSION).contains(&n) => {} Some(n) if n > FORMAT_VERSION => { return Err(invalid(format!( "session format version {n} is newer than this fleetcom \ @@ -234,9 +195,7 @@ fn from_json(text: &str) -> io::Result<(Option, SessionConfig)> { ))); } } - } else { - 1 - }; + } let (name, dirs, flat) = if parsed["dirs"].is_object() { let name = opt_str(&parsed["name"]) .ok_or_else(|| invalid("session field \"name\": expected a string or null".into()))?; @@ -317,10 +276,6 @@ fn from_json(text: &str) -> io::Result<(Option, SessionConfig)> { } cfg.insert(dir.to_string(), entries); } - // In v1, managed launches could only be stored as command text. - if version == 1 { - migrate_v1(&mut cfg); - } Ok((name, cfg)) } @@ -793,120 +748,27 @@ mod tests { assert_eq!(fp(Some(ID)), fp(Some(ID))); } - /// Preserve literal agent words in v2 files; migrate only v1 literals. - #[test] - fn v2_literal_agent_words_stay_literal() { - let (_, cfg) = from_json(&format!( - r#"{{"version": 2, "name": "v", "dirs": {{"d": ["claude", - {{"cmd": "codex resume '{ID}'", "group": "g"}}, {{"agent": "omp"}}]}}}}"# - )) - .unwrap(); - assert_eq!( - cfg["d"], - vec![ - e("claude"), - ge(&format!("codex resume '{ID}'"), "g"), - m("omp", None) - ] - ); - } - - /// Convert the two instrumented v1 forms to managed entries, accepting the original - /// whitespace and quoted IDs. Keep paths, extra arguments, shell syntax, and malformed - /// IDs literal. + /// Preserve literal agent words and resume commands in every accepted version. A v1 + /// file now loads verbatim: with the rewrite to managed entries gone, the spellings + /// it once converted are literal beside a v2 managed entry. #[test] - fn v1_literals_migrate_exactly_the_two_instrumented_shapes() { - let cases: Vec<(String, SessionEntry)> = vec![ - ("claude".into(), m("claude", None)), - ("codex".into(), m("codex", None)), - ("grok".into(), m("grok", None)), - ("omp".into(), m("omp", None)), - (format!("claude --resume {ID}"), m("claude", Some(ID))), - (format!("claude --resume '{ID}'"), m("claude", Some(ID))), - (format!("codex resume '{ID}'"), m("codex", Some(ID))), - (format!("grok --resume {ID}"), m("grok", Some(ID))), - (format!("omp --resume '{ID}'"), m("omp", Some(ID))), - // v1's token tolerance: tabs, repeated and surrounding whitespace. - (" claude\t".into(), m("claude", None)), - (format!("claude\t--resume '{ID}' "), m("claude", Some(ID))), - // Preserve the user's chosen binary path as a literal, without managed resume - // tracking. - ("/usr/local/bin/claude".into(), e("/usr/local/bin/claude")), - ("~/bin/claude".into(), e("~/bin/claude")), - ( - format!("/usr/local/bin/claude --resume '{ID}'"), - e(&format!("/usr/local/bin/claude --resume '{ID}'")), - ), - // Arguments, prompts, the wrong selector, and shell syntax. - ("claude --model opus".into(), e("claude --model opus")), - ("claude 'fix the tests'".into(), e("claude 'fix the tests'")), - (format!("claude -r {ID}"), e(&format!("claude -r {ID}"))), - ( - format!("codex --resume {ID}"), - e(&format!("codex --resume {ID}")), - ), - ( - format!("claude --resume {ID} --model opus"), - e(&format!("claude --resume {ID} --model opus")), - ), - ("claude --resume".into(), e("claude --resume")), - ( - "claude --resume not-a-uuid".into(), - e("claude --resume not-a-uuid"), - ), - ( - format!("claude --resume '{ID}"), - e(&format!("claude --resume '{ID}")), - ), - ( - format!("claude --resume ''{ID}''"), - e(&format!("claude --resume ''{ID}''")), - ), - ("claude --resume $ID".into(), e("claude --resume $ID")), - ("claude | tee log".into(), e("claude | tee log")), - ("claude; ls".into(), e("claude; ls")), - ("FOO=bar claude".into(), e("FOO=bar claude")), - ("'claude'".into(), e("'claude'")), - ("claudius".into(), e("claudius")), - ("vim".into(), e("vim")), - (String::new(), e("")), - ]; - let mut cfg = SessionConfig::from([( - "d".to_string(), - cases.iter().map(|(cmd, _)| e(cmd)).collect(), - )]); - migrate_v1(&mut cfg); - for ((cmd, expected), got) in cases.iter().zip(&cfg["d"]) { - assert_eq!(got, expected, "{cmd:?}"); - } - // Through the loader: every v1 spelling, wrapped and flat, string and - // object form, with labels carried over. - let member = jzon::JsonValue::from(cases[4].0.as_str()).dump(); - for text in [ - format!(r#"{{"version": 1, "name": "v", "dirs": {{"d": [{member}]}}}}"#), - format!(r#"{{"name": "v", "dirs": {{"d": [{member}]}}}}"#), - format!(r#"{{"d": [{member}]}}"#), - format!(r#"{{"version": 1, "d": [{member}]}}"#), - ] { + fn literal_agent_words_stay_literal_in_every_version() { + for version in ["", r#""version": 1, "#, r#""version": 2, "#] { + let text = format!( + r#"{{{version}"name": "v", "dirs": {{"d": ["claude", + {{"cmd": "codex resume '{ID}'", "group": "g"}}, {{"agent": "omp"}}]}}}}"# + ); let (_, cfg) = from_json(&text).unwrap(); - assert_eq!(cfg["d"], vec![m("claude", Some(ID))], "{text}"); + assert_eq!( + cfg["d"], + vec![ + e("claude"), + ge(&format!("codex resume '{ID}'"), "g"), + m("omp", None) + ], + "{text}" + ); } - let (_, cfg) = from_json( - r#"{"d": [{"cmd": "claude", "group": "agents", "name": "pilot"}, - {"cmd": "/usr/local/bin/claude", "name": "mine"}]}"#, - ) - .unwrap(); - assert_eq!( - cfg["d"], - vec![ - SessionEntry { - group: Some("agents".into()), - name: Some("pilot".into()), - ..m("claude", None) - }, - ne("/usr/local/bin/claude", "mine"), - ] - ); } /// Reject the entire file for a managed entry with a `cmd` field, an unregistered @@ -1356,34 +1218,6 @@ mod tests { assert_eq!(list_in(&dir), vec!["old".to_string()]); } - /// Load v1 recovery snapshots as managed entries when the stored strings match the old - /// instrumented forms. Preserve recovery across the upgrade. - #[test] - fn v1_recovery_snapshots_migrate_on_load() { - let base = temp("session_recovery_migrate"); - let rec = recovery_dir(&base); - fs::create_dir_all(&rec).unwrap(); - fs::write( - rec.join("20260714-093015-11.json"), - format!( - r#"{{"version": 1, "name": "autosaved 2026-07-14 09:30", "dirs": {{"~/p": [ - {{"cmd": "claude --resume '{ID}'", "group": "agents"}}, "omp", "cargo test"]}}}}"# - ), - ) - .unwrap(); - assert_eq!( - load_recovery_in(&rec, "20260714-093015-11").unwrap()["~/p"], - vec![ - SessionEntry { - group: Some("agents".into()), - ..m("claude", Some(ID)) - }, - m("omp", None), - e("cargo test"), - ] - ); - } - /// A flat-schema file can be replaced under its filename stem. #[test] fn legacy_file_resaves_under_its_own_stem() { diff --git a/src/terminal/golden.rs b/src/terminal/golden.rs index bfb7de0..210c7c9 100644 --- a/src/terminal/golden.rs +++ b/src/terminal/golden.rs @@ -567,9 +567,7 @@ wrapper_oracle!(wrapper_topregion_scroll, "topregion_scroll.bin"); const REPLY: &str = "PHASE0 COMPLETE. Deterministic local response."; const PROMPT: &str = "Phase zero fixture: reply with the completion marker."; const MANIFEST: &str = include_str!("../../tests/corpus/codex_0158_terminal/manifest.json"); -const STREAMS: [&[u8]; 4] = [ - include_bytes!("../../tests/corpus/codex_0158_terminal/0.157.1-fullscreen-transitions.bin"), - include_bytes!("../../tests/corpus/codex_0158_terminal/0.157.1-fullscreen-fleetcom-load.bin"), +const STREAMS: [&[u8]; 2] = [ include_bytes!("../../tests/corpus/codex_0158_terminal/0.158.0-fullscreen-transitions.bin"), include_bytes!("../../tests/corpus/codex_0158_terminal/0.158.0-fullscreen-fleetcom-load.bin"), ]; @@ -644,9 +642,7 @@ fn codex_release_terminal_transitions() { text.contains("Copied Whole response to clipboard"), "{context}" ); - if name.starts_with("0.158.0") { - assert!(text.contains("Copying Whole response"), "{context}"); - } + assert!(text.contains("Copying Whole response"), "{context}"); } let clipboard = emu.drain_clipboard(); assert_eq!(clipboard.oversized_len, None, "{context}"); diff --git a/tests/corpus/README.md b/tests/corpus/README.md index bd2778d..cb192ab 100644 --- a/tests/corpus/README.md +++ b/tests/corpus/README.md @@ -36,21 +36,35 @@ fleetcom client's output. Use the `preview_*.bin` fixtures to verify summary-adapter extraction, normalization, and fallback behavior. Each fixture is a constructed repaint stream: optional alternate-screen entry, clear, home, then sanitized screen rows -joined with CRLF. Claude and Grok use the alternate screen; Codex and omp are -inline. Identifying and user-configured text is replaced with -alignment-preserving synthetic values. Geometry is 40×120 unless noted. +joined with CRLF. Claude and Grok use the alternate screen. Codex 0.162.1 runs +on the alternate screen from startup, so the five codex fixtures captured on it +(`idle`, `working`, `queued`, `done`, `ran_done`) are prefixed with `1049h`; the older +codex fixtures and omp are inline. Identifying and user-configured text is +replaced with alignment-preserving synthetic values. Geometry is 40×120 unless +noted. -Geometry is 40×80 for `preview_codex_reasoning.bin` and 40×36 for -`preview_codex_queued.bin`. Both are bottom-anchored on a 40-row screen to -reproduce the inline layout. +Geometry is 40×80 for `preview_codex_reasoning.bin`, bottom-anchored on a +40-row screen to reproduce the inline layout. Indentation in the Codex hint-row and approval fixtures is approximate; match -trimmed heads and column-0 structure in their tests. In the Claude waiting -fixture, the welcome box is omitted and agent-roster rows are included below the -input box. In the Claude task-list fixtures, generic phase names are used in a -task-list layout and the welcome box is omitted. In the Claude workflow-wait -fixture, generic wording is used, the welcome box is omitted, and a long blank -gap is included above the input box with a roster below it. The omp fixtures are +trimmed heads and column-0 structure in their tests. In the codex 0.162.1 +fixtures, the first painted row is the `>_ OpenAI Codex (v0.162.1)` header, +with a same-width synthetic working directory on the row below it; the +`GPT-6-Luna max · … in · … out` status line, token counters, clock times, and +prompts are verbatim. In the Claude fixtures with a model prefix, the first +painted rows are claude 2.1.296's three-row logo header (`Claude Code +v2.1.296`, `Fable 5.1 with high effort · Claude Max`, working directory); the +working directory on the header row and on the statusline row below the input +box is a same-width synthetic path. In the fixtures captured on 2.1.296, the +capture environment's `⚠ Transcript saving is off` statusline row is omitted +and left empty; in the three fixtures whose body is from 2.1.215, the 2.1.296 +header is transplanted over the old welcome box, with the box's remaining rows +left empty so the body's row indexes are unchanged. In the Claude waiting fixture, the header is +omitted and agent-roster rows are included below the input box. In the Claude +task-list fixtures, generic phase names are used in a task-list layout and the +header is omitted. In the Claude workflow-wait fixture, generic wording is used, +the header is omitted, and a long blank gap is included above the input box +with a roster below it. The omp fixtures are the 18.8.7 default layout: logo and tip, the status band (`π >` idle, `⠇ 4s >` working), and a `╰─` composer gutter with no top border. The working directory in the band and the tool-call path in the body are replaced with same-width @@ -66,31 +80,36 @@ prevent a byte-for-byte match with the captured header. | Fixture | Scenario | Coverage | | --- | --- | --- | -| `preview_claude_working.bin` | claude spinner with the tmux focus-events hint row | `claude:spinner` extraction through an indented hint row | -| `preview_claude_working_tool.bin` | claude spinner over an indented tool-attachment row | `claude:spinner`; the attachment row is not the action row | -| `preview_claude_action.bin` | claude `⏺ Running 1 shell command…` above the spinner | `claude:action-row` preferred over the rotating verb | -| `preview_claude_approval.bin` | claude file-write approval dialog, input box replaced | `awaiting approval` synthesized by `claude:approval-menu` | -| `preview_claude_idle.bin` | claude idle with the `Try "…"` placeholder | fall-through to the marker; the placeholder excluded from anchors | -| `preview_claude_done.bin` | claude after a finished turn (`✻ Crunched for 4s` in the body) | fall-through; body completion rows are out of the pinned window | -| `preview_claude_body_menu.bin` | approval-menu text quoted in the body while the spinner runs | the pinned spinner preferred; body menu text is ignored | -| `preview_claude_body_menu_idle.bin` | approval-menu text touching the chrome window, input box intact | extraction aborted at a foreign column-0 row; marker fallback | -| `preview_claude_tasklist.bin` | claude spinner above a six-row task-list block, agent roster below the box | `claude:spinner` through a bounded indented block | -| `preview_claude_body_above_tasklist.bin` | spinner-shaped body row above `⏺` prose and the task list | negative: status structure rejected on column-0 prose | -| `preview_codex_working.bin` | codex `• Working (7s • esc to interrupt) · 1 background terminal running · /ps to view · /stop to close` | `codex:working` normalization: affordances stripped, slow suffix kept | -| `preview_codex_working_over_ran.bin` | codex working with a `• Ran` row higher in the same turn | `codex:working` preferred at the pin; stale row excluded | -| `preview_codex_scrollback.bin` | codex finished turn, `• Ran` from the prior turn in scrollback | scan stopped at the reply bullet; floor-tier fallback | -| `preview_codex_ran.bin` | codex transient completion row | `codex:ran` extraction through the `└` attachment row | -| `preview_codex_hint_row.bin` | codex working with `tab to queue message` below the composer, no status line | `codex:working` through the composer pin; no model prefix without one | -| `preview_codex_approval.bin` | codex approval modal: composer and status line replaced by a numbered menu | `awaiting approval` synthesized by `codex:approval-menu` | -| `preview_codex_reasoning.bin` | codex status row with a reasoning phrase over an `• Explored` group and reply bullet; composer carrying text; no status line | header kept verbatim by `codex:working`; preceding `•`-headed rows excluded; no model prefix without a status line | -| `preview_codex_queued.bin` | codex `• Working` row separated from the composer by a `• Queued follow-up inputs` block; `model-with-reasoning · current-dir` status line below | `codex:working` extracted across queued-message heads; model label from the first status-line item | -| `preview_codex_body_menu.bin` | modal-shaped menu quoted in the body, live composer below | negative: modal match suppressed with composer present; floor-tier fallback | -| `preview_claude_waiting.bin` | claude waiting on a backgrounded subagent, `⏺` prose and agent roster around the box | ellipsis-less row extracted verbatim by `claude:waiting`; no model label mid-session | -| `preview_claude_workflow_wait.bin` | claude waiting on a dynamic workflow, with 19 blank rows before the input box and a workflow roster below it | blank rows skipped during `claude:waiting` matching; the roster is excluded | +| `preview_claude_working.bin` | claude 2.1.296, captured 2026-10-10: spinner over the right-aligned `● high · /effort` hint row | `claude:spinner` extraction through an indented hint row | +| `preview_claude_working_tool.bin` | body from 2.1.215, header transplanted from 2.1.296: spinner over an indented tool-attachment row | `claude:spinner`; the attachment row is not the action row | +| `preview_claude_working_agent.bin` | claude 2.1.296, captured 2026-10-10: spinner over a backgrounded `⏺ Agent(…)` row and its `⎿` attachment, agent roster below the box | `claude:spinner`; the attachment is not the action row, the roster is outside the scan | +| `preview_claude_action.bin` | claude 2.1.296, captured 2026-10-10: `⏺ Running 1 shell command…` above a `· Whisking… (9s · ↓ 261 tokens · thought for 2s)` spinner | `claude:action-row` preferred over the rotating verb; `thought for 2s` kept, tickers dropped | +| `preview_claude_approval.bin` | claude 2.1.296, captured 2026-10-10: Bash command approval dialog, input box replaced, four-option menu | `awaiting approval` synthesized by `claude:approval-menu` | +| `preview_claude_idle.bin` | claude 2.1.296, captured 2026-10-10: launch state with the `Try "…"` placeholder, `/rc connecting…` on the header, no statusline rows yet | fall-through to the marker; the placeholder excluded from anchors | +| `preview_claude_done.bin` | claude 2.1.296, captured 2026-10-10: finished turn (`✻ Sautéed for 17s · done 4:14 PM` in the body) with a typed draft in the box | fall-through; the ellipsis-less completion row is rejected as a foreign column-0 row | +| `preview_claude_body_menu.bin` | body from 2.1.215, header transplanted from 2.1.296: approval-menu text quoted in the body while the spinner runs | the pinned spinner preferred; body menu text is ignored | +| `preview_claude_body_menu_idle.bin` | body from 2.1.215, header transplanted from 2.1.296: approval-menu text touching the chrome window, input box intact | extraction aborted at a foreign column-0 row; marker fallback | +| `preview_claude_tasklist.bin` | 2.1.215, not re-observed on 2.1.296: spinner above a six-row task-list block, agent roster below the box | `claude:spinner` through a bounded indented block | +| `preview_claude_body_above_tasklist.bin` | 2.1.215, not re-observed on 2.1.296: spinner-shaped body row above `⏺` prose and the task list | negative: status structure rejected on column-0 prose | +| `preview_codex_working.bin` | codex 0.162.1, captured 2026-10-10: `• Working (3s • esc to interrupt) · 1 background terminal running · /ps to view · /stop to close` | `codex:working` normalization: affordances stripped, slow suffix kept | +| `preview_codex_working_over_ran.bin` | codex 0.144.6: working with a `• Ran` row higher in the same turn | `codex:working` preferred at the pin; stale row excluded | +| `preview_codex_scrollback.bin` | codex 0.144.6: finished turn, `• Ran` from the prior turn in scrollback | scan stopped at the reply bullet; floor-tier fallback | +| `preview_codex_ran.bin` | codex 0.144.6, synthetic: transient completion row (no raw capture holds it) | `codex:ran` extraction through the `└` attachment row | +| `preview_codex_ran_done.bin` | codex 0.162.1, captured 2026-10-10: finished turn, `• Ran curl -sI https://example.com` and its `└` output behind the `• HTTP/2 200` reply bullet | scan stopped at the reply bullet; marker fallback on the alternate screen | +| `preview_codex_queued.bin` | codex 0.162.1, captured 2026-10-10: `• Working` row separated from the composer by a `• Queued follow-up inputs` block with one `↳` message and its `⇧←` hint | `codex:working` extracted across queued-message heads; model label from the `in · out` status line | +| `preview_codex_done.bin` | codex 0.162.1, captured 2026-10-10: finished turn, `• ok` reply over the indented `Worked for 1s • 4:19 PM` completion row | `codex:worked` cut before the clock time | +| `preview_codex_idle.bin` | codex 0.162.1, captured 2026-10-10: launch state, command list over the `› Ask Codex to do anything` placeholder, `0 in · 0 out` status line | fall-through to the marker | +| `preview_codex_hint_row.bin` | codex 0.144.6, paste-derived from a 2026-07-20 sighting: working with `tab to queue message` below the composer, no status line | `codex:working` through the composer pin; no model prefix without one | +| `preview_codex_approval.bin` | codex 0.144.6, paste-derived from a 2026-07-20 sighting: approval modal, composer and status line replaced by a numbered menu | `awaiting approval` synthesized by `codex:approval-menu` | +| `preview_codex_reasoning.bin` | codex 0.147.0: status row with a reasoning phrase over an `• Explored` group and reply bullet; composer carrying text; no status line | header kept verbatim by `codex:working`; preceding `•`-headed rows excluded; no model prefix without a status line | +| `preview_codex_body_menu.bin` | codex 0.144.6, synthetic: modal-shaped menu quoted in the body, live composer below | negative: modal match suppressed with composer present; floor-tier fallback | +| `preview_claude_waiting.bin` | 2.1.215, not re-observed on 2.1.296, where the `✻ Waiting for 1 background agent to finish` row was seen only in the body after the turn: waiting on a backgrounded subagent, `⏺` prose and agent roster around the box | ellipsis-less row extracted verbatim by `claude:waiting`; no model label mid-session | +| `preview_claude_workflow_wait.bin` | 2.1.215, not re-observed on 2.1.296: waiting on a dynamic workflow, with 19 blank rows before the input box and a workflow roster below it | blank rows skipped during `claude:waiting` matching; the roster is excluded | | `preview_grok_working.bin` | grok braille spinner with elapsed/throughput ticker | `grok:spinner` cut at the label's `…`; border label read | | `preview_grok_worked.bin` | grok `Worked for 8.7s` completion row above the box | `grok:worked` kept verbatim | -| `preview_grok_still_running.bin` | grok `◎ 1 subagent still running` above the idle box | `grok:still-running`; border label read | -| `preview_grok_subagent_scrollback.bin` | grok idle with `Subagent running:` in the body, no `◎` row | fall-through; body-shaped text is not status | +| `preview_grok_background_tasks.bin` | grok 1.0.50, captured 2026-10-10: `⠹ Waiting for response…` spinner over the `▾ Tasks 1` panel of a command sent to the background with Ctrl+B; `⠋ - Waiting for response… - … - grok` title announce | `grok:spinner` read over the Tasks panel; border label read | +| `preview_grok_waiting_subagent.bin` | grok 1.0.50, captured 2026-10-10: `⠴ Waiting for subagent…` spinner while a subagent runs; `⠙ - Waiting for subagent… - grok` title announce | `grok:spinner`; a subagent turn is an ordinary spinner row | +| `preview_grok_subagent_scrollback.bin` | grok idle with `Subagent running:` as body text above the box | fall-through; body-shaped text is not status | | `preview_grok_idle.bin` | grok idle session | fall-through to the marker | | `preview_grok_splash.bin` | grok launch splash with resume hint above the box | fall-through; distinct views excluded from anchors | | `preview_grok_idle_titled.bin` | `preview_grok_idle.bin` plus a `Run ls -la list directory file names - grok` title announce | Strip `- grok` and render the session name through the title tier | @@ -106,16 +125,19 @@ prevent a byte-for-byte match with the captured header. | `preview_trunc_grok.bin` | synthetic 40×80: spinner label truncated with the CLI's ellipsis | CLI's own `…` kept verbatim | | `preview_wrap_grok.bin` | synthetic 40×30: ellipsis wrapped onto the next row | structure rejected; marker fallback | -## Codex 0.157.1 and 0.158.0 captures +## Codex 0.158.0 captures The versioned directories contain raw PTY streams from the official macOS arm64 -release executables, recorded on 2026-09-28. Codex ran with isolated homes, -`--no-daemon`, and a loopback Responses server supplying scripted replies: +0.158.0 release executable, recorded on 2026-09-28. Codex ran with isolated +homes, `--no-daemon`, and a loopback Responses server supplying scripted +replies. The 0.157.1 streams recorded alongside them were deleted on +2026-10-10: nothing in the matchers was pinned by them alone, and 0.158.0 itself is +behind the current release, 0.162.1. - [`codex_0158_preview`](codex_0158_preview/README.md) covers working status, queued input, Shift-Left draft retrieval, completed-turn rejection by the - working matcher, ordinary command approval, and the additional terminal-input - approval in 0.158.0. The existing approval matcher recognizes both menus. + working matcher, ordinary command approval, and the terminal-input approval + menu. The existing approval matcher recognizes both menus. - [`codex_0158_terminal`](codex_0158_terminal/README.md) covers direct Codex output across resize, `/new`, alternate-screen teardown, and OSC 52 copy. Separate outer fleetcom captures show resumed history and the exact clipboard diff --git a/tests/corpus/codex_0158_preview/0.157.1-fullscreen-approval.bin b/tests/corpus/codex_0158_preview/0.157.1-fullscreen-approval.bin deleted file mode 100644 index 593ea45..0000000 --- a/tests/corpus/codex_0158_preview/0.157.1-fullscreen-approval.bin +++ /dev/null @@ -1 +0,0 @@ -[?2004h[?1004h]10;?\]11;?\[>c[?2026h[?25l[?1049h[?1007l[?1000h[?1002h[?1006h[?1003h[?25l[?2026h[?25l╭───────────────────────────────────────╮│ >_ OpenAI Codex (v0.157.1) ││ ││ model: loading /model to change ││ directory: loading │╰───────────────────────────────────────╯›Ask Codex to do anything?forshortcuts[0 q╭[?25h[?2026l[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l/private/tmp/…/workspace[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;workspace[?2026h[?25lTip:Run/reviewtogetacodereviewofyourcurrentchanges.phase0-fixture-model default[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l─────────────╮ │ │phase0-fixture-model /model to change │ │─────────────╯[?25h[?2026l[?2026h[?25lPhase zero fixture: replywiththecompletionmarker.[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l› Phasezerofixture:replywiththecompletionmarker.Ask Codex to do anything?forshortcuts[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25l•Working(0s • esc to interrupt)⚠1 warning·f2toview[?25h[?2026l]0;⠙ ⠙ | workspace[?2026h[?25h[?2026l]0;⠹ ⠹ | workspace[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25l◦[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l1[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l2[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦3[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l4[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l5[?25h[?2026l]0;[ ! ] Action Required | workspace[?2026h[?25l•Runningprintf 'PHASE0 APPROVAL\n'Would you like to run the following command?Environment:localReason:Phase zero approval fixture: print a marker.$printf 'PHASE0 APPROVAL\n' › 1. Yes, proceed (y)  2. Yes, and don't ask againforcommandsthatstartwith`printf"PHASE0APPROVAL\\n"`(p)3.No,andtellCodexwhattododifferently(esc)Press enter to confirm or esc to cancel [?2026l[?2026h[?2026l]0;[ . ] Action Required | workspace]0;[ ! ] Action Required | workspace]0;[ . ] Action Required | workspace]0;⠙ workspace[?2026h✗ You canceled the request to run printf 'PHASE0 APPROVAL\n'•Failed (exit 1)printf 'PHASE0 APPROVAL\n' └ (no output)•Working(5s • esc to interrupt) ›Ask Codex to do anythingphase0-fixture-model default? for shortcuts ⚠ 1 warning · f2 to view[?25h[?2026l]0;workspace[?2026h[?25l■ Conversation interrupted - tell the model what to do differently. Something went wrong? Hit `/feedback` to report theissue. Tip:Run/reviewtogetacodereviewofyourcurrentchanges.[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l›Shutting down...[?2026l]0;[?1006l[?1015l[?1003l[?1002l[?1000l[?1007l[?1049l[<1u[?2004l[?1004l[0 q[?25h[0 q[?25h^C \ No newline at end of file diff --git a/tests/corpus/codex_0158_preview/0.157.1-fullscreen-stdin.bin b/tests/corpus/codex_0158_preview/0.157.1-fullscreen-stdin.bin deleted file mode 100644 index 5909ffa..0000000 --- a/tests/corpus/codex_0158_preview/0.157.1-fullscreen-stdin.bin +++ /dev/null @@ -1 +0,0 @@ -[?2004h[?1004h]10;?\]11;?\[>c[?2026h[?25l[?1049h[?1007l[?1000h[?1002h[?1006h[?1003h[?25l[?2026h[?25l╭───────────────────────────────────────╮│ >_ OpenAI Codex (v0.157.1) ││ ││ model: loading /model to change ││ directory: loading │╰───────────────────────────────────────╯›Ask Codex to do anything?forshortcuts[0 q╭[?25h[?2026l[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l───────────────────╮ │ │ │/private/…/0.157.1-fullscreen-stdin/workspace │───────────────────╯[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;workspace[?2026h[?25lTip:Use/keymaptoconfigurekeyboardshortcuts.phase0-fixture-model default[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25lphase0-fixture-model /modeltochange[?25h[?2026l[?2026h[?25lPhase zero fixture: replywiththecompletionmarker.[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l› Phasezerofixture:replywiththecompletionmarker.Ask Codex to do anything?forshortcuts[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l⚠1 warning·f2toview[?25h[?2026l]0;⠙ workspace[?2026h[?25l•Working(0s • esc to interrupt)[?25h[?2026l]0;⠙ ⠙ | workspace[?2026h[?25h[?2026l]0;⠹ ⠹ | workspace[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l1[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l2[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦3[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25l4[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;[ ! ] Action Required | workspace[?2026h[?25l•Runningread line; printf "PHASE0 INPUT: %s\n" "$line"Would you like to run the following command?Environment:localReason:Phase zero input approval fixture: read and echo one line.$read line; printf "PHASE0 INPUT: %s\n" "$line" › 1. Yes, proceed (y)  2. Yes, and don't ask againforcommandsthatstartwith`readline;printf"PHASE0INPUT:%s\n""$line"`(p)3.No,andtellCodexwhattododifferently(esc)Press enter to confirm or esc to cancel [?2026l[?2026h[?2026l]0;[ . ] Action Required | workspace]0;[ ! ] Action Required | workspace]0;⠙ workspace[?2026h✔ You approved codex to run read line; printf "PHASE0 INPUT: %s\n" "$line" this time•Runningread line; printf "PHASE0 INPUT: %s\n" "$line"•Working(5s • esc to interrupt) ›Ask Codex to do anythingphase0-fixture-model default? for shortcuts ⚠ 1 warning · f2 to view[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l6[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l └ fixture PHASE0 INPUT: fixture[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•a read line; printf "PHASE0 INPUT: %s\n" "$line"[?25h[?2026l]0;workspace[?2026h[?25l• PHASE0COMPLETE.Deterministiclocalresponse. 2:42 PM  Tip:Switchmodelsorreasoningeffortquicklywith/model.[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25lesc again o editpreviousmessage[?25h[?2026l[?2026h[?25lTip:Switchmodelsorreasoningeffortquicklywith/model.? for shorcuts   ⚠1 warning·f2toview[?25h[?2026l[?2026h[?25l›Shutting down...[?2026l]0;[?1006l[?1015l[?1003l[?1002l[?1000l[?1007l[?1049l[<1u[?2004l[?1004l[0 q[?25h[0 q[?25h^C \ No newline at end of file diff --git a/tests/corpus/codex_0158_preview/0.157.1-inline.bin b/tests/corpus/codex_0158_preview/0.157.1-inline.bin deleted file mode 100644 index d13337c..0000000 --- a/tests/corpus/codex_0158_preview/0.157.1-inline.bin +++ /dev/null @@ -1,17 +0,0 @@ -[?2004h[?1004h]10;?\]11;?\[>c[?2026h[?2026h[?25l╭───────────────────────────────────────╮│ >_ OpenAI Codex (v0.157.1) ││ ││ model: loading /model to change ││ directory: loading │╰───────────────────────────────────────╯  › Ask Codex to do anything  [0 q╭[?25h[?2026l[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l─────────────╮ │ │ │/private/tmp/…/0.157.1-inline/workspace │─────────────╯[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;workspace[?2026h[?25lphase0-fixture-model default[?25h[?2026l[?2026h[?25h[?2026l[?2026hMMMMMMMM -╭────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.157.1) │ -│ │ -│ model: phase0-fixture-model /model to change │ -│ directory: /private/tmp/…/0.157.1-inline/workspace │ -╰────────────────────────────────────────────────────╯ - - Tip: New Build faster with the Desktop app. Run 'codex app' or visit ]8;;https://chatgpt.com/codex?app-landing-page=truehttps://chatgpt.com/codex?app-landing-page=true]8;;[?25l  › Ask Codex to do anything  phase0-fixture-model default[0 q [?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25lPhase zero fixture: replywiththecompletionmarker.[?25h[?2026l[?2026hMMMM - - -› Phase zero fixture: reply with the completion marker. -[?25lAsk Codex to do anything[0 q [?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l⚠1 warning·f2toview[?25h[?2026l]0;⠙ workspace[?2026h[?25l • Working (0s • esc to interrupt)  › Ask Codex to do anything  phase0-fixture-model default ⚠ 1 warning · f2 to view[0 q [?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l]0;⠹ ⠹ | workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25l◦[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25l1[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25l•[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25l◦[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25l2[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25l•[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25l◦3[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25l•[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l4[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25l◦[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25l•[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25lQueued phase zero messagetab to queue message   100% contex left[?25h[?2026l]0;⠹ workspace[?2026h[?25l5[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25l◦[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l ◦ Working (5s • esc to interrupt) • Queued follow-up inputs ↳ Queued phase zero message ⌥+↑ edit last queued message › Ask Codex to do anything  phase0-fixture-model default ⚠ 1 warning · f2 to view[0 q [?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25l•6[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l • Working (6s • esc to interrupt)  › Queued phase zero message  tab to queue message 100% context left[0 q [?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25l◦[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25l7[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25lAsk Codex to do anythingphase0-fixture-model default⚠1 warning · f2 o view[?25h[?2026l[?2026hMM - -• PHASE0 COMPLETE. Deterministic local response.[?25l  › Ask Codex to do anything  phase0-fixture-model default ⚠ 1 warning · f2 to view[0 q [?25h[?2026l]0;workspace[?2026hMM - - 2:42 PM[?25l[0 q [?25h[?2026l[?2026h[?25l  › Shutting down... [?2026l]0;[<1u[?2004l[?1004l[0 q[?25h[0 q[?25h^C \ No newline at end of file diff --git a/tests/corpus/codex_0158_preview/README.md b/tests/corpus/codex_0158_preview/README.md index b82a485..c91382f 100644 --- a/tests/corpus/codex_0158_preview/README.md +++ b/tests/corpus/codex_0158_preview/README.md @@ -1,11 +1,12 @@ -# Codex 0.157.1 and 0.158.0 preview captures +# Codex 0.158.0 preview captures -Captured 2026-09-28 from the official macOS arm64 release executables in a +Captured 2026-09-28 from the official macOS arm64 release executable in a 40-row × 120-column PTY with `TERM=xterm-256color`. Each file is the complete, unmodified direct Codex PTY stream from startup through teardown. Tests replay prefixes at the byte checkpoints below. There are no resize events in these -six scenarios. The `fullscreen` scenarios use Codex's alternate screen; they -are not captures of an outer fleetcom client. +three scenarios. The `fullscreen` scenarios use Codex's alternate screen; they +are not captures of an outer fleetcom client. The 0.157.1 pair of each +scenario, captured the same day, was deleted on 2026-10-10. The Responses endpoint was a loopback server with scripted replies and harmless tool calls. Each run used an isolated home and workspace, `--no-daemon`, and @@ -17,19 +18,16 @@ behavior. Approval tests also verify preview resolution. The inline scenario submits a prompt, queues `Queued phase zero message` with Tab, retrieves it with Shift-Left (`ESC [ 1 ; 2 D`), clears the draft with -Ctrl-U, and waits for completion. Both releases retrieve the draft, although -0.157.1 displays the older Option-Up hint. The approval scenario declines a -`printf` command. The stdin scenario approves a shell command that reads one -line, then the mock requests `fixture\n` as terminal input. 0.157.1 completes; -0.158.0 displays a second approval menu. Tests check the rendered distinctions -and adapter results against the reviewed capture screens, not generated -expectations. +Ctrl-U, and waits for completion. The approval scenario declines a `printf` +command. The stdin scenario approves a shell command that reads one line, then +the mock requests `fixture\n` as terminal input and a second approval menu is +displayed. Tests check the rendered rows and adapter results against the +reviewed capture screens, not generated expectations. ## Producer provenance | Version | Source commit | Official macOS arm64 tarball SHA-256 | | --- | --- | --- | -| 0.157.1 | `36650394c5b38c2990ccf2a3457165ca3e9d9726` | `3c45b162b7a76f51325015b1d0a8112c73219b7a9b59cd5762c37c9ba55894fa` | | 0.158.0 | `064c6b8c737f5b41d171fdda80bd9ef10ad06eb3` | `341c4a08f9ce1935b3007376dc2a3d50a0a89112930e9a474ae61367218f6e8a` | Imported from the matching scenario's `session.bin` in the Phase 0 evidence @@ -45,11 +43,8 @@ Offsets are exclusive byte ends measured from startup. Dimensions remain | Stream | Tested checkpoints | Bytes | SHA-256 | | --- | --- | ---: | --- | -| `0.157.1-inline.bin` | working 12405; queued 15613; Shift-Left 17650; completed 18716 | 18979 | `3948ac17127d2a8b87e52ce33c21ae5fd481561138534d773e9c857eeedd5a93` | | `0.158.0-inline.bin` | working 11311; queued 14478; Shift-Left 16558; completed 17253 | 17516 | `891a15ed16251b0af90ca3cf06311d401fef8e7a0bf93a3fea240f3a2f74f8ac` | -| `0.157.1-fullscreen-approval.bin` | approval 11965 | 13572 | `cbc65ae4387235d0b6a7d724984125d72bd7ed0348e39f41866b5426783ba99a` | | `0.158.0-fullscreen-approval.bin` | approval 12967 | 14592 | `357b3c8c5b4d6b34b422d08f745d92700b4969635d8e29282d1cb76b48b84d8e` | -| `0.157.1-fullscreen-stdin.bin` | command approval 12494; completed after stdin 16518 | 17327 | `07e950cd24ae9a93684ca427714eb6a9507a64a08987c2de6263476d96bc4efe` | | `0.158.0-fullscreen-stdin.bin` | command approval 13496; stdin approval 17417 | 19416 | `cb8c3662a0121b8dd1fd73ee3d15de3ae9208bdd72f054b1c71c58460d27ab47` | These captures do not establish Linux CLI rendering, real-account behavior, diff --git a/tests/corpus/codex_0158_terminal/0.157.1-fullscreen-fleetcom-load.bin b/tests/corpus/codex_0158_terminal/0.157.1-fullscreen-fleetcom-load.bin deleted file mode 100644 index ff11837..0000000 --- a/tests/corpus/codex_0158_terminal/0.157.1-fullscreen-fleetcom-load.bin +++ /dev/null @@ -1 +0,0 @@ -[?u[?1049h[?25l[?2004h[?1007s[?1007h[?1004s[?1004h[?2026h[?25l fleetcom 0 running · 0 idle · 0 done by state · dir · custom · foreground                                       ❯ n run · @ dir · / find · s sort  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l fleetcom 1 running · 0 idle · 0 done by state · dir · custom · foreground   Running  ✻ codex resume '01a0e9fa-42… 0s                                   loaded 'phase0-saved': 1 task(s)  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l fleetcom 1 running · 0 idle · 0 done by state · dir · custom · foreground   Running  ✻ codex resume '01a0e9fa-42… full-screen 0s                                   loaded 'phase0-saved': 1 task(s)  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l fleetcom 1 running · 0 idle · 0 done by state · dir · custom · foreground   Running  ✻ codex resume '01a0e9fa-42… workspace 0s                                   loaded 'phase0-saved': 1 task(s)  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l fleetcom 1 running · 0 idle · 0 done by state · dir · custom · foreground   Running  ✻ codex resume '01a0e9fa-42… workspace 1s                                   loaded 'phase0-saved': 1 task(s)  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l fleetcom 1 running · 0 idle · 0 done by state · dir · custom · foreground   Running  ✻ codex resume '01a0e9fa-42… workspace 2s                                   loaded 'phase0-saved': 1 task(s)  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l fleetcom 1 running · 0 idle · 0 done by state · dir · custom · foreground   Running  ✻ codex resume '01a0e9fa-42… workspace 3s                                   loaded 'phase0-saved': 1 task(s)  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l fleetcom 1 running · 0 idle · 0 done by state · dir · custom · foreground   Running  ✻ codex resume '01a0e9fa-42… workspace 4s                                   loaded 'phase0-saved': 1 task(s)  ↑↓ select · enter attach · space peek · ? controls [?25l[?2026l[?2026h[?25l [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25l[?2026l[?1000h[?1002h[?1003h[?1015h[?1006h[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM                     Tip: Use /feedback to send logs to the maintainers when something looks off.  › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM                      › Phase zero fixture: reply with the completion marker.   phase0-fixture-model default  [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (0s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (0s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (1s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (1s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (1s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (2s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (2s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (3s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (3s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (4s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (4s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (4s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (4s • esc to interrupt)   › Queued phase zero message   phase0-fixture-model default  tab to queue message [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (5s • esc to interrupt)   › Queued phase zero message   phase0-fixture-model default  tab to queue message [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (5s • esc to interrupt)   › Queued phase zero message   phase0-fixture-model default  tab to queue message [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.            ◦ Working (5s • esc to interrupt)  • Queued follow-up inputs  ↳ Queued phase zero message  ⌥+↑ edit last queued message   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.            • Working (6s • esc to interrupt)  • Queued follow-up inputs  ↳ Queued phase zero message  ⌥+↑ edit last queued message   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (6s • esc to interrupt)   › Queued phase zero message   phase0-fixture-model default  tab to queue message [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (6s • esc to interrupt)   › Queued phase zero message   phase0-fixture-model default  tab to queue message [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                ◦ Working (7s • esc to interrupt)   › Queued phase zero message   phase0-fixture-model default  tab to queue message [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (7s • esc to interrupt)   › Queued phase zero message   phase0-fixture-model default  tab to queue message [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.                • Working (7s • esc to interrupt)   › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.                › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:46 PM             Tip: Use /theme to choose a syntax highlighting theme.  › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:46 PM              › Ask Codex to do anything   phase0-fixture-model default  ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:46 PM            › /copy copy the last response or part of it  › /copy   phase0-fixture-model default  [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:46 PM             Copy to clipboard   › 1. Whole response PHASE0 COMPLETE. Deterministic local response.   enter select · esc back  [?25l [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' Ctrl-\ background [?25l[?2026l]52;c;UEhBU0UwIENPTVBMRVRFLiBEZXRlcm1pbmlzdGljIGxvY2FsIHJlc3BvbnNlLg==[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:46 PM             Tip: Use /theme to choose a syntax highlighting theme.  › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' copied 46 chars [?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.157.1) │ │ │ │ model: phase0-fixture-model /model to change │ │ directory: /private/tmp/…/workspace │ ╰────────────────────────────────────────────────────╯   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:45 PM   › Phase zero fixture: reply with the completion marker.   • PHASE0 COMPLETE. Deterministic local response.   2:46 PM  • Copied Whole response to clipboard           Tip: Use /theme to choose a syntax highlighting theme.  › Ask Codex to do anything   phase0-fixture-model default  ? for shortcuts ⚠ 1 warning · f2 to view [?25h [attached] codex resume '01a0e9fa-426f-7162-a215-3cea7cd6e6dd' copied 46 chars [?25h[?2026l \ No newline at end of file diff --git a/tests/corpus/codex_0158_terminal/0.157.1-fullscreen-transitions.bin b/tests/corpus/codex_0158_terminal/0.157.1-fullscreen-transitions.bin deleted file mode 100644 index b1f52ba..0000000 --- a/tests/corpus/codex_0158_terminal/0.157.1-fullscreen-transitions.bin +++ /dev/null @@ -1 +0,0 @@ -[?2004h[?1004h]10;?\]11;?\[>c[?2026h[?25l[?1049h[?1007l[?1000h[?1002h[?1006h[?1003h[?25l[?2026h[?25l╭───────────────────────────────────────╮│ >_ OpenAI Codex (v0.157.1) ││ ││ model: loading /model to change ││ directory: loading │╰───────────────────────────────────────╯›Ask Codex to do anything?forshortcuts[0 q╭[?25h[?2026l[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l/private/tmp/…/workspace[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;workspace[?2026h[?25lTip:Youcanresumeapreviousconversationbyrunningcodex resumephase0-fixture-model default[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l─────────────╮ │ │phase0-fixture-model /model to change │ │─────────────╯[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25lPhase zero fixture: replywiththecompletionmarker.[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l› Phasezerofixture:replywiththecompletionmarker.Ask Codex to do anything?forshortcuts[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l⚠1 warning·f2toview[?25h[?2026l]0;⠙ workspace[?2026h[?25l•Working(0s • esc to interrupt)[?25h[?2026l]0;⠙ ⠙ | workspace[?2026h[?25h[?2026l]0;⠹ ⠹ | workspace[?2026h[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l1[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l2[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦3[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l4[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25l◦[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l5Queued phase zero messagetab to queue message[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦Working(5s • esc to interrupt)• Queuedfollow-upinputs ↳ Queued phase zero message  ⌥+↑ edit last queued mssageAsk Codex to do anything? for shortcuts ⚠1 warning·f2toview[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•6[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l   •Working (6s • esc to intrrupt)Queued phase zero messagetab to queue message[?25h[?2026l]0;⠧ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l◦[?25h[?2026l]0;⠇ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠏ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠋ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠙ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l7[?25h[?2026l]0;⠹ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠸ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l•[?25h[?2026l]0;⠼ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠴ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l]0;⠦ workspace[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25lAsk Codex to do anything? for shortcuts ⚠1 warning·f2toview[?25h[?2026l[?2026h[?25l• PHASE0COMPLETE.Deterministiclocalresponse. [?25h[?2026l]0;workspace[?2026h[?25l 2:42 PM Tip:Pressctrl+gtoedityourcurrentdraftinanexternaleditor.[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l   [?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l› /copy copy the last response or part of it /copy[?25h[?2026l[?2026h[?25l Copy to clipboard › 1. Whole response PHASE0 COMPLETE. Deterministic local response. enter select · esc back [?2026l[?2026h[?2026l]52;c;UEhBU0UwIENPTVBMRVRFLiBEZXRlcm1pbmlzdGljIGxvY2FsIHJlc3BvbnNlLg==[?2026h Tip:Pressctrl+gtoedityourcurrentdraftinanexternaleditor.›Ask Codex to do anything phase0-fixture-model default? for shortcuts ⚠ 1 warning · f2 to view[?25h[?2026l[?2026h[?25l• CopiedWholeresponsetoclipboard[?25h[?2026l[?2026h[?25l│ ││ model: phase0-fixture-model /model to change ││ directory: /private/tmp/…/workspace │╰────────────────────────────────────────────────────╯› Phasezerofixture:replywiththecompletionmarker.• PHASE0COMPLETE.Deterministiclocalresponse. 2:42 PM • CopiedWholeresponsetoclipboardTip:Pressctrl+rtosearchpreviouslyenteredprompts.›Ask Codex to do anythingphase0-fixture-model default?forshortcuts⚠1 warning·f2toview[0 q│[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l╭────────────────────────────────────────────────────╮│ >_ OpenAI Codex (v0.157.1) ││ ││ model: phase0-fixture-model /model to change ││ directory: /private/tmp/…/workspace │╰────────────────────────────────────────────────────╯› Phasezerofixture:replywiththecompletionmarker.• PHASE0COMPLETE.Deterministiclocalresponse. 2:42 PM • CopiedWholeresponsetoclipboardTip:Pressctrl+gtoedityourcurrentdraftinanexternaleditor.›Ask Codex to do anythingphase0-fixture-model default?forshortcuts⚠1 warning·f2toview[0 q╭[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l   [?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l› /new start a new chat during a conversation /new[?25h[?2026l[?2026h[?25l Tip: Press ctrl+g to edit your current draft in an external editor.Ask Codex to do anything?forshortcuts⚠1 warning·f2toview[?25h[?2026l]0;workspace[?2026h[?25l╭────────────────────────────────────────────────────╮│ >_ OpenAI Codex (v0.157.1) ││ ││ model: phase0-fixture-model /model to change ││ directory: /private/tmp/…/workspace │╰────────────────────────────────────────────────────╯Tokenusage:total=20input=10output=10Tocontinuethissession,runcodex resume 01a0e9f7-8d12-76b1-80a8-a50519150e72Tip:Youcanresumeapreviousconversationbyrunningcodex resume›Ask Codex to do anythingphase0-fixture-model default?forshortcuts[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25h[?2026l[?2026h[?25l›Shutting down...[?2026l]0;[?1006l[?1015l[?1003l[?1002l[?1000l[?1007l[?1049l[<1u[?2004l[?1004l[0 q[?25h[0 q[?25h^C \ No newline at end of file diff --git a/tests/corpus/codex_0158_terminal/README.md b/tests/corpus/codex_0158_terminal/README.md index 5dc88a4..f83050d 100644 --- a/tests/corpus/codex_0158_terminal/README.md +++ b/tests/corpus/codex_0158_terminal/README.md @@ -1,8 +1,9 @@ # Codex terminal release captures -Recorded 2026-09-28 with the official arm64 macOS Codex 0.157.1 and 0.158.0 -executables. `manifest.json` preserves source commits, executable/archive hashes, -capture fleetcom provenance, stream hashes, checkpoint offsets, and resize events. +Recorded 2026-09-28 with the official arm64 macOS Codex 0.158.0 executable; the +0.157.1 pair recorded alongside it was deleted on 2026-10-10. `manifest.json` +preserves the source commit, executable/archive hashes, capture fleetcom +provenance, stream hashes, checkpoint offsets, and resize events. The capture fleetcom tree was d138a752 plus the recorded dirty patch, not a clean release checkout. These are raw terminal bytes, not constructed repaint fixtures. diff --git a/tests/corpus/codex_0158_terminal/manifest.json b/tests/corpus/codex_0158_terminal/manifest.json index 981d6f2..b01a3a6 100644 --- a/tests/corpus/codex_0158_terminal/manifest.json +++ b/tests/corpus/codex_0158_terminal/manifest.json @@ -6,125 +6,15 @@ "dirty_patch_sha256": "a0e37cbc2c4877c058a3bcf295cde301086049ea6ed300b7b9244c3d0d15e182", "fleetcom_binary_sha256": "175904b97d627cdac27f1d062c04a99ff6c63e85f3801a98c5f6920da4a529c6", "versions": { - "0.157.1": { - "binary_sha256": "27ceb5f9b957b43a519efe4eaa3816a0bffb0a531a2c89af18840c0a3c016a7d", - "tarball_sha256": "3c45b162b7a76f51325015b1d0a8112c73219b7a9b59cd5762c37c9ba55894fa" - }, "0.158.0": { "binary_sha256": "788a818fbb9596869c7a487554507cb8bdca17584b8671112b23f9e225ba35c8", "tarball_sha256": "341c4a08f9ce1935b3007376dc2a3d50a0a89112930e9a474ae61367218f6e8a" } }, "source_commits": { - "0.157.1": "36650394c5b38c2990ccf2a3457165ca3e9d9726", "0.158.0": "064c6b8c737f5b41d171fdda80bd9ef10ad06eb3" }, "fixtures": [ - { - "name": "0.157.1-fullscreen-transitions", - "surface": "direct Codex", - "initial_rows": 40, - "initial_cols": 120, - "bytes": 23780, - "sha256": "6757c1fe0246248da21ce43cb8745f54661942f69359a5c1b26bab2688b18a60", - "source_session_sha256": "6757c1fe0246248da21ce43cb8745f54661942f69359a5c1b26bab2688b18a60", - "marks": [ - { - "name": "startup", - "offset": 2396, - "rows": 40, - "cols": 120 - }, - { - "name": "completed", - "offset": 16312, - "rows": 40, - "cols": 120 - }, - { - "name": "copy-menu", - "offset": 17320, - "rows": 40, - "cols": 120 - }, - { - "name": "copied", - "offset": 18119, - "rows": 40, - "cols": 120 - }, - { - "name": "narrow", - "offset": 19460, - "rows": 20, - "cols": 60 - }, - { - "name": "restored", - "offset": 21163, - "rows": 40, - "cols": 120 - }, - { - "name": "new-thread", - "offset": 23499, - "rows": 40, - "cols": 120 - }, - { - "name": "exit", - "offset": 23780, - "rows": 40, - "cols": 120 - } - ], - "resizes": [ - { - "offset": 18119, - "resize": [ - 20, - 60 - ] - }, - { - "offset": 19460, - "resize": [ - 40, - 120 - ] - } - ] - }, - { - "name": "0.157.1-fullscreen-fleetcom-load", - "surface": "outer fleetcom client", - "initial_rows": 40, - "initial_cols": 120, - "bytes": 227987, - "sha256": "1a72dbf52c4db51de4ca0e7b57dec5873eb29d82891a79243995a1911ec2c531", - "source_session_sha256": "045e070f0dd09b9427db1c23bd5b2f0d64a2abcaf80f73a9485776d6de53fcaa", - "marks": [ - { - "name": "attached", - "offset": 47465, - "rows": 40, - "cols": 120 - }, - { - "name": "completed", - "offset": 199735, - "rows": 40, - "cols": 120 - }, - { - "name": "copied", - "offset": 227987, - "rows": 40, - "cols": 120 - } - ], - "resizes": [] - }, { "name": "0.158.0-fullscreen-transitions", "surface": "direct Codex", diff --git a/tests/corpus/preview_claude_action.bin b/tests/corpus/preview_claude_action.bin index 7dce920..2bcbab0 100644 --- a/tests/corpus/preview_claude_action.bin +++ b/tests/corpus/preview_claude_action.bin @@ -1,18 +1,17 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc -❯ run: sleep 5 && echo ok +❯ Use the Bash tool to run ls -la and tell me the file names. + +⏺ Listing the working directory now. + + Listed 1 directory + +⏺ The listing came back empty, which is unusual since ls -la always prints . and ... Checking the working directory + directly. ⏺ Running 1 shell command… @@ -31,10 +30,11 @@ -· Hashing… (3s · ↓ 52 tokens) - tmux focus-events off · add 'set -g focus-events on' to ~/.tmux.conf and reattach for focus tracking +· Whisking… (9s · ↓ 261 tokens · thought for 2s) + ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ❯  ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - (statusline scrubbed: user-configured row) - ⏸ manual mode on · ← 2 agents \ No newline at end of file + + Fable 5.1 (high) | /Users/fleet-pilot/Projects/fixture-sources/corpus-capture/claude-2-1-296/sessions/fixture-pty-s… + ⏵⏵ auto mode on (shift+tab to cycle) \ No newline at end of file diff --git a/tests/corpus/preview_claude_approval.bin b/tests/corpus/preview_claude_approval.bin index 4b2a9ce..3e3d9d0 100644 --- a/tests/corpus/preview_claude_approval.bin +++ b/tests/corpus/preview_claude_approval.bin @@ -1,39 +1,39 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc -❯ echo ok +❯ Use the Bash tool to run ls -la and tell me the file names. - Ran 1 shell command +⏺ Listing the working directory now. -⏺ ok + Listed 1 directory -✻ Crunched for 4s +⏺ ls -la printed nothing, which it never does for an existing directory. Checking the working directory and listing it + by explicit path. -❯ write a file to the current directory with an interesting word as its content - -⏺ Write(word.txt) + Listing the working directory by absolute path and showing exit status + ⎿ $ ls -la "/Users/fleet-pilot/Projects/fixture-sources/corpus-capture/claude-2-1-296/sessions/fixture-pty-session/sc + ratchpad/claude-work" 2>&1; echo "exit=$?" ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - Create file - word.txt + Bash command + Tip: auto mode handles these prompts for you — choose "switch to auto mode" below + List the working directory by absolute path and show exit status ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - 1 petrichor + │ ls -la "/Users/fleet-pilot/Projects/fixture-sources/corpus-capture/claude-2-1-296/sessions/fixture-pty-session/scrat + │ chpad/claude-work" 2>&1; echo "exit=$?" ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - Do you want to create word.txt? + This command requires approval + + Do you want to proceed? ❯ 1. Yes - 2. Yes, allow all edits during this session (shift+tab) - 3. No + 2. Yes, and don’t ask again for: echo "exit=$?" + 3. Yes, and switch to auto mode · auto mode handles these prompts for you + 4. No Esc to cancel · Tab to amend + + diff --git a/tests/corpus/preview_claude_body_menu.bin b/tests/corpus/preview_claude_body_menu.bin index a8a0c81..63f5bdf 100644 --- a/tests/corpus/preview_claude_body_menu.bin +++ b/tests/corpus/preview_claude_body_menu.bin @@ -1,15 +1,15 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc + + + + + + + + ❯ run: sleep 5 && echo ok diff --git a/tests/corpus/preview_claude_body_menu_idle.bin b/tests/corpus/preview_claude_body_menu_idle.bin index 19ae209..63782bc 100644 --- a/tests/corpus/preview_claude_body_menu_idle.bin +++ b/tests/corpus/preview_claude_body_menu_idle.bin @@ -1,15 +1,15 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc + + + + + + + + ❯ echo ok diff --git a/tests/corpus/preview_claude_done.bin b/tests/corpus/preview_claude_done.bin index a153ba9..83ad0a6 100644 --- a/tests/corpus/preview_claude_done.bin +++ b/tests/corpus/preview_claude_done.bin @@ -1,29 +1,29 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc -❯ echo ok +❯ Use the Bash tool to run ls -la and tell me the file names. - Ran 1 shell command +⏺ Listing the working directory now. -⏺ ok + Listed 1 directory -✻ Crunched for 4s +⏺ The listing came back empty, which is unusual since ls -la always prints . and ... Checking the working directory + directly. + Ran 1 shell command +⏺ The directory holds a single file: + - README.md (7 bytes, modified today at 15:32) + The first ls -la run produced no output at all, which is why I re-ran it by absolute path. Your ls is aliased to a + tool that uses a different output format and omits the . and .. entries, which fits the second listing. The initial + empty result most likely came from that alias misbehaving without a terminal attached. Nothing was changed. +✻ Sautéed for 17s · done 4:14 PM @@ -32,9 +32,9 @@ - tmux focus-events off · add 'set -g focus-events on' to ~/.tmux.conf and reattach for focus tracking ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── -❯  +❯ what's in README.md ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - (statusline scrubbed: user-configured row) - ⏸ manual mode on · ← 2 agents \ No newline at end of file + + Fable 5.1 (high) | /Users/fleet-pilot/Projects/fixture-sources/corpus-capture/claude-2-1-296/sessions/fixture-pty-s… + ⏵⏵ auto mode on (shift+tab to cycle) \ No newline at end of file diff --git a/tests/corpus/preview_claude_idle.bin b/tests/corpus/preview_claude_idle.bin index 95604ab..b460389 100644 --- a/tests/corpus/preview_claude_idle.bin +++ b/tests/corpus/preview_claude_idle.bin @@ -1,15 +1,7 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc connecting… @@ -32,9 +24,16 @@ - ● high · /effort + + + + + + + + ● high · /effort ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── -❯ Try "refactor " +❯ Try "how do I log an error?" ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - (statusline scrubbed: user-configured row) - ⏸ manual mode on · ← 2 agents \ No newline at end of file + + diff --git a/tests/corpus/preview_claude_working.bin b/tests/corpus/preview_claude_working.bin index 8805269..be2e2ed 100644 --- a/tests/corpus/preview_claude_working.bin +++ b/tests/corpus/preview_claude_working.bin @@ -1,18 +1,10 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc -❯ run: sleep 5 && echo ok +❯ Reply with exactly the word ok and nothing else. @@ -31,10 +23,18 @@ -✽ Concocting… - tmux focus-events off · add 'set -g focus-events on' to ~/.tmux.conf and reattach for focus tracking + + + + + + + +✶ Moonwalking… + ● high · /effort ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ❯  ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - (statusline scrubbed: user-configured row) - ⏸ manual mode on · ← 2 agents \ No newline at end of file + + Fable 5.1 (high) | /Users/fleet-pilot/Projects/fixture-sources/corpus-capture/claude-2-1-296/sessions/fixture-pty-s… + ⏵⏵ auto mode on (shift+tab to cycle) \ No newline at end of file diff --git a/tests/corpus/preview_claude_working_agent.bin b/tests/corpus/preview_claude_working_agent.bin new file mode 100644 index 0000000..577afe7 --- /dev/null +++ b/tests/corpus/preview_claude_working_agent.bin @@ -0,0 +1,40 @@ +[?1049h + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc + + +❯ Use the Task tool to launch one subagent whose only job is to reply with the word ok, then tell me what it said. + +⏺ Launching one subagent now. + +⏺ Agent(Reply with the word ok) + ⎿  Backgrounded agent (↓ to manage · ctrl+o to expand) + + + + + + + + + + + + + + + + + +✽ Hashing… (2s · ↓ 112 tokens) + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +❯  +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + + Fable 5.1 (high) | /Users/fleet-pilot/Projects/fixture-sources/corpus-capture/claude-2-1-296/sessions/fixture-pty-s… + ⏵⏵ auto mode on (shift+tab to cycle) + + ⏺ main + ◯ general-purpose Reply with the word ok 0s \ No newline at end of file diff --git a/tests/corpus/preview_claude_working_tool.bin b/tests/corpus/preview_claude_working_tool.bin index 154deda..33abda4 100644 --- a/tests/corpus/preview_claude_working_tool.bin +++ b/tests/corpus/preview_claude_working_tool.bin @@ -1,15 +1,15 @@ [?1049h -╭─── Claude Code v2.1.215 ─────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ Tips for getting started │ -│ Welcome back Fleet Pilot! │ Run /init to create a CLAUDE.md file with instructions for Cla… │ -│ │ ─────────────────────────────────────────────────────────────── │ -│ ▐▛███▜▌ │ What's new │ -│ ▝▜█████▛▘ │ Claude no longer runs the `/verify` and `/code-review` skills … │ -│ ▘▘ ▝▝ │ Fixed single-segment `dir/**` allow rules like `Edit(src/**)` … │ -│ Fable 5 with high effort · Claude Max · │ Fixed a permission-check bypass affecting commands run in Wind… │ -│ anonymized-account0@example.invalid's Organization │ /release-notes for more │ -│ ~/Projects/fixture-sources │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ▐▛███▛█ Claude Code v2.1.296 +▝▜██████▀ Fable 5.1 with high effort · Claude Max + ▝▝ ▝▝ /…/Users/fleet-pilot/Projects/fixture-sources/ptys/claude-work · /rc + + + + + + + + ❯ run: sleep 5 && echo ok diff --git a/tests/corpus/preview_codex_done.bin b/tests/corpus/preview_codex_done.bin new file mode 100644 index 0000000..8cf72fa --- /dev/null +++ b/tests/corpus/preview_codex_done.bin @@ -0,0 +1,40 @@ +[?1049h + >_ OpenAI Codex (v0.162.1) + /Users/fleet-pilot/Projects/fixture-sources/pty/codex-cli-0-162-1/…/scratchpad/codex-work + + To get started, describe a task or try one of these commands: + + /init - create an AGENTS.md file with instructions for Codex + /status - show current session configuration + /permissions - choose what Codex is allowed to do + /model - choose what model and reasoning effort to use + /review - review any changes and find issues + + +› Reply with exactly the word ok and nothing else. + + +• ok + + Worked for 1s • 4:19 PM + + + + + + + + + + + + + + + + + +› Ask Codex to do anything + + GPT-6-Luna max · 12.8K used · 20.7K in · 5 out + ← for agents · ? for shortcuts \ No newline at end of file diff --git a/tests/corpus/preview_codex_idle.bin b/tests/corpus/preview_codex_idle.bin new file mode 100644 index 0000000..0955fce --- /dev/null +++ b/tests/corpus/preview_codex_idle.bin @@ -0,0 +1,40 @@ +[?1049h + >_ OpenAI Codex (v0.162.1) + /Users/fleet-pilot/Projects/fixture-sources/pty/codex-cli-0-162-1/…/scratchpad/codex-work + + To get started, describe a task or try one of these commands: + + /init - create an AGENTS.md file with instructions for Codex + /status - show current session configuration + /permissions - choose what Codex is allowed to do + /model - choose what model and reasoning effort to use + /review - review any changes and find issues + + + + + + + + + + + + + + + + + + + + + + + + + +› Ask Codex to do anything + + GPT-6-Luna max · 0 in · 0 out + ← for agents · ? for shortcuts \ No newline at end of file diff --git a/tests/corpus/preview_codex_queued.bin b/tests/corpus/preview_codex_queued.bin index 99cbfb9..c13dda9 100644 --- a/tests/corpus/preview_codex_queued.bin +++ b/tests/corpus/preview_codex_queued.bin @@ -1,10 +1,20 @@ - +[?1049h + >_ OpenAI Codex (v0.162.1) + /Users/fleet-pilot/Projects/fixture-sources/pty/codex-cli-0-162-1/…/scratchpad/codex-work + To get started, describe a task or try one of these commands: + /init - create an AGENTS.md file with instructions for Codex + /status - show current session configuration + /permissions - choose what Codex is allowed to do + /model - choose what model and reasoning effort to use + /review - review any changes and find issues +› Run the shell command sleep 25 and then say done. +• I’ll run sleep 25 now. @@ -15,26 +25,16 @@ -• Working (0s • esc to interrupt) + + +• Working (10s • esc to interrupt) · 1 background terminal running · /ps to view · /stop to close • Queued follow-up inputs - ↳ Hello, world! 0 - ↳ Hello, world! 1 - ↳ Hello, world! 2 - ↳ Hello, world! 3 - ↳ Hello, world! 4 - ↳ Hello, world! 5 - ↳ Hello, world! 6 - ↳ Hello, world! 7 - ↳ Hello, world! 8 - ↳ Hello, world! 9 - ↳ Hello, world! 10 - ↳ Hello, world! 11 - ↳ Hello, world! 12 - ↳ Hello, world! 13 - ↳ Hello, world! 14 - ↳ Hello, world! 15 + ↳ Also tell me the current year. + ⇧← edit last queued message + › Ask Codex to do anything - gpt-5.6-sol default · /tmp/project \ No newline at end of file + GPT-6-Luna max · 0 in · 0 out + ← for agents · ? for shortcuts \ No newline at end of file diff --git a/tests/corpus/preview_codex_ran_done.bin b/tests/corpus/preview_codex_ran_done.bin new file mode 100644 index 0000000..1b5cdaa --- /dev/null +++ b/tests/corpus/preview_codex_ran_done.bin @@ -0,0 +1,40 @@ +[?1049h + >_ OpenAI Codex (v0.162.1) + /Users/fleet-pilot/Projects/fixture-sources/pty/codex-cli-0-162-1/…/scratchpad/codex-work + + To get started, describe a task or try one of these commands: + + /init - create an AGENTS.md file with instructions for Codex + /status - show current session configuration + /permissions - choose what Codex is allowed to do + /model - choose what model and reasoning effort to use + /review - review any changes and find issues + + +› Run the shell command curl -sI https://example.com and show me the first line of output. + + +• I’ll run that command and return its first output line. + +• Ran curl -sI https://example.com + └ cf-ray: 0000000000000000-XXX + alt-svc: h3=":443"; ma=86400 + + + 9 lines (⌃t to expand) + +• HTTP/2 200 + + + + + + + + + + + +› Ask Codex to do anything + + GPT-6-Luna max · 3.92K used · 41.2K in · 106 out + ← for agents · ? for shortcuts \ No newline at end of file diff --git a/tests/corpus/preview_codex_working.bin b/tests/corpus/preview_codex_working.bin index 99e061c..31c331a 100644 --- a/tests/corpus/preview_codex_working.bin +++ b/tests/corpus/preview_codex_working.bin @@ -1,34 +1,28 @@ -╭────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.144.6) │ -│ │ -│ model: gpt-5.6-sol high /model to change │ -│ directory: ~/Projects/fixture-sources │ -╰────────────────────────────────────────────────╯ +[?1049h + >_ OpenAI Codex (v0.162.1) + /Users/fleet-pilot/Projects/fixture-sources/pty/codex-cli-0-162-1/…/scratchpad/codex-work - Tip: New Use /fast to enable our fastest inference with increased plan usage. + To get started, describe a task or try one of these commands: -⚠ MCP client for `demo-mcp-unresolved` failed to start: MCP startup failed: No such file or directory (os error 2) + /init - create an AGENTS.md file with instructions for Codex + /status - show current session configuration + /permissions - choose what Codex is allowed to do + /model - choose what model and reasoning effort to use + /review - review any changes and find issues -• You have 3 usage limit resets available. Run /usage to use one. -⚠ MCP startup incomplete (failed: demo-mcp-unresolved) +› Run the shell command sleep 25 and then say done. -› run: sleep 5 && echo ok +• I’ll run sleep 25 now. -⚠ MCP client for `demo-mcp-unresolved` failed to start: MCP startup failed: No such file or directory (os error 2) -⚠ MCP startup incomplete (failed: demo-mcp-unresolved) -• I’m running that command now. -• Working (7s • esc to interrupt) · 1 background terminal running · /ps to view · /stop to close -› Write tests for @filename - gpt-5.6-sol high · 0 in · 0 out @@ -37,3 +31,10 @@ +• Working (3s • esc to interrupt) · 1 background terminal running · /ps to view · /stop to close + + +› Ask Codex to do anything + + GPT-6-Luna max · 0 in · 0 out + ← for agents · ? for shortcuts \ No newline at end of file diff --git a/tests/corpus/preview_grok_background_tasks.bin b/tests/corpus/preview_grok_background_tasks.bin new file mode 100644 index 0000000..b734804 --- /dev/null +++ b/tests/corpus/preview_grok_background_tasks.bin @@ -0,0 +1,39 @@ +[?1049h]0;⠋ - Waiting for response… - Run sleep 40 command then say done - grok + ~/Projects/fixture-sources/grok 19K / 256K │ [Dashboard] + + + ❯ Run the shell command sleep 40 and then say done. 4:30 PM + + + ◆ Thought for 2.1s + + I'll run sleep 40 and report back when it finishes. 4:30 PM + + ◆ Task started: Sleep for 40 seconds + + + + + + + + + + + + + + + + + + ⠹ Waiting for response… 0.1s 9.5s ⇣19.2k [stop] + + ▾ Tasks 1 + ⸬ Run Sleep for 40 seconds 0s + + ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ ❯ │ + ╰────────────────────────────────────────────────────────────────────────────────── Grok 4.7 (high) · auto-review ─╯ + + Shift+Tab:mode │ Ctrl+c:cancel │ Ctrl+.:shortcuts diff --git a/tests/corpus/preview_grok_still_running.bin b/tests/corpus/preview_grok_still_running.bin deleted file mode 100644 index b9bd28b..0000000 --- a/tests/corpus/preview_grok_still_running.bin +++ /dev/null @@ -1,39 +0,0 @@ -[?1049h - ~/Projects/fixture-sources 3.0K / 500K - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ◎ 1 subagent still running - ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ - │ ❯ │ - ╰────────────────────────────────────────────────────────────────────────────── Grok 4.5 (xhigh) · always-approve ─╯ - - Shift+Tab:mode │ Ctrl+x:shortcuts diff --git a/tests/corpus/preview_grok_waiting_subagent.bin b/tests/corpus/preview_grok_waiting_subagent.bin new file mode 100644 index 0000000..9c1b2ea --- /dev/null +++ b/tests/corpus/preview_grok_waiting_subagent.bin @@ -0,0 +1,39 @@ +[?1049h]0;⠙ - Waiting for subagent… - grok + ~/Projects/fixture-sources/grok 19K / 256K │ [Dashboard] + + + ❯ Launch one subagent whose only job is to reply with the word ok, then tell me what it said. 4:27 PM + + + ◆ Thought for 0.0s + + I'll launch one subagent whose only task is to reply with "ok", then report exactly what it returns. 4:27 PM + + + + + + + + + + + + + + + + + + + + + + + ⠴ Waiting for subagent… 0.0s 2.9s ⇣19.1k [stop] + + ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ ❯ │ + ╰────────────────────────────────────────────────────────────────────────────────── Grok 4.7 (high) · auto-review ─╯ + + Shift+Tab:mode │ Ctrl+c:cancel │ Ctrl+.:shortcuts