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}