diff --git a/INSTRUCTIONS.md b/AGENTS.md similarity index 100% rename from INSTRUCTIONS.md rename to AGENTS.md diff --git a/Cargo.lock b/Cargo.lock index adefac3d..23a6beec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,7 +223,7 @@ dependencies = [ "libloading", "linkme", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "rustc_version_runtime", "serde", "serde_json", @@ -620,18 +620,18 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5" dependencies = [ "proc-macro2", "quote", @@ -652,9 +652,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "candle-core" @@ -670,7 +670,7 @@ dependencies = [ "memmap2", "num-traits", "num_cpus", - "rand 0.9.4", + "rand 0.9.5", "rand_distr", "rayon", "safetensors", @@ -707,7 +707,7 @@ dependencies = [ "candle-nn", "fancy-regex 0.17.0", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rayon", "serde", "serde_json", @@ -726,9 +726,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.66" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "jobserver", @@ -1103,18 +1103,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1122,18 +1122,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ "crossbeam-utils", ] @@ -1150,9 +1150,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crossterm" @@ -1746,7 +1746,7 @@ checksum = "c2d1f04709a8ac06e8e8042875a3c466cc4832d3c1a18dbcb9dba3c6e83046bc" dependencies = [ "half", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_distr", ] @@ -2205,7 +2205,7 @@ dependencies = [ "cfg-if", "crunchy", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_distr", "zerocopy", ] @@ -2265,7 +2265,7 @@ dependencies = [ "libc", "log", "num_cpus", - "rand 0.9.4", + "rand 0.9.5", "reqwest 0.12.28", "serde", "serde_json", @@ -2551,9 +2551,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe112b004901c62c2faa11f4f75e9864e0cc5af8da71c9115d184a3aa888749f" +checksum = "2adf14691c72bcfc1058740436a35bdd3ae9c07d1a941ef00b749e9ea16aefa7" dependencies = [ "crossbeam-deque", "globset", @@ -3069,9 +3069,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" @@ -3280,11 +3280,10 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -3336,7 +3335,7 @@ dependencies = [ "chrono", "getrandom 0.2.17", "http", - "rand 0.8.6", + "rand 0.8.7", "reqwest 0.12.28", "serde", "serde_json", @@ -3421,9 +3420,7 @@ dependencies = [ [[package]] name = "octolib" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f77ac538dccc51e777fa9a56c3f4960088958c6b1d925112c4e0f67cbff892" +version = "0.25.0" dependencies = [ "anyhow", "arc-swap", @@ -3945,9 +3942,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -3956,9 +3953,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4026,7 +4023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", - "rand 0.9.4", + "rand 0.9.5", ] [[package]] @@ -4182,9 +4179,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.4" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" dependencies = [ "aho-corasick", "memchr", @@ -4194,9 +4191,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" dependencies = [ "aho-corasick", "memchr", @@ -4476,9 +4473,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -4753,9 +4750,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -4829,7 +4826,7 @@ dependencies = [ "generator", "hex", "owo-colors", - "rand 0.8.6", + "rand 0.8.7", "rand_core 0.6.4", "rand_pcg 0.3.1", "scoped-tls", @@ -5245,9 +5242,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -5338,9 +5335,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -5371,7 +5368,7 @@ dependencies = [ "monostate", "onig", "paste", - "rand 0.9.4", + "rand 0.9.5", "rayon", "rayon-cond", "regex", @@ -5629,7 +5626,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.4", + "rand 0.9.5", "sha1", "thiserror 2.0.18", ] @@ -5687,7 +5684,7 @@ dependencies = [ "parking_lot", "pastey", "polling", - "rand 0.9.4", + "rand 0.9.5", "rapidhash", "regex", "regex-syntax", @@ -5825,7 +5822,7 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" dependencies = [ - "rand 0.9.4", + "rand 0.9.5", ] [[package]] @@ -6525,18 +6522,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 5a10a1c9..8eb3ce0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ overflow-checks = false # Disable overflow checks in release mode [dependencies] # octolib = { path = "../octolib", default-features = false, features = ["huggingface"] } -octolib = { version = "0.24.0", default-features = false, features = ["huggingface"] } +octolib = { version = "0.25.0", default-features = false, features = ["huggingface"] } tokenizers = { version = "0.22.2", default-features = false } # model's own tokenizer for exact token counts rmcp = { version = "1.7.0", default-features = false } diff --git a/config-templates/default.toml b/config-templates/default.toml index a8ecc130..abec2663 100644 --- a/config-templates/default.toml +++ b/config-templates/default.toml @@ -32,16 +32,6 @@ default = "assistant:concierge" # Default max_tokens for all operations max_tokens = 16384 -# Custom instructions file name (relative to project root) -# This file will be automatically loaded as a user message in new sessions -# Set to empty string to disable: custom_instructions_file_name = "" -custom_instructions_file_name = "INSTRUCTIONS.md" - -# Custom constraints file name (relative to project root) -# This file content will be automatically appended to EACH user request in ... tags -# Set to empty string to disable: custom_constraints_file_name = "" -custom_constraints_file_name = "CONSTRAINTS.md" - # Sandbox mode: restrict all filesystem writes to the current working directory # When enabled, the process and all child processes (shell, MCP servers) cannot # write outside the directory where octomind was launched. diff --git a/doc/reference/03-config-reference.md b/doc/reference/03-config-reference.md index fbca396c..f48b5534 100644 --- a/doc/reference/03-config-reference.md +++ b/doc/reference/03-config-reference.md @@ -13,8 +13,6 @@ All values shown match `config-templates/default.toml`. Fields marked **(require | `model` | string | `"openrouter:anthropic/claude-sonnet-4"` | Default model in `provider:model` format | | `default` | string | `"assistant:concierge"` | Default tag when no TAG passed to `octomind run`. See note below. | | `max_tokens` | u32 | `16384` | Global max tokens for all operations | -| `custom_instructions_file_name` | string | `"INSTRUCTIONS.md"` | File auto-loaded as user message in new sessions. Empty string to disable. | -| `custom_constraints_file_name` | string | `"CONSTRAINTS.md"` | File appended to each request in `` tags. Empty string to disable. | | `sandbox` | bool | `false` | Restrict filesystem writes to working directory. Also available as `--sandbox` CLI flag. | | `auto_capabilities` | bool | `true` | Enable automatic capability activation on user messages. Disable to require manual `capability(action="enable")` calls. | | `system` | string (optional) | _none_ | Legacy global system-prompt override. When set, it applies as a fallback system prompt for roles that define none. Shown commented-out in `default.toml`; prefer per-role `system` instead. | diff --git a/doc/usage/03-configuration.md b/doc/usage/03-configuration.md index ea91f3ac..fd76c400 100644 --- a/doc/usage/03-configuration.md +++ b/doc/usage/03-configuration.md @@ -91,20 +91,9 @@ The default tag `assistant:concierge` is a **tap agent** (`category:variant`) pr `reasoning_effort` is a system-wide hint mapped by each provider to its native thinking knob (effort string, budget tokens, etc.); models without thinking support silently ignore it. You can also change it per-session at runtime with the `/effort` command, which persists the choice in the session file. -## Custom Instructions +## Project Instructions -Octomind auto-loads two project-level files: - -| File | Default | Behavior | -|------|---------|----------| -| `INSTRUCTIONS.md` | `custom_instructions_file_name` | Loaded as user message in new sessions | -| `CONSTRAINTS.md` | `custom_constraints_file_name` | Appended to each request in `` tags | - -Set to empty string to disable: -```toml -custom_instructions_file_name = "" -custom_constraints_file_name = "" -``` +Octomind auto-loads `AGENTS.md` from the project root as a user message in new sessions, following the [AGENTS.md standard](https://agents.md). No configuration needed — the file is loaded whenever it exists. ## Performance Settings diff --git a/doc/usage/05-sessions.md b/doc/usage/05-sessions.md index e2f9dcec..e6ac6dd9 100644 --- a/doc/usage/05-sessions.md +++ b/doc/usage/05-sessions.md @@ -163,18 +163,9 @@ An unrecognized filter silently falls back to showing all messages. Automatic compression also runs as sessions grow. See [Compression](08-compression.md). -## Custom Instructions +## Project Instructions -Octomind auto-loads project files into sessions: - -- **`INSTRUCTIONS.md`** -- loaded as a user message at session start -- **`CONSTRAINTS.md`** -- appended to every user request in `` tags - -Configure in `config.toml`: -```toml -custom_instructions_file_name = "INSTRUCTIONS.md" -custom_constraints_file_name = "CONSTRAINTS.md" -``` +Octomind auto-loads `AGENTS.md` from the project root as a user message at session start, following the [AGENTS.md standard](https://agents.md). The file is loaded whenever it exists — no configuration needed. ## Session Storage diff --git a/src/acp/agent.rs b/src/acp/agent.rs index 028a8a00..6eb5ad71 100644 --- a/src/acp/agent.rs +++ b/src/acp/agent.rs @@ -882,7 +882,6 @@ impl OctomindAgent { crate::mcp::set_session_working_directory(session_cwd.clone()); let config_for_role = self.config.borrow().get_merged_config_for_role(&self.role); - let current_dir = session_cwd.clone(); // Get or create cancellation for this session let mut cancellation = self @@ -1036,12 +1035,7 @@ impl OctomindAgent { } // Add user message - let final_input = crate::session::chat::session::utils::append_constraints_if_exists( - &processed_input, - &config_for_role.custom_constraints_file_name, - ¤t_dir, - ); - if let Err(e) = chat_session.add_user_message(&final_input) { + if let Err(e) = chat_session.add_user_message(&processed_input) { self.sessions .borrow_mut() .insert(session_id.clone(), (chat_session, session_cwd)); diff --git a/src/config/mod.rs b/src/config/mod.rs index db3c6cc5..0e6401da 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -272,12 +272,6 @@ pub struct Config { // Root-level max_tokens setting (used by all commands if specified) pub max_tokens: u32, - // Custom instructions file name (relative to project root) - pub custom_instructions_file_name: String, - - // Custom constraints file name (relative to project root) - pub custom_constraints_file_name: String, - // System-wide configuration settings (not role-specific) pub mcp_response_tokens_threshold: usize, pub max_session_tokens_threshold: usize, diff --git a/src/embeddings/mod.rs b/src/embeddings/mod.rs index 332124d4..70ddc334 100644 --- a/src/embeddings/mod.rs +++ b/src/embeddings/mod.rs @@ -413,7 +413,7 @@ pub async fn embed(text: &str) -> Result> { return Ok(v.clone()); } let p = provider().await?; - let v = p.generate_embedding(text).await?; + let (v, _usage) = p.generate_embedding(text).await?; cache().write().unwrap().insert(key, v.clone()); Ok(v) } @@ -461,7 +461,7 @@ pub async fn embed_many(texts: &[String]) -> Result>> { let p = provider().await?; // MiniLM-L6 is symmetric — embed bare, no query/document prefix. The // query side (`embed`) is already prefix-free; keep both consistent. - let computed = p + let (computed, _usage) = p .generate_embeddings_batch(unique.clone(), InputType::None) .await?; { diff --git a/src/session/chat/session/main_loop.rs b/src/session/chat/session/main_loop.rs index 26b3398e..e2ab27fb 100644 --- a/src/session/chat/session/main_loop.rs +++ b/src/session/chat/session/main_loop.rs @@ -1159,13 +1159,7 @@ pub async fn run_interactive_session( let user_message_index = chat_session.session.messages.len(); // Add user message for standard processing flow. - // Append constraints if configured. - let final_input_with_constraints = super::utils::append_constraints_if_exists( - &final_input, - ¤t_config.custom_constraints_file_name, - ¤t_dir, - ); - chat_session.add_user_message(&final_input_with_constraints)?; + chat_session.add_user_message(&final_input)?; // New user message → run per-message lesson recall on the next API call. chat_session.pending_recall = true; @@ -1562,14 +1556,8 @@ pub async fn run_interactive_session_with_input( input = processed_input; // Add user message - same as interactive. - // Append constraints if configured. let user_message_index = chat_session.session.messages.len(); - let input_with_constraints = super::utils::append_constraints_if_exists( - &input, - ¤t_config.custom_constraints_file_name, - ¤t_dir, - ); - chat_session.add_user_message(&input_with_constraints)?; + chat_session.add_user_message(&input)?; // New user message → run per-message lesson recall on the next API call. chat_session.pending_recall = true; diff --git a/src/session/chat/session/prompt_setup.rs b/src/session/chat/session/prompt_setup.rs index a2458dd4..9371e565 100644 --- a/src/session/chat/session/prompt_setup.rs +++ b/src/session/chat/session/prompt_setup.rs @@ -129,50 +129,52 @@ pub async fn setup_system_prompt_and_cache( ); } - // Check for custom instructions file - let instructions_filename = &config_for_role.custom_instructions_file_name; - if !instructions_filename.is_empty() { - let instructions_path = current_dir.join(instructions_filename); - if instructions_path.exists() { - match std::fs::read_to_string(&instructions_path) { - Ok(instructions_content) => { - if instructions_content.trim().is_empty() { - log_debug!( - "Skipping empty instructions file {}", - instructions_filename - ); - } else { - let processed_instructions = process_placeholders_async_with_role( - &instructions_content, - ¤t_dir, - Some(role), - ) - .await; - - let wrapped = format!( - "\n{}\n", - processed_instructions - ); - chat_session.add_system_managed_user_message(&wrapped)?; - - if supports_caching { - let cache_manager = CacheManager::new(); - cache_manager.add_automatic_cache_markers( - &mut chat_session.session.messages, - has_tools, - supports_caching, - ); - } - - log_info!( - "Added {} content as user message with variable processing", - instructions_filename + // Check for AGENTS.md instructions file + let instructions_path = + current_dir.join(crate::session::chat::session::utils::AGENTS_FILE); + if instructions_path.exists() { + match std::fs::read_to_string(&instructions_path) { + Ok(instructions_content) => { + if instructions_content.trim().is_empty() { + log_debug!( + "Skipping empty instructions file {}", + crate::session::chat::session::utils::AGENTS_FILE + ); + } else { + let processed_instructions = process_placeholders_async_with_role( + &instructions_content, + ¤t_dir, + Some(role), + ) + .await; + + let wrapped = format!( + "\n{}\n", + processed_instructions + ); + chat_session.add_system_managed_user_message(&wrapped)?; + + if supports_caching { + let cache_manager = CacheManager::new(); + cache_manager.add_automatic_cache_markers( + &mut chat_session.session.messages, + has_tools, + supports_caching, ); } + + log_info!( + "Added {} content as user message with variable processing", + crate::session::chat::session::utils::AGENTS_FILE + ); } - Err(e) => { - log_debug!("Failed to read {}: {}", instructions_filename, e); - } + } + Err(e) => { + log_debug!( + "Failed to read {}: {}", + crate::session::chat::session::utils::AGENTS_FILE, + e + ); } } } diff --git a/src/session/chat/session/utils.rs b/src/session/chat/session/utils.rs index c052bfb0..fcd46060 100644 --- a/src/session/chat/session/utils.rs +++ b/src/session/chat/session/utils.rs @@ -18,6 +18,9 @@ use crate::config::Config; use crate::{log_debug, log_info}; use anyhow::Result; +/// Standard project instructions file, auto-loaded at session start when present +pub const AGENTS_FILE: &str = "AGENTS.md"; + // Utility function to format numbers in a human-readable format pub fn format_number(number: u64) -> String { if number == 0 { @@ -89,100 +92,49 @@ pub async fn get_initial_messages( }; initial_messages.push(welcome_msg); - // 2. Generate instructions message if file exists (user role) - let instructions_filename = &config.custom_instructions_file_name; - if !instructions_filename.is_empty() { - let instructions_path = current_dir.join(instructions_filename); - if instructions_path.exists() { - if let Ok(instructions_content) = std::fs::read_to_string(&instructions_path) { - if instructions_content.trim().is_empty() { - log_debug!("Skipping empty instructions file {}", instructions_filename); - } else { - let processed_instructions = - crate::session::helper_functions::process_placeholders_async_with_role( - &instructions_content, - current_dir, - Some(role), - ) - .await; - - let instructions_msg = crate::session::Message { - role: "user".to_string(), - // Wrap in so task/compression/learning logic treats - // this as system-managed (is_real_user_task_message) and never - // mistakes the project instructions for a genuine user task. Mirrors - // the non-interactive path in prompt_setup.rs. - content: format!( - "\n{}\n", - processed_instructions - ), - timestamp: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_secs(), - cached: false, - ..Default::default() - }; - initial_messages.push(instructions_msg); - - log_info!( - "Added {} content as user message with variable processing", - instructions_filename - ); - } + // 2. Generate instructions message if AGENTS.md exists (user role) + let instructions_path = current_dir.join(AGENTS_FILE); + if instructions_path.exists() { + if let Ok(instructions_content) = std::fs::read_to_string(&instructions_path) { + if instructions_content.trim().is_empty() { + log_debug!("Skipping empty instructions file {}", AGENTS_FILE); } else { - log_debug!("Failed to read {}", instructions_filename); + let processed_instructions = + crate::session::helper_functions::process_placeholders_async_with_role( + &instructions_content, + current_dir, + Some(role), + ) + .await; + + let instructions_msg = crate::session::Message { + role: "user".to_string(), + // Wrap in so task/compression/learning logic treats + // this as system-managed (is_real_user_task_message) and never + // mistakes the project instructions for a genuine user task. Mirrors + // the non-interactive path in prompt_setup.rs. + content: format!( + "\n{}\n", + processed_instructions + ), + timestamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(), + cached: false, + ..Default::default() + }; + initial_messages.push(instructions_msg); + + log_info!( + "Added {} content as user message with variable processing", + AGENTS_FILE + ); } + } else { + log_debug!("Failed to read {}", AGENTS_FILE); } } Ok(initial_messages) } - -/// Append constraints from file to user input if file exists -/// Returns the input with constraints appended in ... tags -/// If file doesn't exist or is empty, returns input unchanged -pub fn append_constraints_if_exists( - input: &str, - constraints_filename: &str, - current_dir: &std::path::Path, -) -> String { - // If constraints filename is empty, return input unchanged - if constraints_filename.trim().is_empty() { - return input.to_string(); - } - - // Build path to constraints file - let constraints_path = current_dir.join(constraints_filename); - - // If file doesn't exist, return input unchanged - if !constraints_path.exists() { - return input.to_string(); - } - - // Try to read constraints file - match std::fs::read_to_string(&constraints_path) { - Ok(constraints_content) => { - let trimmed_constraints = constraints_content.trim(); - // If file is empty, return input unchanged - if trimmed_constraints.is_empty() { - return input.to_string(); - } - - // Append constraints in XML tags - format!( - "{}\n\n\n{}\n", - input.trim_end(), - trimmed_constraints - ) - } - Err(e) => { - log_debug!( - "Failed to read constraints file {}: {}", - constraints_filename, - e - ); - input.to_string() - } - } -} diff --git a/src/websocket/server.rs b/src/websocket/server.rs index 91d9da1c..768f148c 100644 --- a/src/websocket/server.rs +++ b/src/websocket/server.rs @@ -967,8 +967,6 @@ async fn handle_user_message( let session_id = session_id.to_string(); - // Get current directory for file operations - let current_dir = crate::mcp::get_thread_working_directory(); let config_for_role = config.get_merged_config_for_role(role); let mut cancellation = SessionCancellation::new(); // The main-call `operation_rx` is created AFTER the pre-user inbox drain @@ -1101,13 +1099,7 @@ async fn handle_user_message( }; // Add user message - let final_input_with_constraints = - crate::session::chat::session::utils::append_constraints_if_exists( - &processed_input, - &config_for_role.custom_constraints_file_name, - ¤t_dir, - ); - if let Err(e) = chat_session.add_user_message(&final_input_with_constraints) { + if let Err(e) = chat_session.add_user_message(&processed_input) { sessions .lock() .await