use serde::Deserialize; use serde::Serialize; use serde_json::Value as JsonValue; use crate::PUBLIC_TOOL_NAME; const MAX_JS_SAFE_INTEGER: u64 = (1_u64 << 53) - 1; const CODE_MODE_ONLY_PREFACE: &str = "Use `exec/wait` tool to run all other tools, do not attempt to use any other tools directly"; const EXEC_DESCRIPTION_TEMPLATE: &str = r#"Run JavaScript code to orchestrate/compose tool calls - Evaluates the provided JavaScript code in a fresh V8 isolate as an async module. - All nested tools are available on the global `tools` object, for example `await tools.exec_command(...)`. Tool names are exposed as normalized JavaScript identifiers, for example `await tools.mcp__ologs__get_profile(...)`. - Nested tool methods take either a string or an object as their input argument. - Nested tools return either an object or a string, based on the description. - Runs raw JavaScript -- no Node, no file system, no network access, no console. - Accepts raw JavaScript source text, not JSON, quoted strings, or markdown code fences. - You may optionally start the tool input with a first-line pragma like `// @exec: {"yield_time_ms": 10000, "max_output_tokens": 1000}`. - `yield_time_ms` asks `exec` to yield early after that many milliseconds if the script is still running. - `max_output_tokens` sets the token budget for direct `exec` results. By default the result is truncated to 10000 tokens. - When the JS code is fully evaluated, the isolate's lifetime ends and unawaited promises are silently discarded. - Global helpers: - `exit()`: Immediately ends the current script successfully (like an early return from the top level). - `text(value: string | number | boolean | undefined | null)`: Appends a text item. Non-string values are stringified with `JSON.stringify(...)` when possible. - `image(imageUrlOrItem: string | { image_url: string; detail?: "auto" | "low" | "high" | "original" | null })`: Appends an image item. `image_url` can be an HTTPS URL or a base64-encoded `data:` URL. - `store(key: string, value: any)`: stores a serializable value under a string key for later `exec` calls in the same session. - `load(key: string)`: returns the stored value for a string key, or `undefined` if it is missing. - `notify(value: string | number | boolean | undefined | null)`: immediately injects an extra `custom_tool_call_output` for the current `exec` call. Values are stringified like `text(...)`. - `ALL_TOOLS`: metadata for the enabled nested tools as `{ name, description }` entries. - `yield_control()`: yields the accumulated output to the model immediately while the script keeps running."#; const WAIT_DESCRIPTION_TEMPLATE: &str = r#"- Use `wait` only after `exec` returns `Script running with cell ID ...`. - `cell_id` identifies the running `exec` cell to resume. - `yield_time_ms` controls how long to wait for more output before yielding again. If omitted, `wait` uses its default wait timeout. - `max_tokens` limits how much new output this wait call returns. - `terminate: true` stops the running cell instead of waiting for more output. - `wait` returns only the new output since the last yield, or the final completion or termination result for that cell. - If the cell is still running, `wait` may yield again with the same `cell_id`. - If the cell has already finished, `wait` returns the completed result and closes the cell."#; pub const CODE_MODE_PRAGMA_PREFIX: &str = "// @exec:"; #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "snake_case")] pub enum CodeModeToolKind { Function, Freeform, } #[derive(Clone, Debug, PartialEq)] pub struct ToolDefinition { pub name: String, pub description: String, pub kind: CodeModeToolKind, pub input_schema: Option, pub output_schema: Option, } #[derive(Debug, Default, Deserialize, PartialEq, Eq)] #[serde(deny_unknown_fields)] struct CodeModeExecPragma { #[serde(default)] yield_time_ms: Option, #[serde(default)] max_output_tokens: Option, } #[derive(Debug, PartialEq, Eq)] pub struct ParsedExecSource { pub code: String, pub yield_time_ms: Option, pub max_output_tokens: Option, } pub fn parse_exec_source(input: &str) -> Result { if input.trim().is_empty() { return Err( "exec expects raw JavaScript source text (non-empty). Provide JS only, optionally with first-line `// @exec: {\"yield_time_ms\": 10000, \"max_output_tokens\": 1000}`.".to_string(), ); } let mut args = ParsedExecSource { code: input.to_string(), yield_time_ms: None, max_output_tokens: None, }; let mut lines = input.splitn(2, '\n'); let first_line = lines.next().unwrap_or_default(); let rest = lines.next().unwrap_or_default(); let trimmed = first_line.trim_start(); let Some(pragma) = trimmed.strip_prefix(CODE_MODE_PRAGMA_PREFIX) else { return Ok(args); }; if rest.trim().is_empty() { return Err( "exec pragma must be followed by JavaScript source on subsequent lines".to_string(), ); } let directive = pragma.trim(); if directive.is_empty() { return Err( "exec pragma must be a JSON object with supported fields `yield_time_ms` and `max_output_tokens`" .to_string(), ); } let value: serde_json::Value = serde_json::from_str(directive).map_err(|err| { format!( "exec pragma must be valid JSON with supported fields `yield_time_ms` and `max_output_tokens`: {err}" ) })?; let object = value.as_object().ok_or_else(|| { "exec pragma must be a JSON object with supported fields `yield_time_ms` and `max_output_tokens`" .to_string() })?; for key in object.keys() { match key.as_str() { "yield_time_ms" | "max_output_tokens" => {} _ => { return Err(format!( "exec pragma only supports `yield_time_ms` and `max_output_tokens`; got `{key}`" )); } } } let pragma: CodeModeExecPragma = serde_json::from_value(value).map_err(|err| { format!( "exec pragma fields `yield_time_ms` and `max_output_tokens` must be non-negative safe integers: {err}" ) })?; if pragma .yield_time_ms .is_some_and(|yield_time_ms| yield_time_ms > MAX_JS_SAFE_INTEGER) { return Err( "exec pragma field `yield_time_ms` must be a non-negative safe integer".to_string(), ); } if pragma.max_output_tokens.is_some_and(|max_output_tokens| { u64::try_from(max_output_tokens) .map(|max_output_tokens| max_output_tokens > MAX_JS_SAFE_INTEGER) .unwrap_or(true) }) { return Err( "exec pragma field `max_output_tokens` must be a non-negative safe integer".to_string(), ); } args.code = rest.to_string(); args.yield_time_ms = pragma.yield_time_ms; args.max_output_tokens = pragma.max_output_tokens; Ok(args) } pub fn is_code_mode_nested_tool(tool_name: &str) -> bool { tool_name != crate::PUBLIC_TOOL_NAME && tool_name != crate::WAIT_TOOL_NAME } pub fn build_exec_tool_description( enabled_tools: &[(String, String)], code_mode_only: bool, ) -> String { if !code_mode_only { return EXEC_DESCRIPTION_TEMPLATE.to_string(); } let mut sections = vec![ CODE_MODE_ONLY_PREFACE.to_string(), EXEC_DESCRIPTION_TEMPLATE.to_string(), ]; if !enabled_tools.is_empty() { let nested_tool_reference = enabled_tools .iter() .map(|(name, nested_description)| { let global_name = normalize_code_mode_identifier(name); format!( "### `{global_name}` (`{name}`)\n{}", nested_description.trim() ) }) .collect::>() .join("\n\n"); sections.push(nested_tool_reference); } sections.join("\n\n") } pub fn build_wait_tool_description() -> &'static str { WAIT_DESCRIPTION_TEMPLATE } pub fn normalize_code_mode_identifier(tool_key: &str) -> String { let mut identifier = String::new(); for (index, ch) in tool_key.chars().enumerate() { let is_valid = if index == 0 { ch == '_' || ch == '$' || ch.is_ascii_alphabetic() } else { ch == '_' || ch == '$' || ch.is_ascii_alphanumeric() }; if is_valid { identifier.push(ch); } else { identifier.push('_'); } } if identifier.is_empty() { "_".to_string() } else { identifier } } pub fn augment_tool_definition(mut definition: ToolDefinition) -> ToolDefinition { if definition.name != PUBLIC_TOOL_NAME { definition.description = append_code_mode_sample_for_definition(&definition); } definition } pub fn enabled_tool_metadata(definition: &ToolDefinition) -> EnabledToolMetadata { EnabledToolMetadata { tool_name: definition.name.clone(), global_name: normalize_code_mode_identifier(&definition.name), description: definition.description.clone(), kind: definition.kind, } } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct EnabledToolMetadata { pub tool_name: String, pub global_name: String, pub description: String, pub kind: CodeModeToolKind, } pub fn append_code_mode_sample( description: &str, tool_name: &str, input_name: &str, input_type: String, output_type: String, ) -> String { let declaration = format!( "declare const tools: {{ {} }};", render_code_mode_tool_declaration(tool_name, input_name, input_type, output_type) ); format!("{description}\n\nexec tool declaration:\n```ts\n{declaration}\n```") } fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String { let input_name = match definition.kind { CodeModeToolKind::Function => "args", CodeModeToolKind::Freeform => "input", }; let input_type = match definition.kind { CodeModeToolKind::Function => definition .input_schema .as_ref() .map(render_json_schema_to_typescript) .unwrap_or_else(|| "unknown".to_string()), CodeModeToolKind::Freeform => "string".to_string(), }; let output_type = definition .output_schema .as_ref() .map(render_json_schema_to_typescript) .unwrap_or_else(|| "unknown".to_string()); append_code_mode_sample( &definition.description, &definition.name, input_name, input_type, output_type, ) } fn render_code_mode_tool_declaration( tool_name: &str, input_name: &str, input_type: String, output_type: String, ) -> String { let tool_name = normalize_code_mode_identifier(tool_name); format!("{tool_name}({input_name}: {input_type}): Promise<{output_type}>;") } pub fn render_json_schema_to_typescript(schema: &JsonValue) -> String { render_json_schema_to_typescript_inner(schema) } fn render_json_schema_to_typescript_inner(schema: &JsonValue) -> String { match schema { JsonValue::Bool(true) => "unknown".to_string(), JsonValue::Bool(false) => "never".to_string(), JsonValue::Object(map) => { if let Some(value) = map.get("const") { return render_json_schema_literal(value); } if let Some(values) = map.get("enum").and_then(JsonValue::as_array) { let rendered = values .iter() .map(render_json_schema_literal) .collect::>(); if !rendered.is_empty() { return rendered.join(" | "); } } for key in ["anyOf", "oneOf"] { if let Some(variants) = map.get(key).and_then(JsonValue::as_array) { let rendered = variants .iter() .map(render_json_schema_to_typescript_inner) .collect::>(); if !rendered.is_empty() { return rendered.join(" | "); } } } if let Some(variants) = map.get("allOf").and_then(JsonValue::as_array) { let rendered = variants .iter() .map(render_json_schema_to_typescript_inner) .collect::>(); if !rendered.is_empty() { return rendered.join(" & "); } } if let Some(schema_type) = map.get("type") { if let Some(types) = schema_type.as_array() { let rendered = types .iter() .filter_map(JsonValue::as_str) .map(|schema_type| render_json_schema_type_keyword(map, schema_type)) .collect::>(); if !rendered.is_empty() { return rendered.join(" | "); } } if let Some(schema_type) = schema_type.as_str() { return render_json_schema_type_keyword(map, schema_type); } } if map.contains_key("properties") || map.contains_key("additionalProperties") || map.contains_key("required") { return render_json_schema_object(map); } if map.contains_key("items") || map.contains_key("prefixItems") { return render_json_schema_array(map); } "unknown".to_string() } _ => "unknown".to_string(), } } fn render_json_schema_type_keyword( map: &serde_json::Map, schema_type: &str, ) -> String { match schema_type { "string" => "string".to_string(), "number" | "integer" => "number".to_string(), "boolean" => "boolean".to_string(), "null" => "null".to_string(), "array" => render_json_schema_array(map), "object" => render_json_schema_object(map), _ => "unknown".to_string(), } } fn render_json_schema_array(map: &serde_json::Map) -> String { if let Some(items) = map.get("items") { let item_type = render_json_schema_to_typescript_inner(items); return format!("Array<{item_type}>"); } if let Some(items) = map.get("prefixItems").and_then(JsonValue::as_array) { let item_types = items .iter() .map(render_json_schema_to_typescript_inner) .collect::>(); if !item_types.is_empty() { return format!("[{}]", item_types.join(", ")); } } "unknown[]".to_string() } fn render_json_schema_object(map: &serde_json::Map) -> String { let required = map .get("required") .and_then(JsonValue::as_array) .map(|items| { items .iter() .filter_map(JsonValue::as_str) .collect::>() }) .unwrap_or_default(); let properties = map .get("properties") .and_then(JsonValue::as_object) .cloned() .unwrap_or_default(); let mut sorted_properties = properties.iter().collect::>(); sorted_properties.sort_unstable_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b)); let mut lines = sorted_properties .into_iter() .map(|(name, value)| { let optional = if required.iter().any(|required_name| required_name == name) { "" } else { "?" }; let property_name = render_json_schema_property_name(name); let property_type = render_json_schema_to_typescript_inner(value); format!("{property_name}{optional}: {property_type};") }) .collect::>(); if let Some(additional_properties) = map.get("additionalProperties") { let property_type = match additional_properties { JsonValue::Bool(true) => Some("unknown".to_string()), JsonValue::Bool(false) => None, value => Some(render_json_schema_to_typescript_inner(value)), }; if let Some(property_type) = property_type { lines.push(format!("[key: string]: {property_type};")); } } else if properties.is_empty() { lines.push("[key: string]: unknown;".to_string()); } if lines.is_empty() { return "{}".to_string(); } format!("{{ {} }}", lines.join(" ")) } fn render_json_schema_property_name(name: &str) -> String { if normalize_code_mode_identifier(name) == name { name.to_string() } else { serde_json::to_string(name).unwrap_or_else(|_| format!("\"{}\"", name.replace('"', "\\\""))) } } fn render_json_schema_literal(value: &JsonValue) -> String { serde_json::to_string(value).unwrap_or_else(|_| "unknown".to_string()) } #[cfg(test)] mod tests { use super::CodeModeToolKind; use super::ParsedExecSource; use super::ToolDefinition; use super::augment_tool_definition; use super::build_exec_tool_description; use super::normalize_code_mode_identifier; use super::parse_exec_source; use pretty_assertions::assert_eq; use serde_json::json; #[test] fn parse_exec_source_without_pragma() { assert_eq!( parse_exec_source("text('hi')").unwrap(), ParsedExecSource { code: "text('hi')".to_string(), yield_time_ms: None, max_output_tokens: None, } ); } #[test] fn parse_exec_source_with_pragma() { assert_eq!( parse_exec_source("// @exec: {\"yield_time_ms\": 10}\ntext('hi')").unwrap(), ParsedExecSource { code: "text('hi')".to_string(), yield_time_ms: Some(10), max_output_tokens: None, } ); } #[test] fn normalize_identifier_rewrites_invalid_characters() { assert_eq!( "mcp__ologs__get_profile", normalize_code_mode_identifier("mcp__ologs__get_profile") ); assert_eq!( "hidden_dynamic_tool", normalize_code_mode_identifier("hidden-dynamic-tool") ); } #[test] fn augment_tool_definition_appends_typed_declaration() { let definition = ToolDefinition { name: "hidden_dynamic_tool".to_string(), description: "Test tool".to_string(), kind: CodeModeToolKind::Function, input_schema: Some(json!({ "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"], "additionalProperties": false })), output_schema: Some(json!({ "type": "object", "properties": { "ok": { "type": "boolean" } }, "required": ["ok"] })), }; let description = augment_tool_definition(definition).description; assert!(description.contains("declare const tools")); assert!( description.contains( "hidden_dynamic_tool(args: { city: string; }): Promise<{ ok: boolean; }>;" ) ); } #[test] fn code_mode_only_description_includes_nested_tools() { let description = build_exec_tool_description( &[("foo".to_string(), "bar".to_string())], /*code_mode_only*/ true, ); assert!(description.contains("### `foo` (`foo`)")); } }