Allow model catalogs to override incremental tool notices (#52736)

## What changed

Add `model_messages.tools.incremental_tools` overrides for the update hint, tool and namespace removal headers, and namespace instruction update and clearing messages. Missing, null, or over-512-byte values fall back to bundled wording; empty strings omit static text while retaining runtime names and instructions.

Substitute `{name}` in namespace message templates and append runtime instructions verbatim. Wording changes alone do not trigger tool catalog updates.

## Testing

Extend unit tests and MCP request-history snapshots to cover custom wording, empty notices, tool removal and restoration, and namespace instruction changes and clearing without repeating unchanged tools or notices.

GitOrigin-RevId: 22dea0a6fb4cfcd646f5e65346424834c8b7d5a2
This commit is contained in:
Alex Kira
2026-10-10 01:14:28 +00:00
committed by copyberry
parent 4bad6d78e9
commit e4490eff65
11 changed files with 493 additions and 52 deletions
@@ -0,0 +1,24 @@
//! Optional model-catalog overrides for harness-owned incremental tool notices.
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use ts_rs::TS;
/// Incremental tool notice overrides; missing/null or >512-byte values use bundled defaults.
/// Empty strings omit static text but retain runtime names and instructions.
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, TS, JsonSchema)]
pub struct IncrementalToolMessages {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_update_hint: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub removed_tools_header: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub removed_namespaces_header: Option<String>,
/// Prefix supporting `{name}`; the namespace instructions are appended verbatim.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub namespace_instructions_prefix: Option<String>,
/// Message supporting `{name}` when a namespace's instructions are cleared.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub namespace_instructions_cleared: Option<String>,
}