diff --git a/crates/client/src/language_execution.rs b/crates/client/src/language_execution.rs index 519a3b934d..2f8fe85248 100644 --- a/crates/client/src/language_execution.rs +++ b/crates/client/src/language_execution.rs @@ -84,8 +84,12 @@ pub struct InlineExecutionOptions { #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub enum JavaScriptModuleFormat { + /// Evaluate each call as an independent root ES module. A retained context + /// preserves `globalThis`, not the module's lexical scope. #[default] Module, + /// Use script/CommonJS semantics, including REPL-style top-level bindings + /// that remain visible to later calls in a retained context. CommonJs, } diff --git a/docs/content/docs/javascript.mdx b/docs/content/docs/javascript.mdx index 3951977f06..54503db4c4 100644 --- a/docs/content/docs/javascript.mdx +++ b/docs/content/docs/javascript.mdx @@ -44,8 +44,8 @@ interpolated into source. ## Keep state between calls -Pass a `contextId` to keep globals, imports, and modules alive across calls in -one retained V8 isolate. +Pass a `contextId` to keep one V8 isolate and its global state alive across +calls. @@ -53,6 +53,12 @@ A context runs one operation at a time — reusing a busy `contextId` fails immediately. Files, npm, Bash, and type checks may pass the same id, but they run in fresh processes and never touch retained memory. +JavaScript defaults to `format: "module"`. Each call is a separate root ES +module, so its imports, top-level declarations, and exports are scoped to that +call. Put values on `globalThis` when a later call in the same context needs +them. For REPL-style script semantics where top-level lexical declarations stay +visible, pass `format: "commonjs"` consistently for that context. + Create the context explicitly before use; an unknown id fails instead of silently starting fresh state. A context pins to the first inline language that used it, though JavaScript and TypeScript intentionally share one isolate. diff --git a/examples/js-sdk-overview/src/contexts.ts b/examples/js-sdk-overview/src/contexts.ts index 469f985843..d35ed31e66 100644 --- a/examples/js-sdk-overview/src/contexts.ts +++ b/examples/js-sdk-overview/src/contexts.ts @@ -5,13 +5,16 @@ const runtime = await AgentOs.create(); try { await runtime.createContext("analysis"); - await runtime.javascript.execute("const answer = 40", { + await runtime.javascript.execute("globalThis.answer = 40", { contextId: "analysis", }); - const result = await runtime.javascript.evaluate("answer + 2", { - contextId: "analysis", - }); + const result = await runtime.javascript.evaluate( + "globalThis.answer + 2", + { + contextId: "analysis", + }, + ); console.log(result.outcome === "succeeded" ? result.value : result.error); // 42 // Delete an idle context when you are done with it. `contexts.reset()` diff --git a/packages/core/src/language-execution.ts b/packages/core/src/language-execution.ts index 041f9a8eee..2a64638597 100644 --- a/packages/core/src/language-execution.ts +++ b/packages/core/src/language-execution.ts @@ -37,6 +37,11 @@ export interface InlineExecutionOptions extends LanguageExecutionOptions { } export interface JavaScriptExecutionOptions extends InlineExecutionOptions { + /** + * `module` (the default) evaluates each call as an independent root ES module. + * A retained context preserves `globalThis`, not the module's lexical scope. + * Use `commonjs` for REPL-style top-level bindings shared by later calls. + */ format?: "module" | "commonjs"; filePath?: string; }