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