mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add non-interactive resume filter option (#15339)
## Summary - add `codex resume --include-non-interactive` to include non-interactive sessions in the picker and `--last` - keep current-provider and cwd filtering behavior unchanged - replace the picker API boolean with a `SessionSourceFilter` enum to avoid a boolean trap ## Tests - `cargo test -p codex-cli` - `cargo test -p codex-tui` - `just fmt` - `just fix -p codex-cli` - `just fix -p codex-tui`
This commit is contained in:
committed by
GitHub
Unverified
parent
fba3c79885
commit
6b10e186c4
@@ -208,6 +208,10 @@ struct ResumeCommand {
|
||||
#[arg(long = "all", default_value_t = false)]
|
||||
all: bool,
|
||||
|
||||
/// Include non-interactive sessions in the resume picker and --last selection.
|
||||
#[arg(long = "include-non-interactive", default_value_t = false)]
|
||||
include_non_interactive: bool,
|
||||
|
||||
#[clap(flatten)]
|
||||
remote: InteractiveRemoteOptions,
|
||||
|
||||
@@ -691,6 +695,7 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
session_id,
|
||||
last,
|
||||
all,
|
||||
include_non_interactive,
|
||||
remote,
|
||||
config_overrides,
|
||||
})) => {
|
||||
@@ -700,6 +705,7 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
session_id,
|
||||
last,
|
||||
all,
|
||||
include_non_interactive,
|
||||
config_overrides,
|
||||
);
|
||||
let exit_info = run_interactive_tui(
|
||||
@@ -1169,6 +1175,7 @@ fn finalize_resume_interactive(
|
||||
session_id: Option<String>,
|
||||
last: bool,
|
||||
show_all: bool,
|
||||
include_non_interactive: bool,
|
||||
resume_cli: TuiCli,
|
||||
) -> TuiCli {
|
||||
// Start with the parsed interactive CLI so resume shares the same
|
||||
@@ -1178,6 +1185,7 @@ fn finalize_resume_interactive(
|
||||
interactive.resume_last = last;
|
||||
interactive.resume_session_id = resume_session_id;
|
||||
interactive.resume_show_all = show_all;
|
||||
interactive.resume_include_non_interactive = include_non_interactive;
|
||||
|
||||
// Merge resume-scoped flags and overrides with highest precedence.
|
||||
merge_interactive_cli_flags(&mut interactive, resume_cli);
|
||||
@@ -1290,6 +1298,7 @@ mod tests {
|
||||
session_id,
|
||||
last,
|
||||
all,
|
||||
include_non_interactive,
|
||||
remote: _,
|
||||
config_overrides: resume_cli,
|
||||
}) = subcommand.expect("resume present")
|
||||
@@ -1303,6 +1312,7 @@ mod tests {
|
||||
session_id,
|
||||
last,
|
||||
all,
|
||||
include_non_interactive,
|
||||
resume_cli,
|
||||
)
|
||||
}
|
||||
@@ -1498,6 +1508,15 @@ mod tests {
|
||||
assert!(interactive.resume_show_all);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resume_include_non_interactive_flag_sets_source_filter_override() {
|
||||
let interactive =
|
||||
finalize_resume_from_args(["codex", "resume", "--include-non-interactive"].as_ref());
|
||||
|
||||
assert!(interactive.resume_picker);
|
||||
assert!(interactive.resume_include_non_interactive);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resume_merges_option_flags_and_full_auto() {
|
||||
let interactive = finalize_resume_from_args(
|
||||
|
||||
@@ -2819,6 +2819,7 @@ impl App {
|
||||
tui,
|
||||
&self.config,
|
||||
/*show_all*/ false,
|
||||
crate::resume_picker::SessionSourceFilter::InteractiveOnly,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
|
||||
@@ -32,6 +32,10 @@ pub struct Cli {
|
||||
#[clap(skip)]
|
||||
pub resume_show_all: bool,
|
||||
|
||||
/// Internal: include non-interactive sessions in resume listings.
|
||||
#[clap(skip)]
|
||||
pub resume_include_non_interactive: bool,
|
||||
|
||||
// Internal controls set by the top-level `codex fork` subcommand.
|
||||
// These are not exposed as user flags on the base `codex` command.
|
||||
#[clap(skip)]
|
||||
|
||||
+19
-2
@@ -845,12 +845,17 @@ async fn run_ratatui_app(
|
||||
} else {
|
||||
Some(config.cwd.as_path())
|
||||
};
|
||||
let allowed_sources = if cli.resume_include_non_interactive {
|
||||
&[][..]
|
||||
} else {
|
||||
INTERACTIVE_SESSION_SOURCES.as_slice()
|
||||
};
|
||||
match RolloutRecorder::find_latest_thread_path(
|
||||
&config,
|
||||
/*page_size*/ 1,
|
||||
/*cursor*/ None,
|
||||
ThreadSortKey::UpdatedAt,
|
||||
INTERACTIVE_SESSION_SOURCES.as_slice(),
|
||||
allowed_sources,
|
||||
Some(provider_filter.as_slice()),
|
||||
&config.model_provider_id,
|
||||
filter_cwd,
|
||||
@@ -888,7 +893,19 @@ async fn run_ratatui_app(
|
||||
_ => resume_picker::SessionSelection::StartFresh,
|
||||
}
|
||||
} else if cli.resume_picker {
|
||||
match resume_picker::run_resume_picker(&mut tui, &config, cli.resume_show_all).await? {
|
||||
let source_filter = if cli.resume_include_non_interactive {
|
||||
resume_picker::SessionSourceFilter::IncludeNonInteractive
|
||||
} else {
|
||||
resume_picker::SessionSourceFilter::InteractiveOnly
|
||||
};
|
||||
match resume_picker::run_resume_picker(
|
||||
&mut tui,
|
||||
&config,
|
||||
cli.resume_show_all,
|
||||
source_filter,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
resume_picker::SessionSelection::Exit => {
|
||||
terminal_restore_guard.restore_silently();
|
||||
session_log::log_session_end();
|
||||
|
||||
@@ -60,6 +60,21 @@ pub enum SessionPickerAction {
|
||||
Fork,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum SessionSourceFilter {
|
||||
InteractiveOnly,
|
||||
IncludeNonInteractive,
|
||||
}
|
||||
|
||||
impl SessionSourceFilter {
|
||||
fn allowed_sources(self) -> &'static [codex_protocol::protocol::SessionSource] {
|
||||
match self {
|
||||
SessionSourceFilter::InteractiveOnly => INTERACTIVE_SESSION_SOURCES.as_slice(),
|
||||
SessionSourceFilter::IncludeNonInteractive => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SessionPickerAction {
|
||||
fn title(self) -> &'static str {
|
||||
match self {
|
||||
@@ -116,15 +131,24 @@ enum BackgroundEvent {
|
||||
/// new sessions appear during pagination.
|
||||
///
|
||||
/// Filtering happens in two layers:
|
||||
/// 1. Provider and source filtering at the backend (only interactive CLI sessions
|
||||
/// for the current model provider).
|
||||
/// 1. Provider and source filtering at the backend (interactive sessions for the
|
||||
/// current model provider by default, optionally including non-interactive
|
||||
/// sessions).
|
||||
/// 2. Working-directory filtering at the picker (unless `--all` is passed).
|
||||
pub async fn run_resume_picker(
|
||||
tui: &mut Tui,
|
||||
config: &Config,
|
||||
show_all: bool,
|
||||
source_filter: SessionSourceFilter,
|
||||
) -> Result<SessionSelection> {
|
||||
run_session_picker(tui, config, show_all, SessionPickerAction::Resume).await
|
||||
run_session_picker(
|
||||
tui,
|
||||
config,
|
||||
show_all,
|
||||
source_filter,
|
||||
SessionPickerAction::Resume,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn run_fork_picker(
|
||||
@@ -132,13 +156,21 @@ pub async fn run_fork_picker(
|
||||
config: &Config,
|
||||
show_all: bool,
|
||||
) -> Result<SessionSelection> {
|
||||
run_session_picker(tui, config, show_all, SessionPickerAction::Fork).await
|
||||
run_session_picker(
|
||||
tui,
|
||||
config,
|
||||
show_all,
|
||||
SessionSourceFilter::InteractiveOnly,
|
||||
SessionPickerAction::Fork,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn run_session_picker(
|
||||
tui: &mut Tui,
|
||||
config: &Config,
|
||||
show_all: bool,
|
||||
source_filter: SessionSourceFilter,
|
||||
action: SessionPickerAction,
|
||||
) -> Result<SessionSelection> {
|
||||
let alt = AltScreenGuard::enter(tui);
|
||||
@@ -154,6 +186,7 @@ async fn run_session_picker(
|
||||
|
||||
let config = config.clone();
|
||||
let loader_tx = bg_tx.clone();
|
||||
let allowed_sources = source_filter.allowed_sources();
|
||||
let page_loader: PageLoader = Arc::new(move |request: PageLoadRequest| {
|
||||
let tx = loader_tx.clone();
|
||||
let config = config.clone();
|
||||
@@ -164,7 +197,7 @@ async fn run_session_picker(
|
||||
PAGE_SIZE,
|
||||
request.cursor.as_ref(),
|
||||
request.sort_key,
|
||||
INTERACTIVE_SESSION_SOURCES.as_slice(),
|
||||
allowed_sources,
|
||||
Some(provider_filter.as_slice()),
|
||||
request.default_provider.as_str(),
|
||||
/*search_term*/ None,
|
||||
|
||||
Reference in New Issue
Block a user