From 3f1e62518678578e6250668fab99769ea716c530 Mon Sep 17 00:00:00 2001
From: Codex Performance Fix <local@codex-performance-fix.invalid>
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<Mutex<()>> = OnceLock::new();
+static UI_HISTORY_MEMORY_CACHE: OnceLock<Mutex<UiHistoryMemoryCache>> = OnceLock::new();
+
+struct CachedUiHistory {
+    cache_bytes: u64,
+    last_used: Instant,
+    items: Arc<Vec<RolloutItem>>,
+}
+
+#[derive(Default)]
+struct UiHistoryMemoryCache {
+    entries: HashMap<ThreadId, CachedUiHistory>,
+    total_bytes: u64,
+}
+
+impl UiHistoryMemoryCache {
+    fn get(&mut self, thread_id: ThreadId, cache_bytes: u64) -> Option<Arc<Vec<RolloutItem>>> {
+        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<Vec<RolloutItem>>) {
+        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<Arc<Vec<RolloutItem>>> {
+    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<Vec<RolloutItem>>,
+) {
+    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<PathBuf, String> {
+    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::<UiHistoryCacheState>(&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::<serde_json::Value>(&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::<Vec<_>>();
+        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<Vec<String>>,
@@ -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<Vec<RolloutItem>, ThreadReadViewError> {
-        match self
+    ) -> Result<Arc<Vec<RolloutItem>>, 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<bool> {
         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<bool> {
+        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

