From bb2446e037054ba138536e359d371552bbf99826 Mon Sep 17 00:00:00 2001 From: Joel Teply Date: Tue, 25 Aug 2026 10:31:15 -0500 Subject: [PATCH 1/3] =?UTF-8?q?feat(web):=20web/search=20+=20web/fetch=20a?= =?UTF-8?q?re=20NATIVE=20=E2=80=94=20she=20can=20forage=20the=20web=20like?= =?UTF-8?q?=20I=20do=20(score=20lever)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Joel (2026-08-25): 'think of the stuff you use and make sure we have it.' I forage the web constantly — library APIs, error messages, docs — and on SWE/ terminal tasks that knowledge often isn't in the repo. But web/search and web/fetch were catalog-only, and a NATIVE-tool-call model (Ornith) can ONLY emit calls for tools in its offered specs (unlike a text model, it has no reach-by-name affordance). So her hands literally could not touch the web mid-task. Declaring native:true puts web foraging in her bounded per-turn surface — offered in full, budget-protected by describe_tool_tokens like every native tool. The backend already works keyless (duckduckgo) or with a BRAVE_API_KEY. Direct capability parity with the agent she's replacing. Battery: web:: + persona_tools + native surface + ratchets green (58 passed). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo --- core/continuum-core/src/commands/web/fetch.rs | 1 + core/continuum-core/src/commands/web/search.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/core/continuum-core/src/commands/web/fetch.rs b/core/continuum-core/src/commands/web/fetch.rs index 7d43f42ec5..d5504da131 100644 --- a/core/continuum-core/src/commands/web/fetch.rs +++ b/core/continuum-core/src/commands/web/fetch.rs @@ -10,6 +10,7 @@ crate::action_command! { 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 beebac23c2..cbad6055ed 100644 --- a/core/continuum-core/src/commands/web/search.rs +++ b/core/continuum-core/src/commands/web/search.rs @@ -12,6 +12,7 @@ crate::action_command! { 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 } From 3f84028d67f8852162856d35af2b49ac8a076395 Mon Sep 17 00:00:00 2001 From: Joel Teply Date: Tue, 25 Aug 2026 12:08:54 -0500 Subject: [PATCH 2/3] fix(web): trim native web descriptions + re-pin the agentic-surface ceiling (regression the ratchet caught) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The native-web change (#2467) grew the agentic surface 8623 → 9513 tokens, over the 8650 ceiling — the #333 ratchet doing its job ('careful not to regress', Joel). SHRUNK FIRST: trimmed both web DESCRIPTIONs to one line each (detail lives in the param docs + commands/help) → 9350. The rest (+727) is the irreducible param schemas of two real tools. Per the ratchet's own contract, re-pinned the ceiling 8650 → 9400 with the addition stated plainly. Not a new overflow: the surface already exceeded the 8192 tight-test window (the budget trims volatile context to fit, specs reserved up front) — this raises the reserved floor. Ornith serves at 166k where the ceiling is irrelevant; the tight-window LCD persona pays with less recall room, a conscious trade for web foraging. Battery: tool_surface + prompt_shaping + ratchets green (25 passed, EXIT=0). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo --- .../src/cognition/llm_deliberation_faculty.rs | 18 ++++++++++++++++-- core/continuum-core/src/commands/web/fetch.rs | 6 ++---- core/continuum-core/src/commands/web/search.rs | 8 ++------ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/core/continuum-core/src/cognition/llm_deliberation_faculty.rs b/core/continuum-core/src/cognition/llm_deliberation_faculty.rs index 80a5326b09..d13207a34b 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 d5504da131..d3f4faa194 100644 --- a/core/continuum-core/src/commands/web/fetch.rs +++ b/core/continuum-core/src/commands/web/fetch.rs @@ -3,10 +3,8 @@ 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, diff --git a/core/continuum-core/src/commands/web/search.rs b/core/continuum-core/src/commands/web/search.rs index cbad6055ed..cf452fa14c 100644 --- a/core/continuum-core/src/commands/web/search.rs +++ b/core/continuum-core/src/commands/web/search.rs @@ -3,12 +3,8 @@ 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, From 52b916772211fcfcd9f29104298dc44732bee868 Mon Sep 17 00:00:00 2001 From: Joel Teply Date: Tue, 25 Aug 2026 12:24:34 -0500 Subject: [PATCH 3/3] fix(web): update web/search test to the trimmed-description contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name_and_description test pinned the OLD verbose DESCRIPTION (brave/ duckduckgo/no-API-key) — but the surface-ceiling fix deliberately moved that detail OUT of DESCRIPTION (which rides the token-budgeted native surface) INTO the adapter param doc. Test now asserts the concise contract: DESCRIPTION says what it does (search web) + points at web/fetch; the adapter/keyless guidance is asserted on the WebSearchParams schema, its new home. A premise change from the ceiling fix, stated as such — not a weakening. Battery: web::search + web::fetch + ratchet green (4 passed). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo --- .../continuum-core/src/commands/web/search.rs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/continuum-core/src/commands/web/search.rs b/core/continuum-core/src/commands/web/search.rs index cf452fa14c..6ba174f41e 100644 --- a/core/continuum-core/src/commands/web/search.rs +++ b/core/continuum-core/src/commands/web/search.rs @@ -19,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"); } }