Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8a65aae
feat(tui): port app-server runtime flow onto current main
fcoury Mar 9, 2026
85f59be
fix(tui): handle request_permissions approvals in app server mode
fcoury Mar 9, 2026
b7e6bbc
fix(core): inherit turn permission grants into spawned sub-agents
fcoury Mar 9, 2026
cf7642c
fix(tui): route typed sub-agent requests to their source thread
fcoury Mar 9, 2026
76d774a
fix(tui): bootstrap fresh in-process child threads from routed sessio…
fcoury Mar 9, 2026
4314b15
fix(tui): keep primary turn bookkeeping isolated from child threads
fcoury Mar 9, 2026
ccb4672
fix(tui): keep child patch approval diffs scoped by thread
fcoury Mar 9, 2026
2d5ac17
fix(tui): share the thread manager with in-process app server sessions
fcoury Mar 9, 2026
cfa1dd1
fix(tui): route background interactive responses through the app server
fcoury Mar 9, 2026
67a7e2f
fix(app-server): reuse shared thread manager auth wiring
fcoury Mar 9, 2026
37516ce
fix(core): seed inherited permissions before thread notifications
fcoury Mar 9, 2026
0636a8e
fix(tui): scope pending interactive requests by thread
fcoury Mar 9, 2026
c822c80
fix(tui): stop blocking quit on in-process runtime shutdown
fcoury Mar 9, 2026
97ed70d
fix(tui): preserve thread id for app-server interactive replies
fcoury Mar 10, 2026
f72754b
fix(tui): forward legacy ops in fresh in-process sessions
fcoury Mar 10, 2026
f34c94b
fix(tui): guard approval handlers before session configured
fcoury Mar 10, 2026
6345cf3
fix(tui): restore in-process interactive request handling
fcoury Mar 10, 2026
fc30513
fix(tui): preserve app-server routing for live agent threads
fcoury Mar 10, 2026
e0e5162
fix(tui): scope in-process agent state by thread
fcoury Mar 10, 2026
b6d80b0
fix(tui): handle child thread shutdown via app-server
fcoury Mar 10, 2026
f483043
fix(core): make shared auth overrides nest safely
fcoury Mar 10, 2026
7d88e5d
fix(core): preserve inherited workspace through nested auth overrides
fcoury Mar 10, 2026
dc6d1b9
fix(tui): keep queued input prompts visible on cancel
fcoury Mar 10, 2026
76397f3
fix(tui): shut down switched threads directly in app-server sessions
fcoury Mar 10, 2026
aa25281
fix(tui): shut down primary in-process sessions on thread closed
fcoury Mar 10, 2026
babdd69
fix(app-server): attach in-process listeners for existing shared threads
fcoury Mar 10, 2026
ac2d0fb
fix(tui): keep queued input and permissions search correct
fcoury Mar 10, 2026
0a1031d
fix(tui): preserve source thread for deferred interactive prompts
fcoury Mar 10, 2026
7267ff6
fix(tui): keep request user input history on the source thread
fcoury Mar 10, 2026
39bc218
fix(tui): keep live child thread ops on direct transport
fcoury Mar 10, 2026
e671ce5
fix(tui): record approval decisions in source transcripts
fcoury Mar 10, 2026
76a4ac7
fix(tui): route primary shutdown through core and replay local histor…
fcoury Mar 10, 2026
388bb6d
fix(tui): refresh external chatgpt auth through shared manager
fcoury Mar 10, 2026
e2b6a05
fix(tui): keep cross-thread overlays during interrupts
fcoury Mar 10, 2026
7c67ca4
fix(tui): preserve legacy notification event ids
fcoury Mar 10, 2026
323b242
fix(tui): align rebase with upstream permissions flow
fcoury Mar 11, 2026
a04b642
fix(app-server): update in-process tests for thread manager api
fcoury Mar 11, 2026
de7153d
fix(tui): dedupe permission prompts and scope auth overrides
fcoury Mar 11, 2026
0c235ed
fix(tui): map new macos permission fields
fcoury Mar 11, 2026
d1d0e12
test(tui): pass cwd to stream controller in tests
fcoury Mar 11, 2026
32ebe5d
fix(tui): box replay events to reduce enum size
fcoury Mar 11, 2026
b821792
test(tui): use agent navigation state in app tests
fcoury Mar 11, 2026
a7cfa4e
fix(tui): dismiss stale inactive-thread prompts on turn end
fcoury Mar 11, 2026
f5664bc
docs(tui): enhanced documentation for key modules
fcoury Mar 11, 2026
c38e51c
test(tui): align rebased tests with upstream apis
fcoury Mar 12, 2026
d32e3fa
fix(core): satisfy fmt gate and seatbelt stderr checks
fcoury Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

