mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
Compare commits
3 Commits
dh--stuff
...
fix-issue-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6084fcf0f3 | ||
|
|
e893e83eb9 | ||
|
|
f89a40a849 |
8
.github/workflows/rust-ci.yml
vendored
8
.github/workflows/rust-ci.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
working-directory: codex-rs
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
- uses: dtolnay/rust-toolchain@1.92
|
||||
with:
|
||||
components: rustfmt
|
||||
- name: cargo fmt
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
working-directory: codex-rs
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
- uses: dtolnay/rust-toolchain@1.92
|
||||
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
|
||||
with:
|
||||
tool: cargo-shear
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
- uses: dtolnay/rust-toolchain@1.92
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
components: clippy
|
||||
@@ -416,7 +416,7 @@ jobs:
|
||||
- name: Install DotSlash
|
||||
uses: facebook/install-dotslash@v2
|
||||
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
- uses: dtolnay/rust-toolchain@1.92
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
|
||||
2
.github/workflows/rust-release.yml
vendored
2
.github/workflows/rust-release.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
- uses: dtolnay/rust-toolchain@1.92
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
|
||||
2
.github/workflows/sdk.yml
vendored
2
.github/workflows/sdk.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
- uses: dtolnay/rust-toolchain@1.92
|
||||
|
||||
- name: build codex
|
||||
run: cargo build --bin codex
|
||||
|
||||
2
.github/workflows/shell-tool-mcp.yml
vendored
2
.github/workflows/shell-tool-mcp.yml
vendored
@@ -93,7 +93,7 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
- uses: dtolnay/rust-toolchain@1.92
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
|
||||
@@ -42,9 +42,12 @@ impl RateLimitStatusPayload {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, Default,
|
||||
)]
|
||||
pub enum PlanType {
|
||||
#[serde(rename = "guest")]
|
||||
#[default]
|
||||
Guest,
|
||||
#[serde(rename = "free")]
|
||||
Free,
|
||||
@@ -71,9 +74,3 @@ pub enum PlanType {
|
||||
#[serde(rename = "edu")]
|
||||
Edu,
|
||||
}
|
||||
|
||||
impl Default for PlanType {
|
||||
fn default() -> PlanType {
|
||||
Self::Guest
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,10 +423,11 @@ impl Default for Notifications {
|
||||
/// Terminals generally encode both mouse wheels and trackpads as the same "scroll up/down" mouse
|
||||
/// button events, without a magnitude. This setting controls whether Codex uses a heuristic to
|
||||
/// infer wheel vs trackpad per stream, or forces a specific behavior.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ScrollInputMode {
|
||||
/// Infer wheel vs trackpad behavior per scroll stream.
|
||||
#[default]
|
||||
Auto,
|
||||
/// Always treat scroll events as mouse-wheel input (fixed lines per tick).
|
||||
Wheel,
|
||||
@@ -434,12 +435,6 @@ pub enum ScrollInputMode {
|
||||
Trackpad,
|
||||
}
|
||||
|
||||
impl Default for ScrollInputMode {
|
||||
fn default() -> Self {
|
||||
Self::Auto
|
||||
}
|
||||
}
|
||||
|
||||
/// Collection of settings that are specific to the TUI.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
|
||||
@@ -340,6 +340,7 @@ mod detect_shell_type_tests {
|
||||
#[cfg(unix)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
@@ -350,7 +351,7 @@ mod tests {
|
||||
|
||||
let shell_path = zsh_shell.shell_path;
|
||||
|
||||
assert_eq!(shell_path, PathBuf::from("/bin/zsh"));
|
||||
assert_eq!(shell_path, Path::new("/bin/zsh"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -360,7 +361,7 @@ mod tests {
|
||||
|
||||
let shell_path = zsh_shell.shell_path;
|
||||
|
||||
assert_eq!(shell_path, PathBuf::from("/bin/zsh"));
|
||||
assert_eq!(shell_path, Path::new("/bin/zsh"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -369,9 +370,9 @@ mod tests {
|
||||
let shell_path = bash_shell.shell_path;
|
||||
|
||||
assert!(
|
||||
shell_path == PathBuf::from("/bin/bash")
|
||||
|| shell_path == PathBuf::from("/usr/bin/bash")
|
||||
|| shell_path == PathBuf::from("/usr/local/bin/bash"),
|
||||
shell_path == Path::new("/bin/bash")
|
||||
|| shell_path == Path::new("/usr/bin/bash")
|
||||
|| shell_path == Path::new("/usr/local/bin/bash"),
|
||||
"shell path: {shell_path:?}",
|
||||
);
|
||||
}
|
||||
@@ -381,7 +382,7 @@ mod tests {
|
||||
let sh_shell = get_shell(ShellType::Sh, None).unwrap();
|
||||
let shell_path = sh_shell.shell_path;
|
||||
assert!(
|
||||
shell_path == PathBuf::from("/bin/sh") || shell_path == PathBuf::from("/usr/bin/sh"),
|
||||
shell_path == Path::new("/bin/sh") || shell_path == Path::new("/usr/bin/sh"),
|
||||
"shell path: {shell_path:?}",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,9 +40,10 @@ struct ReadFileArgs {
|
||||
indentation: Option<IndentationArgs>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum ReadMode {
|
||||
#[default]
|
||||
Slice,
|
||||
Indentation,
|
||||
}
|
||||
@@ -461,12 +462,6 @@ mod defaults {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ReadMode {
|
||||
fn default() -> Self {
|
||||
Self::Slice
|
||||
}
|
||||
}
|
||||
|
||||
pub fn offset() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
@@ -77,8 +77,17 @@ async fn python_multiprocessing_lock_works_under_sandbox() {
|
||||
};
|
||||
|
||||
let python_code = r#"import multiprocessing
|
||||
import sys
|
||||
from multiprocessing import Lock, Process
|
||||
|
||||
# Python 3.14 defaults to forkserver on some Linux distros, which can
|
||||
# be blocked by the sandbox. Force fork to keep the test stable.
|
||||
if sys.platform.startswith("linux"):
|
||||
try:
|
||||
multiprocessing.set_start_method("fork")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
def f(lock):
|
||||
with lock:
|
||||
print("Lock acquired in child process")
|
||||
|
||||
@@ -1656,21 +1656,18 @@ pub struct ReviewLineRange {
|
||||
pub end: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Display, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[derive(
|
||||
Debug, Clone, Copy, Display, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS, Default,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecCommandSource {
|
||||
#[default]
|
||||
Agent,
|
||||
UserShell,
|
||||
UnifiedExecStartup,
|
||||
UnifiedExecInteraction,
|
||||
}
|
||||
|
||||
impl Default for ExecCommandSource {
|
||||
fn default() -> Self {
|
||||
Self::Agent
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
pub struct ExecCommandBeginEvent {
|
||||
/// Identifier so this can be paired with the ExecCommandEnd event.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.90.0"
|
||||
channel = "1.92.0"
|
||||
components = ["clippy", "rustfmt", "rust-src"]
|
||||
|
||||
@@ -179,7 +179,7 @@ if (-not (Ensure-Command 'cargo')) {
|
||||
Write-Host "==> Configuring Rust toolchain per rust-toolchain.toml" -ForegroundColor Cyan
|
||||
|
||||
# Pin to the workspace toolchain and install components
|
||||
$toolchain = '1.90.0'
|
||||
$toolchain = '1.92.0'
|
||||
& rustup toolchain install $toolchain --profile minimal | Out-Host
|
||||
& rustup default $toolchain | Out-Host
|
||||
& rustup component add clippy rustfmt rust-src --toolchain $toolchain | Out-Host
|
||||
|
||||
@@ -758,73 +758,61 @@ impl App {
|
||||
// Leaving alt-screen may blank the inline viewport; force a redraw either way.
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
AppEvent::OpenForkPicker => {
|
||||
match crate::resume_picker::run_fork_picker(
|
||||
tui,
|
||||
&self.config.codex_home,
|
||||
&self.config.model_provider_id,
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
SessionSelection::Fork(path) => {
|
||||
let summary = session_summary(
|
||||
self.chat_widget.token_usage(),
|
||||
self.chat_widget.thread_id(),
|
||||
);
|
||||
match self
|
||||
.server
|
||||
.fork_thread(usize::MAX, self.config.clone(), path.clone())
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
self.shutdown_current_thread().await;
|
||||
let init = crate::chatwidget::ChatWidgetInit {
|
||||
config: self.config.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: self.app_event_tx.clone(),
|
||||
initial_prompt: None,
|
||||
initial_images: Vec::new(),
|
||||
enhanced_keys_supported: self.enhanced_keys_supported,
|
||||
auth_manager: self.auth_manager.clone(),
|
||||
models_manager: self.server.get_models_manager(),
|
||||
feedback: self.feedback.clone(),
|
||||
is_first_run: false,
|
||||
model: Some(self.current_model.clone()),
|
||||
};
|
||||
self.chat_widget = ChatWidget::new_from_existing(
|
||||
init,
|
||||
forked.thread,
|
||||
forked.session_configured,
|
||||
);
|
||||
self.current_model = model_info.slug.clone();
|
||||
if let Some(summary) = summary {
|
||||
let mut lines: Vec<Line<'static>> =
|
||||
vec![summary.usage_line.clone().into()];
|
||||
if let Some(command) = summary.resume_command {
|
||||
let spans = vec![
|
||||
"To continue this session, run ".into(),
|
||||
command.cyan(),
|
||||
];
|
||||
lines.push(spans.into());
|
||||
}
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
AppEvent::ForkCurrentSession => {
|
||||
let summary =
|
||||
session_summary(self.chat_widget.token_usage(), self.chat_widget.thread_id());
|
||||
if let Some(path) = self.chat_widget.rollout_path() {
|
||||
match self
|
||||
.server
|
||||
.fork_thread(usize::MAX, self.config.clone(), path.clone())
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
self.shutdown_current_thread().await;
|
||||
let init = crate::chatwidget::ChatWidgetInit {
|
||||
config: self.config.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: self.app_event_tx.clone(),
|
||||
initial_prompt: None,
|
||||
initial_images: Vec::new(),
|
||||
enhanced_keys_supported: self.enhanced_keys_supported,
|
||||
auth_manager: self.auth_manager.clone(),
|
||||
models_manager: self.server.get_models_manager(),
|
||||
feedback: self.feedback.clone(),
|
||||
is_first_run: false,
|
||||
model: Some(self.current_model.clone()),
|
||||
};
|
||||
self.chat_widget = ChatWidget::new_from_existing(
|
||||
init,
|
||||
forked.thread,
|
||||
forked.session_configured,
|
||||
);
|
||||
self.current_model = model_info.slug.clone();
|
||||
if let Some(summary) = summary {
|
||||
let mut lines: Vec<Line<'static>> =
|
||||
vec![summary.usage_line.clone().into()];
|
||||
if let Some(command) = summary.resume_command {
|
||||
let spans = vec![
|
||||
"To continue this session, run ".into(),
|
||||
command.cyan(),
|
||||
];
|
||||
lines.push(spans.into());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = path.display();
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to fork session from {path_display}: {err}"
|
||||
));
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = path.display();
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to fork current session from {path_display}: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
SessionSelection::Exit
|
||||
| SessionSelection::StartFresh
|
||||
| SessionSelection::Resume(_) => {}
|
||||
} else {
|
||||
self.chat_widget
|
||||
.add_error_message("Current session is not ready to fork yet.".to_string());
|
||||
}
|
||||
|
||||
// Leaving alt-screen may blank the inline viewport; force a redraw either way.
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
AppEvent::InsertHistoryCell(cell) => {
|
||||
|
||||
@@ -53,8 +53,8 @@ pub(crate) enum AppEvent {
|
||||
/// Open the resume picker inside the running TUI session.
|
||||
OpenResumePicker,
|
||||
|
||||
/// Open the fork picker inside the running TUI session.
|
||||
OpenForkPicker,
|
||||
/// Fork the current session into a new thread.
|
||||
ForkCurrentSession,
|
||||
|
||||
/// Request to exit the application.
|
||||
///
|
||||
|
||||
@@ -1994,7 +1994,7 @@ impl ChatWidget {
|
||||
self.app_event_tx.send(AppEvent::OpenResumePicker);
|
||||
}
|
||||
SlashCommand::Fork => {
|
||||
self.app_event_tx.send(AppEvent::OpenForkPicker);
|
||||
self.app_event_tx.send(AppEvent::ForkCurrentSession);
|
||||
}
|
||||
SlashCommand::Init => {
|
||||
let init_target = self.config.cwd.join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
|
||||
@@ -1532,12 +1532,12 @@ async fn slash_resume_opens_picker() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_fork_opens_picker() {
|
||||
async fn slash_fork_requests_current_fork() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
chat.dispatch_command(SlashCommand::Fork);
|
||||
|
||||
assert_matches!(rx.try_recv(), Ok(AppEvent::OpenForkPicker));
|
||||
assert_matches!(rx.try_recv(), Ok(AppEvent::ForkCurrentSession));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -275,7 +275,6 @@ impl<'a> FlexRenderable<'a> {
|
||||
};
|
||||
let child_size = child.desired_height(area.width).min(max_child_extent);
|
||||
child_sizes[i] = child_size;
|
||||
allocated_size += child_size;
|
||||
allocated_flex_space += child_size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ impl SlashCommand {
|
||||
SlashCommand::Compact => "summarize conversation to prevent hitting the context limit",
|
||||
SlashCommand::Review => "review my current changes and find issues",
|
||||
SlashCommand::Resume => "resume a saved chat",
|
||||
SlashCommand::Fork => "fork a saved chat",
|
||||
SlashCommand::Fork => "fork the current chat",
|
||||
// SlashCommand::Undo => "ask Codex to undo a turn",
|
||||
SlashCommand::Quit | SlashCommand::Exit => "exit Codex",
|
||||
SlashCommand::Diff => "show git diff (including untracked files)",
|
||||
|
||||
@@ -6,7 +6,7 @@ Use /approvals to control when Codex asks for confirmation.
|
||||
Run /review to get a code review of your current changes.
|
||||
Use /skills to list available skills or ask Codex to use one.
|
||||
Use /status to see the current model, approvals, and token usage.
|
||||
Use /fork to branch a saved chat into a new thread.
|
||||
Use /fork to branch the current chat into a new thread.
|
||||
Use /init to create an AGENTS.md with project-specific guidance.
|
||||
Use /mcp to list configured MCP tools.
|
||||
You can run any shell command from Codex using `!` (e.g. `!ls`)
|
||||
|
||||
@@ -1531,72 +1531,62 @@ impl App {
|
||||
// Leaving alt-screen may blank the inline viewport; force a redraw either way.
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
AppEvent::OpenForkPicker => {
|
||||
match crate::resume_picker::run_fork_picker(
|
||||
tui,
|
||||
&self.config.codex_home,
|
||||
&self.config.model_provider_id,
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
SessionSelection::Fork(path) => {
|
||||
let summary = session_summary(
|
||||
self.chat_widget.token_usage(),
|
||||
self.chat_widget.conversation_id(),
|
||||
);
|
||||
match self
|
||||
.server
|
||||
.fork_thread(usize::MAX, self.config.clone(), path.clone())
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
self.shutdown_current_conversation().await;
|
||||
let init = crate::chatwidget::ChatWidgetInit {
|
||||
config: self.config.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: self.app_event_tx.clone(),
|
||||
initial_prompt: None,
|
||||
initial_images: Vec::new(),
|
||||
enhanced_keys_supported: self.enhanced_keys_supported,
|
||||
auth_manager: self.auth_manager.clone(),
|
||||
models_manager: self.server.get_models_manager(),
|
||||
feedback: self.feedback.clone(),
|
||||
is_first_run: false,
|
||||
model: Some(self.current_model.clone()),
|
||||
};
|
||||
self.chat_widget = ChatWidget::new_from_existing(
|
||||
init,
|
||||
forked.thread,
|
||||
forked.session_configured,
|
||||
);
|
||||
if let Some(summary) = summary {
|
||||
let mut lines: Vec<Line<'static>> =
|
||||
vec![summary.usage_line.clone().into()];
|
||||
if let Some(command) = summary.resume_command {
|
||||
let spans = vec![
|
||||
"To continue this session, run ".into(),
|
||||
command.cyan(),
|
||||
];
|
||||
lines.push(spans.into());
|
||||
}
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
AppEvent::ForkCurrentSession => {
|
||||
let summary = session_summary(
|
||||
self.chat_widget.token_usage(),
|
||||
self.chat_widget.conversation_id(),
|
||||
);
|
||||
if let Some(path) = self.chat_widget.rollout_path() {
|
||||
match self
|
||||
.server
|
||||
.fork_thread(usize::MAX, self.config.clone(), path.clone())
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
self.shutdown_current_conversation().await;
|
||||
let init = crate::chatwidget::ChatWidgetInit {
|
||||
config: self.config.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: self.app_event_tx.clone(),
|
||||
initial_prompt: None,
|
||||
initial_images: Vec::new(),
|
||||
enhanced_keys_supported: self.enhanced_keys_supported,
|
||||
auth_manager: self.auth_manager.clone(),
|
||||
models_manager: self.server.get_models_manager(),
|
||||
feedback: self.feedback.clone(),
|
||||
is_first_run: false,
|
||||
model: Some(self.current_model.clone()),
|
||||
};
|
||||
self.chat_widget = ChatWidget::new_from_existing(
|
||||
init,
|
||||
forked.thread,
|
||||
forked.session_configured,
|
||||
);
|
||||
if let Some(summary) = summary {
|
||||
let mut lines: Vec<Line<'static>> =
|
||||
vec![summary.usage_line.clone().into()];
|
||||
if let Some(command) = summary.resume_command {
|
||||
let spans = vec![
|
||||
"To continue this session, run ".into(),
|
||||
command.cyan(),
|
||||
];
|
||||
lines.push(spans.into());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = path.display();
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to fork session from {path_display}: {err}"
|
||||
));
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = path.display();
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to fork current session from {path_display}: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
SessionSelection::Exit
|
||||
| SessionSelection::StartFresh
|
||||
| SessionSelection::Resume(_) => {}
|
||||
} else {
|
||||
self.chat_widget
|
||||
.add_error_message("Current session is not ready to fork yet.".to_string());
|
||||
}
|
||||
|
||||
// Leaving alt-screen may blank the inline viewport; force a redraw either way.
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
AppEvent::InsertHistoryCell(cell) => {
|
||||
|
||||
@@ -47,8 +47,8 @@ pub(crate) enum AppEvent {
|
||||
/// Open the resume picker inside the running TUI session.
|
||||
OpenResumePicker,
|
||||
|
||||
/// Open the fork picker inside the running TUI session.
|
||||
OpenForkPicker,
|
||||
/// Fork the current session into a new thread.
|
||||
ForkCurrentSession,
|
||||
|
||||
/// Request to exit the application.
|
||||
///
|
||||
|
||||
@@ -1769,7 +1769,7 @@ impl ChatWidget {
|
||||
self.app_event_tx.send(AppEvent::OpenResumePicker);
|
||||
}
|
||||
SlashCommand::Fork => {
|
||||
self.app_event_tx.send(AppEvent::OpenForkPicker);
|
||||
self.app_event_tx.send(AppEvent::ForkCurrentSession);
|
||||
}
|
||||
SlashCommand::Init => {
|
||||
let init_target = self.config.cwd.join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
|
||||
@@ -1338,12 +1338,12 @@ async fn slash_resume_opens_picker() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_fork_opens_picker() {
|
||||
async fn slash_fork_requests_current_fork() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
chat.dispatch_command(SlashCommand::Fork);
|
||||
|
||||
assert_matches!(rx.try_recv(), Ok(AppEvent::OpenForkPicker));
|
||||
assert_matches!(rx.try_recv(), Ok(AppEvent::ForkCurrentSession));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -275,7 +275,6 @@ impl<'a> FlexRenderable<'a> {
|
||||
};
|
||||
let child_size = child.desired_height(area.width).min(max_child_extent);
|
||||
child_sizes[i] = child_size;
|
||||
allocated_size += child_size;
|
||||
allocated_flex_space += child_size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl SlashCommand {
|
||||
SlashCommand::Compact => "summarize conversation to prevent hitting the context limit",
|
||||
SlashCommand::Review => "review my current changes and find issues",
|
||||
SlashCommand::Resume => "resume a saved chat",
|
||||
SlashCommand::Fork => "fork a saved chat",
|
||||
SlashCommand::Fork => "fork the current chat",
|
||||
// SlashCommand::Undo => "ask Codex to undo a turn",
|
||||
SlashCommand::Quit | SlashCommand::Exit => "exit Codex",
|
||||
SlashCommand::Diff => "show git diff (including untracked files)",
|
||||
|
||||
@@ -6,7 +6,7 @@ Use /approvals to control when Codex asks for confirmation.
|
||||
Run /review to get a code review of your current changes.
|
||||
Use /skills to list available skills or ask Codex to use one.
|
||||
Use /status to see the current model, approvals, and token usage.
|
||||
Use /fork to branch a saved chat into a new thread.
|
||||
Use /fork to branch the current chat into a new thread.
|
||||
Use /init to create an AGENTS.md with project-specific guidance.
|
||||
Use /mcp to list configured MCP tools.
|
||||
You can run any shell command from Codex using `!` (e.g. `!ls`)
|
||||
|
||||
@@ -76,19 +76,14 @@ struct Payload {
|
||||
refresh_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
enum SetupMode {
|
||||
#[default]
|
||||
Full,
|
||||
ReadAclsOnly,
|
||||
}
|
||||
|
||||
impl Default for SetupMode {
|
||||
fn default() -> Self {
|
||||
Self::Full
|
||||
}
|
||||
}
|
||||
|
||||
fn log_line(log: &mut File, msg: &str) -> Result<()> {
|
||||
let ts = chrono::Utc::now().to_rfc3339();
|
||||
writeln!(log, "[{ts}] {msg}")?;
|
||||
|
||||
Reference in New Issue
Block a user