mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
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:
@@ -70,6 +70,7 @@ async fn dispatch_revokes_access_and_holds_call_id_through_callbacks() -> anyhow
|
||||
vec![],
|
||||
ToolMode::Direct,
|
||||
Default::default(),
|
||||
/*exec_definitions*/ None,
|
||||
/*tool_namespaces_info*/ None,
|
||||
&[],
|
||||
);
|
||||
|
||||
@@ -107,10 +107,6 @@ impl TopLevelToolsState {
|
||||
}
|
||||
|
||||
/// Track granular exec updates as part of this tool catalog.
|
||||
#[cfg_attr(
|
||||
not(test),
|
||||
expect(dead_code, reason = "Activated by the follow-up runtime integration")
|
||||
)]
|
||||
pub(crate) fn with_incremental_exec_tools(
|
||||
mut self,
|
||||
exec_definitions: Option<Arc<Vec<ExecDefinition>>>,
|
||||
|
||||
@@ -328,6 +328,7 @@ impl StepContext {
|
||||
Vec::new(),
|
||||
ToolMode::Direct,
|
||||
BTreeMap::new(),
|
||||
/*exec_definitions*/ None,
|
||||
/*tool_namespaces_info*/ None,
|
||||
&[],
|
||||
)),
|
||||
|
||||
@@ -208,11 +208,15 @@ impl Session {
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let definitions = codex_tools::create_tools_json_for_responses_lite(&specs)?;
|
||||
world_state.add_section(TopLevelToolsState::new(
|
||||
let mut top_level = TopLevelToolsState::new(
|
||||
model_messages.incremental_tools(),
|
||||
definitions,
|
||||
Some(Arc::clone(&extension_metrics)),
|
||||
)?);
|
||||
)?;
|
||||
if let Some(entries) = step_context.tool_router.exec_definitions() {
|
||||
top_level = top_level.with_incremental_exec_tools(Some(entries));
|
||||
}
|
||||
world_state.add_section(top_level);
|
||||
}
|
||||
world_state.add_section(model_state);
|
||||
let token_budget_enabled = turn_context.config.features.enabled(Feature::TokenBudget)
|
||||
|
||||
@@ -497,6 +497,7 @@ mod tests {
|
||||
Vec::new(),
|
||||
ToolMode::CodeModeOnly,
|
||||
BTreeMap::new(),
|
||||
/*exec_definitions*/ None,
|
||||
/*tool_namespaces_info*/ None,
|
||||
&[],
|
||||
));
|
||||
|
||||
@@ -831,6 +831,7 @@ mod tests {
|
||||
Vec::new(),
|
||||
ToolMode::Direct,
|
||||
BTreeMap::new(),
|
||||
/*exec_definitions*/ None,
|
||||
/*tool_namespaces_info*/ None,
|
||||
&[],
|
||||
));
|
||||
@@ -1019,6 +1020,7 @@ mod tests {
|
||||
Vec::new(),
|
||||
ToolMode::Direct,
|
||||
BTreeMap::new(),
|
||||
/*exec_definitions*/ None,
|
||||
/*tool_namespaces_info*/ None,
|
||||
&[],
|
||||
));
|
||||
|
||||
@@ -43,6 +43,7 @@ async fn spawned_dispatch_preserves_caller_context(level: LevelFilter) -> anyhow
|
||||
Vec::new(),
|
||||
ToolMode::Direct,
|
||||
BTreeMap::new(),
|
||||
/*exec_definitions*/ None,
|
||||
/*tool_namespaces_info*/ None,
|
||||
&[],
|
||||
));
|
||||
|
||||
@@ -101,6 +101,7 @@ pub struct ToolRouter {
|
||||
model_visible_specs: Arc<[ToolSpec]>,
|
||||
tool_mode: ToolMode,
|
||||
code_mode_tool_names: BTreeMap<String, ToolName>,
|
||||
exec_definitions: Option<Arc<Vec<codex_code_mode::ExecDefinition>>>,
|
||||
tool_namespaces_info: Option<TurnToolNamespacesInfo>,
|
||||
can_manage_children: bool,
|
||||
}
|
||||
@@ -141,6 +142,7 @@ impl ToolRouter {
|
||||
model_visible_specs: Vec<ToolSpec>,
|
||||
tool_mode: ToolMode,
|
||||
code_mode_tool_names: BTreeMap<String, ToolName>,
|
||||
exec_definitions: Option<Arc<Vec<codex_code_mode::ExecDefinition>>>,
|
||||
tool_namespaces_info: Option<TurnToolNamespacesInfo>,
|
||||
child_management_tools: &[ToolName],
|
||||
) -> Self {
|
||||
@@ -149,6 +151,7 @@ impl ToolRouter {
|
||||
model_visible_specs: model_visible_specs.into(),
|
||||
tool_mode,
|
||||
code_mode_tool_names,
|
||||
exec_definitions,
|
||||
tool_namespaces_info,
|
||||
can_manage_children: false,
|
||||
};
|
||||
@@ -163,6 +166,10 @@ impl ToolRouter {
|
||||
Arc::clone(&self.model_visible_specs)
|
||||
}
|
||||
|
||||
pub(crate) fn exec_definitions(&self) -> Option<Arc<Vec<codex_code_mode::ExecDefinition>>> {
|
||||
self.exec_definitions.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn tool_mode(&self) -> ToolMode {
|
||||
self.tool_mode
|
||||
}
|
||||
|
||||
@@ -497,7 +497,7 @@ pub(crate) fn finalize_tool_router(
|
||||
registry.mcp_namespaces(),
|
||||
)
|
||||
.map_err(|error| CodexErrorDetails::InvalidRequest(error.to_string()))?;
|
||||
let code_mode_tool_names =
|
||||
let (code_mode_tool_names, exec_definitions) =
|
||||
register_code_mode_executors(turn_context, model_info, &mut registry, &indirect_prefixes);
|
||||
let include_tool_namespaces_info = turn_context
|
||||
.config
|
||||
@@ -580,6 +580,7 @@ pub(crate) fn finalize_tool_router(
|
||||
model_visible_specs,
|
||||
tool_mode,
|
||||
code_mode_tool_names,
|
||||
exec_definitions,
|
||||
tool_namespaces_info,
|
||||
&child_management_tools,
|
||||
))
|
||||
@@ -1052,10 +1053,13 @@ fn register_code_mode_executors(
|
||||
model_info: &ModelInfo,
|
||||
registry: &mut ToolRegistry,
|
||||
indirect_prefixes: &IndirectNamespacePrefixes<'_>,
|
||||
) -> BTreeMap<String, ToolName> {
|
||||
) -> (
|
||||
BTreeMap<String, ToolName>,
|
||||
Option<Arc<Vec<codex_code_mode::ExecDefinition>>>,
|
||||
) {
|
||||
let tool_mode = effective_tool_mode(turn_context, model_info);
|
||||
if !matches!(tool_mode, ToolMode::CodeMode | ToolMode::CodeModeOnly) {
|
||||
return BTreeMap::new();
|
||||
return (BTreeMap::new(), None);
|
||||
}
|
||||
|
||||
let mut code_mode_tool_names = BTreeMap::new();
|
||||
@@ -1109,21 +1113,30 @@ fn register_code_mode_executors(
|
||||
}
|
||||
enabled_tools
|
||||
.sort_by(|left, right| compare_code_mode_tools(left, right, &namespace_descriptions));
|
||||
let (exec_description, _) = codex_code_mode::build_exec_tool_description_and_definitions(
|
||||
&enabled_tools,
|
||||
&deferred_tools,
|
||||
&namespace_descriptions,
|
||||
turn_context.config.code_mode.default_exec_yield_time_ms,
|
||||
tool_mode == ToolMode::CodeModeOnly,
|
||||
let image_detail_visibility =
|
||||
if unified_image_budget_enabled(&turn_context.config.features, model_info) {
|
||||
codex_code_mode::ImageDetailVisibility::Hidden
|
||||
} else {
|
||||
codex_code_mode::ImageDetailVisibility::Visible
|
||||
},
|
||||
deferred_tool_discovery,
|
||||
/*incremental_exec_enabled*/ false,
|
||||
model_messages.code_mode(),
|
||||
);
|
||||
};
|
||||
let features = &turn_context.config.features;
|
||||
let incremental_exec_enabled = model_info.use_responses_lite
|
||||
&& features.enabled(Feature::IncrementalTools)
|
||||
&& features.enabled(Feature::IncrementalExecTools)
|
||||
&& tool_mode == ToolMode::CodeModeOnly;
|
||||
let (exec_description, exec_definitions) =
|
||||
codex_code_mode::build_exec_tool_description_and_definitions(
|
||||
&enabled_tools,
|
||||
&deferred_tools,
|
||||
&namespace_descriptions,
|
||||
turn_context.config.code_mode.default_exec_yield_time_ms,
|
||||
tool_mode == ToolMode::CodeModeOnly,
|
||||
image_detail_visibility,
|
||||
deferred_tool_discovery,
|
||||
incremental_exec_enabled,
|
||||
model_messages.code_mode(),
|
||||
);
|
||||
let exec_definitions = incremental_exec_enabled.then(|| Arc::new(exec_definitions));
|
||||
let execute_handler = CodeModeExecuteHandler::new(
|
||||
create_code_mode_tool(exec_description),
|
||||
code_mode_nested_tool_specs,
|
||||
@@ -1135,7 +1148,7 @@ fn register_code_mode_executors(
|
||||
)));
|
||||
registry.prepend_trusted(Arc::new(execute_handler));
|
||||
|
||||
code_mode_tool_names
|
||||
(code_mode_tool_names, exec_definitions)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all, fields(tool_spec_count = specs.len()))]
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
+443
@@ -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.
|
||||
Reference in New Issue
Block a user