adk_code/types.rs
1//! Core execution types for the code execution substrate.
2//!
3//! This module defines the typed primitives shared by all execution backends,
4//! language-preset tools, and Studio integration:
5//!
6//! - [`ExecutionLanguage`] — supported execution languages
7//! - [`ExecutionPayload`] — source code or guest module bytes
8//! - [`ExecutionIsolation`] — backend isolation class
9//! - [`SandboxPolicy`] — requested sandbox controls
10//! - [`BackendCapabilities`] — what a backend can actually enforce
11//! - [`ExecutionRequest`] — full execution request
12//! - [`ExecutionResult`] — structured execution outcome
13//! - [`ExecutionStatus`] — terminal execution status
14
15use serde::{Deserialize, Serialize};
16use serde_json::Value;
17use std::path::PathBuf;
18use std::time::Duration;
19
20/// One megabyte in bytes, used as the default stdout/stderr limit.
21const ONE_MB: usize = 1_048_576;
22
23/// Supported execution languages.
24///
25/// `Rust` is the primary first-class language. Other languages are available
26/// through appropriate backends.
27///
28/// # Example
29///
30/// ```rust
31/// use adk_code::ExecutionLanguage;
32///
33/// let lang = ExecutionLanguage::Rust;
34/// assert_eq!(lang, ExecutionLanguage::Rust);
35/// ```
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub enum ExecutionLanguage {
39 /// Rust — the primary authored-code path.
40 Rust,
41 /// JavaScript — secondary scripting and transform support.
42 JavaScript,
43 /// WebAssembly guest module execution.
44 Wasm,
45 /// Python — in-process Monty execution (`embedded-python` feature) or
46 /// container-backed execution.
47 Python,
48 /// Raw command execution (shell, interpreter, etc.).
49 Command,
50}
51
52impl std::fmt::Display for ExecutionLanguage {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 Self::Rust => write!(f, "Rust"),
56 Self::JavaScript => write!(f, "JavaScript"),
57 Self::Wasm => write!(f, "Wasm"),
58 Self::Python => write!(f, "Python"),
59 Self::Command => write!(f, "Command"),
60 }
61 }
62}
63
64/// Format of a precompiled guest module.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66pub enum GuestModuleFormat {
67 /// WebAssembly binary format.
68 Wasm,
69}
70
71/// The code or module to execute.
72///
73/// Source payloads carry inline code strings. Guest module payloads carry
74/// precompiled binary modules (e.g., `.wasm` files).
75///
76/// # Example
77///
78/// ```rust
79/// use adk_code::ExecutionPayload;
80///
81/// let payload = ExecutionPayload::Source {
82/// code: "fn run(input: serde_json::Value) -> serde_json::Value { input }".to_string(),
83/// };
84/// ```
85#[derive(Debug, Clone)]
86pub enum ExecutionPayload {
87 /// Inline source code to compile and/or interpret.
88 Source {
89 /// The source code string.
90 code: String,
91 },
92 /// A precompiled guest module (e.g., WASM).
93 GuestModule {
94 /// The binary format of the guest module.
95 format: GuestModuleFormat,
96 /// The raw module bytes.
97 bytes: Vec<u8>,
98 },
99}
100
101/// Backend isolation class.
102///
103/// Makes the isolation model explicit so that host-local and container-backed
104/// execution cannot be presented as equivalent.
105///
106/// # Example
107///
108/// ```rust
109/// use adk_code::ExecutionIsolation;
110///
111/// let iso = ExecutionIsolation::ContainerEphemeral;
112/// assert_ne!(iso, ExecutionIsolation::HostLocal);
113/// ```
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub enum ExecutionIsolation {
117 /// Execution runs in the same process (e.g., embedded JS engine).
118 InProcess,
119 /// Execution runs as a local host process without strong OS isolation.
120 HostLocal,
121 /// Execution runs in an ephemeral container destroyed after completion.
122 ContainerEphemeral,
123 /// Execution runs in a persistent container that survives across requests.
124 ContainerPersistent,
125 /// Execution runs on a remote provider-hosted service.
126 ProviderHosted,
127}
128
129/// Network access policy for sandboxed execution.
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131pub enum NetworkPolicy {
132 /// No network access allowed.
133 Disabled,
134 /// Network access is permitted.
135 Enabled,
136}
137
138/// Filesystem access policy for sandboxed execution.
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub enum FilesystemPolicy {
141 /// No filesystem access.
142 None,
143 /// Read-only access to a workspace root.
144 WorkspaceReadOnly {
145 /// The workspace root path.
146 root: PathBuf,
147 },
148 /// Read-write access to a workspace root.
149 WorkspaceReadWrite {
150 /// The workspace root path.
151 root: PathBuf,
152 },
153 /// Explicit path-level access control.
154 Paths {
155 /// Paths with read-only access.
156 read_only: Vec<PathBuf>,
157 /// Paths with read-write access.
158 read_write: Vec<PathBuf>,
159 },
160}
161
162/// Environment variable access policy for sandboxed execution.
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub enum EnvironmentPolicy {
165 /// No environment variables exposed.
166 None,
167 /// Only the listed environment variable names are exposed.
168 AllowList(Vec<String>),
169}
170
171/// Sandbox policy describing the requested execution constraints.
172///
173/// Backends compare this policy against their [`BackendCapabilities`] and
174/// reject execution if they cannot enforce a requested control.
175///
176/// # Example
177///
178/// ```rust
179/// use adk_code::SandboxPolicy;
180///
181/// let policy = SandboxPolicy::strict_rust();
182/// assert_eq!(policy.max_stdout_bytes, 1_048_576);
183/// ```
184#[derive(Debug, Clone)]
185pub struct SandboxPolicy {
186 /// Network access policy.
187 pub network: NetworkPolicy,
188 /// Filesystem access policy.
189 pub filesystem: FilesystemPolicy,
190 /// Environment variable access policy.
191 pub environment: EnvironmentPolicy,
192 /// Maximum execution duration.
193 pub timeout: Duration,
194 /// Maximum bytes captured from stdout before truncation.
195 pub max_stdout_bytes: usize,
196 /// Maximum bytes captured from stderr before truncation.
197 pub max_stderr_bytes: usize,
198 /// Working directory for execution, if any.
199 pub working_directory: Option<PathBuf>,
200}
201
202impl SandboxPolicy {
203 /// Strict policy for Rust sandbox execution.
204 ///
205 /// - No network access
206 /// - No filesystem access
207 /// - No environment variables
208 /// - 30-second timeout
209 /// - 1 MB stdout/stderr limits
210 pub fn strict_rust() -> Self {
211 Self {
212 network: NetworkPolicy::Disabled,
213 filesystem: FilesystemPolicy::None,
214 environment: EnvironmentPolicy::None,
215 timeout: Duration::from_secs(30),
216 max_stdout_bytes: ONE_MB,
217 max_stderr_bytes: ONE_MB,
218 working_directory: None,
219 }
220 }
221
222 /// Host-local policy for backends that run on the host without isolation.
223 ///
224 /// Unlike [`strict_rust`](Self::strict_rust), this policy uses
225 /// `NetworkPolicy::Enabled` and `FilesystemPolicy::None` so that
226 /// host-local backends (which cannot enforce network or filesystem
227 /// restrictions) pass policy validation. The trade-off is that the
228 /// executed code has the same network and filesystem access as the
229 /// host process.
230 ///
231 /// - Network access: allowed (host-local cannot restrict)
232 /// - Filesystem access: none requested
233 /// - Environment variables: none exposed
234 /// - 30-second timeout
235 /// - 1 MB stdout/stderr limits
236 pub fn host_local() -> Self {
237 Self {
238 network: NetworkPolicy::Enabled,
239 filesystem: FilesystemPolicy::None,
240 environment: EnvironmentPolicy::None,
241 timeout: Duration::from_secs(30),
242 max_stdout_bytes: ONE_MB,
243 max_stderr_bytes: ONE_MB,
244 working_directory: None,
245 }
246 }
247
248 /// Strict policy for embedded JavaScript execution.
249 ///
250 /// Same defaults as Rust but with a shorter 5-second timeout,
251 /// appropriate for lightweight transforms and scripting.
252 pub fn strict_js() -> Self {
253 Self {
254 network: NetworkPolicy::Disabled,
255 filesystem: FilesystemPolicy::None,
256 environment: EnvironmentPolicy::None,
257 timeout: Duration::from_secs(5),
258 max_stdout_bytes: ONE_MB,
259 max_stderr_bytes: ONE_MB,
260 working_directory: None,
261 }
262 }
263
264 /// Strict policy for embedded Python (Monty) execution.
265 ///
266 /// - No network access (Monty has no network surface regardless)
267 /// - No filesystem access
268 /// - No environment variables
269 /// - 30-second timeout
270 /// - 1 MB stdout/stderr limits
271 pub fn strict_python() -> Self {
272 Self {
273 network: NetworkPolicy::Disabled,
274 filesystem: FilesystemPolicy::None,
275 environment: EnvironmentPolicy::None,
276 timeout: Duration::from_secs(30),
277 max_stdout_bytes: ONE_MB,
278 max_stderr_bytes: ONE_MB,
279 working_directory: None,
280 }
281 }
282}
283
284impl Default for SandboxPolicy {
285 /// Sensible defaults: no network, no filesystem, no env vars, 30s timeout, 1 MB limits.
286 fn default() -> Self {
287 Self::strict_rust()
288 }
289}
290
291/// Capabilities that a backend can actually enforce.
292///
293/// This makes the isolation model explicit so callers and docs can distinguish
294/// what a backend claims from what it can guarantee.
295///
296/// # Example
297///
298/// ```rust
299/// use adk_code::{BackendCapabilities, ExecutionIsolation};
300///
301/// let caps = BackendCapabilities {
302/// isolation: ExecutionIsolation::ContainerEphemeral,
303/// enforce_network_policy: true,
304/// enforce_filesystem_policy: true,
305/// enforce_environment_policy: true,
306/// enforce_timeout: true,
307/// supports_structured_output: true,
308/// supports_process_execution: false,
309/// supports_persistent_workspace: false,
310/// supports_interactive_sessions: false,
311/// };
312/// assert!(caps.enforce_network_policy);
313/// ```
314#[derive(Debug, Clone, PartialEq, Eq)]
315pub struct BackendCapabilities {
316 /// The isolation class this backend provides.
317 pub isolation: ExecutionIsolation,
318 /// Whether the backend can enforce network restrictions.
319 pub enforce_network_policy: bool,
320 /// Whether the backend can enforce filesystem restrictions.
321 pub enforce_filesystem_policy: bool,
322 /// Whether the backend can enforce environment variable restrictions.
323 pub enforce_environment_policy: bool,
324 /// Whether the backend can enforce execution timeouts.
325 pub enforce_timeout: bool,
326 /// Whether the backend supports structured JSON output.
327 pub supports_structured_output: bool,
328 /// Whether the backend supports spawning child processes.
329 pub supports_process_execution: bool,
330 /// Whether the backend supports persistent workspaces across requests.
331 pub supports_persistent_workspace: bool,
332 /// Whether the backend supports interactive/REPL-style sessions.
333 pub supports_interactive_sessions: bool,
334}
335
336/// A full execution request.
337///
338/// Combines language, payload, sandbox policy, optional I/O, and identity
339/// into a single typed request that backends can validate and execute.
340///
341/// # Example
342///
343/// ```rust
344/// use adk_code::{ExecutionRequest, ExecutionLanguage, ExecutionPayload, SandboxPolicy};
345///
346/// let request = ExecutionRequest {
347/// language: ExecutionLanguage::Rust,
348/// payload: ExecutionPayload::Source {
349/// code: r#"fn run(input: serde_json::Value) -> serde_json::Value { input }"#.to_string(),
350/// },
351/// argv: vec![],
352/// stdin: None,
353/// input: None,
354/// sandbox: SandboxPolicy::strict_rust(),
355/// identity: None,
356/// };
357/// ```
358#[derive(Debug, Clone)]
359pub struct ExecutionRequest {
360 /// The target execution language.
361 pub language: ExecutionLanguage,
362 /// The code or module to execute.
363 pub payload: ExecutionPayload,
364 /// Command-line arguments passed to the executed program.
365 pub argv: Vec<String>,
366 /// Optional stdin bytes fed to the executed program.
367 pub stdin: Option<Vec<u8>>,
368 /// Optional structured JSON input injected through a controlled harness.
369 pub input: Option<Value>,
370 /// The sandbox policy for this execution.
371 pub sandbox: SandboxPolicy,
372 /// Optional execution identity for audit and telemetry correlation.
373 pub identity: Option<String>,
374}
375
376/// Terminal status of an execution.
377///
378/// Distinguishes compile failures from runtime failures, timeouts, and rejections.
379///
380/// # Example
381///
382/// ```rust
383/// use adk_code::ExecutionStatus;
384///
385/// let status = ExecutionStatus::CompileFailed;
386/// assert_ne!(status, ExecutionStatus::Failed);
387/// ```
388#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
389#[serde(rename_all = "camelCase")]
390pub enum ExecutionStatus {
391 /// Execution completed successfully.
392 Success,
393 /// Execution exceeded the configured timeout.
394 Timeout,
395 /// Compilation or build step failed (distinct from runtime failure).
396 CompileFailed,
397 /// Runtime execution failed.
398 Failed,
399 /// Execution was rejected before running (policy or scope check).
400 Rejected,
401}
402
403/// Execution metadata for telemetry, audit, and artifact correlation.
404///
405/// Captures backend name, language, duration, status, and correlation identity
406/// so that executions can be traced and audited across sessions and invocations.
407///
408/// # Example
409///
410/// ```rust
411/// use adk_code::{ExecutionMetadata, ExecutionLanguage, ExecutionStatus, ExecutionIsolation};
412///
413/// let meta = ExecutionMetadata {
414/// backend_name: "rust-sandbox".to_string(),
415/// language: ExecutionLanguage::Rust,
416/// isolation: ExecutionIsolation::HostLocal,
417/// status: ExecutionStatus::Success,
418/// duration_ms: 42,
419/// identity: Some("inv-123".to_string()),
420/// artifact_refs: vec![],
421/// };
422/// assert_eq!(meta.backend_name, "rust-sandbox");
423/// ```
424#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
425#[serde(rename_all = "camelCase")]
426pub struct ExecutionMetadata {
427 /// Name of the backend that executed the request.
428 pub backend_name: String,
429 /// Language that was executed.
430 pub language: ExecutionLanguage,
431 /// Isolation class of the backend.
432 pub isolation: ExecutionIsolation,
433 /// Terminal execution status.
434 pub status: ExecutionStatus,
435 /// Execution wall-clock duration in milliseconds.
436 pub duration_ms: u64,
437 /// Correlation identity (invocation ID, session ID, etc.) when available.
438 pub identity: Option<String>,
439 /// References to artifacts stored externally (e.g., large outputs).
440 pub artifact_refs: Vec<ArtifactRef>,
441}
442
443/// Reference to an externally stored artifact.
444///
445/// When execution output exceeds inline size limits, the result can reference
446/// artifacts stored through ADK artifact mechanisms instead of forcing large
447/// binary data into inline JSON strings.
448///
449/// # Example
450///
451/// ```rust
452/// use adk_code::ArtifactRef;
453///
454/// let artifact = ArtifactRef {
455/// key: "stdout-full".to_string(),
456/// size_bytes: 2_000_000,
457/// content_type: Some("text/plain".to_string()),
458/// };
459/// assert_eq!(artifact.key, "stdout-full");
460/// ```
461#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
462#[serde(rename_all = "camelCase")]
463pub struct ArtifactRef {
464 /// Artifact storage key or identifier.
465 pub key: String,
466 /// Size of the artifact in bytes.
467 pub size_bytes: u64,
468 /// MIME content type, if known.
469 pub content_type: Option<String>,
470}
471
472/// Structured result of a code execution.
473///
474/// Captures stdout, stderr, structured output, truncation flags, exit code,
475/// duration, and optional execution metadata so downstream consumers can
476/// reason about outcomes reliably.
477///
478/// # Example
479///
480/// ```rust
481/// use adk_code::{ExecutionResult, ExecutionStatus};
482///
483/// let result = ExecutionResult {
484/// status: ExecutionStatus::Success,
485/// stdout: "hello\n".to_string(),
486/// stderr: String::new(),
487/// output: Some(serde_json::json!({ "answer": 42 })),
488/// exit_code: Some(0),
489/// stdout_truncated: false,
490/// stderr_truncated: false,
491/// duration_ms: 37,
492/// metadata: None,
493/// };
494/// assert_eq!(result.status, ExecutionStatus::Success);
495/// ```
496#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
497#[serde(rename_all = "camelCase")]
498pub struct ExecutionResult {
499 /// Terminal execution status.
500 pub status: ExecutionStatus,
501 /// Captured stdout text (may be truncated).
502 pub stdout: String,
503 /// Captured stderr text (may be truncated).
504 pub stderr: String,
505 /// Optional structured JSON output from the executed code.
506 pub output: Option<Value>,
507 /// Process exit code, if available.
508 pub exit_code: Option<i32>,
509 /// Whether stdout was truncated due to size limits.
510 pub stdout_truncated: bool,
511 /// Whether stderr was truncated due to size limits.
512 pub stderr_truncated: bool,
513 /// Execution wall-clock duration in milliseconds.
514 pub duration_ms: u64,
515 /// Optional execution metadata for telemetry and audit.
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub metadata: Option<ExecutionMetadata>,
518}