Wire incremental exec tool updates into Code Mode sessions (#60163)

## Why

Changes to nested tools should update the model's catalog without repeating the full `functions.exec` declaration or rewriting retained context.

## What changed

Pass structured exec definitions through the tool router into the session's tool world state. Enable granular updates for Responses Lite in `CodeModeOnly` when both `IncrementalTools` and `IncrementalExecTools` are enabled.

Append changed tool definitions, namespace instructions, and removals as developer messages. Keep common exec instructions and shared types frozen until compaction, then install the latest full catalog.

## Testing

Add feature-gating coverage and a scenario snapshot covering tool additions and removals, namespace instruction changes, eager/deferred transitions, re-enabling tools, retained context, and catalog restoration after compaction.

GitOrigin-RevId: bbf850d4e5018388eeb4c09345d885d776962e09
This commit is contained in:
Alex Kira
2026-11-07 19:26:20 +00:00
committed by copyberry
parent 2bc8e7f545
commit 33b3f80a09
13 changed files with 882 additions and 21 deletions
@@ -1,8 +1,11 @@
//! Wait descriptions and parameter schemas resolve independently without changing other tools.
use anyhow::Result;
use codex_features::Feature;
use codex_protocol::openai_models::ToolMessages;
use codex_protocol::openai_models::ToolMode;
use codex_protocol::protocol::ThreadSettingsOverrides;
use core_test_support::responses;
use core_test_support::responses::mount_sse_sequence;
use core_test_support::responses::sse_completed;
use core_test_support::responses::start_mock_server;
@@ -71,3 +74,85 @@ async fn code_mode_wait_overrides_are_independent(
assert_eq!(requests[1].body_json()["tools"], expected);
Ok(())
}
#[test_case(false; "disabled")]
#[test_case(true; "enabled")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn exec_tools_follow_incremental_exec_flag(incremental_exec_enabled: bool) -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let mock = mount_sse_sequence(
&server,
vec![sse_completed("response-0"), sse_completed("response-1")],
)
.await;
let mut builder = test_codex().with_config(move |config| {
config
.features
.enable(Feature::IncrementalTools)
.expect("enable incremental tools");
config
.features
.set_enabled(Feature::IncrementalExecTools, incremental_exec_enabled)
.expect("set incremental exec tools");
config.code_mode.disable_in_process_fallback = true;
});
for (slug, instructions) in [
("gate-initial", "Initial code instructions."),
("gate-updated", "Updated code instructions."),
] {
builder = builder.with_model_info_override(slug, move |model| {
model.use_responses_lite = true;
model.tool_mode = Some(ToolMode::CodeModeOnly);
model.supports_search_tool = false;
model.model_messages.as_mut().expect("model messages").tools = Some(
serde_json::from_value(json!({
"code_mode": {"exec": {"description": instructions}},
}))
.expect("code mode messages"),
);
});
}
let test = builder
.with_model("gate-initial")
.build_with_auto_env(&server)
.await?;
test.submit_turn("Begin with the original instructions.")
.await?;
core_test_support::submit_thread_settings(
&test.codex,
ThreadSettingsOverrides {
model: Some("gate-updated".to_string()),
..Default::default()
},
)
.await?;
test.submit_text_turn("Continue with changed instructions.")
.await?;
let definitions = mock
.requests()
.iter()
.map(|request| {
request
.inputs_of_type("additional_tools")
.iter()
.rev()
.find_map(|item| responses::namespace_child_tool(item, "functions", "exec"))
.expect("exec definition")
.clone()
})
.collect::<Vec<_>>();
assert_eq!(definitions.len(), 2);
let initial_description = definitions[0]["description"].as_str().expect("description");
assert!(initial_description.starts_with("Initial code instructions."));
let mut expected = definitions[0].clone();
if !incremental_exec_enabled {
expected["description"] = json!(initial_description.replacen(
"Initial code instructions.",
"Updated code instructions.",
1
));
}
assert_eq!(definitions[1], expected);
Ok(())
}
+2
View File
@@ -125,6 +125,8 @@ mod audio_file;
#[path = "scenarios_retained_tool_outputs.rs"]
mod retained_tool_outputs;
#[path = "scenarios_exec_tools.rs"]
mod exec_tools;
#[path = "scenarios_incremental_tools.rs"]
mod incremental_tools;
#[path = "scenarios_incremental_tools_resume.rs"]
@@ -0,0 +1,305 @@
//! Exec tools append changed entries and refresh frozen exec sections only at compaction.
use anyhow::Result;
use codex_features::Feature;
use codex_protocol::openai_models::ToolMode;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::ThreadSettingsOverrides;
use core_test_support::context_snapshot;
use core_test_support::context_snapshot::ContextSnapshotOptions;
use core_test_support::responses;
use core_test_support::skip_if_no_network;
use core_test_support::skip_if_wine_exec;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_mcp_server;
use core_test_support::wait_for_successful_turn;
use pretty_assertions::assert_eq;
use serde_json::json;
use std::collections::HashMap;
use super::super::rmcp_client::remote_aware_environment_id;
use super::super::rmcp_client::remote_aware_stdio_server_bin;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn changed_sections_and_entries_append_without_repeating_the_catalog() -> Result<()> {
skip_if_no_network!(Ok(()));
skip_if_wine_exec!(Ok(()), "requires a Windows test_stdio_server fixture");
let server = responses::start_mock_server().await;
let command = remote_aware_stdio_server_bin()?;
let environment_id = remote_aware_environment_id();
let server_config = |tools: &[&str], instructions: &str, omitted: &[&str]| {
serde_json::from_value(json!({
"command": command,
"environment_id": environment_id,
"env": {"MCP_TEST_SERVER_INSTRUCTIONS": instructions},
"enabled_tools": tools,
"omit_tools_from": omitted,
"default_tools_approval_mode": "approve",
}))
};
let original_instructions = "Echo service instructions.";
let updated_instructions = "Echo service instructions. Durations are milliseconds.";
let initial_server = server_config(&["echo"], original_instructions, &["deferred"])?;
let mut builder = test_codex()
.with_config(move |config| {
super::configure_scenario_catalog(config);
for feature in [
Feature::IncrementalTools,
Feature::IncrementalExecTools,
Feature::DeferredToolWorldState,
] {
config
.features
.enable(feature)
.expect("enable catalog updates");
}
config
.features
.disable(Feature::CodeModeOnlyStrictThirdPartyTools)
.expect("allow eager MCP tools");
config.code_mode.disable_in_process_fallback = true;
config
.mcp_servers
.set(HashMap::from([(
"echo_service".to_string(),
initial_server,
)]))
.expect("set MCP fixture");
})
.with_model_info_override("catalog-initial", |model| {
model.use_responses_lite = true;
model.tool_mode = Some(ToolMode::CodeModeOnly);
model.supports_search_tool = true;
});
for (slug, description, deferred_guidance, shared_types) in [
(
"catalog-updated",
"Run JavaScript. Await every tool call before returning.",
"Find deferred tools in ALL_TOOLS.",
"type CallToolResult<T = unknown> = { content: unknown[]; structuredContent?: T; isError?: boolean };",
),
(
"catalog-compacted",
"Run JavaScript. Await every tool call and inspect its result before returning.",
"Find deferred tools by name in ALL_TOOLS.",
"type CallToolResult<T = unknown> = { content: unknown[]; structuredContent?: T; isError?: boolean; _meta?: unknown };",
),
] {
builder = builder.with_model_info_override(slug, move |model| {
model.use_responses_lite = true;
model.tool_mode = Some(ToolMode::CodeModeOnly);
model.supports_search_tool = true;
model.model_messages.as_mut().expect("model messages").tools = Some(
serde_json::from_value(json!({
"code_mode": {
"exec": {"description": description},
"deferred_nested_tools_guidance": deferred_guidance,
"mcp_typescript_preamble": shared_types,
},
"mcp_resources": {"read_mcp_resource": {
"description": "Read a selected resource revision.",
"parameters": json!({
"type": "object",
"properties": {"server": {"type": "string"}, "uri": {"type": "string"}, "revision": {"type": "integer"}},
"required": ["server", "uri"],
"additionalProperties": false,
}).to_string(),
}},
})).expect("updated tool messages"),
);
});
}
let test = builder
.with_model("catalog-initial")
.build_with_auto_env(&server)
.await?;
wait_for_mcp_server(&test.codex, "echo_service").await?;
let mut events = (0..12)
.map(|index| responses::sse_completed(&format!("response-{index}")))
.collect::<Vec<_>>();
events.push(responses::sse(vec![
json!({"type": "response.output_item.done", "item": {
"type": "compaction", "encrypted_content": "CATALOG_CHECKPOINT",
}}),
responses::ev_completed("response-12"),
]));
events.push(responses::sse_completed("response-13"));
let mock = responses::mount_sse_sequence(&server, events).await;
test.submit_text_turn("Initial eager catalog: echo.")
.await?;
// Refresh through the public MCP configuration and call boundaries, as production does.
let ready = Some(json!({"message": "catalog ready"}));
for (tools, instructions, prompt) in [
(
vec!["echo", "cwd"],
original_instructions,
"Nested tool addition: cwd.",
),
(
vec!["echo", "cwd"],
updated_instructions,
"Namespace instructions redefinition: durations.",
),
(
vec!["echo"],
updated_instructions,
"Nested tool removal: cwd.",
),
] {
let current = test.codex.config().await;
let mut updated = current.as_ref().clone();
updated.mcp_servers.set(HashMap::from([(
"echo_service".to_string(),
server_config(&tools, instructions, &["deferred"])?,
)]))?;
let _ = test.codex.refresh_mcp_config(current, updated).await;
test.codex
.call_mcp_tool("echo_service", "echo", ready.clone(), /*meta*/ None)
.await?;
test.submit_text_turn(prompt).await?;
}
test.submit_text_turn("Unchanged catalog: emit no update.")
.await?;
for (tools, instructions, omitted, prompt) in [
(
vec!["echo"],
"",
vec!["deferred"],
"Namespace instructions removal: keep echo.",
),
(vec!["echo"], "", vec![], "Eager to deferred: echo."),
(
vec!["echo"],
updated_instructions,
vec!["deferred"],
"Deferred to eager: echo.",
),
(vec![], "", vec![], "Remove all echo service entries."),
(
vec!["echo", "cwd"],
updated_instructions,
vec!["deferred"],
"Re-enable echo service entries: echo and cwd.",
),
] {
let current = test.codex.config().await;
let mut updated = current.as_ref().clone();
updated.mcp_servers.set(if tools.is_empty() {
HashMap::new()
} else {
HashMap::from([(
"echo_service".to_string(),
server_config(&tools, instructions, &omitted)?,
)])
})?;
let _ = test.codex.refresh_mcp_config(current, updated).await;
let result = test
.codex
.call_mcp_tool("echo_service", "echo", ready.clone(), /*meta*/ None)
.await;
if tools.is_empty() {
assert!(result.is_err(), "removed tools must not remain callable");
} else {
result?;
}
test.submit_text_turn(prompt).await?;
}
core_test_support::submit_thread_settings(
&test.codex,
ThreadSettingsOverrides {
model: Some("catalog-updated".to_string()),
..Default::default()
},
)
.await?;
test.submit_text_turn(
"Combined redefinition: resource revision, common instructions, and shared types.",
)
.await?;
core_test_support::submit_thread_settings(
&test.codex,
ThreadSettingsOverrides {
model: Some("catalog-compacted".to_string()),
..Default::default()
},
)
.await?;
test.submit_text_turn("Only common instructions and shared types change: wait for compaction.")
.await?;
test.codex.submit(Op::Compact).await?;
wait_for_successful_turn(&test.codex).await;
test.submit_text_turn("After compaction: restore the full current catalog.")
.await?;
let requests = mock.requests();
assert_eq!(requests.len(), 14);
let mut previous = Vec::new();
let mut declarations = Vec::new();
// Snapshots normalize IDs and metadata, so check the retained raw prefix separately.
for (index, request) in requests.iter().enumerate() {
let input = request.input();
let suffix = if index == 13 {
input.as_slice()
} else {
assert_eq!(
&input[..previous.len()],
previous.as_slice(),
"retained context changed"
);
&input[previous.len()..]
};
declarations.push(
suffix
.iter()
.filter(|item| item["type"] == "additional_tools")
.filter_map(|item| responses::namespace_child_tool(item, "functions", "exec"))
.cloned()
.collect::<Vec<_>>(),
);
previous = input;
}
// Native declarations are fingerprinted in snapshots; keep their invariants readable here.
let mut native_fields = declarations[0][0].clone();
native_fields["description"] = json!(null);
for exec in declarations.iter().flatten() {
let mut actual = exec.clone();
actual["description"] = json!(null);
assert_eq!(actual, native_fields, "compaction preserves native fields");
}
for request in &requests[10..13] {
let retained_input = serde_json::to_string(&request.input())?;
for frozen in [
"Run JavaScript. Await every tool call",
"Find deferred tools in ALL_TOOLS.",
"Find deferred tools by name in ALL_TOOLS.",
"content: unknown[]; structuredContent?: T; isError?: boolean",
] {
assert!(
!retained_input.contains(frozen),
"frozen documentation appeared before compaction: {frozen}"
);
}
}
let restored = declarations[13][0]["description"]
.as_str()
.expect("compacted catalog");
for expected in [
"Await every tool call and inspect its result",
"Find deferred tools by name in ALL_TOOLS.",
"isError?: boolean; _meta?: unknown",
"revision?: number",
"### `mcp__echo_service__echo`",
"### `mcp__echo_service__cwd`",
updated_instructions,
] {
assert!(restored.contains(expected), "compaction omitted {expected}");
}
let snapshot = context_snapshot::format_request_history_snapshot(
"Code Mode appends complete changed tool entries and namespace instructions in combined developer messages. Namespace clearing, eager/deferred movement, remove-all and re-enabling preserve context. Common instructions and shared types stay frozen while nested methods update; compaction installs the latest full catalog.",
&requests,
&ContextSnapshotOptions::default().rewrite_known_segments(),
);
insta::assert_snapshot!("incremental_exec_tools", snapshot);
Ok(())
}
@@ -0,0 +1,443 @@
---
source: core/tests/suite/scenarios_exec_tools.rs
expression: snapshot
---
Scenario: Code Mode appends complete changed tool entries and namespace instructions in combined developer messages. Namespace clearing, eager/deferred movement, remove-all and re-enabling preserve context. Common instructions and shared types stay frozen while nested methods update; compaction installs the latest full catalog.
## Window 1
-- request 1 (turn) --
00:additional_tools/developer (1; hash=b2c6697017b587f1):
- namespace/functions; hash=e9ce2d0830f744bb
- custom/exec
- function/wait
- function/request_user_input
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <tools>
Deferred tool namespaces:
- multi_agent_v1: Tools for spawning and managing sub-agents.
</tools>
03:message/user:
<ENVIRONMENT_CONTEXT>
04:message/user:
Initial eager catalog: echo.
-- request 2 (turn) --
05:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
### `mcp__echo_service__cwd`
Return the current working directory of this test server process.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__cwd(args: {}): Promise<CallToolResult<{ cwd: string; }>>; };
```
06:message/user:
Nested tool addition: cwd.
-- request 3 (turn) --
07:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
08:message/user:
Namespace instructions redefinition: durations.
-- request 4 (turn) --
09:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Tool: tools.mcp__echo_service__cwd
10:message/user:
Nested tool removal: cwd.
-- request 5 (turn) --
11:message/user:
Unchanged catalog: emit no update.
-- request 6 (turn) --
12:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Namespace instructions: mcp__echo_service
13:message/user:
Namespace instructions removal: keep echo.
-- request 7 (turn) --
14:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Tool: tools.mcp__echo_service__echo
15:message/developer:
<tools>
Added deferred tool namespaces:
- mcp__echo_service
</tools>
16:message/user:
Eager to deferred: echo.
-- request 8 (turn) --
17:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
### `mcp__echo_service__echo`
Echo back the provided message and include environment data.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__echo(args: { env_var?: string; message: string; }): Promise<CallToolResult<{ echo: string; env: string | null; }>>; };
```
18:message/developer:
<tools>
Removed deferred tool namespaces:
- mcp__echo_service
</tools>
19:message/user:
Deferred to eager: echo.
-- request 9 (turn) --
20:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Namespace instructions: mcp__echo_service
- Tool: tools.mcp__echo_service__echo
21:message/user:
Remove all echo service entries.
-- request 10 (turn) --
22:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
### `mcp__echo_service__cwd`
Return the current working directory of this test server process.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__cwd(args: {}): Promise<CallToolResult<{ cwd: string; }>>; };
```
### `mcp__echo_service__echo`
Echo back the provided message and include environment data.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__echo(args: { env_var?: string; message: string; }): Promise<CallToolResult<{ echo: string; env: string | null; }>>; };
```
23:message/user:
Re-enable echo service entries: echo and cwd.
## Window 2 (after request 10: settings changed (model))
-- request 11 (turn) --
00:additional_tools/developer (1; hash=b2c6697017b587f1):
- namespace/functions; hash=e9ce2d0830f744bb
- custom/exec
- function/wait
- function/request_user_input
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <tools>
Deferred tool namespaces:
- multi_agent_v1: Tools for spawning and managing sub-agents.
</tools>
03:message/user:
<ENVIRONMENT_CONTEXT>
04:message/user:
Initial eager catalog: echo.
05:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
### `mcp__echo_service__cwd`
Return the current working directory of this test server process.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__cwd(args: {}): Promise<CallToolResult<{ cwd: string; }>>; };
```
06:message/user:
Nested tool addition: cwd.
07:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
08:message/user:
Namespace instructions redefinition: durations.
09:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Tool: tools.mcp__echo_service__cwd
10:message/user:
Nested tool removal: cwd.
11:message/user:
Unchanged catalog: emit no update.
12:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Namespace instructions: mcp__echo_service
13:message/user:
Namespace instructions removal: keep echo.
14:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Tool: tools.mcp__echo_service__echo
15:message/developer:
<tools>
Added deferred tool namespaces:
- mcp__echo_service
</tools>
16:message/user:
Eager to deferred: echo.
17:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
### `mcp__echo_service__echo`
Echo back the provided message and include environment data.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__echo(args: { env_var?: string; message: string; }): Promise<CallToolResult<{ echo: string; env: string | null; }>>; };
```
18:message/developer:
<tools>
Removed deferred tool namespaces:
- mcp__echo_service
</tools>
19:message/user:
Deferred to eager: echo.
20:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Namespace instructions: mcp__echo_service
- Tool: tools.mcp__echo_service__echo
21:message/user:
Remove all echo service entries.
22:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
### `mcp__echo_service__cwd`
Return the current working directory of this test server process.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__cwd(args: {}): Promise<CallToolResult<{ cwd: string; }>>; };
```
### `mcp__echo_service__echo`
Echo back the provided message and include environment data.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__echo(args: { env_var?: string; message: string; }): Promise<CallToolResult<{ echo: string; env: string | null; }>>; };
```
23:message/user:
Re-enable echo service entries: echo and cwd.
24:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
### `read_mcp_resource`
Read a selected resource revision.
exec tool declaration:
```ts
declare const tools: { read_mcp_resource(args: { revision?: number; server: string; uri: string; }): Promise<unknown>; };
```
25:message/developer:
<model_instructions>
The current model is catalog-updated.
</model_instructions>
26:message/user:
Combined redefinition: resource revision, common instructions, and shared types.
## Window 3 (after request 11: settings changed (model))
-- request 12 (turn) --
00:additional_tools/developer (1; hash=b2c6697017b587f1):
- namespace/functions; hash=e9ce2d0830f744bb
- custom/exec
- function/wait
- function/request_user_input
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <tools>
Deferred tool namespaces:
- multi_agent_v1: Tools for spawning and managing sub-agents.
</tools>
03:message/user:
<ENVIRONMENT_CONTEXT>
04:message/user:
Initial eager catalog: echo.
05:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
### `mcp__echo_service__cwd`
Return the current working directory of this test server process.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__cwd(args: {}): Promise<CallToolResult<{ cwd: string; }>>; };
```
06:message/user:
Nested tool addition: cwd.
07:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
08:message/user:
Namespace instructions redefinition: durations.
09:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Tool: tools.mcp__echo_service__cwd
10:message/user:
Nested tool removal: cwd.
11:message/user:
Unchanged catalog: emit no update.
12:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Namespace instructions: mcp__echo_service
13:message/user:
Namespace instructions removal: keep echo.
14:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Tool: tools.mcp__echo_service__echo
15:message/developer:
<tools>
Added deferred tool namespaces:
- mcp__echo_service
</tools>
16:message/user:
Eager to deferred: echo.
17:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
### `mcp__echo_service__echo`
Echo back the provided message and include environment data.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__echo(args: { env_var?: string; message: string; }): Promise<CallToolResult<{ echo: string; env: string | null; }>>; };
```
18:message/developer:
<tools>
Removed deferred tool namespaces:
- mcp__echo_service
</tools>
19:message/user:
Deferred to eager: echo.
20:message/developer:
Listed `Tool:` entries are unavailable until redefined or rediscovered. `Namespace instructions:` removes guidance only. Unlisted entries are unchanged.
- Namespace instructions: mcp__echo_service
- Tool: tools.mcp__echo_service__echo
21:message/user:
Remove all echo service entries.
22:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
## mcp__echo_service
Echo service instructions. Durations are milliseconds.
### `mcp__echo_service__cwd`
Return the current working directory of this test server process.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__cwd(args: {}): Promise<CallToolResult<{ cwd: string; }>>; };
```
### `mcp__echo_service__echo`
Echo back the provided message and include environment data.
exec tool declaration:
```ts
declare const tools: { mcp__echo_service__echo(args: { env_var?: string; message: string; }): Promise<CallToolResult<{ echo: string; env: string | null; }>>; };
```
23:message/user:
Re-enable echo service entries: echo and cwd.
24:message/developer:
Use these definitions and namespace instructions with `functions.exec`. Each replaces its earlier versio...ls keep their latest definitions and `tools` methods. [hash=4d13008df632810d]
## Namespace: functions
### `read_mcp_resource`
Read a selected resource revision.
exec tool declaration:
```ts
declare const tools: { read_mcp_resource(args: { revision?: number; server: string; uri: string; }): Promise<unknown>; };
```
25:message/developer:
<model_instructions>
The current model is catalog-updated.
</model_instructions>
26:message/user:
Combined redefinition: resource revision, common instructions, and shared types.
27:message/developer:
<model_instructions>
The current model is catalog-compacted.
</model_instructions>
28:message/user:
Only common instructions and shared types change: wait for compaction.
-- request 13 (compaction) --
29:compaction_trigger
## Window 4 (after request 13: input diverged at item 00)
-- request 14 (turn) --
00:additional_tools/developer (1; hash=c3cdfbca78ae91e2):
- namespace/functions; hash=36087e37fe329f5c
- custom/exec
- function/wait
- function/request_user_input
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/user:
Initial eager catalog: echo.
03:message/user:
Nested tool addition: cwd.
04:message/user:
Namespace instructions redefinition: durations.
05:message/user:
Nested tool removal: cwd.
06:message/user:
Unchanged catalog: emit no update.
07:message/user:
Namespace instructions removal: keep echo.
08:message/user:
Eager to deferred: echo.
09:message/user:
Deferred to eager: echo.
10:message/user:
Remove all echo service entries.
11:message/user:
Re-enable echo service entries: echo and cwd.
12:message/user:
Combined redefinition: resource revision, common instructions, and shared types.
13:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <tools>
Deferred tool namespaces:
- multi_agent_v1: Tools for spawning and managing sub-agents.
</tools>
14:message/user:
<ENVIRONMENT_CONTEXT>
15:message/user:
Only common instructions and shared types change: wait for compaction.
16:compaction:encrypted=true; chars=18; hash=1049063b3b4d3e47
17:message/user:
After compaction: restore the full current catalog.