diff --git a/core/continuum-core/src/cognition/llm_deliberation_faculty.rs b/core/continuum-core/src/cognition/llm_deliberation_faculty.rs index 80a5326b0..d13207a34 100644 --- a/core/continuum-core/src/cognition/llm_deliberation_faculty.rs +++ b/core/continuum-core/src/cognition/llm_deliberation_faculty.rs @@ -3858,12 +3858,26 @@ mod tests { // draft cost 238 tokens; trimming the DESCRIPTION to its contract (full // accumulated CSS, replace-wholesale layer, delta meaning) recovered 47. // What is left is the irreducible cost of one discoverable verb. - const AGENTIC_SURFACE_CEILING: u32 = 8650; + // + // 8650 → 9400, stated plainly (2026-08-25): two NATIVE web verbs — + // `web/search` + `web/fetch` (+727 tokens even after trimming both + // DESCRIPTIONs to one line each; the rest is their irreducible param + // schemas). Web FORAGING is a deliberate capability-parity add (Joel: "make + // sure we have it" — the operator uses web lookup constantly on SWE/task + // work, and a native-call model like Ornith can ONLY emit calls for tools in + // its offered specs, so catalog-only web was unreachable to her hands). The + // surface already exceeded the 8192 tight-test window before this (8623 > + // 8192) — the budget trims VOLATILE context (recall/RAG) to fit and reserves + // specs up front, so this raises the reserved floor, it does not introduce a + // new overflow. Ornith serves at 166k where this ceiling is irrelevant; the + // tight-window LCD persona (which does not do web research) pays with less + // recall room, a conscious trade. If a third addition wants in, SHRINK first. + const AGENTIC_SURFACE_CEILING: u32 = 9400; let surface = faculty.describe_tool_tokens() as u32 + faculty.framing_floor_tokens(); assert!( surface <= AGENTIC_SURFACE_CEILING, - "the agentic surface is now {surface} tokens (measured 8623, ceiling \ + "the agentic surface is now {surface} tokens (measured 9350, ceiling \ {AGENTIC_SURFACE_CEILING}) — framing/tools grew. Shrink the surface (#333) \ or state plainly what was added and re-pin the ceiling" ); diff --git a/core/continuum-core/src/commands/web/fetch.rs b/core/continuum-core/src/commands/web/fetch.rs index 7d43f42ec..d3f4faa19 100644 --- a/core/continuum-core/src/commands/web/fetch.rs +++ b/core/continuum-core/src/commands/web/fetch.rs @@ -3,13 +3,12 @@ use super::{web_fetch, WebFetchParams, WebFetchResult}; crate::action_command! { - /// Fetch a URL and READ its text — a doc page, API reference, article, or a result - /// `web/search` returned. Strips scripts/markup and returns clean, capped text you can - /// quote or reason over. This is how you actually read what you find online instead of - /// guessing from a snippet. Pair it with `web/search`: search to find the page, fetch to read it. + /// Read a web page's text (a doc, API reference, or a web/search result). Strips + /// markup, returns clean capped text; `filter` greps it to just what you need. pub struct WebFetch; name: "web/fetch", access: AiSafe, + native: true, // her hands must be able to REACH the web (a native-call model can only emit calls in its offered specs); I forage constantly, so must she — direct SWE/task score lever params: WebFetchParams, output: WebFetchResult, run(_this, _ctx, p) => { web_fetch(p).await } diff --git a/core/continuum-core/src/commands/web/search.rs b/core/continuum-core/src/commands/web/search.rs index beebac23c..6ba174f41 100644 --- a/core/continuum-core/src/commands/web/search.rs +++ b/core/continuum-core/src/commands/web/search.rs @@ -3,15 +3,12 @@ use super::{web_search, WebSearchParams, WebSearchResult}; crate::action_command! { - /// Search the WEB for current information beyond your memory — docs, articles, - /// papers, news, how-tos. Returns ranked results (title, url, snippet) you can - /// cite or read further. Pick a backend with `adapter`: "brave" (best — needs a - /// free BRAVE_API_KEY) or "duckduckgo" (keyless, works with no setup). Omit - /// `adapter` to auto-use the best available — so this works even with no API - /// key configured. Use it to forage for what you don't know yet. + /// Search the web for current info beyond your memory (docs, APIs, errors, + /// how-tos). Returns ranked results (title, url, snippet) to read with web/fetch. pub struct WebSearch; name: "web/search", access: AiSafe, + native: true, // her hands must be able to REACH the web (a native-call model can only emit calls in its offered specs); I forage constantly, so must she — direct SWE/task score lever params: WebSearchParams, output: WebSearchResult, run(_this, _ctx, p) => { web_search(p).await } @@ -22,14 +19,22 @@ mod tests { use super::*; use crate::sdk_codegen::ActionCommand; - // what this catches: the wire name + a description that names BOTH adapters and - // the keyless guarantee — the persona is offered "web/search" with guidance that - // it works without a key. Name is the routing key; a drift unwires the hand. + // what this catches: the wire name (routing key — a drift unwires the hand) and + // that the description is the CONCISE model-facing line it became when web went + // NATIVE (2026-08-25): the adapter/keyless detail moved OUT of DESCRIPTION — which + // rides the token-budgeted native surface — and INTO the `adapter` param doc, so + // the surface stayed under its ceiling. The description must still say what the + // tool DOES (search the web) and point at its partner (web/fetch); the + // adapter/keyless guidance is asserted on the PARAM, its new home. #[test] fn name_and_description() { assert_eq!(WebSearch::NAME, "web/search"); - assert!(WebSearch::DESCRIPTION.contains("brave")); - assert!(WebSearch::DESCRIPTION.contains("duckduckgo")); - assert!(WebSearch::DESCRIPTION.contains("no API key")); + let d = WebSearch::DESCRIPTION.to_lowercase(); + assert!(d.contains("search") && d.contains("web"), "names what it does: {}", WebSearch::DESCRIPTION); + assert!(d.contains("web/fetch"), "points at its read partner"); + // The adapter/keyless detail now lives on the param, not the surface-costed DESCRIPTION. + let schema = serde_json::to_string(&schemars::schema_for!(WebSearchParams)).unwrap_or_default(); + assert!(schema.contains("brave") && schema.contains("duckduckgo"), + "adapter guidance lives in the param schema now"); } }