Skip to main content

soma_provider_adapters/
codemode.rs

1//! Thin bridge from a drop-in provider tool onto `soma-codemode`'s sandboxed
2//! JS snippet runner, satisfying plan section 3.9's
3//! `provider-adapters::codemode delegates to soma-codemode` bridge.
4//!
5//! There is exactly one Code Mode execution engine in the workspace
6//! (`soma_codemode::execute::execute_inline`, which spawns the bounded
7//! `soma-codemode-runner` subprocess); this adapter calls it directly rather
8//! than re-implementing any part of the runner, sandbox, or result-shaping
9//! pipeline.
10//!
11//! `soma_provider_core::ProviderKind` has no `CodeMode` variant yet, so this
12//! adapter is not reachable through [`crate::manifest_file::build_provider`]'s
13//! drop-in-manifest kind dispatch — it is real, compiles, and is
14//! unit-tested, but wiring a `ProviderKind::CodeMode` drop-in manifest shape
15//! is a schema change appropriately scoped to a follow-up.
16
17use std::sync::{Arc, Mutex};
18
19use async_trait::async_trait;
20use serde_json::json;
21use soma_codemode::{execute::execute_inline, CodeModeConfig, UiLink};
22use soma_provider_core::{Provider, ProviderCall, ProviderCatalog, ProviderError, ProviderOutput};
23
24/// Runs one fixed JS snippet on every call, ignoring `call.params` beyond
25/// exposing them to the snippet as `soma.input` is out of scope for this
26/// thin bridge — the snippet itself decides what, if anything, to do with
27/// arguments passed through the surrounding provider manifest.
28#[derive(Clone)]
29pub struct CodeModeSnippetProvider {
30    catalog: ProviderCatalog,
31    code: String,
32    config: CodeModeConfig,
33}
34
35impl CodeModeSnippetProvider {
36    pub fn new(catalog: ProviderCatalog, code: impl Into<String>, config: CodeModeConfig) -> Self {
37        Self {
38            catalog,
39            code: code.into(),
40            config,
41        }
42    }
43
44    pub fn arc(
45        catalog: ProviderCatalog,
46        code: impl Into<String>,
47        config: CodeModeConfig,
48    ) -> Arc<Self> {
49        Arc::new(Self::new(catalog, code, config))
50    }
51}
52
53#[async_trait]
54impl Provider for CodeModeSnippetProvider {
55    fn catalog(&self) -> ProviderCatalog {
56        self.catalog.clone()
57    }
58
59    async fn call(&self, call: ProviderCall) -> Result<ProviderOutput, ProviderError> {
60        let ui_capture: Arc<Mutex<Option<UiLink>>> = Arc::new(Mutex::new(None));
61        let outcome = execute_inline(&self.code, self.config.clone(), ui_capture)
62            .await
63            .map_err(|error| {
64                ProviderError::execution(&self.catalog.provider.name, call.action.clone(), error)
65                    .with_provider_kind("codemode")
66            })?;
67        Ok(ProviderOutput::json(json!({
68            "result": outcome.display_response.result,
69            "logs": outcome.display_response.logs,
70        })))
71    }
72}
73
74#[cfg(test)]
75#[path = "codemode_tests.rs"]
76mod tests;