1use agentos_kernel::resource_accounting::ResourceLimits;
9use agentos_vm_config::{ResourceLimitsConfig, VmLimitsConfig};
10
11use crate::SidecarCoreError;
12
13pub const DEFAULT_MAX_FETCH_RESPONSE_BYTES: usize = 1024 * 1024;
16
17pub const DEFAULT_TOOL_TIMEOUT_MS: u64 = 30_000;
18pub const MAX_TOOL_TIMEOUT_MS: u64 = 300_000;
19pub const MAX_REGISTERED_TOOLKITS: usize = 64;
20pub const MAX_REGISTERED_TOOLS_PER_VM: usize = 256;
21pub const MAX_TOOLS_PER_TOOLKIT: usize = 64;
22pub const MAX_TOOL_SCHEMA_BYTES: usize = 16 * 1024;
23pub const MAX_TOOL_EXAMPLES_PER_TOOL: usize = 16;
24pub const MAX_TOOL_EXAMPLE_INPUT_BYTES: usize = 4 * 1024;
25
26pub const MAX_PERSISTED_MANIFEST_BYTES: usize = 64 * 1024 * 1024;
27pub const MAX_PERSISTED_MANIFEST_FILE_BYTES: u64 = 1024 * 1024 * 1024;
28
29pub const DEFAULT_ACP_MAX_READ_LINE_BYTES: usize = 16 * 1024 * 1024;
30pub const DEFAULT_ACP_STDOUT_BUFFER_BYTE_LIMIT: usize = 1024 * 1024;
31
32pub const DEFAULT_JS_CAPTURED_OUTPUT_LIMIT_BYTES: usize = 16 * 1024 * 1024;
33pub const DEFAULT_JS_STDIN_BUFFER_LIMIT_BYTES: usize = 16 * 1024 * 1024;
34pub const DEFAULT_JS_EVENT_PAYLOAD_LIMIT_BYTES: usize = 1024 * 1024;
35pub const DEFAULT_V8_IPC_MAX_FRAME_BYTES: u32 = 64 * 1024 * 1024;
36pub const DEFAULT_V8_HEAP_LIMIT_MB: u32 = 128;
37pub const DEFAULT_V8_CPU_TIME_LIMIT_MS: u32 = 30_000;
38pub const DEFAULT_V8_WALL_CLOCK_LIMIT_MS: u32 = 0;
39pub const DEFAULT_NODE_IMPORT_CACHE_MATERIALIZE_TIMEOUT_MS: u64 = 30_000;
40
41pub const DEFAULT_PYTHON_OUTPUT_BUFFER_MAX_BYTES: usize = 1024 * 1024;
42pub const DEFAULT_PYTHON_EXECUTION_TIMEOUT_MS: u64 = 5 * 60 * 1000;
43pub const DEFAULT_PYTHON_MAX_OLD_SPACE_MB: usize = 0;
45pub const DEFAULT_PYTHON_VFS_RPC_TIMEOUT_MS: u64 = 30 * 1000;
46
47pub const DEFAULT_WASM_MAX_MODULE_FILE_BYTES: u64 = 256 * 1024 * 1024;
48pub const DEFAULT_WASM_CAPTURED_OUTPUT_LIMIT_BYTES: usize = 16 * 1024 * 1024;
49pub const DEFAULT_WASM_SYNC_READ_LIMIT_BYTES: usize = 16 * 1024 * 1024;
50pub const DEFAULT_WASM_PREWARM_TIMEOUT_MS: u64 = 30_000;
51pub const DEFAULT_WASM_RUNNER_HEAP_LIMIT_MB: u32 = 2048;
52
53#[derive(Debug, Clone, PartialEq, Eq, Default)]
57pub struct VmLimits {
58 pub resources: ResourceLimits,
60 pub http: HttpLimits,
61 pub tools: ToolLimits,
62 pub plugins: PluginLimits,
63 pub acp: AcpLimits,
64 pub js_runtime: JsRuntimeLimits,
65 pub python: PythonLimits,
66 pub wasm: WasmLimits,
67}
68
69pub fn virtual_os_cpu_count(resource_limits: &ResourceLimits) -> usize {
70 resource_limits.virtual_cpu_count.unwrap_or(1).max(1)
71}
72
73pub fn virtual_os_totalmem_bytes(resource_limits: &ResourceLimits) -> u64 {
74 resource_limits
75 .max_wasm_memory_bytes
76 .unwrap_or(1024 * 1024 * 1024)
77}
78
79pub fn virtual_os_freemem_bytes(resource_limits: &ResourceLimits) -> u64 {
80 resource_limits
81 .max_wasm_memory_bytes
82 .unwrap_or(512 * 1024 * 1024)
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct HttpLimits {
87 pub max_fetch_response_bytes: usize,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct ToolLimits {
93 pub default_tool_timeout_ms: u64,
94 pub max_tool_timeout_ms: u64,
95 pub max_registered_toolkits: usize,
96 pub max_registered_tools_per_vm: usize,
97 pub max_tools_per_toolkit: usize,
98 pub max_tool_schema_bytes: usize,
99 pub max_tool_examples_per_tool: usize,
100 pub max_tool_example_input_bytes: usize,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct PluginLimits {
105 pub max_persisted_manifest_bytes: usize,
106 pub max_persisted_manifest_file_bytes: u64,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
110pub struct AcpLimits {
111 pub max_read_line_bytes: usize,
113 pub stdout_buffer_byte_limit: usize,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct JsRuntimeLimits {
119 pub v8_heap_limit_mb: Option<u32>,
123 pub sync_rpc_wait_timeout_ms: Option<u64>,
125 pub cpu_time_limit_ms: u32,
127 pub wall_clock_limit_ms: u32,
129 pub import_cache_materialize_timeout_ms: u64,
131 pub captured_output_limit_bytes: usize,
132 pub stdin_buffer_limit_bytes: usize,
133 pub event_payload_limit_bytes: usize,
134 pub v8_ipc_max_frame_bytes: u32,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct PythonLimits {
141 pub output_buffer_max_bytes: usize,
142 pub execution_timeout_ms: u64,
143 pub max_old_space_mb: usize,
145 pub vfs_rpc_timeout_ms: u64,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct WasmLimits {
150 pub max_module_file_bytes: u64,
151 pub captured_output_limit_bytes: usize,
152 pub sync_read_limit_bytes: usize,
154 pub prewarm_timeout_ms: u64,
156 pub runner_heap_limit_mb: u32,
158}
159
160impl Default for HttpLimits {
161 fn default() -> Self {
162 Self {
163 max_fetch_response_bytes: DEFAULT_MAX_FETCH_RESPONSE_BYTES,
164 }
165 }
166}
167
168impl Default for ToolLimits {
169 fn default() -> Self {
170 Self {
171 default_tool_timeout_ms: DEFAULT_TOOL_TIMEOUT_MS,
172 max_tool_timeout_ms: MAX_TOOL_TIMEOUT_MS,
173 max_registered_toolkits: MAX_REGISTERED_TOOLKITS,
174 max_registered_tools_per_vm: MAX_REGISTERED_TOOLS_PER_VM,
175 max_tools_per_toolkit: MAX_TOOLS_PER_TOOLKIT,
176 max_tool_schema_bytes: MAX_TOOL_SCHEMA_BYTES,
177 max_tool_examples_per_tool: MAX_TOOL_EXAMPLES_PER_TOOL,
178 max_tool_example_input_bytes: MAX_TOOL_EXAMPLE_INPUT_BYTES,
179 }
180 }
181}
182
183impl Default for PluginLimits {
184 fn default() -> Self {
185 Self {
186 max_persisted_manifest_bytes: MAX_PERSISTED_MANIFEST_BYTES,
187 max_persisted_manifest_file_bytes: MAX_PERSISTED_MANIFEST_FILE_BYTES,
188 }
189 }
190}
191
192impl Default for AcpLimits {
193 fn default() -> Self {
194 Self {
195 max_read_line_bytes: DEFAULT_ACP_MAX_READ_LINE_BYTES,
196 stdout_buffer_byte_limit: DEFAULT_ACP_STDOUT_BUFFER_BYTE_LIMIT,
197 }
198 }
199}
200
201impl Default for JsRuntimeLimits {
202 fn default() -> Self {
203 Self {
204 v8_heap_limit_mb: Some(DEFAULT_V8_HEAP_LIMIT_MB),
207 sync_rpc_wait_timeout_ms: None,
208 cpu_time_limit_ms: DEFAULT_V8_CPU_TIME_LIMIT_MS,
209 wall_clock_limit_ms: DEFAULT_V8_WALL_CLOCK_LIMIT_MS,
210 import_cache_materialize_timeout_ms: DEFAULT_NODE_IMPORT_CACHE_MATERIALIZE_TIMEOUT_MS,
211 captured_output_limit_bytes: DEFAULT_JS_CAPTURED_OUTPUT_LIMIT_BYTES,
212 stdin_buffer_limit_bytes: DEFAULT_JS_STDIN_BUFFER_LIMIT_BYTES,
213 event_payload_limit_bytes: DEFAULT_JS_EVENT_PAYLOAD_LIMIT_BYTES,
214 v8_ipc_max_frame_bytes: DEFAULT_V8_IPC_MAX_FRAME_BYTES,
215 }
216 }
217}
218
219impl Default for PythonLimits {
220 fn default() -> Self {
221 Self {
222 output_buffer_max_bytes: DEFAULT_PYTHON_OUTPUT_BUFFER_MAX_BYTES,
223 execution_timeout_ms: DEFAULT_PYTHON_EXECUTION_TIMEOUT_MS,
224 max_old_space_mb: DEFAULT_PYTHON_MAX_OLD_SPACE_MB,
225 vfs_rpc_timeout_ms: DEFAULT_PYTHON_VFS_RPC_TIMEOUT_MS,
226 }
227 }
228}
229
230impl Default for WasmLimits {
231 fn default() -> Self {
232 Self {
233 max_module_file_bytes: DEFAULT_WASM_MAX_MODULE_FILE_BYTES,
234 captured_output_limit_bytes: DEFAULT_WASM_CAPTURED_OUTPUT_LIMIT_BYTES,
235 sync_read_limit_bytes: DEFAULT_WASM_SYNC_READ_LIMIT_BYTES,
236 prewarm_timeout_ms: DEFAULT_WASM_PREWARM_TIMEOUT_MS,
237 runner_heap_limit_mb: DEFAULT_WASM_RUNNER_HEAP_LIMIT_MB,
238 }
239 }
240}
241
242pub fn vm_limits_from_config(
243 config: Option<&VmLimitsConfig>,
244 sidecar_max_frame_bytes: usize,
245) -> Result<VmLimits, SidecarCoreError> {
246 let mut limits = VmLimits::default();
247 let Some(config) = config else {
248 validate_vm_limits(&limits, sidecar_max_frame_bytes)?;
249 return Ok(limits);
250 };
251
252 if let Some(resources) = config.resources.as_ref() {
253 apply_resource_limits_config(&mut limits.resources, resources)?;
254 }
255 if let Some(http) = config.http.as_ref() {
256 set_usize(
257 &mut limits.http.max_fetch_response_bytes,
258 http.max_fetch_response_bytes,
259 "limits.http.maxFetchResponseBytes",
260 )?;
261 }
262 if let Some(tools) = config.tools.as_ref() {
263 set_u64(
264 &mut limits.tools.default_tool_timeout_ms,
265 tools.default_tool_timeout_ms,
266 "limits.tools.defaultToolTimeoutMs",
267 )?;
268 set_u64(
269 &mut limits.tools.max_tool_timeout_ms,
270 tools.max_tool_timeout_ms,
271 "limits.tools.maxToolTimeoutMs",
272 )?;
273 set_usize(
274 &mut limits.tools.max_registered_toolkits,
275 tools.max_registered_toolkits,
276 "limits.tools.maxRegisteredToolkits",
277 )?;
278 set_usize(
279 &mut limits.tools.max_registered_tools_per_vm,
280 tools.max_registered_tools_per_vm,
281 "limits.tools.maxRegisteredToolsPerVm",
282 )?;
283 set_usize(
284 &mut limits.tools.max_tools_per_toolkit,
285 tools.max_tools_per_toolkit,
286 "limits.tools.maxToolsPerToolkit",
287 )?;
288 set_usize(
289 &mut limits.tools.max_tool_schema_bytes,
290 tools.max_tool_schema_bytes,
291 "limits.tools.maxToolSchemaBytes",
292 )?;
293 set_usize(
294 &mut limits.tools.max_tool_examples_per_tool,
295 tools.max_tool_examples_per_tool,
296 "limits.tools.maxToolExamplesPerTool",
297 )?;
298 set_usize(
299 &mut limits.tools.max_tool_example_input_bytes,
300 tools.max_tool_example_input_bytes,
301 "limits.tools.maxToolExampleInputBytes",
302 )?;
303 }
304 if let Some(plugins) = config.plugins.as_ref() {
305 set_usize(
306 &mut limits.plugins.max_persisted_manifest_bytes,
307 plugins.max_persisted_manifest_bytes,
308 "limits.plugins.maxPersistedManifestBytes",
309 )?;
310 set_u64(
311 &mut limits.plugins.max_persisted_manifest_file_bytes,
312 plugins.max_persisted_manifest_file_bytes,
313 "limits.plugins.maxPersistedManifestFileBytes",
314 )?;
315 }
316 if let Some(acp) = config.acp.as_ref() {
317 set_usize(
318 &mut limits.acp.max_read_line_bytes,
319 acp.max_read_line_bytes,
320 "limits.acp.maxReadLineBytes",
321 )?;
322 set_usize(
323 &mut limits.acp.stdout_buffer_byte_limit,
324 acp.stdout_buffer_byte_limit,
325 "limits.acp.stdoutBufferByteLimit",
326 )?;
327 }
328 if let Some(js_runtime) = config.js_runtime.as_ref() {
329 if let Some(value) = js_runtime.v8_heap_limit_mb {
330 limits.js_runtime.v8_heap_limit_mb = Some(
331 u32::try_from(value)
332 .map_err(|_| integer_too_large("limits.jsRuntime.v8HeapLimitMb", value))?,
333 );
334 }
335 if let Some(value) = js_runtime.cpu_time_limit_ms {
336 limits.js_runtime.cpu_time_limit_ms = u32::try_from(value)
337 .map_err(|_| integer_too_large("limits.jsRuntime.cpuTimeLimitMs", value))?;
338 }
339 if let Some(value) = js_runtime.wall_clock_limit_ms {
340 limits.js_runtime.wall_clock_limit_ms = u32::try_from(value)
341 .map_err(|_| integer_too_large("limits.jsRuntime.wallClockLimitMs", value))?;
342 }
343 set_u64(
344 &mut limits.js_runtime.import_cache_materialize_timeout_ms,
345 js_runtime.import_cache_materialize_timeout_ms,
346 "limits.jsRuntime.importCacheMaterializeTimeoutMs",
347 )?;
348 set_usize(
349 &mut limits.js_runtime.captured_output_limit_bytes,
350 js_runtime.captured_output_limit_bytes,
351 "limits.jsRuntime.capturedOutputLimitBytes",
352 )?;
353 set_usize(
354 &mut limits.js_runtime.stdin_buffer_limit_bytes,
355 js_runtime.stdin_buffer_limit_bytes,
356 "limits.jsRuntime.stdinBufferLimitBytes",
357 )?;
358 set_usize(
359 &mut limits.js_runtime.event_payload_limit_bytes,
360 js_runtime.event_payload_limit_bytes,
361 "limits.jsRuntime.eventPayloadLimitBytes",
362 )?;
363 if let Some(value) = js_runtime.v8_ipc_max_frame_bytes {
364 limits.js_runtime.v8_ipc_max_frame_bytes = u32::try_from(value)
365 .map_err(|_| integer_too_large("limits.jsRuntime.v8IpcMaxFrameBytes", value))?;
366 }
367 if let Some(value) = js_runtime.sync_rpc_wait_timeout_ms {
368 limits.js_runtime.sync_rpc_wait_timeout_ms = Some(value);
369 }
370 }
371 if let Some(python) = config.python.as_ref() {
372 set_usize(
373 &mut limits.python.output_buffer_max_bytes,
374 python.output_buffer_max_bytes,
375 "limits.python.outputBufferMaxBytes",
376 )?;
377 set_u64(
378 &mut limits.python.execution_timeout_ms,
379 python.execution_timeout_ms,
380 "limits.python.executionTimeoutMs",
381 )?;
382 set_usize(
383 &mut limits.python.max_old_space_mb,
384 python.max_old_space_mb,
385 "limits.python.maxOldSpaceMb",
386 )?;
387 set_u64(
388 &mut limits.python.vfs_rpc_timeout_ms,
389 python.vfs_rpc_timeout_ms,
390 "limits.python.vfsRpcTimeoutMs",
391 )?;
392 }
393 if let Some(wasm) = config.wasm.as_ref() {
394 set_u64(
395 &mut limits.wasm.max_module_file_bytes,
396 wasm.max_module_file_bytes,
397 "limits.wasm.maxModuleFileBytes",
398 )?;
399 set_usize(
400 &mut limits.wasm.captured_output_limit_bytes,
401 wasm.captured_output_limit_bytes,
402 "limits.wasm.capturedOutputLimitBytes",
403 )?;
404 set_usize(
405 &mut limits.wasm.sync_read_limit_bytes,
406 wasm.sync_read_limit_bytes,
407 "limits.wasm.syncReadLimitBytes",
408 )?;
409 set_u64(
410 &mut limits.wasm.prewarm_timeout_ms,
411 wasm.prewarm_timeout_ms,
412 "limits.wasm.prewarmTimeoutMs",
413 )?;
414 if let Some(value) = wasm.runner_heap_limit_mb {
415 limits.wasm.runner_heap_limit_mb = u32::try_from(value)
416 .map_err(|_| integer_too_large("limits.wasm.runnerHeapLimitMb", value))?;
417 }
418 }
419
420 validate_vm_limits(&limits, sidecar_max_frame_bytes)?;
421 Ok(limits)
422}
423
424fn apply_resource_limits_config(
425 limits: &mut ResourceLimits,
426 config: &ResourceLimitsConfig,
427) -> Result<(), SidecarCoreError> {
428 set_optional_usize(
429 &mut limits.virtual_cpu_count,
430 config.cpu_count,
431 "limits.resources.cpuCount",
432 )?;
433 set_optional_usize(
434 &mut limits.max_processes,
435 config.max_processes,
436 "limits.resources.maxProcesses",
437 )?;
438 set_optional_usize(
439 &mut limits.max_open_fds,
440 config.max_open_fds,
441 "limits.resources.maxOpenFds",
442 )?;
443 set_optional_usize(
444 &mut limits.max_pipes,
445 config.max_pipes,
446 "limits.resources.maxPipes",
447 )?;
448 set_optional_usize(
449 &mut limits.max_ptys,
450 config.max_ptys,
451 "limits.resources.maxPtys",
452 )?;
453 set_optional_usize(
454 &mut limits.max_sockets,
455 config.max_sockets,
456 "limits.resources.maxSockets",
457 )?;
458 set_optional_usize(
459 &mut limits.max_connections,
460 config.max_connections,
461 "limits.resources.maxConnections",
462 )?;
463 set_optional_usize(
464 &mut limits.max_socket_buffered_bytes,
465 config.max_socket_buffered_bytes,
466 "limits.resources.maxSocketBufferedBytes",
467 )?;
468 set_optional_usize(
469 &mut limits.max_socket_datagram_queue_len,
470 config.max_socket_datagram_queue_len,
471 "limits.resources.maxSocketDatagramQueueLen",
472 )?;
473 set_optional_u64(
474 &mut limits.max_filesystem_bytes,
475 config.max_filesystem_bytes,
476 );
477 set_optional_usize(
478 &mut limits.max_inode_count,
479 config.max_inode_count,
480 "limits.resources.maxInodeCount",
481 )?;
482 set_optional_u64(
483 &mut limits.max_blocking_read_ms,
484 config.max_blocking_read_ms,
485 );
486 set_optional_usize(
487 &mut limits.max_pread_bytes,
488 config.max_pread_bytes,
489 "limits.resources.maxPreadBytes",
490 )?;
491 set_optional_usize(
492 &mut limits.max_fd_write_bytes,
493 config.max_fd_write_bytes,
494 "limits.resources.maxFdWriteBytes",
495 )?;
496 set_optional_usize(
497 &mut limits.max_process_argv_bytes,
498 config.max_process_argv_bytes,
499 "limits.resources.maxProcessArgvBytes",
500 )?;
501 set_optional_usize(
502 &mut limits.max_process_env_bytes,
503 config.max_process_env_bytes,
504 "limits.resources.maxProcessEnvBytes",
505 )?;
506 set_optional_usize(
507 &mut limits.max_readdir_entries,
508 config.max_readdir_entries,
509 "limits.resources.maxReaddirEntries",
510 )?;
511 set_optional_usize(
512 &mut limits.max_recursive_fs_depth,
513 config.max_recursive_fs_depth,
514 "limits.resources.maxRecursiveFsDepth",
515 )?;
516 set_optional_usize(
517 &mut limits.max_recursive_fs_entries,
518 config.max_recursive_fs_entries,
519 "limits.resources.maxRecursiveFsEntries",
520 )?;
521 set_optional_u64(&mut limits.max_wasm_fuel, config.max_wasm_fuel);
522 set_optional_u64(
523 &mut limits.max_wasm_memory_bytes,
524 config.max_wasm_memory_bytes,
525 );
526 set_optional_usize(
527 &mut limits.max_wasm_stack_bytes,
528 config.max_wasm_stack_bytes,
529 "limits.resources.maxWasmStackBytes",
530 )?;
531 Ok(())
532}
533
534fn set_usize(target: &mut usize, value: Option<u64>, key: &str) -> Result<(), SidecarCoreError> {
535 if let Some(value) = value {
536 *target = usize::try_from(value).map_err(|_| integer_too_large(key, value))?;
537 }
538 Ok(())
539}
540
541fn set_u64(target: &mut u64, value: Option<u64>, _key: &str) -> Result<(), SidecarCoreError> {
542 if let Some(value) = value {
543 *target = value;
544 }
545 Ok(())
546}
547
548fn set_optional_usize(
549 target: &mut Option<usize>,
550 value: Option<u64>,
551 key: &str,
552) -> Result<(), SidecarCoreError> {
553 if let Some(value) = value {
554 *target = Some(usize::try_from(value).map_err(|_| integer_too_large(key, value))?);
555 }
556 Ok(())
557}
558
559fn set_optional_u64(target: &mut Option<u64>, value: Option<u64>) {
560 if let Some(value) = value {
561 *target = Some(value);
562 }
563}
564
565fn integer_too_large(key: &str, value: u64) -> SidecarCoreError {
566 SidecarCoreError::new(format!("{key} value {value} does not fit this platform"))
567}
568
569pub fn validate_vm_limits(
572 limits: &VmLimits,
573 sidecar_max_frame_bytes: usize,
574) -> Result<(), SidecarCoreError> {
575 if limits.http.max_fetch_response_bytes == 0 {
576 return Err(SidecarCoreError::new(
577 "limits.http.max_fetch_response_bytes must be greater than zero".to_string(),
578 ));
579 }
580 if limits.http.max_fetch_response_bytes > sidecar_max_frame_bytes {
581 return Err(SidecarCoreError::new(format!(
582 "limits.http.max_fetch_response_bytes ({}) must be <= the sidecar wire frame cap ({})",
583 limits.http.max_fetch_response_bytes, sidecar_max_frame_bytes
584 )));
585 }
586
587 if limits.tools.default_tool_timeout_ms > limits.tools.max_tool_timeout_ms {
588 return Err(SidecarCoreError::new(format!(
589 "limits.tools.default_tool_timeout_ms ({}) must be <= limits.tools.max_tool_timeout_ms ({})",
590 limits.tools.default_tool_timeout_ms, limits.tools.max_tool_timeout_ms
591 )));
592 }
593
594 let nonzero_usize: [(&str, usize); 13] = [
595 (
596 "limits.tools.max_registered_toolkits",
597 limits.tools.max_registered_toolkits,
598 ),
599 (
600 "limits.tools.max_registered_tools_per_vm",
601 limits.tools.max_registered_tools_per_vm,
602 ),
603 (
604 "limits.tools.max_tools_per_toolkit",
605 limits.tools.max_tools_per_toolkit,
606 ),
607 (
608 "limits.tools.max_tool_schema_bytes",
609 limits.tools.max_tool_schema_bytes,
610 ),
611 (
612 "limits.tools.max_tool_example_input_bytes",
613 limits.tools.max_tool_example_input_bytes,
614 ),
615 (
616 "limits.plugins.max_persisted_manifest_bytes",
617 limits.plugins.max_persisted_manifest_bytes,
618 ),
619 (
620 "limits.acp.max_read_line_bytes",
621 limits.acp.max_read_line_bytes,
622 ),
623 (
624 "limits.acp.stdout_buffer_byte_limit",
625 limits.acp.stdout_buffer_byte_limit,
626 ),
627 (
628 "limits.js_runtime.captured_output_limit_bytes",
629 limits.js_runtime.captured_output_limit_bytes,
630 ),
631 (
632 "limits.js_runtime.stdin_buffer_limit_bytes",
633 limits.js_runtime.stdin_buffer_limit_bytes,
634 ),
635 (
636 "limits.js_runtime.event_payload_limit_bytes",
637 limits.js_runtime.event_payload_limit_bytes,
638 ),
639 (
640 "limits.python.output_buffer_max_bytes",
641 limits.python.output_buffer_max_bytes,
642 ),
643 (
644 "limits.wasm.captured_output_limit_bytes",
645 limits.wasm.captured_output_limit_bytes,
646 ),
647 ];
648 for (key, value) in nonzero_usize {
649 if value == 0 {
650 return Err(SidecarCoreError::new(format!(
651 "{key} must be greater than zero"
652 )));
653 }
654 }
655
656 if limits.wasm.sync_read_limit_bytes == 0 {
657 return Err(SidecarCoreError::new(
658 "limits.wasm.sync_read_limit_bytes must be greater than zero".to_string(),
659 ));
660 }
661 if limits.wasm.prewarm_timeout_ms == 0 {
662 return Err(SidecarCoreError::new(
663 "limits.wasm.prewarm_timeout_ms must be greater than zero".to_string(),
664 ));
665 }
666 if limits.wasm.runner_heap_limit_mb == 0 {
667 return Err(SidecarCoreError::new(
668 "limits.wasm.runner_heap_limit_mb must be greater than zero".to_string(),
669 ));
670 }
671 if limits.wasm.max_module_file_bytes == 0 {
672 return Err(SidecarCoreError::new(
673 "limits.wasm.max_module_file_bytes must be greater than zero".to_string(),
674 ));
675 }
676 if limits.js_runtime.v8_ipc_max_frame_bytes == 0 {
677 return Err(SidecarCoreError::new(
678 "limits.js_runtime.v8_ipc_max_frame_bytes must be greater than zero".to_string(),
679 ));
680 }
681 if limits.python.execution_timeout_ms == 0 {
682 return Err(SidecarCoreError::new(
683 "limits.python.execution_timeout_ms must be greater than zero".to_string(),
684 ));
685 }
686 if limits.python.vfs_rpc_timeout_ms == 0 {
687 return Err(SidecarCoreError::new(
688 "limits.python.vfs_rpc_timeout_ms must be greater than zero".to_string(),
689 ));
690 }
691 if let Some(0) = limits.js_runtime.v8_heap_limit_mb {
692 return Err(SidecarCoreError::new(
693 "limits.js_runtime.v8_heap_limit_mb must be greater than zero".to_string(),
694 ));
695 }
696 if limits.js_runtime.import_cache_materialize_timeout_ms == 0 {
697 return Err(SidecarCoreError::new(
698 "limits.js_runtime.import_cache_materialize_timeout_ms must be greater than zero"
699 .to_string(),
700 ));
701 }
702
703 Ok(())
704}