111 changes: 110 additions & 1 deletion codex-rs/app-server-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::Result as JsonRpcResult;
use codex_arg0::Arg0DispatchPaths;
use codex_core::ThreadManager;
use codex_core::config::Config;
use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::config_loader::LoaderOverrides;
Expand Down Expand Up @@ -129,6 +130,8 @@ pub struct InProcessClientStartArgs {
pub arg0_paths: Arg0DispatchPaths,
/// Shared config used to initialize app-server runtime.
pub config: Arc<Config>,
/// Optional thread manager to reuse instead of creating a private one.
pub thread_manager: Option<Arc<ThreadManager>>,
/// CLI config overrides that are already parsed into TOML values.
pub cli_overrides: Vec<(String, TomlValue)>,
/// Loader override knobs used by config API paths.
Expand Down Expand Up @@ -182,6 +185,7 @@ impl InProcessClientStartArgs {
InProcessStartArgs {
arg0_paths: self.arg0_paths,
config: self.config,
thread_manager: self.thread_manager,
cli_overrides: self.cli_overrides,
loader_overrides: self.loader_overrides,
cloud_requirements: self.cloud_requirements,
Expand Down Expand Up @@ -606,7 +610,10 @@ mod tests {
use codex_app_server_protocol::SessionSource as ApiSessionSource;
use codex_app_server_protocol::ThreadStartParams;
use codex_app_server_protocol::ThreadStartResponse;
use codex_core::CodexAuth;
use codex_core::ThreadManager;
use codex_core::config::ConfigBuilder;
use codex_core::test_support::thread_manager_with_models_provider_and_home;
use pretty_assertions::assert_eq;
use tokio::time::Duration;
use tokio::time::timeout;
Expand All @@ -623,9 +630,11 @@ mod tests {
session_source: SessionSource,
channel_capacity: usize,
) -> InProcessAppServerClient {
let config = Arc::new(build_test_config().await);
InProcessAppServerClient::start(InProcessClientStartArgs {
arg0_paths: Arg0DispatchPaths::default(),
config: Arc::new(build_test_config().await),
config,
thread_manager: None,
cli_overrides: Vec::new(),
loader_overrides: LoaderOverrides::default(),
cloud_requirements: CloudRequirementsLoader::default(),
Expand All @@ -647,6 +656,14 @@ mod tests {
start_test_client_with_capacity(session_source, DEFAULT_IN_PROCESS_CHANNEL_CAPACITY).await
}

fn shared_test_thread_manager(config: &Config) -> Arc<ThreadManager> {
Arc::new(thread_manager_with_models_provider_and_home(
CodexAuth::from_api_key("test"),
config.model_provider.clone(),
config.codex_home.clone(),
))
}

#[tokio::test]
async fn typed_request_roundtrip_works() {
let client = start_test_client(SessionSource::Exec).await;
Expand Down Expand Up @@ -702,6 +719,98 @@ mod tests {
}
}

#[tokio::test]
async fn shared_thread_manager_observes_threads_started_in_process() {
let config = Arc::new(build_test_config().await);
let thread_manager = shared_test_thread_manager(&config);
let client = InProcessAppServerClient::start(InProcessClientStartArgs {
arg0_paths: Arg0DispatchPaths::default(),
config,
thread_manager: Some(Arc::clone(&thread_manager)),
cli_overrides: Vec::new(),
loader_overrides: LoaderOverrides::default(),
cloud_requirements: CloudRequirementsLoader::default(),
feedback: CodexFeedback::new(),
config_warnings: Vec::new(),
session_source: SessionSource::Cli,
enable_codex_api_key_env: false,
client_name: "codex-app-server-client-test".to_string(),
client_version: "0.0.0-test".to_string(),
experimental_api: true,
opt_out_notification_methods: Vec::new(),
channel_capacity: DEFAULT_IN_PROCESS_CHANNEL_CAPACITY,
})
.await
.expect("in-process app-server client should start");

let response: ThreadStartResponse = client
.request_typed(ClientRequest::ThreadStart {
request_id: RequestId::Integer(3),
params: ThreadStartParams {
ephemeral: Some(true),
..ThreadStartParams::default()
},
})
.await
.expect("thread/start should succeed");
let thread_id = codex_protocol::ThreadId::from_string(&response.thread.id)
.expect("thread/start returned valid thread id");

thread_manager
.get_thread(thread_id)
.await
.expect("shared thread manager should see in-process threads");
client.shutdown().await.expect("shutdown should complete");
}

#[tokio::test]
async fn shared_thread_manager_reuses_configured_auth_manager() {
let mut config = build_test_config().await;
config.forced_chatgpt_workspace_id = Some("workspace-123".to_string());
let config = Arc::new(config);
let thread_manager = shared_test_thread_manager(&config);

assert_eq!(
thread_manager.auth_manager().forced_chatgpt_workspace_id(),
None
);
assert_eq!(
thread_manager.auth_manager().has_external_auth_refresher(),
false
);

let client = InProcessAppServerClient::start(InProcessClientStartArgs {
arg0_paths: Arg0DispatchPaths::default(),
config,
thread_manager: Some(Arc::clone(&thread_manager)),
cli_overrides: Vec::new(),
loader_overrides: LoaderOverrides::default(),
cloud_requirements: CloudRequirementsLoader::default(),
feedback: CodexFeedback::new(),
config_warnings: Vec::new(),
session_source: SessionSource::Cli,
enable_codex_api_key_env: false,
client_name: "codex-app-server-client-test".to_string(),
client_version: "0.0.0-test".to_string(),
experimental_api: true,
opt_out_notification_methods: Vec::new(),
channel_capacity: DEFAULT_IN_PROCESS_CHANNEL_CAPACITY,
})
.await
.expect("in-process app-server client should start");

assert_eq!(
thread_manager.auth_manager().forced_chatgpt_workspace_id(),
Some("workspace-123".to_string())
);
assert_eq!(
thread_manager.auth_manager().has_external_auth_refresher(),
true
);

client.shutdown().await.expect("shutdown should complete");
}

#[tokio::test]
async fn tiny_channel_capacity_still_supports_request_roundtrip() {
let client = start_test_client_with_capacity(SessionSource::Exec, 1).await;
Expand Down
4 changes: 4 additions & 0 deletions codex-rs/app-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3228,6 +3228,10 @@ impl CodexMessageProcessor {
self.thread_manager.subscribe_thread_created()
}

pub(crate) fn thread_manager(&self) -> &Arc<ThreadManager> {
&self.thread_manager
}

pub(crate) async fn connection_initialized(&self, connection_id: ConnectionId) {
self.thread_state_manager
.connection_initialized(connection_id)
Expand Down
Loading
Loading