Skip to main content

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