| 1 | Author: Danny Milosavljevic <[email protected]>
|
| 2 | Date: 2026-04-13
|
| 3 | License: ASL2.0
|
| 4 |
|
| 5 | Make V8 an optional dependency in codex-code-mode.
|
| 6 |
|
| 7 | The V8 engine is only needed for actually executing JavaScript in the
|
| 8 | code-mode sandbox. The description helpers, tool name constants, and
|
| 9 | data types do not require V8. Gate the runtime and service modules
|
| 10 | behind a "v8-runtime" feature so that consumers like codex-acp that
|
| 11 | never use code-mode can avoid the V8 build dependency.
|
| 12 |
|
| 13 | |
| 14 | |
| 15 | @@ -12,6 +12,10 @@
|
| 16 | [lints]
|
| 17 | workspace = true
|
| 18 |
|
| 19 | +[features]
|
| 20 | +default = ["v8-runtime"]
|
| 21 | +v8-runtime = ["dep:v8"]
|
| 22 | +
|
| 23 | [dependencies]
|
| 24 | async-trait = { workspace = true }
|
| 25 | serde = { workspace = true, features = ["derive"] }
|
| 26 | @@ -19,7 +23,7 @@
|
| 27 | tokio = { workspace = true, features = ["macros", "rt", "sync", "time"] }
|
| 28 | tokio-util = { workspace = true, features = ["rt"] }
|
| 29 | tracing = { workspace = true }
|
| 30 | -v8 = { workspace = true }
|
| 31 | +v8 = { workspace = true, optional = true }
|
| 32 |
|
| 33 | [dev-dependencies]
|
| 34 | pretty_assertions = { workspace = true }
|
| 35 | |
| 36 | |
| 37 | @@ -1,6 +1,8 @@
|
| 38 | mod description;
|
| 39 | mod response;
|
| 40 | +#[cfg(feature = "v8-runtime")]
|
| 41 | mod runtime;
|
| 42 | +#[cfg(feature = "v8-runtime")]
|
| 43 | mod service;
|
| 44 |
|
| 45 | pub use description::CODE_MODE_PRAGMA_PREFIX;
|
| 46 | @@ -16,15 +18,82 @@
|
| 47 | pub use description::render_json_schema_to_typescript;
|
| 48 | pub use response::FunctionCallOutputContentItem;
|
| 49 | pub use response::ImageDetail;
|
| 50 | +
|
| 51 | +#[cfg(feature = "v8-runtime")]
|
| 52 | pub use runtime::DEFAULT_EXEC_YIELD_TIME_MS;
|
| 53 | +#[cfg(feature = "v8-runtime")]
|
| 54 | pub use runtime::DEFAULT_MAX_OUTPUT_TOKENS_PER_EXEC_CALL;
|
| 55 | +#[cfg(feature = "v8-runtime")]
|
| 56 | pub use runtime::DEFAULT_WAIT_YIELD_TIME_MS;
|
| 57 | +#[cfg(feature = "v8-runtime")]
|
| 58 | pub use runtime::ExecuteRequest;
|
| 59 | +#[cfg(feature = "v8-runtime")]
|
| 60 | pub use runtime::RuntimeResponse;
|
| 61 | +#[cfg(feature = "v8-runtime")]
|
| 62 | pub use runtime::WaitRequest;
|
| 63 | +#[cfg(feature = "v8-runtime")]
|
| 64 | pub use service::CodeModeService;
|
| 65 | +#[cfg(feature = "v8-runtime")]
|
| 66 | pub use service::CodeModeTurnHost;
|
| 67 | +#[cfg(feature = "v8-runtime")]
|
| 68 | pub use service::CodeModeTurnWorker;
|
| 69 |
|
| 70 | +// Stub types when V8 is not available.
|
| 71 | +#[cfg(not(feature = "v8-runtime"))]
|
| 72 | +pub const DEFAULT_EXEC_YIELD_TIME_MS: u64 = 10_000;
|
| 73 | +#[cfg(not(feature = "v8-runtime"))]
|
| 74 | +pub const DEFAULT_MAX_OUTPUT_TOKENS_PER_EXEC_CALL: usize = 10_000;
|
| 75 | +#[cfg(not(feature = "v8-runtime"))]
|
| 76 | +pub const DEFAULT_WAIT_YIELD_TIME_MS: u64 = 10_000;
|
| 77 | +
|
| 78 | +#[cfg(not(feature = "v8-runtime"))]
|
| 79 | +pub struct ExecuteRequest {
|
| 80 | + pub tool_call_id: String,
|
| 81 | + pub enabled_tools: Vec<ToolDefinition>,
|
| 82 | + pub source: String,
|
| 83 | + pub stored_values: std::collections::HashMap<String, serde_json::Value>,
|
| 84 | + pub yield_time_ms: Option<u64>,
|
| 85 | + pub max_output_tokens: Option<usize>,
|
| 86 | +}
|
| 87 | +
|
| 88 | +#[cfg(not(feature = "v8-runtime"))]
|
| 89 | +#[derive(Clone, Debug)]
|
| 90 | +pub struct WaitRequest {
|
| 91 | + pub cell_id: String,
|
| 92 | + pub yield_time_ms: u64,
|
| 93 | + pub terminate: bool,
|
| 94 | +}
|
| 95 | +
|
| 96 | +#[cfg(not(feature = "v8-runtime"))]
|
| 97 | +#[derive(Debug, PartialEq)]
|
| 98 | +pub enum RuntimeResponse {
|
| 99 | + Yielded { cell_id: String, content_items: Vec<FunctionCallOutputContentItem> },
|
| 100 | + Terminated { cell_id: String, content_items: Vec<FunctionCallOutputContentItem> },
|
| 101 | + Result { cell_id: String, content_items: Vec<FunctionCallOutputContentItem>, stored_values: std::collections::HashMap<String, serde_json::Value>, error_text: Option<String> },
|
| 102 | +}
|
| 103 | +
|
| 104 | +#[cfg(not(feature = "v8-runtime"))]
|
| 105 | +#[async_trait::async_trait]
|
| 106 | +pub trait CodeModeTurnHost: Send + Sync {
|
| 107 | + async fn invoke_tool(&self, tool_name: String, input: Option<serde_json::Value>, cancellation_token: tokio_util::sync::CancellationToken) -> Result<serde_json::Value, String>;
|
| 108 | + async fn notify(&self, call_id: String, cell_id: String, text: String) -> Result<(), String>;
|
| 109 | +}
|
| 110 | +
|
| 111 | +#[cfg(not(feature = "v8-runtime"))]
|
| 112 | +pub struct CodeModeService;
|
| 113 | +
|
| 114 | +#[cfg(not(feature = "v8-runtime"))]
|
| 115 | +impl CodeModeService {
|
| 116 | + pub fn new() -> Self { Self }
|
| 117 | + pub async fn stored_values(&self) -> std::collections::HashMap<String, serde_json::Value> { Default::default() }
|
| 118 | + pub async fn replace_stored_values(&self, _values: std::collections::HashMap<String, serde_json::Value>) {}
|
| 119 | + pub async fn execute(&self, _request: ExecuteRequest) -> Result<RuntimeResponse, String> { Err("code-mode requires v8-runtime feature".into()) }
|
| 120 | + pub async fn wait(&self, _request: WaitRequest) -> Result<RuntimeResponse, String> { Err("code-mode requires v8-runtime feature".into()) }
|
| 121 | + pub fn start_turn_worker(&self, _host: std::sync::Arc<dyn CodeModeTurnHost>) -> CodeModeTurnWorker { CodeModeTurnWorker }
|
| 122 | +}
|
| 123 | +
|
| 124 | +#[cfg(not(feature = "v8-runtime"))]
|
| 125 | +pub struct CodeModeTurnWorker;
|
| 126 | +
|
| 127 | pub const PUBLIC_TOOL_NAME: &str = "exec";
|
| 128 | pub const WAIT_TOOL_NAME: &str = "wait";
|