mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Restore the daemon auto-start feature flag (#59447)
## What changed Honor `features.daemon_auto_start` as a stable, default-enabled setting instead of ignoring it with a deprecation warning. Apply it to interactive daemon auto-start and remote-control daemon eligibility, and accept it in managed feature requirements. Preserve explicit `false` values when saving the setting, even when the server reports a disabled default. Record the setting and disabled auto-start outcomes in launch telemetry. ## Testing Update coverage for restarting a discovered daemon with auto-start disabled and for persisting an explicit opt-out when the server default is disabled. Adjust embedded-backend tests to disable auto-start through configuration. GitOrigin-RevId: cbf079e3e27b4a7e6e802bf8f19dae653dd7472c
This commit is contained in:
@@ -16,6 +16,7 @@ use codex_app_server_protocol::RemoteControlPairingStartResponse;
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
@@ -160,7 +161,8 @@ async fn daemon_eligible(root_config_overrides: &CliConfigOverrides) -> anyhow::
|
||||
let Ok(config) = ConfigBuilder::default().build().await else {
|
||||
return Ok(false);
|
||||
};
|
||||
Ok(!codex_tui::uses_wsl_drvfs(&config.codex_home))
|
||||
Ok(config.features.enabled(Feature::DaemonAutoStart)
|
||||
&& !codex_tui::uses_wsl_drvfs(&config.codex_home))
|
||||
}
|
||||
|
||||
fn print_remote_control_progress(json: bool, message: &str) -> anyhow::Result<()> {
|
||||
|
||||
@@ -40,7 +40,7 @@ async fn agents_checks_trust_when_starting_after_dismissing_overview() -> Result
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(unix)]
|
||||
async fn discovered_managed_daemon_can_restart() -> Result<()> {
|
||||
async fn discovered_managed_daemon_can_restart_with_auto_start_disabled() -> Result<()> {
|
||||
daemon_startup("discovered").await
|
||||
}
|
||||
|
||||
@@ -194,10 +194,8 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
{}\n\
|
||||
notice.model_migrations.\"gpt-5.6-terra\" = \"gpt-6-sol\"\n\
|
||||
[projects.{}]\ntrust_level = \"{}\"\n",
|
||||
if command == "start" {
|
||||
"features.daemon_auto_start=false"
|
||||
} else if command == "discovered" {
|
||||
"tui.status_line=[\"model\",\"thread-id\",\"thread-name\"]"
|
||||
if command == "discovered" {
|
||||
"tui.status_line=[\"model\",\"thread-id\",\"thread-name\"]\nfeatures.daemon_auto_start=false"
|
||||
} else if bedrock_onboarding {
|
||||
"features.gcp_setup_wizard=false"
|
||||
} else {
|
||||
@@ -332,6 +330,7 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
}
|
||||
"entercontinue·escback"
|
||||
} else if command == "discovered" {
|
||||
args.extend(["--disable".into(), "daemon_auto_start".into()]);
|
||||
// Empty drafts need not survive a daemon restart. Renaming materializes the
|
||||
// thread; wait for the confirmed footer name, not the echoed command.
|
||||
steps.push_back(("GPT-5.6-Terra", b"/rename daemon-restart-test\r"));
|
||||
|
||||
@@ -192,6 +192,7 @@ cli_auth_credentials_store = "file"
|
||||
chatgpt_base_url = "{}/source/backend-api"
|
||||
analytics.enabled = false
|
||||
check_for_update_on_startup = false
|
||||
features.daemon_auto_start = {}
|
||||
features.mcp_oauth_refresh_coordination = true
|
||||
model_provider = "local"
|
||||
model = "test-model"
|
||||
@@ -210,6 +211,7 @@ trust_level = "trusted"
|
||||
trust_level = "trusted"
|
||||
"#,
|
||||
server.uri(),
|
||||
backend == "daemon",
|
||||
server.uri(),
|
||||
serde_json::to_string(&source)?,
|
||||
serde_json::to_string(&launcher)?
|
||||
@@ -388,9 +390,6 @@ trust_level = "trusted"
|
||||
.mount(&server)
|
||||
.await;
|
||||
let mut args = worktree_args.clone();
|
||||
if backend == "embedded" && !analytics {
|
||||
args.push("--no-daemon".into());
|
||||
}
|
||||
if backend == "daemon" && previous.is_empty() {
|
||||
args.extend(["-c".into(), "features.auth_elicitation=true".into()]);
|
||||
}
|
||||
@@ -699,6 +698,14 @@ trust_level = "trusted"
|
||||
("terminal_rows", "40-79"),
|
||||
("multiplexer", "none"),
|
||||
("daemon_selection_reason", "incompatible_option"),
|
||||
(
|
||||
"daemon_auto_start",
|
||||
if backend == "daemon" {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
},
|
||||
),
|
||||
("auto_update", "enabled"),
|
||||
("auto_update_setting", "default"),
|
||||
("update_interval_setting", "default"),
|
||||
|
||||
@@ -12446,7 +12446,6 @@ shell_tool = false
|
||||
|
||||
#[test_case::test_case(Feature::Personality; "personality")]
|
||||
#[test_case::test_case(Feature::GuardianThreadContext; "guardian thread context")]
|
||||
#[test_case::test_case(Feature::DaemonAutoStart; "daemon auto start")]
|
||||
#[test_case::test_case(Feature::Worktrees; "worktrees")]
|
||||
#[test_case::test_case(Feature::RealtimeConversation; "realtime conversation")]
|
||||
#[test_case::test_case(Feature::ShellZshFork; "shell zsh fork")]
|
||||
|
||||
@@ -244,10 +244,7 @@ fn parse_feature_requirements(
|
||||
if key == Feature::Personality.key() {
|
||||
continue;
|
||||
}
|
||||
if matches!(
|
||||
key.as_str(),
|
||||
"daemon_auto_start" | "worktrees" | "realtime_conversation"
|
||||
) {
|
||||
if key == Feature::Worktrees.key() || key == Feature::RealtimeConversation.key() {
|
||||
push_feature_requirement_warning(
|
||||
&mut startup_warnings,
|
||||
format!(
|
||||
|
||||
@@ -122,7 +122,7 @@ pub enum Feature {
|
||||
/// Store CLI auth in the encrypted local secrets backend when keyring storage is selected.
|
||||
SecretAuthStorage,
|
||||
|
||||
/// Removed compatibility flag. Eligible interactive launches always start or reuse the daemon.
|
||||
/// Automatically start the shared local daemon for eligible interactive launches.
|
||||
DaemonAutoStart,
|
||||
/// Allow the TUI to discover and select among multiple conversation backends.
|
||||
TuiMultiBackend,
|
||||
@@ -738,13 +738,6 @@ impl Features {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
"daemon_auto_start" => {
|
||||
self.record_legacy_usage_force(
|
||||
"features.daemon_auto_start",
|
||||
Feature::DaemonAutoStart,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
"transcript_v2" => {
|
||||
self.record_legacy_usage_force("features.transcript_v2", Feature::TranscriptV2);
|
||||
continue;
|
||||
@@ -868,10 +861,6 @@ fn legacy_usage_notice(alias: &str, feature: Feature) -> (String, Option<String>
|
||||
"`[features.guardianv2].thread_context` is deprecated and ignored.".to_string(),
|
||||
Some("Thread-owned Guardian context is always enabled. Remove `thread_context` from [features.guardianv2] in config.toml, including profile overrides.".to_string()),
|
||||
),
|
||||
Feature::DaemonAutoStart => (
|
||||
"`[features].daemon_auto_start` is deprecated and ignored.".to_string(),
|
||||
Some("Remove `daemon_auto_start` from [features] in config.toml or profile configuration files. Use `--no-daemon` to run without the shared background server.".to_string()),
|
||||
),
|
||||
Feature::TranscriptV2 => (
|
||||
"`[features].transcript_v2` is deprecated and ignored.".to_string(),
|
||||
Some("Use `[tui].fullscreen_transcript` in config.toml instead.".to_string()),
|
||||
@@ -1136,7 +1125,7 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
FeatureSpec {
|
||||
id: Feature::DaemonAutoStart,
|
||||
key: "daemon_auto_start",
|
||||
stage: Stage::Removed,
|
||||
stage: Stage::Stable,
|
||||
default_enabled: true,
|
||||
},
|
||||
FeatureSpec {
|
||||
|
||||
@@ -282,10 +282,7 @@ impl BottomPaneView for ExperimentalFeaturesView {
|
||||
let mut count = 0;
|
||||
for feature in features {
|
||||
if feature.stage != ExperimentalFeatureStage::Beta
|
||||
|| matches!(
|
||||
feature.name.as_str(),
|
||||
"daemon_auto_start" | "worktrees" | "realtime_conversation"
|
||||
)
|
||||
|| matches!(feature.name.as_str(), "worktrees" | "realtime_conversation")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ fn experimental_features_discovery_preserves_server_order_and_accepts_new_keys()
|
||||
.send(Ok(vec![
|
||||
server_feature("network_proxy"),
|
||||
server_feature("future_feature"),
|
||||
server_feature("daemon_auto_start"),
|
||||
server_feature("worktrees"),
|
||||
server_feature("realtime_conversation"),
|
||||
stable,
|
||||
|
||||
@@ -109,7 +109,8 @@ pub(crate) async fn write(
|
||||
let key = format!("features.{}", serde_json::json!(name));
|
||||
Ok(crate::config_update::replace_config_value(
|
||||
key,
|
||||
if *enabled || feature.default_enabled {
|
||||
// Keep the daemon opt-out explicit even before rollout defaults enable it.
|
||||
if *enabled || feature.default_enabled || name == "daemon_auto_start" {
|
||||
serde_json::json!(enabled)
|
||||
} else {
|
||||
serde_json::Value::Null
|
||||
|
||||
@@ -181,6 +181,7 @@ async fn experimental_feature_writes_use_server_defaults_and_refresh_configured_
|
||||
"initialize" => json!({"userAgent": "experimental-test"}),
|
||||
"experimentalFeature/list" => {
|
||||
json!({"data": [
|
||||
{"name": "daemon_auto_start", "stage": "beta", "displayName": null, "description": null, "announcement": null, "enabled": !wrote || scenario == "overridden", "defaultEnabled": false},
|
||||
{"name": "default_off", "stage": "beta", "displayName": null, "description": null, "announcement": null, "enabled": !wrote || scenario == "overridden", "defaultEnabled": false},
|
||||
{"name": "default_on", "stage": "beta", "displayName": null, "description": null, "announcement": null, "enabled": !wrote, "defaultEnabled": true},
|
||||
{"name": "new_server_feature", "stage": "beta", "displayName": null, "description": null, "announcement": null, "enabled": wrote, "defaultEnabled": false}
|
||||
@@ -191,6 +192,7 @@ async fn experimental_feature_writes_use_server_defaults_and_refresh_configured_
|
||||
request.params,
|
||||
Some(json!({
|
||||
"edits": [
|
||||
{"keyPath": "features.\"daemon_auto_start\"", "value": false, "mergeStrategy": "replace"},
|
||||
{"keyPath": "features.\"default_off\"", "value": null, "mergeStrategy": "replace"},
|
||||
{"keyPath": "features.\"default_on\"", "value": false, "mergeStrategy": "replace"},
|
||||
{"keyPath": "features.\"new_server_feature\"", "value": true, "mergeStrategy": "replace"}
|
||||
@@ -227,6 +229,7 @@ async fn experimental_feature_writes_use_server_defaults_and_refresh_configured_
|
||||
handle,
|
||||
thread_id,
|
||||
vec![
|
||||
("daemon_auto_start".to_string(), false),
|
||||
("default_off".to_string(), false),
|
||||
("default_on".to_string(), false),
|
||||
("new_server_feature".to_string(), true),
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
---
|
||||
source: tui/src/history_cell/tests.rs
|
||||
expression: "render_lines(&cell.transcript_lines(80)).join(\"\\n\")"
|
||||
---
|
||||
⚠ `[features].daemon_auto_start` is deprecated and ignored.
|
||||
Remove `daemon_auto_start` from [features] in config.toml or profile
|
||||
configuration files. Use `--no-daemon` to run without the shared background
|
||||
server.
|
||||
@@ -2973,26 +2973,15 @@ fn deprecation_notice_renders_summary_with_details() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_feature_migration_notices() {
|
||||
for (key, snapshot) in [
|
||||
("shell_zsh_fork", "legacy_zsh_migration_notice_snapshot"),
|
||||
("daemon_auto_start", "daemon_auto_start_migration_notice"),
|
||||
] {
|
||||
let mut features = codex_features::Features::with_defaults();
|
||||
features.apply_map(&std::collections::BTreeMap::from([(
|
||||
key.to_string(),
|
||||
false,
|
||||
)]));
|
||||
let usage = features
|
||||
.legacy_feature_usages()
|
||||
.next()
|
||||
.expect("migration notice");
|
||||
let cell = new_deprecation_notice(usage.summary.clone(), usage.details.clone());
|
||||
insta::assert_snapshot!(
|
||||
snapshot,
|
||||
render_lines(&cell.transcript_lines(/*width*/ 80)).join("\n")
|
||||
);
|
||||
}
|
||||
fn legacy_zsh_migration_notice_snapshot() {
|
||||
let mut features = codex_features::Features::default();
|
||||
features.record_legacy_usage_force("shell_zsh_fork", codex_features::Feature::ShellZshFork);
|
||||
let usage = features
|
||||
.legacy_feature_usages()
|
||||
.next()
|
||||
.expect("migration notice");
|
||||
let cell = new_deprecation_notice(usage.summary.clone(), usage.details.clone());
|
||||
insta::assert_snapshot!(render_lines(&cell.transcript_lines(/*width*/ 80)).join("\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -568,6 +568,7 @@ pub(super) async fn run_main_inner(
|
||||
None
|
||||
};
|
||||
let auto_start_daemon = cfg!(any(unix, windows))
|
||||
&& (cli.agents_overview || config.features.enabled(Feature::DaemonAutoStart))
|
||||
&& implicit_connection
|
||||
&& !cli.no_daemon
|
||||
&& !app_server_target.uses_remote_workspace();
|
||||
@@ -732,7 +733,7 @@ pub(super) async fn run_main_inner(
|
||||
(_, Some(_)) => "incompatible_option",
|
||||
_ if auto_start_daemon => "auto_start",
|
||||
(AppServerTarget::LocalDaemon { .. }, _) => "existing_daemon",
|
||||
(AppServerTarget::Embedded, None) => "unclassified",
|
||||
(AppServerTarget::Embedded, None) => "auto_start_disabled",
|
||||
};
|
||||
let daemon_settings = if metrics.is_some() {
|
||||
codex_app_server_daemon::telemetry::settings_tags(&config.codex_home)
|
||||
@@ -743,7 +744,17 @@ pub(super) async fn run_main_inner(
|
||||
};
|
||||
let mut launch_tags = daemon_settings.to_vec();
|
||||
launch_tags.extend(terminal_size_tags);
|
||||
launch_tags.push(("daemon_selection_reason", selection_reason));
|
||||
launch_tags.extend([
|
||||
("daemon_selection_reason", selection_reason),
|
||||
(
|
||||
"daemon_auto_start",
|
||||
if config.features.enabled(Feature::DaemonAutoStart) {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
},
|
||||
),
|
||||
]);
|
||||
let selected_local_daemon = matches!(app_server_target, AppServerTarget::LocalDaemon { .. });
|
||||
let launch_telemetry = move |app_server_mode| {
|
||||
let Some(metrics) = metrics else {
|
||||
@@ -803,6 +814,9 @@ pub(super) async fn run_main_inner(
|
||||
Some(daemon_startup::WSL_DRVFS_EXCLUSION) => "wsl_drvfs",
|
||||
Some("this Windows launcher") => "windows_detach_restricted",
|
||||
Some("daemon feature settings") => "daemon_compatibility",
|
||||
None if selection_reason == "auto_start_disabled" => {
|
||||
"auto_start_disabled_no_daemon"
|
||||
}
|
||||
None if selected_local_daemon => "daemon_connection_failed",
|
||||
None if selection_reason == "agents" => "agents_no_daemon_discovered",
|
||||
Some(_) | None => "unclassified",
|
||||
|
||||
@@ -21,43 +21,37 @@ async fn incompatible_daemon_can_run_embedded_for_explicit_features() -> Result<
|
||||
let socket = codex_app_server_client::app_server_control_socket_path(home.path())?;
|
||||
std::fs::create_dir_all(socket.parent().unwrap())?;
|
||||
let listener = UnixListener::bind(socket.as_path())?;
|
||||
let server_home = home.path().to_path_buf();
|
||||
let server = tokio::spawn(async move {
|
||||
// Startup probes the running daemon, then checks its feature settings.
|
||||
for _ in 0..2 {
|
||||
let mut socket = loop {
|
||||
let (stream, _) = listener.accept().await?;
|
||||
if let Ok(socket) = tokio_tungstenite::accept_async(stream).await {
|
||||
break socket;
|
||||
}
|
||||
let mut socket = loop {
|
||||
let (stream, _) = listener.accept().await?;
|
||||
if let Ok(socket) = tokio_tungstenite::accept_async(stream).await {
|
||||
break socket;
|
||||
}
|
||||
};
|
||||
while let Some(Ok(Message::Text(text))) = socket.next().await {
|
||||
let JSONRPCMessage::Request(request) = serde_json::from_str(&text)? else {
|
||||
continue;
|
||||
};
|
||||
while let Some(Ok(Message::Text(text))) = socket.next().await {
|
||||
let JSONRPCMessage::Request(request) = serde_json::from_str(&text)? else {
|
||||
continue;
|
||||
};
|
||||
let response = match request.method.as_str() {
|
||||
"initialize" => {
|
||||
json!({"id": request.id, "result": {"userAgent": "daemon-test/0.0.0", "codexHome": server_home, "platformFamily": std::env::consts::FAMILY, "platformOs": std::env::consts::OS}})
|
||||
}
|
||||
"config/read" => {
|
||||
json!({"id": request.id, "result": {"config": {}, "layers": []}})
|
||||
}
|
||||
"configRequirements/read" => {
|
||||
json!({"id": request.id, "result": {"requirements": null}})
|
||||
}
|
||||
"experimentalFeature/list" => {
|
||||
json!({"id": request.id, "result": {"data": [{
|
||||
let response = match request.method.as_str() {
|
||||
"initialize" => {
|
||||
json!({"id": request.id, "result": {"userAgent": "daemon-test/0.0.0"}})
|
||||
}
|
||||
"config/read" => {
|
||||
json!({"id": request.id, "result": {"config": {}, "layers": []}})
|
||||
}
|
||||
"configRequirements/read" => {
|
||||
json!({"id": request.id, "result": {"requirements": null}})
|
||||
}
|
||||
"experimentalFeature/list" => json!({"id": request.id, "result": {"data": [{
|
||||
"name": "api_key_model_discovery", "stage": "stable",
|
||||
"displayName": null, "description": null, "announcement": null,
|
||||
"enabled": scenario == "explicit disable", "defaultEnabled": true,
|
||||
}], "nextCursor": null}})
|
||||
}
|
||||
_ => panic!("unexpected request: {}", request.method),
|
||||
};
|
||||
socket
|
||||
.send(Message::Text(response.to_string().into()))
|
||||
.await?;
|
||||
}
|
||||
}], "nextCursor": null}}),
|
||||
_ => panic!("unexpected request: {}", request.method),
|
||||
};
|
||||
socket
|
||||
.send(Message::Text(response.to_string().into()))
|
||||
.await?;
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
});
|
||||
|
||||
@@ -39,7 +39,6 @@ async fn connected_trust_cancellation_and_acceptance_control_task_creation() ->
|
||||
let cwd = repo_root.clone();
|
||||
let methods = Arc::new(Mutex::new(Vec::new()));
|
||||
let requests = Arc::clone(&methods);
|
||||
let server_home = codex_home.path().to_path_buf();
|
||||
let server = tokio::spawn(async move {
|
||||
let mut clients = tokio::task::JoinSet::new();
|
||||
let trust_reads = Arc::new(AtomicUsize::new(0));
|
||||
@@ -49,7 +48,6 @@ async fn connected_trust_cancellation_and_acceptance_control_task_creation() ->
|
||||
.await
|
||||
.expect("accept fake app-server client");
|
||||
let cwd = cwd.clone();
|
||||
let server_home = server_home.clone();
|
||||
let methods = Arc::clone(&requests);
|
||||
let trust_reads = Arc::clone(&trust_reads);
|
||||
clients.spawn(async move {
|
||||
@@ -76,7 +74,7 @@ async fn connected_trust_cancellation_and_acceptance_control_task_creation() ->
|
||||
};
|
||||
methods.lock().unwrap().push(request.method.clone());
|
||||
let result = match request.method.as_str() {
|
||||
"initialize" => json!({"userAgent": "trust-pty/0.0.0", "codexHome": server_home, "platformFamily": std::env::consts::FAMILY, "platformOs": std::env::consts::OS}),
|
||||
"initialize" => json!({"userAgent": "trust-pty"}),
|
||||
"experimentalFeature/list" => json!({"data": (["api_key_model_discovery", "code_mode_host", "auth_elicitation"].map(|name| json!({
|
||||
"name": name, "stage": "stable", "displayName": null,
|
||||
"description": null, "announcement": null,
|
||||
|
||||
@@ -35,8 +35,7 @@ fn focus_gained_with_unanswered_palette_queries_preserves_immediate_input() -> R
|
||||
let codex_home = tempfile::tempdir()?;
|
||||
write_test_config(codex_home.path(), &repo_root)?;
|
||||
|
||||
let mut terminal =
|
||||
PtyCodex::start(&repo_root, codex_home, &["--no-daemon", "--no-alt-screen"])?;
|
||||
let mut terminal = PtyCodex::start(&repo_root, codex_home, &["--no-alt-screen"])?;
|
||||
terminal.wait_for_startup()?;
|
||||
|
||||
let startup_output_len = terminal.output.len();
|
||||
@@ -166,7 +165,7 @@ fn default_owned_screen_entry_paints_before_sync_ends_and_exit_clears_inline_dra
|
||||
let repo_root = codex_utils_cargo_bin::repo_root()?;
|
||||
let codex_home = tempfile::tempdir()?;
|
||||
write_test_config(codex_home.path(), &repo_root)?;
|
||||
let mut terminal = PtyCodex::start(&repo_root, codex_home, &["--no-daemon"])?;
|
||||
let mut terminal = PtyCodex::start(&repo_root, codex_home, &[])?;
|
||||
terminal.wait_for_startup()?;
|
||||
let deadline = Instant::now() + STARTUP_TIMEOUT;
|
||||
while !terminal.parser.screen().alternate_screen() && Instant::now() < deadline {
|
||||
@@ -281,7 +280,7 @@ fn fullscreen_transcript_can_opt_out_to_terminal_scrollback() -> Result<()> {
|
||||
let mut terminal = PtyCodex::start(
|
||||
&repo_root,
|
||||
codex_home,
|
||||
&["--no-daemon", "-c", "tui.fullscreen_transcript=false"],
|
||||
&["-c", "tui.fullscreen_transcript=false"],
|
||||
)?;
|
||||
terminal.wait_for_startup()?;
|
||||
terminal.wait_for_screen("GPT-5.6-Terra")?;
|
||||
@@ -571,6 +570,7 @@ pub(super) fn write_test_config(codex_home: &Path, repo_root: &Path) -> Result<(
|
||||
let config = format!(
|
||||
"model = \"gpt-5.6-terra\"\nmodel_provider = \"openai\"\n\
|
||||
suppress_unstable_features_warning = true\nanalytics.enabled = false\n\
|
||||
features.daemon_auto_start = false\n\
|
||||
notice.model_migrations.\"gpt-5.6-terra\" = \"gpt-6-sol\"\n\n\
|
||||
[projects.\"{repo_root}\"]\ntrust_level = \"trusted\"\n"
|
||||
);
|
||||
@@ -589,6 +589,12 @@ fn no_daemon_skips_startup_and_discovery() -> Result<()> {
|
||||
let workspace = tempfile::tempdir()?;
|
||||
let home = tempfile::tempdir()?;
|
||||
write_test_config(home.path(), workspace.path())?;
|
||||
let config = home.path().join("config.toml");
|
||||
let contents = std::fs::read_to_string(&config)?;
|
||||
std::fs::write(
|
||||
config,
|
||||
contents.replace("features.daemon_auto_start = false\n", ""),
|
||||
)?;
|
||||
let socket_path = codex_app_server_client::app_server_control_socket_path(home.path())?;
|
||||
std::fs::create_dir_all(socket_path.as_path().parent().unwrap())?;
|
||||
let listener = if running {
|
||||
@@ -631,6 +637,12 @@ fn auto_daemon_start_failure_exits_with_manual_fallback_hint() -> Result<()> {
|
||||
let workspace = tempfile::tempdir()?;
|
||||
let home = tempfile::tempdir()?;
|
||||
write_test_config(home.path(), workspace.path())?;
|
||||
let config = home.path().join("config.toml");
|
||||
let contents = std::fs::read_to_string(&config)?;
|
||||
std::fs::write(
|
||||
config,
|
||||
contents.replace("features.daemon_auto_start = false\n", ""),
|
||||
)?;
|
||||
// An incomplete selected package must fail without installing a replacement.
|
||||
std::fs::create_dir_all(home.path().join("packages/app-server-daemon/current"))?;
|
||||
let mut terminal = PtyCodex::start(
|
||||
|
||||
@@ -41,7 +41,6 @@ async fn automatic_reconnect_restores_draft_and_routes_new_notifications() -> Re
|
||||
let (submitted_tx, submitted_rx) = tokio::sync::oneshot::channel();
|
||||
let server_cwd = workspace_path.clone();
|
||||
let id = "00000000-0000-0000-0000-000000000001";
|
||||
let server_home = codex_home.path().to_path_buf();
|
||||
let server = tokio::spawn(async move {
|
||||
let mut methods = Vec::new();
|
||||
let mut restore_rx = Some(restore_rx);
|
||||
@@ -53,8 +52,8 @@ async fn automatic_reconnect_restores_draft_and_routes_new_notifications() -> Re
|
||||
"status": {"type": "active", "activeFlags": []}, "cwd": server_cwd,
|
||||
"cliVersion": "0.0.0", "source": "cli", "turns": [{"id": "running", "items": [], "status": "inProgress", "error": null}]
|
||||
});
|
||||
// Startup probes the running daemon and checks compatibility before opening the session.
|
||||
for connection in -2..7 {
|
||||
// The first connection checks daemon compatibility before TUI startup.
|
||||
for connection in -1..7 {
|
||||
let mut socket = loop {
|
||||
let (stream, _) = listener.accept().await?;
|
||||
// Startup probes the default daemon socket before opening its WebSocket.
|
||||
@@ -106,9 +105,7 @@ async fn automatic_reconnect_restores_draft_and_routes_new_notifications() -> Re
|
||||
continue;
|
||||
}
|
||||
let result = match request.method.as_str() {
|
||||
"initialize" => {
|
||||
json!({"userAgent": "reconnect-pty/0.0.0", "codexHome": server_home, "platformFamily": std::env::consts::FAMILY, "platformOs": std::env::consts::OS})
|
||||
}
|
||||
"initialize" => json!({"userAgent": "reconnect-pty"}),
|
||||
// An older daemon can omit the client's default-disabled features.
|
||||
"experimentalFeature/list" => {
|
||||
json!({"data": (["api_key_model_discovery", "code_mode_host", "auth_elicitation"].map(|name| json!({
|
||||
|
||||
@@ -15,7 +15,7 @@ fn startup_records_screen_reader_detection() -> Result<()> {
|
||||
let path = home.path().join("config.toml");
|
||||
let contents = std::fs::read_to_string(&path)?;
|
||||
std::fs::write(&path, format!("{contents}\n[tui]\nanimations = true\n"))?;
|
||||
let mut terminal = PtyCodex::start(&repo_root, home, &["--no-daemon"])?;
|
||||
let mut terminal = PtyCodex::start(&repo_root, home, &[])?;
|
||||
terminal.wait_for_startup()?;
|
||||
let deadline = Instant::now() + Duration::from_secs(/*secs*/ 30);
|
||||
loop {
|
||||
|
||||
@@ -17,7 +17,7 @@ fn tui_mode_picker_applies_only_after_restart() -> Result<()> {
|
||||
),
|
||||
)?;
|
||||
for (launch, current) in [false, true, false].into_iter().enumerate() {
|
||||
let mut terminal = PtyCodex::start(&root, home, &["--no-daemon"])?;
|
||||
let mut terminal = PtyCodex::start(&root, home, &[])?;
|
||||
terminal.wait_for_startup()?;
|
||||
// The header precedes session setup; wait for the thread ID before sending commands.
|
||||
let deadline = Instant::now() + STARTUP_TIMEOUT;
|
||||
|
||||
@@ -99,7 +99,6 @@ async fn picker_side_worktree_fork_and_cd_run_on_the_production_stack() -> Resul
|
||||
&root,
|
||||
codex_home,
|
||||
&[
|
||||
"--no-daemon",
|
||||
"--no-alt-screen",
|
||||
"-c",
|
||||
"features.worktrees=true",
|
||||
|
||||
Reference in New Issue
Block a user