From c24b0fcc781abff06b9268f61eeefebb42a04272 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Mon, 1 Sep 2025 20:20:33 -0700 Subject: [PATCH] generalize to all docs --- codex-rs/Cargo.lock | 36 +++++++++++ codex-rs/core/Cargo.toml | 1 + codex-rs/core/src/codex.rs | 8 +-- codex-rs/core/src/config.rs | 1 - codex-rs/core/src/config_edit_tool.rs | 91 +++++++++++++++++++-------- codex-rs/core/src/lib.rs | 2 +- codex-rs/core/src/openai_tools.rs | 20 +++--- 7 files changed, 116 insertions(+), 43 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index c4678213cb..d7ce8f8d06 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -739,6 +739,7 @@ dependencies = [ "rand 0.9.2", "regex-lite", "reqwest", + "rust-embed", "seccompiler", "serde", "serde_bytes", @@ -4023,6 +4024,41 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "rust-embed" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.104", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" +dependencies = [ + "globset", + "sha2", + "walkdir", +] + [[package]] name = "rustc-demangle" version = "0.1.25" diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index ef93b1a585..d59448baba 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -59,6 +59,7 @@ tree-sitter-bash = "0.25.0" uuid = { version = "1", features = ["serde", "v4"] } whoami = "1.6.1" wildmatch = "2.4.0" +rust-embed = { version = "8.5.0", features = ["include-exclude"] } [target.'cfg(target_os = "linux")'.dependencies] diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 68360b235d..a34bc7608e 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -41,6 +41,9 @@ use crate::client::ModelClient; use crate::client_common::Prompt; use crate::client_common::ResponseEvent; use crate::config::Config; +use crate::config_edit_tool::handle_get_config; +use crate::config_edit_tool::handle_set_config; +use crate::config_edit_tool::handle_show_codex_docs; use crate::config_types::ShellEnvironmentPolicy; use crate::conversation_history::ConversationHistory; use crate::environment_context::EnvironmentContext; @@ -70,9 +73,6 @@ use crate::openai_tools::ToolsConfigParams; use crate::openai_tools::get_openai_tools; use crate::parse_command::parse_command; use crate::plan_tool::handle_update_plan; -use crate::config_edit_tool::handle_get_config; -use crate::config_edit_tool::handle_set_config; -use crate::config_edit_tool::handle_show_config_docs; use crate::project_doc::get_user_instructions; use crate::protocol::AgentMessageDeltaEvent; use crate::protocol::AgentMessageEvent; @@ -2193,7 +2193,7 @@ async fn handle_function_call( "update_plan" => handle_update_plan(sess, arguments, sub_id, call_id).await, "get_config" => handle_get_config(sess, arguments, sub_id, call_id).await, "set_config" => handle_set_config(sess, arguments, sub_id, call_id).await, - "show_config_docs" => handle_show_config_docs(sess, arguments, sub_id, call_id).await, + "show_codex_docs" => handle_show_codex_docs(sess, arguments, sub_id, call_id).await, EXEC_COMMAND_TOOL_NAME => { // TODO(mbolin): Sandbox check. let exec_params = match serde_json::from_str::(&arguments) { diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index a20c1b2eef..7845f5d4ce 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -171,7 +171,6 @@ pub struct Config { pub tools_web_search_request: bool, - /// The value for the `originator` header included with Responses API requests. pub responses_originator_header: String, diff --git a/codex-rs/core/src/config_edit_tool.rs b/codex-rs/core/src/config_edit_tool.rs index ad155ef305..9154c85340 100644 --- a/codex-rs/core/src/config_edit_tool.rs +++ b/codex-rs/core/src/config_edit_tool.rs @@ -3,21 +3,55 @@ use crate::config::find_codex_home; use crate::openai_tools::JsonSchema; use crate::openai_tools::OpenAiTool; use crate::openai_tools::ResponsesApiTool; +use crate::protocol::ReviewDecision; +use codex_apply_patch::ApplyPatchAction; +use codex_apply_patch::MaybeApplyPatchVerified; +use codex_apply_patch::maybe_parse_apply_patch_verified; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseInputItem; +use rust_embed::RustEmbed; use serde::Deserialize; use std::collections::BTreeMap; use std::fs; -use std::path::{Path, PathBuf}; -use crate::protocol::ReviewDecision; -use codex_apply_patch::maybe_parse_apply_patch_verified; -use codex_apply_patch::MaybeApplyPatchVerified; -use codex_apply_patch::ApplyPatchAction; +use std::path::Path; +use std::path::PathBuf; const CONFIG_TOML_FILE: &str = "config.toml"; // Embed docs at compile time. -const CONFIG_DOCS_MD: &str = include_str!("../../../docs/config.md"); +// README from the repo root +const README_MD: &str = include_str!("../../../README.md"); +// Entire docs directory from the repo root (only *.md files are embedded) +#[derive(RustEmbed)] +#[folder = "../../docs"] +#[include = "**/*.md"] +struct EmbeddedDocs; + +fn push_separator(buf: &mut String) { + buf.push_str("\n\n---\n\n"); +} + +fn build_all_codex_docs() -> String { + let mut out = String::new(); + out.push_str("# Codex Documentation\n\n"); + out.push_str("\n\n"); + out.push_str(README_MD); + + // Add markdown files from ../docs recursively (embedded at compile time) + let mut paths: Vec = EmbeddedDocs::iter() + .map(|p| p.as_ref().to_string()) + .collect(); + paths.sort(); + for path in paths.into_iter() { + if let Some(file) = EmbeddedDocs::get(&path) { + push_separator(&mut out); + out.push_str(&format!("\n\n", path)); + out.push_str(&String::from_utf8_lossy(&file.data)); + } + } + + out +} /// get_config() — fetches the current config.toml. pub(crate) fn create_get_config_tool() -> OpenAiTool { @@ -59,11 +93,11 @@ pub(crate) fn create_set_config_tool() -> OpenAiTool { }) } -/// show_config_docs() — returns the configuration documentation. -pub(crate) fn create_show_config_docs_tool() -> OpenAiTool { +/// show_codex_docs() — returns Codex documentation. +pub(crate) fn create_show_codex_docs_tool() -> OpenAiTool { OpenAiTool::Function(ResponsesApiTool { - name: "show_config_docs".to_string(), - description: "Returns the configuration documentation. If you need to know how to configure something in Codex or what configuration options are available, call this tool and use it to inform your answer.".to_string(), + name: "show_codex_docs".to_string(), + description: "Returns Codex documentation, including the repo README and all user docs under docs/. Use this when you need information about configuration, setup, features, or usage.".to_string(), strict: false, parameters: JsonSchema::Object { properties: BTreeMap::new(), @@ -122,7 +156,7 @@ pub(crate) async fn handle_set_config( content: format!("failed to parse function arguments: {e}"), success: None, }, - } + }; } }; // Validate TOML and ensure it can be materialized into a runtime Config. @@ -195,22 +229,24 @@ pub(crate) async fn handle_set_config( if v.last().is_some_and(|l| l.is_empty()) { v.pop(); } - v.into_iter().map(|l| l.to_string()).collect::>() + v.into_iter() + .map(|l| l.to_string()) + .collect::>() }; let patch_body = if let Some(curr) = ¤t { let mut body = format!("*** Update File: {}\n@@\n", path.display()); for line in make_lines(curr) { - body.push_str(&format!("-{}\n", line)); + body.push_str(&format!("-{line}\n")); } for line in make_lines(&args.new_config) { - body.push_str(&format!("+{}\n", line)); + body.push_str(&format!("+{line}\n")); } body } else { let mut body = format!("*** Add File: {}\n", path.display()); for line in make_lines(&args.new_config) { - body.push_str(&format!("+{}\n", line)); + body.push_str(&format!("+{line}\n")); } body }; @@ -265,16 +301,16 @@ pub(crate) async fn handle_set_config( }; } } - if let Some(parent) = path.parent() { - if let Err(e) = fs::create_dir_all(parent) { - return ResponseInputItem::FunctionCallOutput { - call_id, - output: FunctionCallOutputPayload { - content: format!("failed to create config directory: {e}"), - success: Some(false), - }, - }; - } + if let Some(parent) = path.parent() + && let Err(e) = fs::create_dir_all(parent) + { + return ResponseInputItem::FunctionCallOutput { + call_id, + output: FunctionCallOutputPayload { + content: format!("failed to create config directory: {e}"), + success: Some(false), + }, + }; } match fs::write(&path, args.new_config.as_bytes()) { Ok(_) => ResponseInputItem::FunctionCallOutput { @@ -294,16 +330,17 @@ pub(crate) async fn handle_set_config( } } -pub(crate) async fn handle_show_config_docs( +pub(crate) async fn handle_show_codex_docs( _session: &Session, _arguments: String, _sub_id: String, call_id: String, ) -> ResponseInputItem { + let content = build_all_codex_docs(); ResponseInputItem::FunctionCallOutput { call_id, output: FunctionCallOutputPayload { - content: CONFIG_DOCS_MD.to_string(), + content, success: Some(true), }, } diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index f30e9dc304..9563ddc463 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -40,11 +40,11 @@ pub use model_provider_info::create_oss_provider_with_base_url; mod conversation_manager; pub use conversation_manager::ConversationManager; pub use conversation_manager::NewConversation; +mod config_edit_tool; pub mod model_family; mod openai_model_info; mod openai_tools; pub mod plan_tool; -mod config_edit_tool; pub mod project_doc; mod rollout; pub(crate) mod safety; diff --git a/codex-rs/core/src/openai_tools.rs b/codex-rs/core/src/openai_tools.rs index 5c1cca0f7c..75633414f5 100644 --- a/codex-rs/core/src/openai_tools.rs +++ b/codex-rs/core/src/openai_tools.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use crate::config_edit_tool::create_get_config_tool; use crate::config_edit_tool::create_set_config_tool; -use crate::config_edit_tool::create_show_config_docs_tool; +use crate::config_edit_tool::create_show_codex_docs_tool; use crate::model_family::ModelFamily; use crate::plan_tool::PLAN_TOOL; use crate::protocol::AskForApproval; @@ -576,7 +576,7 @@ pub(crate) fn get_openai_tools( // Always include internal config tools. tools.push(create_get_config_tool()); tools.push(create_set_config_tool()); - tools.push(create_show_config_docs_tool()); + tools.push(create_show_codex_docs_tool()); // Include the view_image tool so the agent can attach images to context. if config.include_view_image_tool { @@ -658,7 +658,7 @@ mod tests { "web_search", "get_config", "set_config", - "show_config_docs", + "show_codex_docs", "view_image", ], ); @@ -687,7 +687,7 @@ mod tests { "web_search", "get_config", "set_config", - "show_config_docs", + "show_codex_docs", "view_image", ], ); @@ -751,7 +751,7 @@ mod tests { "web_search", "get_config", "set_config", - "show_config_docs", + "show_codex_docs", "view_image", "test_server/do_something_cool", ], @@ -872,7 +872,7 @@ mod tests { "shell", "get_config", "set_config", - "show_config_docs", + "show_codex_docs", "view_image", "test_server/cool", "test_server/do", @@ -925,7 +925,7 @@ mod tests { "web_search", "get_config", "set_config", - "show_config_docs", + "show_codex_docs", "dash/search", ], ); @@ -993,7 +993,7 @@ mod tests { "get_config", "set_config", "view_image", - "show_config_docs", + "show_codex_docs", "dash/paginate", ], ); @@ -1057,7 +1057,7 @@ mod tests { "web_search", "get_config", "set_config", - "show_config_docs", + "show_codex_docs", "dash/tags", ], ); @@ -1125,7 +1125,7 @@ mod tests { "web_search", "get_config", "set_config", - "show_config_docs", + "show_codex_docs", "dash/value", ], );