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;
}