diff --git a/Cargo.lock b/Cargo.lock index 660f1b6..37c3e3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,6 +196,49 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "age" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07d86e4272c093c88caf7864a2d09af52a5159180848ca4832a3cdbd7d014d5" +dependencies = [ + "age-core", + "base64 0.21.7", + "bech32", + "chacha20poly1305", + "cookie-factory", + "hmac 0.12.1", + "i18n-embed", + "i18n-embed-fl", + "lazy_static", + "nom 7.1.3", + "pin-project", + "rand 0.8.6", + "rust-embed", + "scrypt", + "sha2 0.10.9", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "age-core" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2bf6a89c984ca9d850913ece2da39e1d200563b0a94b002b253beee4c5acf99" +dependencies = [ + "base64 0.21.7", + "chacha20poly1305", + "cookie-factory", + "hkdf 0.12.4", + "io_tee", + "nom 7.1.3", + "rand 0.8.6", + "secrecy", + "sha2 0.10.9", +] + [[package]] name = "agent-client-protocol" version = "0.14.0" @@ -209,7 +252,7 @@ dependencies = [ "futures", "futures-concurrency", "jsonrpcmsg", - "rustc-hash", + "rustc-hash 2.1.2", "schemars 1.2.1", "serde", "serde_json", @@ -1041,7 +1084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", - "base64", + "base64 0.22.1", "bytes", "futures-util", "http 1.4.1", @@ -1106,6 +1149,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -1128,6 +1177,21 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "beef" version = "0.5.2" @@ -1149,7 +1213,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 2.1.2", "shlex 1.3.0", "syn 2.0.117", ] @@ -1454,6 +1518,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + [[package]] name = "chacha20" version = "0.10.0" @@ -1465,6 +1540,19 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20 0.9.1", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chardetng" version = "0.1.17" @@ -1614,6 +1702,8 @@ dependencies = [ "codex-extension-api", "codex-login", "codex-mcp-server", + "codex-memories-extension", + "codex-memories-write", "codex-models-manager", "codex-protocol", "codex-shell-command", @@ -1640,7 +1730,7 @@ version = "0.137.0" source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "chrono", "codex-protocol", "crypto_box", @@ -1679,7 +1769,7 @@ source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f74 dependencies = [ "async-channel", "async-trait", - "base64", + "base64 0.22.1", "bytes", "chrono", "codex-client", @@ -1783,6 +1873,33 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "codex-backend-client" +version = "0.137.0" +source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" +dependencies = [ + "anyhow", + "codex-api", + "codex-backend-openapi-models", + "codex-client", + "codex-login", + "codex-model-provider", + "codex-protocol", + "reqwest 0.12.28", + "serde", + "serde_json", +] + +[[package]] +name = "codex-backend-openapi-models" +version = "0.137.0" +source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" +dependencies = [ + "serde", + "serde_json", + "serde_with", +] + [[package]] name = "codex-client" version = "0.137.0" @@ -1836,7 +1953,7 @@ source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f74 dependencies = [ "anyhow", "async-trait", - "base64", + "base64 0.22.1", "codex-app-server-protocol", "codex-execpolicy", "codex-features", @@ -1864,7 +1981,7 @@ dependencies = [ "sha2 0.10.9", "thiserror 2.0.18", "tokio", - "toml", + "toml 0.9.12+spec-1.1.0", "toml_edit 0.24.1+spec-1.1.0", "tonic", "tonic-prost", @@ -1906,7 +2023,7 @@ dependencies = [ "arc-swap", "async-channel", "async-trait", - "base64", + "base64 0.22.1", "bm25", "chrono", "clap", @@ -1988,7 +2105,7 @@ dependencies = [ "tokio", "tokio-tungstenite 0.28.0", "tokio-util", - "toml", + "toml 0.9.12+spec-1.1.0", "toml_edit 0.24.1+spec-1.1.0", "tracing", "url", @@ -2029,7 +2146,7 @@ dependencies = [ "tempfile", "thiserror 2.0.18", "tokio", - "toml", + "toml 0.9.12+spec-1.1.0", "tracing", "url", "zip", @@ -2061,7 +2178,7 @@ dependencies = [ "serde_yaml", "shlex 1.3.0", "tokio", - "toml", + "toml 0.9.12+spec-1.1.0", "tracing", "zip", ] @@ -2074,7 +2191,7 @@ dependencies = [ "arc-swap", "async-trait", "axum", - "base64", + "base64 0.22.1", "bytes", "codex-api", "codex-app-server-protocol", @@ -2094,7 +2211,7 @@ dependencies = [ "tokio", "tokio-tungstenite 0.28.0", "tokio-util", - "toml", + "toml 0.9.12+spec-1.1.0", "tracing", "uuid", ] @@ -2145,7 +2262,7 @@ dependencies = [ "codex-protocol", "schemars 0.8.22", "serde", - "toml", + "toml 0.9.12+spec-1.1.0", "tracing", ] @@ -2279,7 +2396,7 @@ version = "0.137.0" source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "chrono", "codex-agent-identity", "codex-app-server-protocol", @@ -2363,6 +2480,27 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "codex-memories-extension" +version = "0.137.0" +source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" +dependencies = [ + "async-trait", + "codex-core", + "codex-extension-api", + "codex-features", + "codex-otel", + "codex-tools", + "codex-utils-absolute-path", + "codex-utils-output-truncation", + "codex-utils-template", + "schemars 0.8.22", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", +] + [[package]] name = "codex-memories-read" version = "0.137.0" @@ -2373,6 +2511,37 @@ dependencies = [ "codex-utils-absolute-path", ] +[[package]] +name = "codex-memories-write" +version = "0.137.0" +source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" +dependencies = [ + "anyhow", + "chrono", + "codex-backend-client", + "codex-config", + "codex-core", + "codex-features", + "codex-git-utils", + "codex-login", + "codex-otel", + "codex-protocol", + "codex-rollout", + "codex-rollout-trace", + "codex-secrets", + "codex-state", + "codex-terminal-detection", + "codex-utils-absolute-path", + "codex-utils-output-truncation", + "codex-utils-template", + "futures", + "serde", + "serde_json", + "tokio", + "tracing", + "uuid", +] + [[package]] name = "codex-model-provider" version = "0.137.0" @@ -2435,7 +2604,7 @@ source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f74 dependencies = [ "anyhow", "async-trait", - "base64", + "base64 0.22.1", "chrono", "clap", "codex-utils-absolute-path", @@ -2568,7 +2737,7 @@ name = "codex-response-debug-context" version = "0.137.0" source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" dependencies = [ - "base64", + "base64 0.22.1", "codex-api", "http 1.4.1", "serde_json", @@ -2581,7 +2750,7 @@ source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f74 dependencies = [ "anyhow", "axum", - "base64", + "base64 0.22.1", "bytes", "codex-api", "codex-client", @@ -2667,12 +2836,31 @@ dependencies = [ "which 8.0.3", ] +[[package]] +name = "codex-secrets" +version = "0.137.0" +source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" +dependencies = [ + "age", + "anyhow", + "base64 0.22.1", + "codex-git-utils", + "codex-keyring-store", + "rand 0.9.4", + "regex", + "schemars 0.8.22", + "serde", + "serde_json", + "sha2 0.10.9", + "tracing", +] + [[package]] name = "codex-shell-command" version = "0.137.0" source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" dependencies = [ - "base64", + "base64 0.22.1", "codex-protocol", "codex-utils-absolute-path", "once_cell", @@ -2828,7 +3016,7 @@ dependencies = [ "codex-protocol", "codex-shell-command", "serde", - "toml", + "toml 0.9.12+spec-1.1.0", ] [[package]] @@ -2845,7 +3033,7 @@ name = "codex-utils-image" version = "0.137.0" source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" dependencies = [ - "base64", + "base64 0.22.1", "codex-utils-cache", "image", "mime_guess", @@ -2859,7 +3047,7 @@ version = "0.137.0" source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" dependencies = [ "serde_json", - "toml", + "toml 0.9.12+spec-1.1.0", ] [[package]] @@ -2942,7 +3130,7 @@ version = "0.137.0" source = "git+https://github.com/openai/codex?tag=rust-v0.137.0#f221438b691b8f749d98f22077c93ebe01923fbe" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "chrono", "codex-otel", "codex-protocol", @@ -3073,6 +3261,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + [[package]] name = "cookie_store" version = "0.22.1" @@ -4115,6 +4312,15 @@ dependencies = [ "libc", ] +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml 0.5.11", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -4167,6 +4373,50 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash 1.1.0", + "self_cell 0.10.3", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eebbe59450baee8282d71676f3bfed5689aeab00b27545e83e5f14b1195e8b0" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +dependencies = [ + "thiserror 1.0.69", +] + [[package]] name = "flume" version = "0.12.0" @@ -5447,7 +5697,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "headers-core", "http 1.4.1", @@ -5751,7 +6001,7 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", @@ -5770,6 +6020,72 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "i18n-config" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef" +dependencies = [ + "basic-toml", + "log", + "serde", + "serde_derive", + "thiserror 1.0.69", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669ffc2c93f97e6ddf06ddbe999fcd6782e3342978bb85f7d3c087c7978404c4" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "log", + "parking_lot", + "rust-embed", + "thiserror 1.0.69", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04b2969d0b3fc6143776c535184c19722032b43e6a642d710fa3f88faec53c2d" +dependencies = [ + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "proc-macro-error2", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.117", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -6124,6 +6440,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "intl-memoizer" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + [[package]] name = "inventory" version = "0.3.24" @@ -6143,6 +6478,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + [[package]] name = "ipconfig" version = "0.3.4" @@ -6362,7 +6703,7 @@ version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64", + "base64 0.22.1", "js-sys", "pem", "ring", @@ -7070,7 +7411,7 @@ version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "getrandom 0.2.17", "http 1.4.1", @@ -7401,7 +7742,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ - "base64", + "base64 0.22.1", "const-hex", "opentelemetry", "opentelemetry_sdk", @@ -7548,7 +7889,7 @@ version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ - "base64", + "base64 0.22.1", "serde_core", ] @@ -7794,6 +8135,28 @@ dependencies = [ "toml_edit 0.25.12+spec-1.1.0", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -7922,7 +8285,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.1.2", "rustls", "socket2 0.6.4", "thiserror 2.0.18", @@ -7943,7 +8306,7 @@ dependencies = [ "lru-slab", "rand 0.9.4", "ring", - "rustc-hash", + "rustc-hash 2.1.2", "rustls", "rustls-pki-types", "slab", @@ -8059,7 +8422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453d60af031e23af2d48995e41b17023f6150044738680508b63671f8d7417dd" dependencies = [ "ahash", - "base64", + "base64 0.22.1", "bitflags 2.13.0", "chrono", "const_format", @@ -8139,7 +8502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d74fe0cd9bd4440827dc6dc0f504cf66065396532e798891dee2c1b740b2285" dependencies = [ "ahash", - "base64", + "base64 0.22.1", "chrono", "const_format", "httpdate", @@ -8345,7 +8708,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "chacha20", + "chacha20 0.10.0", "getrandom 0.4.2", "rand_core 0.10.1", ] @@ -8544,7 +8907,7 @@ version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "cookie", "cookie_store", @@ -8595,7 +8958,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-core", "futures-util", @@ -8676,7 +9039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", "chrono", "futures", @@ -8717,6 +9080,40 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "rust-embed" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.117", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +dependencies = [ + "sha2 0.10.9", + "walkdir", +] + [[package]] name = "rust-stemmers" version = "1.2.0" @@ -8733,6 +9130,12 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.2" @@ -9037,6 +9440,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2 0.10.9", +] + [[package]] name = "sec1" version = "0.7.3" @@ -9060,6 +9474,15 @@ dependencies = [ "libc", ] +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + [[package]] name = "secret-service" version = "4.0.0" @@ -9115,6 +9538,21 @@ dependencies = [ "libc", ] +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.2.2", +] + +[[package]] +name = "self_cell" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + [[package]] name = "semver" version = "1.0.28" @@ -9370,7 +9808,7 @@ version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ - "base64", + "base64 0.22.1", "bs58", "chrono", "hex", @@ -9666,7 +10104,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "cfg-if", "chrono", @@ -9774,7 +10212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags 2.13.0", "byteorder", "chrono", @@ -10500,6 +10938,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -10580,7 +11027,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", "http 1.4.1", "http-body 1.0.1", @@ -10848,6 +11295,15 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash 2.1.2", +] + [[package]] name = "typenum" version = "1.20.1" @@ -10880,6 +11336,25 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unic-langid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" +dependencies = [ + "serde", + "tinystr", +] + [[package]] name = "unicase" version = "2.9.0" @@ -10971,7 +11446,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ - "base64", + "base64 0.22.1", "der 0.8.0", "log", "native-tls", @@ -10988,7 +11463,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ - "base64", + "base64 0.22.1", "http 1.4.1", "httparse", "log", @@ -11888,6 +12363,18 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + [[package]] name = "x509-parser" version = "0.18.1" diff --git a/Cargo.toml b/Cargo.toml index c12e1d5..a6c997b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ codex-mcp-server = { git = "https://github.com/openai/codex", tag = "rust-v0.137 codex-models-manager = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } codex-protocol = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } codex-login = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } +codex-memories-extension = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } +codex-memories-write = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } codex-shell-command = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } codex-thread-store = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } codex-utils-approval-presets = { git = "https://github.com/openai/codex", tag = "rust-v0.137.0" } diff --git a/src/codex_agent.rs b/src/codex_agent.rs index b9cb0c0..f204749 100644 --- a/src/codex_agent.rs +++ b/src/codex_agent.rs @@ -15,11 +15,11 @@ use acp::{Agent, Client, ConnectTo, ConnectionTo, Error}; use agent_client_protocol as acp; use codex_config::{DEFAULT_MCP_SERVER_ENVIRONMENT_ID, McpServerConfig, McpServerTransportConfig}; use codex_core::{ - NewThread, RolloutRecorder, StateDbHandle, ThreadManager, config::Config, + CodexThread, NewThread, RolloutRecorder, StateDbHandle, ThreadManager, config::Config, find_thread_path_by_id_str, init_state_db, resolve_installation_id, thread_store_from_config, }; use codex_exec_server::{EnvironmentManager, ExecServerRuntimePaths}; -use codex_extension_api::empty_extension_registry; +use codex_extension_api::{ExtensionRegistry, ExtensionRegistryBuilder}; use codex_login::{ CODEX_API_KEY_ENV_VAR, OPENAI_API_KEY_ENV_VAR, auth::{AuthManager, CodexAuth, read_codex_api_key_from_env, read_openai_api_key_from_env}, @@ -54,13 +54,15 @@ pub struct CodexAgent { /// The underlying codex configuration config: Config, /// Thread manager for handling sessions - thread_manager: ThreadManager, + thread_manager: Arc, /// Store for listing and updating persisted thread metadata thread_store: Arc, /// SQLite-backed Codex state index, when initialization succeeds state_db: Option, /// Active sessions mapped by `SessionId` sessions: Arc>>>, + /// Memory startup handles for active Codex sessions. + memory_startup_sessions: Arc>>>, /// Session working directories for filesystem sandboxing session_roots: Arc>>, } @@ -68,6 +70,71 @@ pub struct CodexAgent { const SESSION_LIST_PAGE_SIZE: usize = 25; const SESSION_TITLE_MAX_GRAPHEMES: usize = 120; +trait MemoryStartupSession: Send + Sync { + fn start(&self, source: SessionSource); +} + +struct CodexMemoryStartupSession { + thread_manager: Arc, + auth_manager: Arc, + thread_id: ThreadId, + thread: Arc, +} + +impl MemoryStartupSession for CodexMemoryStartupSession { + fn start(&self, source: SessionSource) { + let thread_manager = self.thread_manager.clone(); + let auth_manager = self.auth_manager.clone(); + let thread_id = self.thread_id; + let thread = self.thread.clone(); + + tokio::spawn(async move { + let config = thread.config().await; + codex_memories_write::start_memories_startup_task( + thread_manager, + auth_manager, + thread_id, + thread, + config, + &source, + ); + }); + } +} + +fn acp_session_source() -> SessionSource { + SessionSource::Mcp +} + +fn listable_session_sources() -> Vec { + vec![ + SessionSource::Cli, + SessionSource::VSCode, + SessionSource::Mcp, + SessionSource::Unknown, + ] +} + +fn built_in_extension_registry() -> Arc> { + let mut registry = ExtensionRegistryBuilder::new(); + codex_memories_extension::install(&mut registry, None); + Arc::new(registry.build()) +} + +fn start_memory_startup_session( + sessions: &Mutex>>, + session_id: &SessionId, + source: SessionSource, +) -> bool { + let session = sessions.lock().unwrap().remove(session_id); + if let Some(session) = session { + session.start(source); + true + } else { + false + } +} + impl CodexAgent { /// Create a new `CodexAgent` with the given configuration pub async fn new( @@ -94,18 +161,18 @@ impl CodexAgent { ); let thread_store = thread_store_from_config(&config, state_db.clone()); let installation_id = resolve_installation_id(&config.codex_home).await?; - let thread_manager = ThreadManager::new( + let thread_manager = Arc::new(ThreadManager::new( &config, auth_manager.clone(), - SessionSource::Unknown, + acp_session_source(), environment_manager, - empty_extension_registry(), + built_in_extension_registry(), None, thread_store.clone(), state_db.clone(), installation_id, None, - ); + )); Ok(Self { auth_manager, client_capabilities, @@ -114,6 +181,7 @@ impl CodexAgent { thread_store, state_db, sessions: Arc::default(), + memory_startup_sessions: Arc::default(), session_roots, }) } @@ -320,6 +388,23 @@ impl CodexAgent { .clone()) } + fn register_memory_startup_session( + &self, + session_id: SessionId, + thread_id: ThreadId, + thread: Arc, + ) { + self.memory_startup_sessions.lock().unwrap().insert( + session_id, + Arc::new(CodexMemoryStartupSession { + thread_manager: self.thread_manager.clone(), + auth_manager: self.auth_manager.clone(), + thread_id, + thread, + }), + ); + } + async fn check_auth(&self) -> Result<(), Error> { if self.config.model_provider_id == "openai" && self.auth_manager.auth().await.is_none() @@ -569,13 +654,14 @@ impl CodexAgent { let NewThread { thread_id, - thread, + thread: codex_thread, session_configured: _, } = Box::pin(self.thread_manager.start_thread(config.clone())) .await .map_err(|_e| Error::internal_error())?; let session_id = Self::session_id_from_thread_id(thread_id); + let memory_thread = codex_thread.clone(); // Record the session root for filesystem sandboxing. self.session_roots .lock() @@ -583,7 +669,7 @@ impl CodexAgent { .insert(session_id.clone(), config.cwd.to_path_buf()); let thread = Arc::new(Thread::new( session_id.clone(), - thread, + codex_thread, self.auth_manager.clone(), Arc::new(self.thread_manager.get_models_manager()), self.client_capabilities.clone(), @@ -591,6 +677,7 @@ impl CodexAgent { cx, )); let load = thread.load().await?; + self.register_memory_startup_session(session_id.clone(), thread_id, memory_thread); self.sessions .lock() @@ -683,8 +770,8 @@ impl CodexAgent { let config = self.build_session_config(&cwd, mcp_servers)?; let NewThread { - thread_id: _, - thread, + thread_id, + thread: codex_thread, session_configured: _, } = Box::pin(self.thread_manager.resume_thread_from_rollout( config.clone(), @@ -697,19 +784,21 @@ impl CodexAgent { let thread = Arc::new(Thread::new( session_id.clone(), - thread, + codex_thread.clone(), self.auth_manager.clone(), Arc::new(self.thread_manager.get_models_manager()), self.client_capabilities.clone(), config.clone(), cx, )); + let memory_thread = codex_thread; if replay_history { thread.replay_history(rollout_items).await?; } let load = thread.load().await?; + self.register_memory_startup_session(session_id.clone(), thread_id, memory_thread); self.session_roots .lock() @@ -729,11 +818,7 @@ impl CodexAgent { self.check_auth().await?; let ListSessionsRequest { cwd, cursor, .. } = request; - let allowed_sources = [ - SessionSource::Cli, - SessionSource::VSCode, - SessionSource::Unknown, - ]; + let allowed_sources = listable_session_sources(); let cwd_filter = cwd.clone(); let page = self @@ -743,7 +828,7 @@ impl CodexAgent { cursor, sort_key: StoreThreadSortKey::UpdatedAt, sort_direction: StoreSortDirection::Desc, - allowed_sources: allowed_sources.to_vec(), + allowed_sources: allowed_sources.clone(), model_providers: None, cwd_filters: cwd.map(|cwd| vec![cwd]), archived: false, @@ -789,6 +874,10 @@ impl CodexAgent { ) .await; self.sessions.lock().unwrap().remove(&request.session_id); + self.memory_startup_sessions + .lock() + .unwrap() + .remove(&request.session_id); self.session_roots .lock() .unwrap() @@ -802,8 +891,17 @@ impl CodexAgent { self.check_auth().await?; // Get the session state - let thread = self.get_thread(&request.session_id)?; - let stop_reason = thread.prompt(request).await?; + let session_id = request.session_id.clone(); + let thread = self.get_thread(&session_id)?; + let stop_reason_rx = thread.start_prompt(request).await?; + start_memory_startup_session( + &self.memory_startup_sessions, + &session_id, + self.thread_manager.session_source(), + ); + let stop_reason = stop_reason_rx + .await + .map_err(|e| Error::internal_error().data(e.to_string()))??; Ok(PromptResponse::new(stop_reason)) } @@ -946,7 +1044,107 @@ fn stored_session_title(name: Option<&str>, preview: &str) -> Option { #[cfg(test)] mod tests { + use std::collections::HashMap; + use std::path::PathBuf; + use std::sync::Arc; + use std::sync::Mutex; + use super::*; + use codex_config::TomlValue; + use codex_extension_api::{ExtensionData, PromptSlot, ThreadStartInput}; + + #[test] + fn built_in_extension_registry_installs_memories_extension_contributors() { + let registry = built_in_extension_registry(); + + assert_eq!(registry.thread_lifecycle_contributors().len(), 1); + assert_eq!(registry.config_contributors().len(), 1); + assert_eq!(registry.context_contributors().len(), 1); + assert_eq!(registry.tool_contributors().len(), 1); + } + + #[test] + fn acp_sessions_use_mcp_source_and_remain_listable() { + assert_eq!(acp_session_source(), SessionSource::Mcp); + assert!(listable_session_sources().contains(&SessionSource::Mcp)); + } + + #[test] + fn start_memory_startup_session_uses_session_source_once_for_known_session() { + let session_id = SessionId::new("session"); + let calls = Arc::new(Mutex::new(Vec::new())); + let mut sessions = HashMap::new(); + sessions.insert( + session_id.clone(), + Arc::new(RecordingMemoryStartupSession { + calls: calls.clone(), + }) as Arc, + ); + + let sessions = Mutex::new(sessions); + + assert!(start_memory_startup_session( + &sessions, + &session_id, + acp_session_source(), + )); + assert!(!start_memory_startup_session( + &sessions, + &session_id, + acp_session_source(), + )); + assert_eq!(*calls.lock().unwrap(), vec![SessionSource::Mcp]); + } + + #[tokio::test] + async fn memory_summary_reaches_prompt_context_after_thread_start() -> anyhow::Result<()> { + let temp_home = TestCodexHome::new()?; + let memories_dir = temp_home.path.join("memories"); + std::fs::create_dir_all(&memories_dir)?; + std::fs::write( + memories_dir.join("memory_summary.md"), + "Remember ACP memory wiring reaches model context.", + )?; + + let config = Config::load_default_with_cli_overrides_for_codex_home( + temp_home.path.clone(), + vec![("features.memories".to_string(), TomlValue::Boolean(true))], + ) + .await?; + let registry = built_in_extension_registry(); + let session_store = ExtensionData::new("session"); + let thread_store = ExtensionData::new("thread"); + + for contributor in registry.thread_lifecycle_contributors() { + contributor + .on_thread_start(ThreadStartInput { + config: &config, + session_source: &acp_session_source(), + persistent_thread_state_available: true, + session_store: &session_store, + thread_store: &thread_store, + }) + .await; + } + + let mut fragments = Vec::new(); + for contributor in registry.context_contributors() { + fragments.extend(contributor.contribute(&session_store, &thread_store).await); + } + + assert!( + fragments.iter().any(|fragment| { + fragment.slot() == PromptSlot::DeveloperPolicy + && fragment + .text() + .contains("Remember ACP memory wiring reaches model context.") + && fragment.text().contains("memory_summary.md") + }), + "memory summary should be contributed to the developer prompt after thread start" + ); + + Ok(()) + } #[test] fn stored_session_title_prefers_thread_name() { @@ -967,4 +1165,34 @@ mod tests { Some("preview".to_string()) ); } + + struct RecordingMemoryStartupSession { + calls: Arc>>, + } + + impl MemoryStartupSession for RecordingMemoryStartupSession { + fn start(&self, source: SessionSource) { + self.calls.lock().unwrap().push(source); + } + } + + struct TestCodexHome { + path: PathBuf, + } + + impl TestCodexHome { + fn new() -> anyhow::Result { + let path = std::env::temp_dir() + .join("codex-acp-memory-e2e") + .join(uuid::Uuid::new_v4().to_string()); + std::fs::create_dir_all(&path)?; + Ok(Self { path }) + } + } + + impl Drop for TestCodexHome { + fn drop(&mut self) { + drop(std::fs::remove_dir_all(&self.path)); + } + } } diff --git a/src/thread.rs b/src/thread.rs index fd1f1c2..daf594f 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -370,7 +370,10 @@ impl Thread { .map_err(|e| Error::internal_error().data(e.to_string()))? } - pub async fn prompt(&self, request: PromptRequest) -> Result { + pub async fn start_prompt( + &self, + request: PromptRequest, + ) -> Result>, Error> { let (response_tx, response_rx) = oneshot::channel(); let message = ThreadMessage::Prompt { @@ -380,8 +383,6 @@ impl Thread { drop(self.message_tx.send(message)); response_rx - .await - .map_err(|e| Error::internal_error().data(e.to_string()))?? .await .map_err(|e| Error::internal_error().data(e.to_string()))? }