From 3f1e62518678578e6250668fab99769ea716c530 Mon Sep 17 00:00:00 2001 From: Codex Performance Fix Date: Wed, 15 Jul 2026 11:48:56 -0400 Subject: [PATCH] Fix desktop history replay and background logging --- codex-rs/Cargo.lock | 264 ++++++------ .../request_processors/thread_processor.rs | 391 +++++++++++++++++- codex-rs/state/src/log_db.rs | 6 +- codex-rs/state/src/runtime/threads.rs | 31 +- 4 files changed, 544 insertions(+), 148 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 023c5dfaa3..b1ebc2ebd8 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -394,7 +394,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "app_test_support" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -1868,7 +1868,7 @@ dependencies = [ [[package]] name = "codex-agent-graph-store" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-protocol", "codex-state", @@ -1882,7 +1882,7 @@ dependencies = [ [[package]] name = "codex-agent-identity" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -1901,7 +1901,7 @@ dependencies = [ [[package]] name = "codex-analytics" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-app-server-protocol", "codex-git-utils", @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "codex-ansi-escape" -version = "0.0.0" +version = "0.144.2" dependencies = [ "ansi-to-tui", "ratatui", @@ -1931,7 +1931,7 @@ dependencies = [ [[package]] name = "codex-api" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "assert_matches", @@ -1966,7 +1966,7 @@ dependencies = [ [[package]] name = "codex-app-server" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "app_test_support", @@ -2060,7 +2060,7 @@ dependencies = [ [[package]] name = "codex-app-server-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-app-server", "codex-app-server-protocol", @@ -2087,7 +2087,7 @@ dependencies = [ [[package]] name = "codex-app-server-daemon" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-app-server-protocol", @@ -2108,7 +2108,7 @@ dependencies = [ [[package]] name = "codex-app-server-protocol" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -2137,7 +2137,7 @@ dependencies = [ [[package]] name = "codex-app-server-test-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -2159,7 +2159,7 @@ dependencies = [ [[package]] name = "codex-app-server-transport" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "axum", @@ -2201,7 +2201,7 @@ dependencies = [ [[package]] name = "codex-apply-patch" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "assert_cmd", @@ -2221,7 +2221,7 @@ dependencies = [ [[package]] name = "codex-arg0" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-apply-patch", @@ -2241,7 +2241,7 @@ dependencies = [ [[package]] name = "codex-async-utils" -version = "0.0.0" +version = "0.144.2" dependencies = [ "pretty_assertions", "tokio", @@ -2250,7 +2250,7 @@ dependencies = [ [[package]] name = "codex-aws-auth" -version = "0.0.0" +version = "0.144.2" dependencies = [ "aws-config", "aws-credential-types", @@ -2265,7 +2265,7 @@ dependencies = [ [[package]] name = "codex-backend-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-api", @@ -2282,7 +2282,7 @@ dependencies = [ [[package]] name = "codex-backend-openapi-models" -version = "0.0.0" +version = "0.144.2" dependencies = [ "serde", "serde_json", @@ -2291,7 +2291,7 @@ dependencies = [ [[package]] name = "codex-bwrap" -version = "0.0.0" +version = "0.144.2" dependencies = [ "cc", "libc", @@ -2300,7 +2300,7 @@ dependencies = [ [[package]] name = "codex-chatgpt" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -2321,7 +2321,7 @@ dependencies = [ [[package]] name = "codex-cli" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "app_test_support", @@ -2398,7 +2398,7 @@ dependencies = [ [[package]] name = "codex-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-http-client", "eventsource-stream", @@ -2410,7 +2410,7 @@ dependencies = [ [[package]] name = "codex-cloud-config" -version = "0.0.0" +version = "0.144.2" dependencies = [ "base64 0.22.1", "chrono", @@ -2434,7 +2434,7 @@ dependencies = [ [[package]] name = "codex-cloud-tasks" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -2465,7 +2465,7 @@ dependencies = [ [[package]] name = "codex-cloud-tasks-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -2479,7 +2479,7 @@ dependencies = [ [[package]] name = "codex-cloud-tasks-mock-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "chrono", "codex-cloud-tasks-client", @@ -2488,7 +2488,7 @@ dependencies = [ [[package]] name = "codex-code-mode" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-code-mode-protocol", "codex-protocol", @@ -2504,7 +2504,7 @@ dependencies = [ [[package]] name = "codex-code-mode-host" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-code-mode", @@ -2520,7 +2520,7 @@ dependencies = [ [[package]] name = "codex-code-mode-protocol" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-protocol", "pretty_assertions", @@ -2532,11 +2532,11 @@ dependencies = [ [[package]] name = "codex-collaboration-mode-templates" -version = "0.0.0" +version = "0.144.2" [[package]] name = "codex-config" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -2584,7 +2584,7 @@ dependencies = [ [[package]] name = "codex-connectors" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-config", @@ -2602,7 +2602,7 @@ dependencies = [ [[package]] name = "codex-connectors-extension" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-connectors", "codex-core-plugins", @@ -2614,7 +2614,7 @@ dependencies = [ [[package]] name = "codex-context-fragments" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-protocol", "codex-utils-string", @@ -2622,7 +2622,7 @@ dependencies = [ [[package]] name = "codex-core" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "arc-swap", @@ -2749,7 +2749,7 @@ dependencies = [ [[package]] name = "codex-core-api" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-analytics", "codex-app-server-protocol", @@ -2770,7 +2770,7 @@ dependencies = [ [[package]] name = "codex-core-plugins" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -2818,7 +2818,7 @@ dependencies = [ [[package]] name = "codex-core-skills" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-analytics", @@ -2852,7 +2852,7 @@ dependencies = [ [[package]] name = "codex-exec" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "assert_cmd", @@ -2898,7 +2898,7 @@ dependencies = [ [[package]] name = "codex-exec-server" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "arc-swap", @@ -2950,7 +2950,7 @@ dependencies = [ [[package]] name = "codex-exec-server-protocol" -version = "0.0.0" +version = "0.144.2" dependencies = [ "base64 0.22.1", "codex-file-system", @@ -2965,7 +2965,7 @@ dependencies = [ [[package]] name = "codex-execpolicy" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -2982,7 +2982,7 @@ dependencies = [ [[package]] name = "codex-execpolicy-legacy" -version = "0.0.0" +version = "0.144.2" dependencies = [ "allocative", "anyhow", @@ -3002,7 +3002,7 @@ dependencies = [ [[package]] name = "codex-experimental-api-macros" -version = "0.0.0" +version = "0.144.2" dependencies = [ "proc-macro2", "quote", @@ -3011,7 +3011,7 @@ dependencies = [ [[package]] name = "codex-extension-api" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-config", "codex-context-fragments", @@ -3025,7 +3025,7 @@ dependencies = [ [[package]] name = "codex-extension-items" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-utils-absolute-path", "pretty_assertions", @@ -3037,7 +3037,7 @@ dependencies = [ [[package]] name = "codex-external-agent-migration" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-hooks", "pretty_assertions", @@ -3049,7 +3049,7 @@ dependencies = [ [[package]] name = "codex-external-agent-sessions" -version = "0.0.0" +version = "0.144.2" dependencies = [ "chrono", "codex-app-server-protocol", @@ -3063,7 +3063,7 @@ dependencies = [ [[package]] name = "codex-features" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-otel", "codex-protocol", @@ -3076,7 +3076,7 @@ dependencies = [ [[package]] name = "codex-feedback" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-login", @@ -3090,7 +3090,7 @@ dependencies = [ [[package]] name = "codex-file-search" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -3106,7 +3106,7 @@ dependencies = [ [[package]] name = "codex-file-system" -version = "0.0.0" +version = "0.144.2" dependencies = [ "bytes", "codex-protocol", @@ -3118,7 +3118,7 @@ dependencies = [ [[package]] name = "codex-file-watcher" -version = "0.0.0" +version = "0.144.2" dependencies = [ "notify", "pretty_assertions", @@ -3129,7 +3129,7 @@ dependencies = [ [[package]] name = "codex-git-utils" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -3154,7 +3154,7 @@ dependencies = [ [[package]] name = "codex-goal-extension" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -3176,7 +3176,7 @@ dependencies = [ [[package]] name = "codex-guardian" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-core", "codex-extension-api", @@ -3185,7 +3185,7 @@ dependencies = [ [[package]] name = "codex-home" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-extension-api", "codex-utils-absolute-path", @@ -3196,7 +3196,7 @@ dependencies = [ [[package]] name = "codex-hooks" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -3219,7 +3219,7 @@ dependencies = [ [[package]] name = "codex-http-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "bytes", "codex-utils-cargo-bin", @@ -3250,7 +3250,7 @@ dependencies = [ [[package]] name = "codex-image-generation-extension" -version = "0.0.0" +version = "0.144.2" dependencies = [ "base64 0.22.1", "codex-api", @@ -3277,7 +3277,7 @@ dependencies = [ [[package]] name = "codex-install-context" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-utils-absolute-path", "codex-utils-home-dir", @@ -3287,7 +3287,7 @@ dependencies = [ [[package]] name = "codex-keyring-store" -version = "0.0.0" +version = "0.144.2" dependencies = [ "keyring", "tracing", @@ -3295,7 +3295,7 @@ dependencies = [ [[package]] name = "codex-linux-sandbox" -version = "0.0.0" +version = "0.144.2" dependencies = [ "clap", "codex-core", @@ -3320,7 +3320,7 @@ dependencies = [ [[package]] name = "codex-lmstudio" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-core", "codex-model-provider-info", @@ -3334,7 +3334,7 @@ dependencies = [ [[package]] name = "codex-login" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -3375,7 +3375,7 @@ dependencies = [ [[package]] name = "codex-mcp" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "arc-swap", @@ -3410,7 +3410,7 @@ dependencies = [ [[package]] name = "codex-mcp-extension" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-config", "codex-connectors-extension", @@ -3435,7 +3435,7 @@ dependencies = [ [[package]] name = "codex-mcp-server" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-arg0", @@ -3469,7 +3469,7 @@ dependencies = [ [[package]] name = "codex-memories-extension" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-core", "codex-extension-api", @@ -3490,7 +3490,7 @@ dependencies = [ [[package]] name = "codex-memories-read" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-protocol", "codex-shell-command", @@ -3500,7 +3500,7 @@ dependencies = [ [[package]] name = "codex-memories-write" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -3537,7 +3537,7 @@ dependencies = [ [[package]] name = "codex-message-history" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-config", "memchr", @@ -3551,7 +3551,7 @@ dependencies = [ [[package]] name = "codex-model-provider" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-agent-identity", "codex-api", @@ -3574,7 +3574,7 @@ dependencies = [ [[package]] name = "codex-model-provider-info" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-api", "codex-protocol", @@ -3590,7 +3590,7 @@ dependencies = [ [[package]] name = "codex-models-manager" -version = "0.0.0" +version = "0.144.2" dependencies = [ "chrono", "codex-collaboration-mode-templates", @@ -3610,7 +3610,7 @@ dependencies = [ [[package]] name = "codex-network-proxy" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -3646,7 +3646,7 @@ dependencies = [ [[package]] name = "codex-ollama" -version = "0.0.0" +version = "0.144.2" dependencies = [ "assert_matches", "async-stream", @@ -3666,7 +3666,7 @@ dependencies = [ [[package]] name = "codex-otel" -version = "0.0.0" +version = "0.144.2" dependencies = [ "chrono", "codex-api", @@ -3697,7 +3697,7 @@ dependencies = [ [[package]] name = "codex-plugin" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-config", "codex-protocol", @@ -3710,7 +3710,7 @@ dependencies = [ [[package]] name = "codex-process-hardening" -version = "0.0.0" +version = "0.144.2" dependencies = [ "libc", "pretty_assertions", @@ -3718,7 +3718,7 @@ dependencies = [ [[package]] name = "codex-prompts" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-context-fragments", @@ -3732,7 +3732,7 @@ dependencies = [ [[package]] name = "codex-protocol" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chardetng", @@ -3774,7 +3774,7 @@ dependencies = [ [[package]] name = "codex-realtime-webrtc" -version = "0.0.0" +version = "0.144.2" dependencies = [ "libwebrtc", "thiserror 2.0.18", @@ -3783,7 +3783,7 @@ dependencies = [ [[package]] name = "codex-response-debug-context" -version = "0.0.0" +version = "0.144.2" dependencies = [ "base64 0.22.1", "codex-api", @@ -3794,7 +3794,7 @@ dependencies = [ [[package]] name = "codex-responses-api-proxy" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -3811,7 +3811,7 @@ dependencies = [ [[package]] name = "codex-rmcp-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "axum", @@ -3852,7 +3852,7 @@ dependencies = [ [[package]] name = "codex-rollout" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -3876,7 +3876,7 @@ dependencies = [ [[package]] name = "codex-rollout-trace" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-code-mode", @@ -3892,7 +3892,7 @@ dependencies = [ [[package]] name = "codex-sandboxing" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-network-proxy", @@ -3915,7 +3915,7 @@ dependencies = [ [[package]] name = "codex-secrets" -version = "0.0.0" +version = "0.144.2" dependencies = [ "age", "anyhow", @@ -3936,7 +3936,7 @@ dependencies = [ [[package]] name = "codex-shell-command" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -3957,7 +3957,7 @@ dependencies = [ [[package]] name = "codex-shell-escalation" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -3977,7 +3977,7 @@ dependencies = [ [[package]] name = "codex-skills" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-utils-absolute-path", "include_dir", @@ -3986,7 +3986,7 @@ dependencies = [ [[package]] name = "codex-skills-extension" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-core-skills", "codex-exec-server", @@ -4008,7 +4008,7 @@ dependencies = [ [[package]] name = "codex-state" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "chrono", @@ -4032,7 +4032,7 @@ dependencies = [ [[package]] name = "codex-stdio-to-uds" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-uds", @@ -4044,7 +4044,7 @@ dependencies = [ [[package]] name = "codex-terminal-detection" -version = "0.0.0" +version = "0.144.2" dependencies = [ "pretty_assertions", "tracing", @@ -4052,7 +4052,7 @@ dependencies = [ [[package]] name = "codex-test-binary-support" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-arg0", "tempfile", @@ -4060,7 +4060,7 @@ dependencies = [ [[package]] name = "codex-thread-manager-sample" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "clap", @@ -4071,7 +4071,7 @@ dependencies = [ [[package]] name = "codex-thread-store" -version = "0.0.0" +version = "0.144.2" dependencies = [ "chrono", "codex-git-utils", @@ -4093,7 +4093,7 @@ dependencies = [ [[package]] name = "codex-tools" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-code-mode", "codex-connectors", @@ -4118,7 +4118,7 @@ dependencies = [ [[package]] name = "codex-tui" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "app_test_support", @@ -4228,7 +4228,7 @@ dependencies = [ [[package]] name = "codex-uds" -version = "0.0.0" +version = "0.144.2" dependencies = [ "async-io", "pretty_assertions", @@ -4240,7 +4240,7 @@ dependencies = [ [[package]] name = "codex-utils-absolute-path" -version = "0.0.0" +version = "0.144.2" dependencies = [ "dirs", "dunce", @@ -4254,14 +4254,14 @@ dependencies = [ [[package]] name = "codex-utils-approval-presets" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-protocol", ] [[package]] name = "codex-utils-cache" -version = "0.0.0" +version = "0.144.2" dependencies = [ "lru 0.16.3", "sha1 0.10.6", @@ -4270,7 +4270,7 @@ dependencies = [ [[package]] name = "codex-utils-cargo-bin" -version = "0.0.0" +version = "0.144.2" dependencies = [ "assert_cmd", "runfiles", @@ -4279,7 +4279,7 @@ dependencies = [ [[package]] name = "codex-utils-cli" -version = "0.0.0" +version = "0.144.2" dependencies = [ "clap", "codex-protocol", @@ -4291,15 +4291,15 @@ dependencies = [ [[package]] name = "codex-utils-elapsed" -version = "0.0.0" +version = "0.144.2" [[package]] name = "codex-utils-fuzzy-match" -version = "0.0.0" +version = "0.144.2" [[package]] name = "codex-utils-home-dir" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-utils-absolute-path", "dirs", @@ -4309,7 +4309,7 @@ dependencies = [ [[package]] name = "codex-utils-image" -version = "0.0.0" +version = "0.144.2" dependencies = [ "base64 0.22.1", "codex-utils-cache", @@ -4322,7 +4322,7 @@ dependencies = [ [[package]] name = "codex-utils-json-to-toml" -version = "0.0.0" +version = "0.144.2" dependencies = [ "pretty_assertions", "serde_json", @@ -4331,7 +4331,7 @@ dependencies = [ [[package]] name = "codex-utils-oss" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-core", "codex-lmstudio", @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "codex-utils-output-truncation" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-protocol", "codex-utils-string", @@ -4350,7 +4350,7 @@ dependencies = [ [[package]] name = "codex-utils-path" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-utils-absolute-path", "dunce", @@ -4360,7 +4360,7 @@ dependencies = [ [[package]] name = "codex-utils-path-uri" -version = "0.0.0" +version = "0.144.2" dependencies = [ "base64 0.22.1", "codex-utils-absolute-path", @@ -4376,7 +4376,7 @@ dependencies = [ [[package]] name = "codex-utils-plugins" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-exec-server", "codex-utils-absolute-path", @@ -4389,7 +4389,7 @@ dependencies = [ [[package]] name = "codex-utils-pty" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "filedescriptor", @@ -4405,7 +4405,7 @@ dependencies = [ [[package]] name = "codex-utils-readiness" -version = "0.0.0" +version = "0.144.2" dependencies = [ "assert_matches", "thiserror 2.0.18", @@ -4415,14 +4415,14 @@ dependencies = [ [[package]] name = "codex-utils-rustls-provider" -version = "0.0.0" +version = "0.144.2" dependencies = [ "rustls", ] [[package]] name = "codex-utils-sandbox-summary" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-core", "codex-model-provider-info", @@ -4433,7 +4433,7 @@ dependencies = [ [[package]] name = "codex-utils-sleep-inhibitor" -version = "0.0.0" +version = "0.144.2" dependencies = [ "core-foundation 0.9.4", "libc", @@ -4443,14 +4443,14 @@ dependencies = [ [[package]] name = "codex-utils-stream-parser" -version = "0.0.0" +version = "0.144.2" dependencies = [ "pretty_assertions", ] [[package]] name = "codex-utils-string" -version = "0.0.0" +version = "0.144.2" dependencies = [ "pretty_assertions", "regex-lite", @@ -4460,14 +4460,14 @@ dependencies = [ [[package]] name = "codex-utils-template" -version = "0.0.0" +version = "0.144.2" dependencies = [ "pretty_assertions", ] [[package]] name = "codex-v8-poc" -version = "0.0.0" +version = "0.144.2" dependencies = [ "pretty_assertions", "v8", @@ -4475,7 +4475,7 @@ dependencies = [ [[package]] name = "codex-web-search-extension" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-api", "codex-core", @@ -4495,7 +4495,7 @@ dependencies = [ [[package]] name = "codex-websocket-client" -version = "0.0.0" +version = "0.144.2" dependencies = [ "codex-http-client", "codex-utils-rustls-provider", @@ -4511,7 +4511,7 @@ dependencies = [ [[package]] name = "codex-windows-sandbox" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -4768,7 +4768,7 @@ dependencies = [ [[package]] name = "core_test_support" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "assert_cmd", @@ -9154,7 +9154,7 @@ dependencies = [ [[package]] name = "mcp_test_support" -version = "0.0.0" +version = "0.144.2" dependencies = [ "anyhow", "codex-login", diff --git a/codex-rs/app-server/src/request_processors/thread_processor.rs b/codex-rs/app-server/src/request_processors/thread_processor.rs index fc6a3281b4..fc80da0d9a 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor.rs @@ -6,12 +6,323 @@ use codex_protocol::config_types::MultiAgentMode; use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS; use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE; use codex_protocol::protocol::ThreadHistoryMode; +use serde::Deserialize; +use serde::Serialize; +use std::io::SeekFrom; +use std::sync::OnceLock; +use tokio::io::AsyncBufReadExt; +use tokio::io::AsyncReadExt; +use tokio::io::AsyncSeekExt; +use tokio::io::AsyncWriteExt; +use tokio::io::BufReader; const THREAD_LIST_DEFAULT_LIMIT: usize = 25; const THREAD_LIST_MAX_LIMIT: usize = 100; const CODEX_TUI_CLIENT_NAME: &str = "codex-tui"; const THREAD_ROLLBACK_DEPRECATION_SUMMARY: &str = "thread/rollback is deprecated and will be removed soon"; +const UI_HISTORY_CACHE_VERSION: &str = "v1"; +const UI_HISTORY_MEMORY_CACHE_MAX_BYTES: u64 = 768 * 1024 * 1024; + +static UI_HISTORY_CACHE_LOCK: OnceLock> = OnceLock::new(); +static UI_HISTORY_MEMORY_CACHE: OnceLock> = OnceLock::new(); + +struct CachedUiHistory { + cache_bytes: u64, + last_used: Instant, + items: Arc>, +} + +#[derive(Default)] +struct UiHistoryMemoryCache { + entries: HashMap, + total_bytes: u64, +} + +impl UiHistoryMemoryCache { + fn get(&mut self, thread_id: ThreadId, cache_bytes: u64) -> Option>> { + let stale_bytes = self + .entries + .get(&thread_id) + .filter(|entry| entry.cache_bytes != cache_bytes) + .map(|entry| entry.cache_bytes); + if let Some(stale_bytes) = stale_bytes { + self.entries.remove(&thread_id); + self.total_bytes = self.total_bytes.saturating_sub(stale_bytes); + } + let entry = self.entries.get_mut(&thread_id)?; + entry.last_used = Instant::now(); + Some(Arc::clone(&entry.items)) + } + + fn insert(&mut self, thread_id: ThreadId, cache_bytes: u64, items: Arc>) { + if cache_bytes > UI_HISTORY_MEMORY_CACHE_MAX_BYTES { + return; + } + if let Some(replaced) = self.entries.remove(&thread_id) { + self.total_bytes = self.total_bytes.saturating_sub(replaced.cache_bytes); + } + self.total_bytes += cache_bytes; + self.entries.insert( + thread_id, + CachedUiHistory { + cache_bytes, + last_used: Instant::now(), + items, + }, + ); + while self.total_bytes > UI_HISTORY_MEMORY_CACHE_MAX_BYTES { + let Some(oldest_thread_id) = self + .entries + .iter() + .min_by_key(|(_, entry)| entry.last_used) + .map(|(thread_id, _)| *thread_id) + else { + break; + }; + if let Some(removed) = self.entries.remove(&oldest_thread_id) { + self.total_bytes = self.total_bytes.saturating_sub(removed.cache_bytes); + } + } + } +} + +async fn get_ui_history_from_memory( + thread_id: ThreadId, + cache_path: &Path, +) -> Option>> { + let cache_bytes = tokio::fs::metadata(cache_path).await.ok()?.len(); + UI_HISTORY_MEMORY_CACHE + .get_or_init(|| Mutex::new(UiHistoryMemoryCache::default())) + .lock() + .await + .get(thread_id, cache_bytes) +} + +async fn put_ui_history_in_memory( + thread_id: ThreadId, + cache_path: &Path, + items: Arc>, +) { + let Ok(metadata) = tokio::fs::metadata(cache_path).await else { + return; + }; + UI_HISTORY_MEMORY_CACHE + .get_or_init(|| Mutex::new(UiHistoryMemoryCache::default())) + .lock() + .await + .insert(thread_id, metadata.len(), items); +} + +#[derive(Debug, Deserialize, Serialize)] +struct UiHistoryCacheState { + source_path: String, + source_bytes: u64, + cache_bytes: u64, +} + +/// Build a UI-only rollout that omits compaction payloads. Compaction summaries are needed when +/// resuming the model, but the desktop history builder only uses their presence as a marker. Some +/// long-running rollouts contain gigabytes of repeated summaries, so replaying them for every +/// `thread/turns/list` request needlessly stalls the entire desktop. +async fn build_ui_history_cache( + codex_home: &Path, + thread_id: ThreadId, + source_path: &Path, +) -> Result { + if source_path + .extension() + .and_then(|extension| extension.to_str()) + != Some("jsonl") + { + return Err("UI history cache only supports JSONL rollouts".to_string()); + } + + let cache_lock = UI_HISTORY_CACHE_LOCK.get_or_init(|| Mutex::new(())); + let _guard = cache_lock.lock().await; + + let source_path_text = source_path.to_string_lossy().into_owned(); + let source_path = tokio::fs::canonicalize(source_path) + .await + .map_err(|err| format!("failed to resolve rollout path: {err}"))?; + let source_len = tokio::fs::metadata(&source_path) + .await + .map_err(|err| format!("failed to inspect rollout: {err}"))? + .len(); + let cache_dir = codex_home + .join("cache") + .join(format!("ui-history-{UI_HISTORY_CACHE_VERSION}")); + tokio::fs::create_dir_all(&cache_dir) + .await + .map_err(|err| format!("failed to create UI history cache: {err}"))?; + + let cache_path = cache_dir.join(format!("{thread_id}.jsonl")); + let state_path = cache_dir.join(format!("{thread_id}.state.json")); + let state = match tokio::fs::read(&state_path).await { + Ok(bytes) => serde_json::from_slice::(&bytes).ok(), + Err(_) => None, + }; + let cache_len = tokio::fs::metadata(&cache_path) + .await + .ok() + .map(|metadata| metadata.len()); + let can_append = state.as_ref().is_some_and(|state| { + state.source_path == source_path_text + && state.source_bytes <= source_len + && cache_len == Some(state.cache_bytes) + }); + + let (source_offset, cache_offset, output_path) = if can_append { + let state = state.expect("state checked above"); + (state.source_bytes, state.cache_bytes, cache_path.clone()) + } else { + let temp_path = cache_dir.join(format!("{thread_id}.rebuild.jsonl")); + (0, 0, temp_path) + }; + + let (processed_source_bytes, written_cache_bytes) = transform_ui_history_lines( + &source_path, + source_offset, + source_len, + &output_path, + can_append, + ) + .await?; + let final_source_bytes = source_offset + processed_source_bytes; + let final_cache_bytes = cache_offset + written_cache_bytes; + + if !can_append { + if tokio::fs::try_exists(&cache_path).await.unwrap_or(false) { + tokio::fs::remove_file(&cache_path) + .await + .map_err(|err| format!("failed to replace stale UI history cache: {err}"))?; + } + tokio::fs::rename(&output_path, &cache_path) + .await + .map_err(|err| format!("failed to publish UI history cache: {err}"))?; + } + + let state = UiHistoryCacheState { + source_path: source_path_text, + source_bytes: final_source_bytes, + cache_bytes: final_cache_bytes, + }; + tokio::fs::write( + &state_path, + serde_json::to_vec(&state) + .map_err(|err| format!("failed to encode UI history cache state: {err}"))?, + ) + .await + .map_err(|err| format!("failed to save UI history cache state: {err}"))?; + + Ok(cache_path) +} + +async fn transform_ui_history_lines( + source_path: &Path, + source_offset: u64, + source_len: u64, + output_path: &Path, + append: bool, +) -> Result<(u64, u64), String> { + let mut source = tokio::fs::File::open(source_path) + .await + .map_err(|err| format!("failed to open rollout: {err}"))?; + source + .seek(SeekFrom::Start(source_offset)) + .await + .map_err(|err| format!("failed to seek rollout: {err}"))?; + let remaining = source_len.saturating_sub(source_offset); + let mut source = BufReader::new(source.take(remaining)); + let mut output = tokio::fs::OpenOptions::new() + .create(true) + .write(true) + .append(append) + .truncate(!append) + .open(output_path) + .await + .map_err(|err| format!("failed to open UI history cache: {err}"))?; + let mut line = Vec::new(); + let mut processed_source_bytes = 0_u64; + let mut written_cache_bytes = 0_u64; + + loop { + line.clear(); + let read = source + .read_until(b'\n', &mut line) + .await + .map_err(|err| format!("failed to read rollout: {err}"))?; + if read == 0 { + break; + } + // A live writer may be in the middle of a line. Leave it for the next incremental pass. + if !line.ends_with(b"\n") { + break; + } + processed_source_bytes += read as u64; + + let transformed = if line + .windows(b"\"type\":\"compacted\"".len()) + .any(|window| window == b"\"type\":\"compacted\"") + { + let mut value = serde_json::from_slice::(&line) + .map_err(|err| format!("failed to decode compacted rollout item: {err}"))?; + value["payload"] = serde_json::json!({ "message": "" }); + let mut encoded = serde_json::to_vec(&value) + .map_err(|err| format!("failed to encode compacted rollout item: {err}"))?; + encoded.push(b'\n'); + encoded + } else { + line.clone() + }; + output + .write_all(&transformed) + .await + .map_err(|err| format!("failed to write UI history cache: {err}"))?; + written_cache_bytes += transformed.len() as u64; + } + + output + .flush() + .await + .map_err(|err| format!("failed to flush UI history cache: {err}"))?; + Ok((processed_source_bytes, written_cache_bytes)) +} + +#[cfg(test)] +mod ui_history_cache_tests { + use super::*; + + #[tokio::test] + async fn strips_compaction_payload_without_touching_other_lines() { + let temp = tempfile::tempdir().expect("temp dir"); + let source_path = temp.path().join("source.jsonl"); + let cache_path = temp.path().join("cache.jsonl"); + let regular = r#"{"type":"event_msg","payload":{"type":"token_count","info":null}}"#; + let compacted = r#"{"type":"compacted","payload":{"message":"large repeated summary","window_number":8}}"#; + let source = format!("{regular}\n{compacted}\n"); + tokio::fs::write(&source_path, source.as_bytes()) + .await + .expect("write source"); + + let (read, written) = + transform_ui_history_lines(&source_path, 0, source.len() as u64, &cache_path, false) + .await + .expect("transform"); + + assert_eq!(read, source.len() as u64); + let cache = tokio::fs::read_to_string(&cache_path) + .await + .expect("read cache"); + assert_eq!(written, cache.len() as u64); + let lines = cache.lines().collect::>(); + assert_eq!(lines[0], regular); + let compacted: serde_json::Value = + serde_json::from_str(lines[1]).expect("decode compacted line"); + assert_eq!(compacted["payload"], serde_json::json!({ "message": "" })); + assert!(source.contains("large repeated summary")); + } +} struct ThreadListFilters { model_providers: Option>, @@ -2453,7 +2764,7 @@ impl ThreadRequestProcessor { None }; build_thread_turns_page_response( - &items, + items.as_slice(), self.thread_watch_manager .loaded_status_for_thread(&thread_uuid.to_string()) .await, @@ -2533,23 +2844,87 @@ impl ThreadRequestProcessor { async fn load_thread_turns_list_history( &self, thread_id: ThreadId, - ) -> Result, ThreadReadViewError> { - match self + ) -> Result>, ThreadReadViewError> { + let summary = self .thread_store .read_thread(StoreReadThreadParams { thread_id, include_archived: true, - include_history: true, + include_history: false, }) - .await - { + .await; + match summary { Ok(stored_thread) => { + if let Some(rollout_path) = stored_thread.rollout_path.as_deref() { + match build_ui_history_cache( + self.config.codex_home.as_path(), + thread_id, + rollout_path, + ) + .await + { + Ok(cache_path) => { + if let Some(items) = + get_ui_history_from_memory(thread_id, &cache_path).await + { + return Ok(items); + } + match self + .thread_store + .read_thread_by_rollout_path(StoreReadThreadByRolloutPathParams { + rollout_path: cache_path.clone(), + include_archived: true, + include_history: true, + }) + .await + { + Ok(cached_thread) => { + let history = cached_thread.history.ok_or_else(|| { + ThreadReadViewError::Internal(format!( + "thread store did not return cached history for thread {thread_id}" + )) + })?; + let items = Arc::new(history.items); + put_ui_history_in_memory( + thread_id, + &cache_path, + Arc::clone(&items), + ) + .await; + return Ok(items); + } + Err(err) => tracing::warn!( + %thread_id, + error = %err, + "failed to read UI history cache; falling back to the original rollout" + ), + } + } + Err(err) => tracing::warn!( + %thread_id, + error = %err, + "failed to build UI history cache; falling back to the original rollout" + ), + } + } + + let stored_thread = self + .thread_store + .read_thread(StoreReadThreadParams { + thread_id, + include_archived: true, + include_history: true, + }) + .await + .map_err(|err| { + ThreadReadViewError::Internal(format!("failed to read thread: {err}")) + })?; let history = stored_thread.history.ok_or_else(|| { ThreadReadViewError::Internal(format!( "thread store did not return history for thread {thread_id}" )) })?; - return Ok(history.items); + return Ok(Arc::new(history.items)); } Err(ThreadStoreError::InvalidRequest { message }) if message == format!("no rollout found for thread id {thread_id}") => {} @@ -2586,7 +2961,7 @@ impl ThreadRequestProcessor { thread .load_history(/*include_archived*/ true) .await - .map(|history| history.items) + .map(|history| Arc::new(history.items)) .map_err(|err| thread_turns_list_history_load_error(thread_id, err)) } diff --git a/codex-rs/state/src/log_db.rs b/codex-rs/state/src/log_db.rs index 2cf0f5d9af..0613515b17 100644 --- a/codex-rs/state/src/log_db.rs +++ b/codex-rs/state/src/log_db.rs @@ -52,10 +52,14 @@ const LOG_FLUSH_INTERVAL: Duration = Duration::from_secs(2); pub fn default_filter() -> Targets { Targets::new() - .with_default(LevelFilter::TRACE) + .with_default(LevelFilter::INFO) .with_target("log", LevelFilter::OFF) .with_target("codex_otel.log_only", LevelFilter::OFF) .with_target("codex_otel.trace_safe", LevelFilter::OFF) + .with_target("codex_api::sse::responses", LevelFilter::WARN) + .with_target("codex_core::stream_events_utils", LevelFilter::WARN) + .with_target("codex_http_client::transport", LevelFilter::WARN) + .with_target("codex_mcp::connection_manager", LevelFilter::WARN) } #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/codex-rs/state/src/runtime/threads.rs b/codex-rs/state/src/runtime/threads.rs index c80b2b6716..5585eac0e4 100644 --- a/codex-rs/state/src/runtime/threads.rs +++ b/codex-rs/state/src/runtime/threads.rs @@ -532,7 +532,8 @@ ON CONFLICT(child_thread_id) DO NOTHING ) -> anyhow::Result { let updated_at = self.allocate_thread_updated_at(metadata.updated_at)?; let recency_at = self.allocate_thread_recency_at(metadata.recency_at)?; - let preview = metadata_preview(metadata); + let title = truncate_metadata_text(metadata.title.as_str(), 160); + let preview = truncate_metadata_text(metadata_preview(metadata), 500); let result = sqlx::query( r#" INSERT INTO threads ( @@ -600,8 +601,8 @@ ON CONFLICT(id) DO NOTHING ) .bind(metadata.cwd.display().to_string()) .bind(metadata.cli_version.as_str()) - .bind(metadata.title.as_str()) - .bind(preview) + .bind(title.as_str()) + .bind(preview.as_str()) .bind(metadata.sandbox_policy.as_str()) .bind(metadata.approval_mode.as_str()) .bind(metadata.tokens_used) @@ -637,8 +638,9 @@ ON CONFLICT(id) DO NOTHING thread_id: ThreadId, title: &str, ) -> anyhow::Result { + let title = truncate_metadata_text(title, 160); let result = sqlx::query("UPDATE threads SET title = ? WHERE id = ?") - .bind(title) + .bind(title.as_str()) .bind(thread_id.to_string()) .execute(self.pool.as_ref()) .await?; @@ -783,7 +785,8 @@ WHERE id = ? ) -> anyhow::Result<()> { let updated_at = self.allocate_thread_updated_at(metadata.updated_at)?; let insert_recency_at = self.allocate_thread_recency_at(metadata.recency_at)?; - let preview = metadata_preview(metadata); + let title = truncate_metadata_text(metadata.title.as_str(), 160); + let preview = truncate_metadata_text(metadata_preview(metadata), 500); // Backfill/reconcile callers merge existing git info before upserting, but that // read/modify/write is not atomic. Preserve non-null SQLite git fields here so // an explicit metadata update cannot be lost if a stale rollout upsert lands later. @@ -883,8 +886,8 @@ ON CONFLICT(id) DO UPDATE SET ) .bind(metadata.cwd.display().to_string()) .bind(metadata.cli_version.as_str()) - .bind(metadata.title.as_str()) - .bind(preview) + .bind(title.as_str()) + .bind(preview.as_str()) .bind(metadata.sandbox_policy.as_str()) .bind(metadata.approval_mode.as_str()) .bind(metadata.tokens_used) @@ -1413,6 +1416,14 @@ fn metadata_preview(metadata: &crate::ThreadMetadata) -> &str { .unwrap_or_default() } +fn truncate_metadata_text(value: &str, max_chars: usize) -> String { + if value.chars().count() <= max_chars { + value.to_string() + } else { + value.chars().take(max_chars).collect() + } +} + #[cfg(test)] mod tests { use super::*; @@ -1428,6 +1439,12 @@ mod tests { use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::ThreadHistoryMode; use pretty_assertions::assert_eq; + + #[test] + fn metadata_text_truncation_is_character_safe() { + assert_eq!(truncate_metadata_text("short", 10), "short"); + assert_eq!(truncate_metadata_text("ab🙂cd", 3), "ab🙂"); + } use serde_json::json; use std::path::PathBuf; -- 2.53.0.windows.2