(
source: &str,
inputs: serde_json::Value,
registry: Arc<ToolRegistry>,
ctx: ToolContext,
allowed_tools: HashSet<String>,
limits: ScriptLimits,
)
| 262 | } |
| 263 | |
| 264 | async fn run_quickjs_script( |
| 265 | source: &str, |
| 266 | inputs: serde_json::Value, |
| 267 | registry: Arc<ToolRegistry>, |
| 268 | ctx: ToolContext, |
| 269 | allowed_tools: HashSet<String>, |
| 270 | limits: ScriptLimits, |
| 271 | ) -> Result<ToolOutput> { |
| 272 | // A script that can delegate runs child agents (each a full LLM turn, often |
| 273 | // 30s to several minutes), so the 30s default is far too short and silently |
| 274 | // times out real workflows. Default delegation-capable scripts to a generous |
| 275 | // timeout; pure compute/search scripts keep the short default. An explicit |
| 276 | // limits.timeoutMs always wins. |
| 277 | let delegating = allowed_tools.contains("parallel_task") || allowed_tools.contains("task"); |
| 278 | let timeout_ms = limits.timeout_ms.unwrap_or(if delegating { |
| 279 | DELEGATION_SCRIPT_TIMEOUT_MS |
| 280 | } else { |
| 281 | DEFAULT_SCRIPT_TIMEOUT_MS |
| 282 | }); |
| 283 | let max_tool_calls = limits |
| 284 | .max_tool_calls |
| 285 | .unwrap_or(DEFAULT_SCRIPT_MAX_TOOL_CALLS); |
| 286 | let max_output_bytes = limits |
| 287 | .max_output_bytes |
| 288 | .unwrap_or(DEFAULT_SCRIPT_MAX_OUTPUT_BYTES); |
| 289 | let executable_source = script_source_with_host_entrypoint(source)?; |
| 290 | // Captured on the outer multi-threaded runtime (we're async here, before the |
| 291 | // VM's nested single-thread runtime is built) so host tool calls fan out. |
| 292 | let outer = tokio::runtime::Handle::current(); |
| 293 | let state = Arc::new(Mutex::new(ScriptVmState { |
| 294 | registry, |
| 295 | ctx, |
| 296 | allowed_tools, |
| 297 | max_tool_calls, |
| 298 | max_output_bytes, |
| 299 | tool_calls: 0, |
| 300 | records: Vec::new(), |
| 301 | outer, |
| 302 | })); |
| 303 | |
| 304 | let vm_state = Arc::clone(&state); |
| 305 | let result = timeout( |
| 306 | Duration::from_millis(timeout_ms), |
| 307 | tokio::task::spawn_blocking(move || { |
| 308 | let runtime = tokio::runtime::Builder::new_current_thread() |
| 309 | .enable_all() |
| 310 | .build() |
| 311 | .map_err(|err| anyhow!("failed to create program VM runtime: {err}"))?; |
| 312 | runtime.block_on(run_embedded_script( |
| 313 | executable_source, |
| 314 | inputs, |
| 315 | vm_state, |
| 316 | timeout_ms, |
| 317 | )) |
| 318 | }), |
| 319 | ) |
| 320 | .await; |
| 321 |
no test coverage detected