Ver código fonte

refactor(codemode): rename Sandbox terminology to CodeMode (#36768)

Aiden Cline 4 semanas atrás
pai
commit
ecb5754f4c

+ 2 - 2
packages/codemode/AGENTS.md

@@ -16,8 +16,8 @@
 
 ## Future Design Notes
 
-- If a captured user-visible output channel returns (an earlier `output.text`/`output.file`/`output.image` API was removed from v1), keep `output` as its name, distinct from the program return value: `return` stays the structured result for the model, while `output.*` describes artifacts the host may render into a conversation or UI after execution. Keep this host-neutral and let applications decide how captured output is delivered. In v1, hosts collect media host-side (outside the sandbox) instead.
-- Improve the sandbox failure taxonomy. Distinguish parse/compile mistakes, unsupported syntax, user-thrown errors, invalid returned data, tool refusal, tool internal failure, timeout, and genuine runtime defects so agents can recover accurately instead of treating everything as a generic execution failure.
+- If a captured user-visible output channel returns (an earlier `output.text`/`output.file`/`output.image` API was removed from v1), keep `output` as its name, distinct from the program return value: `return` stays the structured result for the model, while `output.*` describes artifacts the host may render into a conversation or UI after execution. Keep this host-neutral and let applications decide how captured output is delivered. In v1, hosts collect media host-side (outside CodeMode) instead.
+- Improve the failure taxonomy. Distinguish parse/compile mistakes, unsupported syntax, user-thrown errors, invalid returned data, tool refusal, tool internal failure, timeout, and genuine runtime defects so agents can recover accurately instead of treating everything as a generic execution failure.
 - Preserve the public/private error split. Tool authors should be able to return a safe model-visible message while retaining a private cause for host diagnostics. Unknown host failures must remain sanitized by default.
 - Think deliberately about richer binary boundaries before allowing `Blob`, `File`, `ArrayBuffer`, streams, or typed arrays beyond today's JSON-like values. If CodeMode supports binary tool args/results, use explicit tagged data shapes and clear size limits rather than relying on ambient runtime serialization.
 - Keep host capabilities explicit. Globals such as `fetch`, `crypto`, filesystem handles, extra modules, or network clients should be opt-in runtime capabilities with obvious policy defaults, not ambient authority. Default to unavailable unless a host deliberately provides the capability.

+ 4 - 4
packages/codemode/interpreter-support.md

@@ -20,7 +20,7 @@ ultimate source of truth.
 - [x] Top-level `await` and `return` through the program's implicit async-function scope.
 - [x] Explicit `return`, final top-level expression as a REPL-style result, and `null` when no value is produced.
 - [x] JSON-like host boundaries with `undefined` and non-finite numbers normalized to `null`.
-- [x] Live Date, RegExp, Map, Set, URL, and URLSearchParams values inside the sandbox.
+- [x] Live Date, RegExp, Map, Set, URL, and URLSearchParams values inside CodeMode.
 - [x] Tool calls through the host-provided `tools` tree only.
 - [x] The global `search(...)` built-in: synchronous tool discovery that counts as an admitted tool call and is
       shadowable by program declarations like other globals.
@@ -94,7 +94,7 @@ ultimate source of truth.
 - [x] Optional property access and optional calls.
 - [x] Function/tool calls and spread arguments.
 - [x] Sequence expressions (the comma operator).
-- [x] `await` for sandbox promises; a plain value passes through unchanged, though every `await` still defers its
+- [x] `await` for CodeMode promises; a plain value passes through unchanged, though every `await` still defers its
       continuation one reaction turn.
 - [x] `new` for Error types, Date, RegExp, Map, Set, URL, URLSearchParams, and Promise.
 - [x] Arithmetic operators: `+`, `-`, `*`, `/`, `%`, and `**`.
@@ -109,7 +109,7 @@ ultimate source of truth.
 
 ## Promises and tools
 
-- [x] Tool calls start eagerly and return supervised, run-once sandbox promises.
+- [x] Tool calls start eagerly and return supervised, run-once CodeMode promises.
 - [x] Direct `await`, repeated awaits, and implicit resolution when a promise is returned from a function/program.
 - [x] `Promise.resolve` and `Promise.reject`.
 - [x] `Promise.all`, `Promise.allSettled`, `Promise.race`, and `Promise.any` over supported collections containing
@@ -148,7 +148,7 @@ ultimate source of truth.
 - [x] Computed property names and object spread.
 - [x] `Object.keys`, `Object.values`, `Object.entries`, `Object.hasOwn`, `Object.assign`, and `Object.fromEntries`.
 - [x] `Object.keys` over arrays and tool references.
-- [x] Object identity is preserved by in-sandbox Object helpers.
+- [x] Object identity is preserved by in-CodeMode Object helpers.
 - [x] Blocked access to `__proto__`, `constructor`, and `prototype`.
 - [ ] `Object.is`; runtime and tool-reference identity semantics need to be defined first.
 - [ ] `Object.groupBy`.

+ 25 - 25
packages/codemode/src/interpreter/methods.ts

@@ -13,13 +13,13 @@ import {
 import { rejectCircularInsertion } from "./references.js"
 import { isBlockedMember, type SafeObject } from "../tool-runtime.js"
 import {
-  SandboxDate,
-  SandboxMap,
-  SandboxPromise,
-  SandboxRegExp,
-  SandboxSet,
-  SandboxURL,
-  SandboxURLSearchParams,
+  CodeModeDate,
+  CodeModeMap,
+  CodeModePromise,
+  CodeModeRegExp,
+  CodeModeSet,
+  CodeModeURL,
+  CodeModeURLSearchParams,
 } from "../values.js"
 import { invokeDateMethod, invokeDateStatic } from "../stdlib/date.js"
 import { invokeJsonMethod } from "../stdlib/json.js"
@@ -33,7 +33,7 @@ import { boundedData, coerceToNumber, coerceToString, invokeCoercion } from "../
 
 export type CallbackRunner<R> = {
   readonly invokeFunction: (fn: CodeModeFunction, args: Array<unknown>) => Effect.Effect<unknown, unknown, R>
-  readonly settlePromise: (promise: SandboxPromise) => Effect.Effect<unknown, unknown, never>
+  readonly settlePromise: (promise: CodeModePromise) => Effect.Effect<unknown, unknown, never>
 }
 
 export const invokeIntrinsic = <R>(
@@ -57,22 +57,22 @@ export const invokeIntrinsic = <R>(
   if (Array.isArray(ref.receiver)) {
     return invokeArrayMethod(runner, ref.receiver, ref.name, args, node)
   }
-  if (ref.receiver instanceof SandboxDate) {
+  if (ref.receiver instanceof CodeModeDate) {
     return Effect.succeed(invokeDateMethod(ref.receiver, ref.name, node))
   }
-  if (ref.receiver instanceof SandboxRegExp) {
+  if (ref.receiver instanceof CodeModeRegExp) {
     return Effect.succeed(invokeRegExpMethod(ref.receiver, ref.name, args, node))
   }
-  if (ref.receiver instanceof SandboxMap) {
+  if (ref.receiver instanceof CodeModeMap) {
     return invokeMapMethod(runner, ref.receiver, ref.name, args, node)
   }
-  if (ref.receiver instanceof SandboxSet) {
+  if (ref.receiver instanceof CodeModeSet) {
     return invokeSetMethod(runner, ref.receiver, ref.name, args, node)
   }
-  if (ref.receiver instanceof SandboxURL) {
+  if (ref.receiver instanceof CodeModeURL) {
     return Effect.succeed(invokeURLMethod(ref.receiver, ref.name, node))
   }
-  if (ref.receiver instanceof SandboxURLSearchParams) {
+  if (ref.receiver instanceof CodeModeURLSearchParams) {
     return invokeURLSearchParamsMethod(runner, ref.receiver, ref.name, args, node)
   }
   throw new InterpreterRuntimeError(`Method '${ref.name}' is not available in CodeMode.`, node)
@@ -153,7 +153,7 @@ const invokeStringMethod = (value: string, name: string, args: Array<unknown>, n
         result = [value]
         break
       }
-      if (args[0] instanceof SandboxRegExp) {
+      if (args[0] instanceof CodeModeRegExp) {
         result = value.split(args[0].regex, optNum(1))
         break
       }
@@ -181,7 +181,7 @@ const invokeStringMethod = (value: string, name: string, args: Array<unknown>, n
       break
     case "replace":
     case "replaceAll": {
-      if (args[0] instanceof SandboxRegExp) {
+      if (args[0] instanceof CodeModeRegExp) {
         const pattern = args[0].regex
         const replacement = str(1)
         if (name === "replaceAll" && !pattern.global) {
@@ -278,13 +278,13 @@ const invokeArrayStatic = (name: string, args: Array<unknown>, node: AstNode): u
           [supportedSyntaxMessage],
         )
       }
-      if (args[0] instanceof SandboxMap) return Array.from(args[0].map.entries(), ([key, item]) => [key, item])
-      if (args[0] instanceof SandboxSet) return Array.from(args[0].set.values())
-      if (args[0] instanceof SandboxURLSearchParams) {
+      if (args[0] instanceof CodeModeMap) return Array.from(args[0].map.entries(), ([key, item]) => [key, item])
+      if (args[0] instanceof CodeModeSet) return Array.from(args[0].set.values())
+      if (args[0] instanceof CodeModeURLSearchParams) {
         return Array.from(args[0].params.entries(), ([key, value]) => [key, value])
       }
       const source = args[0]
-      if (source instanceof SandboxPromise) {
+      if (source instanceof CodeModePromise) {
         throw new InterpreterRuntimeError(
           "Array.from received an un-awaited Promise; await it before creating the array.",
           node,
@@ -341,7 +341,7 @@ const invokeStringReplacer = <R>(
   }
 
   const pattern = args[0]
-  if (pattern instanceof SandboxRegExp) {
+  if (pattern instanceof CodeModeRegExp) {
     if (name === "replaceAll" && !pattern.regex.global) {
       throw new InterpreterRuntimeError(
         `String.replaceAll requires a regular expression with the global (g) flag: write /${pattern.regex.source}/${pattern.regex.flags}g, or use String.replace to replace only the first match.`,
@@ -364,7 +364,7 @@ const invokeStringReplacer = <R>(
     for (const match of matches) {
       const replacement = yield* apply(match.args)
       const resolved =
-        args[1] instanceof CodeModeFunction && args[1].async && replacement instanceof SandboxPromise
+        args[1] instanceof CodeModeFunction && args[1].async && replacement instanceof CodeModePromise
           ? yield* runner.settlePromise(replacement)
           : replacement
       output.push(
@@ -404,7 +404,7 @@ export const applyCollectionCallback = <R>(
 
 const invokeMapMethod = <R>(
   runner: CallbackRunner<R>,
-  target: SandboxMap,
+  target: CodeModeMap,
   name: string,
   args: Array<unknown>,
   node: AstNode,
@@ -446,7 +446,7 @@ const invokeMapMethod = <R>(
 
 const invokeSetMethod = <R>(
   runner: CallbackRunner<R>,
-  target: SandboxSet,
+  target: CodeModeSet,
   name: string,
   args: Array<unknown>,
   node: AstNode,
@@ -485,7 +485,7 @@ const invokeSetMethod = <R>(
 
 const invokeURLSearchParamsMethod = <R>(
   runner: CallbackRunner<R>,
-  target: SandboxURLSearchParams,
+  target: CodeModeURLSearchParams,
   name: string,
   args: Array<unknown>,
   node: AstNode,

+ 3 - 3
packages/codemode/src/interpreter/model.ts

@@ -1,5 +1,5 @@
 import type { SafeObject } from "../tool-runtime.js"
-import type { SandboxPromise, SandboxURL } from "../values.js"
+import type { CodeModePromise, CodeModeURL } from "../values.js"
 
 export type SourcePosition = {
   line: number
@@ -35,7 +35,7 @@ export type StatementResult =
   | { kind: "continue" }
 
 export type MemberReference = {
-  target: SafeObject | Array<unknown> | SandboxURL
+  target: SafeObject | Array<unknown> | CodeModeURL
   key: string | number
 }
 
@@ -71,7 +71,7 @@ export type PromiseInstanceMethodName = "then" | "catch" | "finally"
 
 export class PromiseInstanceMethodReference {
   constructor(
-    readonly promise: SandboxPromise,
+    readonly promise: CodeModePromise,
     readonly name: PromiseInstanceMethodName,
   ) {}
 }

+ 26 - 26
packages/codemode/src/interpreter/promises.ts

@@ -17,24 +17,24 @@ import { applyCollectionCallback, type CallbackRunner } from "./methods.js"
 import { typeofValue } from "./references.js"
 import { spreadItems } from "../stdlib/collections.js"
 import { createAggregateErrorValue } from "../stdlib/value.js"
-import { SandboxPromise } from "../values.js"
+import { CodeModePromise } from "../values.js"
 
 // Observation only controls rejection reporting; program completion interrupts all promise work.
 export class PromiseRuntime<R> {
-  private readonly active = new Set<SandboxPromise>()
-  private readonly ids = new WeakMap<SandboxPromise, number>()
-  private readonly observed = new WeakSet<SandboxPromise>()
+  private readonly active = new Set<CodeModePromise>()
+  private readonly ids = new WeakMap<CodeModePromise, number>()
+  private readonly observed = new WeakSet<CodeModePromise>()
   private readonly failures = new Map<number, Diagnostic>()
   private nextID = 0
 
   constructor(private readonly scope: Scope.Scope) {}
 
-  create(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<SandboxPromise, never, R> {
+  create(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<CodeModePromise, never, R> {
     return Effect.suspend(() => {
       // Allocate before forking so reruns get distinct IDs and diagnostics retain creation order.
       const id = this.nextID++
       return Effect.map(Effect.forkIn(effect, this.scope, { startImmediately: true }), (fiber) => {
-        const promise = new SandboxPromise(fiber)
+        const promise = new CodeModePromise(fiber)
         this.active.add(promise)
         this.ids.set(promise, id)
         fiber.addObserver((exit) => {
@@ -55,14 +55,14 @@ export class PromiseRuntime<R> {
   }
 
   // Observation must be recorded when responsibility transfers, before the consumer fiber runs.
-  markObserved(promise: SandboxPromise): void {
+  markObserved(promise: CodeModePromise): void {
     this.observed.add(promise)
     const id = this.ids.get(promise)
     this.ids.delete(promise)
     if (id !== undefined) this.failures.delete(id)
   }
 
-  await(promise: SandboxPromise): Effect.Effect<Exit.Exit<unknown, unknown>> {
+  await(promise: CodeModePromise): Effect.Effect<Exit.Exit<unknown, unknown>> {
     return Fiber.await(promise.fiber)
   }
 
@@ -94,7 +94,7 @@ export const invokePromiseMethod = <R>(
 ): Effect.Effect<unknown, unknown, R> => {
   if (ref.name === "resolve") {
     const value = args[0]
-    return value instanceof SandboxPromise ? Effect.succeed(value) : promises.create(Effect.succeed(value))
+    return value instanceof CodeModePromise ? Effect.succeed(value) : promises.create(Effect.succeed(value))
   }
   if (ref.name === "reject") {
     return promises.create(Effect.fail(new ProgramThrow(args[0])))
@@ -114,19 +114,19 @@ export const invokePromiseMethod = <R>(
   const items = Array.from(spread)
 
   for (const item of items) {
-    if (item instanceof SandboxPromise) promises.markObserved(item)
+    if (item instanceof CodeModePromise) promises.markObserved(item)
   }
 
   switch (ref.name) {
     case "all": {
       const observations = items.map((item) =>
-        item instanceof SandboxPromise ? Effect.flatten(promises.await(item)) : Effect.succeed(item),
+        item instanceof CodeModePromise ? Effect.flatten(promises.await(item)) : Effect.succeed(item),
       )
       return promises.create(settleAfterTurn(Effect.all(observations, { concurrency: "unbounded" })))
     }
     case "allSettled": {
       const observations = items.map((item) =>
-        item instanceof SandboxPromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
+        item instanceof CodeModePromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
       )
       return promises.create(
         settleAfterTurn(
@@ -168,13 +168,13 @@ export const invokePromiseMethod = <R>(
         )
       }
       const observations = items.map((item) =>
-        item instanceof SandboxPromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
+        item instanceof CodeModePromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
       )
       return promises.create(settleAfterTurn(Effect.flatten(Effect.raceAll(observations))))
     }
     case "any": {
       const flipped = items.map((item) =>
-        item instanceof SandboxPromise
+        item instanceof CodeModePromise
           ? Effect.flatMap(promises.await(item), (exit) => {
               if (Exit.isSuccess(exit)) return Effect.fail(new PromiseAnyFulfilled(exit.value))
               if (Cause.hasInterruptsOnly(exit.cause)) return Effect.failCause(exit.cause)
@@ -201,7 +201,7 @@ export const invokePromiseInstanceMethod = <R>(
   ref: PromiseInstanceMethodReference,
   args: Array<unknown>,
   node: AstNode,
-): Effect.Effect<SandboxPromise, never, R> => {
+): Effect.Effect<CodeModePromise, never, R> => {
   const method = `Promise.prototype.${ref.name}`
   promises.markObserved(ref.promise)
   if (ref.name === "finally") {
@@ -217,7 +217,7 @@ export const constructPromise = <R>(
   promises: PromiseRuntime<R>,
   executor: unknown,
   node: AstNode,
-): Effect.Effect<SandboxPromise, unknown, R> => {
+): Effect.Effect<CodeModePromise, unknown, R> => {
   if (!(executor instanceof CodeModeFunction)) {
     throw new InterpreterRuntimeError(
       "new Promise(...) expects an executor function (e.g. new Promise((resolve, reject) => { ... })).",
@@ -226,10 +226,10 @@ export const constructPromise = <R>(
   }
   return Effect.gen(function* () {
     const deferred = Deferred.makeUnsafe<unknown, unknown>()
-    const box: { own?: SandboxPromise } = {}
+    const box: { own?: CodeModePromise } = {}
     const promise = yield* promises.create(
       Effect.flatMap(Deferred.await(deferred), (value) => {
-        if (!(value instanceof SandboxPromise)) return Effect.succeed(value)
+        if (!(value instanceof CodeModePromise)) return Effect.succeed(value)
         if (value === box.own) return Effect.fail(selfResolutionError(node))
         return runner.settlePromise(value)
       }),
@@ -281,7 +281,7 @@ const reactionHandler = (value: unknown, method: string, node: AstNode): Reactio
 // Teardown bypasses handlers; settled reactions yield once so handlers never run inline.
 const reactionExit = <R>(
   promises: PromiseRuntime<R>,
-  source: SandboxPromise,
+  source: CodeModePromise,
 ): Effect.Effect<Exit.Exit<unknown, unknown>, unknown, R> =>
   Effect.gen(function* () {
     const exit = yield* promises.await(source)
@@ -293,13 +293,13 @@ const reactionExit = <R>(
 const chainReaction = <R>(
   runner: CallbackRunner<R>,
   promises: PromiseRuntime<R>,
-  source: SandboxPromise,
+  source: CodeModePromise,
   onFulfilled: ReactionHandler | undefined,
   onRejected: ReactionHandler | undefined,
   method: string,
   node: AstNode,
-): Effect.Effect<SandboxPromise, never, R> => {
-  const box: { derived?: SandboxPromise } = {}
+): Effect.Effect<CodeModePromise, never, R> => {
+  const box: { derived?: CodeModePromise } = {}
   const body = Effect.gen(function* () {
     const exit = yield* reactionExit(promises, source)
     const handler = Exit.isSuccess(exit) ? onFulfilled : onRejected
@@ -307,7 +307,7 @@ const chainReaction = <R>(
     const input = Exit.isSuccess(exit) ? exit.value : caughtErrorValue(Cause.squash(exit.cause))
     const result = yield* applyCollectionCallback(runner, handler, method, node)([input])
     if (result === box.derived) return yield* Effect.fail(selfResolutionError(node))
-    if (result instanceof SandboxPromise) return yield* runner.settlePromise(result)
+    if (result instanceof CodeModePromise) return yield* runner.settlePromise(result)
     return result
   })
   return Effect.map(promises.create(body), (derived) => {
@@ -319,17 +319,17 @@ const chainReaction = <R>(
 const chainFinally = <R>(
   runner: CallbackRunner<R>,
   promises: PromiseRuntime<R>,
-  source: SandboxPromise,
+  source: CodeModePromise,
   cleanup: ReactionHandler | undefined,
   method: string,
   node: AstNode,
-): Effect.Effect<SandboxPromise, never, R> =>
+): Effect.Effect<CodeModePromise, never, R> =>
   promises.create(
     Effect.gen(function* () {
       const exit = yield* reactionExit(promises, source)
       if (cleanup !== undefined) {
         const result = yield* applyCollectionCallback(runner, cleanup, method, node)([])
-        if (result instanceof SandboxPromise) yield* runner.settlePromise(result)
+        if (result instanceof CodeModePromise) yield* runner.settlePromise(result)
       }
       return yield* exit
     }),

+ 5 - 5
packages/codemode/src/interpreter/references.ts

@@ -15,7 +15,7 @@ import {
   UriFunction,
 } from "./model.js"
 import { ToolReference } from "../tool-runtime.js"
-import { isSandboxValue, SandboxPromise } from "../values.js"
+import { isCodeModeValue, CodeModePromise } from "../values.js"
 
 export const isRuntimeReference = (value: unknown): boolean =>
   value instanceof CodeModeFunction ||
@@ -26,13 +26,13 @@ export const isRuntimeReference = (value: unknown): boolean =>
   value instanceof PromiseNamespace ||
   value instanceof PromiseMethodReference ||
   value instanceof PromiseInstanceMethodReference ||
-  value instanceof SandboxPromise ||
+  value instanceof CodeModePromise ||
   value instanceof CoercionFunction ||
   value instanceof UriFunction ||
   value instanceof SearchFunction ||
   value instanceof PromiseCapabilityFunction ||
   value instanceof ErrorConstructorReference ||
-  isSandboxValue(value)
+  isCodeModeValue(value)
 
 export const containsRuntimeReference = (value: unknown, seen = new Set<object>()): boolean => {
   if (isRuntimeReference(value)) return true
@@ -46,9 +46,9 @@ export const containsRuntimeReference = (value: unknown, seen = new Set<object>(
   return contains
 }
 
-// Sandbox values are data here, not opaque interpreter references.
+// CodeMode values are data here, not opaque interpreter references.
 export const containsOpaqueReference = (value: unknown, seen = new Set<object>()): boolean => {
-  if (isSandboxValue(value)) return false
+  if (isCodeModeValue(value)) return false
   if (isRuntimeReference(value)) return true
   if (value === null || typeof value !== "object") return false
   if (seen.has(value)) return false

+ 64 - 64
packages/codemode/src/interpreter/runtime.ts

@@ -73,14 +73,14 @@ import {
   valueConstructors,
 } from "../stdlib/value.js"
 import {
-  isSandboxValue,
-  SandboxDate,
-  SandboxMap,
-  SandboxPromise,
-  SandboxRegExp,
-  SandboxSet,
-  SandboxURL,
-  SandboxURLSearchParams,
+  isCodeModeValue,
+  CodeModeDate,
+  CodeModeMap,
+  CodeModePromise,
+  CodeModeRegExp,
+  CodeModeSet,
+  CodeModeURL,
+  CodeModeURLSearchParams,
 } from "../values.js"
 
 const instanceofValue = (lhs: unknown, rhs: unknown, node: AstNode): boolean => {
@@ -91,24 +91,24 @@ const instanceofValue = (lhs: unknown, rhs: unknown, node: AstNode): boolean =>
   if (rhs instanceof GlobalNamespace) {
     switch (rhs.name) {
       case "Date":
-        return lhs instanceof SandboxDate
+        return lhs instanceof CodeModeDate
       case "RegExp":
-        return lhs instanceof SandboxRegExp
+        return lhs instanceof CodeModeRegExp
       case "Map":
-        return lhs instanceof SandboxMap
+        return lhs instanceof CodeModeMap
       case "Set":
-        return lhs instanceof SandboxSet
+        return lhs instanceof CodeModeSet
       case "URL":
-        return lhs instanceof SandboxURL
+        return lhs instanceof CodeModeURL
       case "URLSearchParams":
-        return lhs instanceof SandboxURLSearchParams
+        return lhs instanceof CodeModeURLSearchParams
       case "Array":
         return Array.isArray(lhs)
       case "Object":
         return lhs !== null && (typeof lhs === "object" || typeofValue(lhs) === "function")
     }
   }
-  if (rhs instanceof PromiseNamespace) return lhs instanceof SandboxPromise
+  if (rhs instanceof PromiseNamespace) return lhs instanceof CodeModePromise
   if (rhs instanceof CoercionFunction && (rhs.name === "Number" || rhs.name === "String" || rhs.name === "Boolean")) {
     return false
   }
@@ -226,7 +226,7 @@ export class Interpreter<R> {
       }
 
       // The implicit async body adopts returned promises before copy-out.
-      if (value instanceof SandboxPromise) value = yield* self.settlePromise(value)
+      if (value instanceof CodeModePromise) value = yield* self.settlePromise(value)
       return value
     }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())))
   }
@@ -235,16 +235,16 @@ export class Interpreter<R> {
   private createToolCallPromise(
     path: ReadonlyArray<string>,
     args: Array<unknown>,
-  ): Effect.Effect<SandboxPromise, never, R> {
+  ): Effect.Effect<CodeModePromise, never, R> {
     return this.createPromise(Effect.suspend(() => this.invokeTool(path, args)))
   }
 
-  private createPromise(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<SandboxPromise, never, R> {
+  private createPromise(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<CodeModePromise, never, R> {
     return this.promises.create(effect)
   }
 
   // Fiber exits make settlement idempotent; yielding prevents inline continuation.
-  private settlePromise(promise: SandboxPromise): Effect.Effect<unknown, unknown, never> {
+  private settlePromise(promise: CodeModePromise): Effect.Effect<unknown, unknown, never> {
     const promises = this.promises
     return Effect.suspend(() => {
       promises.markObserved(promise)
@@ -971,7 +971,7 @@ export class Interpreter<R> {
         // Await always suspends, including for plain values.
         const self = this
         return Effect.flatMap(this.evaluateExpression(getNode(node, "argument")), (value) =>
-          value instanceof SandboxPromise ? self.settlePromise(value) : Effect.as(Effect.yieldNow, value),
+          value instanceof CodeModePromise ? self.settlePromise(value) : Effect.as(Effect.yieldNow, value),
         )
       }
       case "NewExpression":
@@ -1019,23 +1019,23 @@ export class Interpreter<R> {
     throw unsupportedSyntax("NewExpression", node)
   }
 
-  private constructDate(args: Array<unknown>): SandboxDate {
-    if (args.length === 0) return new SandboxDate(Date.now())
+  private constructDate(args: Array<unknown>): CodeModeDate {
+    if (args.length === 0) return new CodeModeDate(Date.now())
     if (args.length === 1) {
       const arg = args[0]
-      if (arg instanceof SandboxDate) return new SandboxDate(arg.time)
-      if (typeof arg === "number") return new SandboxDate(new Date(arg).getTime())
-      if (typeof arg === "string") return new SandboxDate(Date.parse(arg))
-      return new SandboxDate(Number.NaN)
+      if (arg instanceof CodeModeDate) return new CodeModeDate(arg.time)
+      if (typeof arg === "number") return new CodeModeDate(new Date(arg).getTime())
+      if (typeof arg === "string") return new CodeModeDate(Date.parse(arg))
+      return new CodeModeDate(Number.NaN)
     }
     const parts = args.map((arg) => coerceToNumber(arg))
-    return new SandboxDate(new Date(...(parts as [number, number])).getTime())
+    return new CodeModeDate(new Date(...(parts as [number, number])).getTime())
   }
 
-  private constructRegExp(args: Array<unknown>, node: AstNode): SandboxRegExp {
+  private constructRegExp(args: Array<unknown>, node: AstNode): CodeModeRegExp {
     const first = args[0]
     const pattern =
-      first instanceof SandboxRegExp ? first.regex.source : first === undefined ? "" : coerceToString(first)
+      first instanceof CodeModeRegExp ? first.regex.source : first === undefined ? "" : coerceToString(first)
     const flagsArg = args[1]
     if (flagsArg !== undefined && typeof flagsArg !== "string") {
       throw new InterpreterRuntimeError(
@@ -1043,9 +1043,9 @@ export class Interpreter<R> {
         node,
       )
     }
-    const flags = flagsArg ?? (first instanceof SandboxRegExp ? first.regex.flags : "")
+    const flags = flagsArg ?? (first instanceof CodeModeRegExp ? first.regex.flags : "")
     try {
-      return new SandboxRegExp(pattern, flags)
+      return new CodeModeRegExp(pattern, flags)
     } catch (error) {
       const reason = regexFailureReason(error)
       throw new InterpreterRuntimeError(
@@ -1057,12 +1057,12 @@ export class Interpreter<R> {
     }
   }
 
-  private constructMap(init: unknown, node: AstNode): SandboxMap {
-    const target = new SandboxMap()
+  private constructMap(init: unknown, node: AstNode): CodeModeMap {
+    const target = new CodeModeMap()
     if (init === undefined || init === null) return target
     const entries = Array.isArray(init)
       ? init
-      : init instanceof SandboxMap
+      : init instanceof CodeModeMap
         ? Array.from(init.map.entries(), ([key, item]): Array<unknown> => [key, item])
         : undefined
     if (entries === undefined) {
@@ -1080,12 +1080,12 @@ export class Interpreter<R> {
     return target
   }
 
-  private constructSet(init: unknown, node: AstNode): SandboxSet {
-    const target = new SandboxSet()
+  private constructSet(init: unknown, node: AstNode): CodeModeSet {
+    const target = new CodeModeSet()
     if (init === undefined || init === null) return target
     const items = Array.isArray(init)
       ? init
-      : init instanceof SandboxSet
+      : init instanceof CodeModeSet
         ? Array.from(init.set.values())
         : typeof init === "string"
           ? Array.from(init)
@@ -1097,7 +1097,7 @@ export class Interpreter<R> {
     return target
   }
 
-  private constructURL(args: Array<unknown>, node: AstNode): SandboxURL {
+  private constructURL(args: Array<unknown>, node: AstNode): CodeModeURL {
     if (args.length === 0) {
       throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node).as(
         "TypeError",
@@ -1106,7 +1106,7 @@ export class Interpreter<R> {
     const input = urlArgument(args[0], "new URL input")
     const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base")
     try {
-      return new SandboxURL(new URL(input, base))
+      return new CodeModeURL(new URL(input, base))
     } catch {
       throw new InterpreterRuntimeError(
         `new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`,
@@ -1115,16 +1115,16 @@ export class Interpreter<R> {
     }
   }
 
-  private constructURLSearchParams(init: unknown, node: AstNode): SandboxURLSearchParams {
-    if (init === undefined) return new SandboxURLSearchParams(new URLSearchParams())
-    if (init instanceof SandboxURLSearchParams) {
-      return new SandboxURLSearchParams(new URLSearchParams(init.params))
+  private constructURLSearchParams(init: unknown, node: AstNode): CodeModeURLSearchParams {
+    if (init === undefined) return new CodeModeURLSearchParams(new URLSearchParams())
+    if (init instanceof CodeModeURLSearchParams) {
+      return new CodeModeURLSearchParams(new URLSearchParams(init.params))
     }
-    if (typeof init === "string") return new SandboxURLSearchParams(new URLSearchParams(init))
+    if (typeof init === "string") return new CodeModeURLSearchParams(new URLSearchParams(init))
     if (init === null || typeof init === "number" || typeof init === "boolean") {
-      return new SandboxURLSearchParams(new URLSearchParams(coerceToString(init)))
+      return new CodeModeURLSearchParams(new URLSearchParams(coerceToString(init)))
     }
-    if (init instanceof SandboxMap) {
+    if (init instanceof CodeModeMap) {
       return this.constructURLSearchParams(
         Array.from(init.map.entries(), ([key, value]) => [key, value]),
         node,
@@ -1143,9 +1143,9 @@ export class Interpreter<R> {
           string,
         ]
       })
-      return new SandboxURLSearchParams(new URLSearchParams(entries))
+      return new CodeModeURLSearchParams(new URLSearchParams(entries))
     }
-    if (isSandboxValue(init)) return new SandboxURLSearchParams(new URLSearchParams())
+    if (isCodeModeValue(init)) return new CodeModeURLSearchParams(new URLSearchParams())
     const data = boundedData(init, "new URLSearchParams input")
     if (data === null || typeof data !== "object") {
       throw new InterpreterRuntimeError(
@@ -1153,7 +1153,7 @@ export class Interpreter<R> {
         node,
       ).as("TypeError")
     }
-    return new SandboxURLSearchParams(
+    return new CodeModeURLSearchParams(
       new URLSearchParams(Object.fromEntries(Object.entries(data).map(([key, value]) => [key, coerceToString(value)]))),
     )
   }
@@ -1176,7 +1176,7 @@ export class Interpreter<R> {
     // Null-prototype data needs explicit primitive coercion; identity and `in` retain raw objects.
     // Dates use string coercion for `+` and epoch time elsewhere.
     const coerceOperand = (operand: unknown): unknown => {
-      if (operand instanceof SandboxDate) return operator === "+" ? coerceToString(operand) : operand.time
+      if (operand instanceof CodeModeDate) return operator === "+" ? coerceToString(operand) : operand.time
       return operand !== null && typeof operand === "object" ? coerceToString(operand) : operand
     }
     const bothObjects = lhs !== null && typeof lhs === "object" && rhs !== null && typeof rhs === "object"
@@ -1261,7 +1261,7 @@ export class Interpreter<R> {
         throw new InterpreterRuntimeError("Unary operators require data values in CodeMode.", node, "InvalidDataValue")
       }
       const operand =
-        value instanceof SandboxDate
+        value instanceof CodeModeDate
           ? value.time
           : value !== null && typeof value === "object"
             ? coerceToString(value)
@@ -1520,11 +1520,11 @@ export class Interpreter<R> {
     })
     if (!fn.async) return run
     // The initial yield assigns `box.own` before the body can self-resolve.
-    const box: { own?: SandboxPromise } = {}
+    const box: { own?: CodeModePromise } = {}
     return Effect.map(
       this.createPromise(
         Effect.flatMap(run, (value) => {
-          if (!(value instanceof SandboxPromise)) return Effect.succeed(value)
+          if (!(value instanceof CodeModePromise)) return Effect.succeed(value)
           if (value === box.own) return Effect.fail(selfResolutionError())
           return invocation.settlePromise(value)
         }),
@@ -1546,7 +1546,7 @@ export class Interpreter<R> {
 
         if (property.type === "SpreadElement") {
           const spread = yield* self.evaluateExpression(getNode(property, "argument"))
-          if (spread === null || spread === undefined || isSandboxValue(spread)) continue
+          if (spread === null || spread === undefined || isCodeModeValue(spread)) continue
           if (typeof spread !== "object" || Array.isArray(spread) || isRuntimeReference(spread)) {
             throw new InterpreterRuntimeError(
               "Object spread requires a data object in CodeMode.",
@@ -1748,28 +1748,28 @@ export class Interpreter<R> {
         if (objectValue.name === "String" && stringStatics.has(key)) return new GlobalMethodReference("String", key)
       }
 
-      if (objectValue instanceof SandboxDate) {
+      if (objectValue instanceof CodeModeDate) {
         if (typeof key === "string" && dateMethods.has(key)) return new IntrinsicReference(objectValue, key)
         return new ComputedValue(undefined)
       }
-      if (objectValue instanceof SandboxRegExp) {
+      if (objectValue instanceof CodeModeRegExp) {
         if (typeof key === "string" && regexpProperties.has(key)) {
           return new ComputedValue((objectValue.regex as unknown as Record<string, unknown>)[key])
         }
         if (typeof key === "string" && regexpMethods.has(key)) return new IntrinsicReference(objectValue, key)
         return new ComputedValue(undefined)
       }
-      if (objectValue instanceof SandboxMap) {
+      if (objectValue instanceof CodeModeMap) {
         if (key === "size") return new ComputedValue(objectValue.map.size)
         if (typeof key === "string" && mapMethods.has(key)) return new IntrinsicReference(objectValue, key)
         return new ComputedValue(undefined)
       }
-      if (objectValue instanceof SandboxSet) {
+      if (objectValue instanceof CodeModeSet) {
         if (key === "size") return new ComputedValue(objectValue.set.size)
         if (typeof key === "string" && setMethods.has(key)) return new IntrinsicReference(objectValue, key)
         return new ComputedValue(undefined)
       }
-      if (objectValue instanceof SandboxURL) {
+      if (objectValue instanceof CodeModeURL) {
         if (key === "searchParams") {
           return new ComputedValue(objectValue.searchParams)
         }
@@ -1777,7 +1777,7 @@ export class Interpreter<R> {
         if (typeof key === "string" && urlProperties.has(key)) return { target: objectValue, key }
         return new ComputedValue(undefined)
       }
-      if (objectValue instanceof SandboxURLSearchParams) {
+      if (objectValue instanceof CodeModeURLSearchParams) {
         if (key === "size") return new ComputedValue(objectValue.params.size)
         if (typeof key === "string" && urlSearchParamsMethods.has(key)) {
           return new IntrinsicReference(objectValue, key)
@@ -1786,7 +1786,7 @@ export class Interpreter<R> {
       }
 
       // Reject unknown promise properties so a missing await cannot hide.
-      if (objectValue instanceof SandboxPromise) {
+      if (objectValue instanceof CodeModePromise) {
         if (key === "then" || key === "catch" || key === "finally") {
           return new PromiseInstanceMethodReference(objectValue, key)
         }
@@ -1851,7 +1851,7 @@ export class Interpreter<R> {
         }
         return reference.key === "length" ? reference.target.length : reference.target[Number(reference.key)]
       }
-      if (reference.target instanceof SandboxURL) {
+      if (reference.target instanceof CodeModeURL) {
         return (reference.target.url as unknown as Record<string, unknown>)[String(reference.key)]
       }
       return reference.target[String(reference.key)]
@@ -1891,7 +1891,7 @@ export class Interpreter<R> {
       }
       const key = Array.isArray(reference.target) ? Number(reference.key) : String(reference.key)
       const current =
-        reference.target instanceof SandboxURL
+        reference.target instanceof CodeModeURL
           ? (reference.target.url as unknown as Record<string, unknown>)[key]
           : (reference.target as Record<PropertyKey, unknown>)[key]
       const { write, next, result } = yield* compute(current)
@@ -1915,7 +1915,7 @@ export class Interpreter<R> {
       target[index] = next
       return
     }
-    if (reference.target instanceof SandboxURL) {
+    if (reference.target instanceof CodeModeURL) {
       const property = key as string
       if (!urlWritableProperties.has(property)) {
         throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node).as("TypeError")

+ 4 - 4
packages/codemode/src/stdlib/collections.ts

@@ -43,9 +43,9 @@ export const setMethods = new Set(["add", "has", "delete", "clear", "forEach", "
 export const spreadItems = (value: unknown): Array<unknown> | undefined => {
   if (Array.isArray(value)) return value
   if (typeof value === "string") return Array.from(value)
-  if (value instanceof SandboxMap) return Array.from(value.map.entries(), ([key, item]) => [key, item])
-  if (value instanceof SandboxSet) return Array.from(value.set.values())
-  if (value instanceof SandboxURLSearchParams) return Array.from(value.params.entries(), ([key, item]) => [key, item])
+  if (value instanceof CodeModeMap) return Array.from(value.map.entries(), ([key, item]) => [key, item])
+  if (value instanceof CodeModeSet) return Array.from(value.set.values())
+  if (value instanceof CodeModeURLSearchParams) return Array.from(value.params.entries(), ([key, item]) => [key, item])
   return undefined
 }
-import { SandboxMap, SandboxSet, SandboxURLSearchParams } from "../values.js"
+import { CodeModeMap, CodeModeSet, CodeModeURLSearchParams } from "../values.js"

+ 17 - 17
packages/codemode/src/stdlib/console.ts

@@ -1,14 +1,14 @@
 import { containsOpaqueReference, containsRuntimeReference, isRuntimeReference } from "../interpreter/references.js"
 import { copyIn, copyOut } from "../tool-runtime.js"
 import {
-  isSandboxValue,
-  SandboxDate,
-  SandboxMap,
-  SandboxPromise,
-  SandboxRegExp,
-  SandboxSet,
-  SandboxURL,
-  SandboxURLSearchParams,
+  isCodeModeValue,
+  CodeModeDate,
+  CodeModeMap,
+  CodeModePromise,
+  CodeModeRegExp,
+  CodeModeSet,
+  CodeModeURL,
+  CodeModeURLSearchParams,
 } from "../values.js"
 import { boundedData, coerceToString } from "./value.js"
 
@@ -34,14 +34,14 @@ const formatConsoleValue = (value: unknown, seen: Set<object>, depth: number): s
   if (typeof value === "string") return JSON.stringify(value)
   if (typeof value === "number" || typeof value === "boolean") return String(value)
   if (typeof value !== "object") return String(value)
-  if (value instanceof SandboxPromise) return "[Promise (await it to get its value)]"
-  if (value instanceof SandboxDate) return coerceToString(value)
-  if (value instanceof SandboxRegExp) return coerceToString(value)
-  if (value instanceof SandboxURL) return coerceToString(value)
-  if (value instanceof SandboxURLSearchParams) return coerceToString(value)
+  if (value instanceof CodeModePromise) return "[Promise (await it to get its value)]"
+  if (value instanceof CodeModeDate) return coerceToString(value)
+  if (value instanceof CodeModeRegExp) return coerceToString(value)
+  if (value instanceof CodeModeURL) return coerceToString(value)
+  if (value instanceof CodeModeURLSearchParams) return coerceToString(value)
   if (depth > MAX_CONSOLE_DEPTH) return "..."
   if (seen.has(value)) return "[Circular]"
-  if (value instanceof SandboxMap) {
+  if (value instanceof CodeModeMap) {
     seen.add(value)
     try {
       const entries = Array.from(value.map.entries(), ([key, item]): Array<unknown> => [key, item])
@@ -50,7 +50,7 @@ const formatConsoleValue = (value: unknown, seen: Set<object>, depth: number): s
       seen.delete(value)
     }
   }
-  if (value instanceof SandboxSet) {
+  if (value instanceof CodeModeSet) {
     seen.add(value)
     try {
       return `Set(${value.set.size}) ${formatConsoleValue(Array.from(value.set.values()), seen, depth + 1)}`
@@ -100,14 +100,14 @@ const consoleTableRows = (
   if (Array.isArray(data)) {
     return data.map((item, index) => ({ index: String(index), values: consoleTableValues(item, columns) }))
   }
-  if (data !== null && typeof data === "object" && !isSandboxValue(data)) {
+  if (data !== null && typeof data === "object" && !isCodeModeValue(data)) {
     return Object.entries(data).map(([index, item]) => ({ index, values: consoleTableValues(item, columns) }))
   }
   return [{ index: "0", values: { Value: data } }]
 }
 
 const consoleTableValues = (value: unknown, columns: ReadonlyArray<string> | undefined): Record<string, unknown> => {
-  if (value !== null && typeof value === "object" && !Array.isArray(value) && !isSandboxValue(value)) {
+  if (value !== null && typeof value === "object" && !Array.isArray(value) && !isCodeModeValue(value)) {
     const source = value as Record<string, unknown>
     if (columns !== undefined) return Object.fromEntries(columns.map((column) => [column, source[column]]))
     return Object.fromEntries(Object.entries(source))

+ 2 - 2
packages/codemode/src/stdlib/date.ts

@@ -36,7 +36,7 @@ export const invokeDateStatic = (name: string, args: Array<unknown>, node: AstNo
   }
 }
 
-export const invokeDateMethod = (value: SandboxDate, name: string, node: AstNode): unknown => {
+export const invokeDateMethod = (value: CodeModeDate, name: string, node: AstNode): unknown => {
   const hosted = new Date(value.time)
   switch (name) {
     case "getTime":
@@ -88,5 +88,5 @@ export const invokeDateMethod = (value: SandboxDate, name: string, node: AstNode
   }
 }
 import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
-import { SandboxDate } from "../values.js"
+import { CodeModeDate } from "../values.js"
 import { coerceToNumber, coerceToString } from "./value.js"

+ 9 - 9
packages/codemode/src/stdlib/object.ts

@@ -1,6 +1,6 @@
 import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
 import { isBlockedMember } from "../tool-runtime.js"
-import { isSandboxValue, SandboxMap, SandboxPromise, SandboxSet, SandboxURLSearchParams } from "../values.js"
+import { isCodeModeValue, CodeModeMap, CodeModePromise, CodeModeSet, CodeModeURLSearchParams } from "../values.js"
 import { boundedData, coerceToString } from "./value.js"
 
 export const objectMethodsPreservingIdentity = new Set(["assign", "values", "entries", "fromEntries"])
@@ -9,8 +9,8 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
   const requireObject = (): Record<string, unknown> => {
     const input = args[0]
     if (Array.isArray(input)) return input as unknown as Record<string, unknown>
-    if (isSandboxValue(input)) return {}
-    if (input instanceof SandboxPromise) {
+    if (isCodeModeValue(input)) return {}
+    if (input instanceof CodeModePromise) {
       throw new InterpreterRuntimeError(
         `Object.${name} received an un-awaited Promise; await it before inspecting the result.`,
         node,
@@ -46,12 +46,12 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
       return Object.hasOwn(requireObject(), String(args[1]))
     case "assign": {
       const target = args[0]
-      if (target === null || typeof target !== "object" || Array.isArray(target) || isSandboxValue(target)) {
+      if (target === null || typeof target !== "object" || Array.isArray(target) || isCodeModeValue(target)) {
         throw new InterpreterRuntimeError("Object.assign expects a data object target.", node)
       }
       const out = target as Record<string, unknown>
       for (const source of args.slice(1)) {
-        if (source === null || source === undefined || isSandboxValue(source)) continue
+        if (source === null || source === undefined || isCodeModeValue(source)) continue
         if (typeof source !== "object" || Array.isArray(source)) {
           throw new InterpreterRuntimeError("Object.assign expects data objects.", node)
         }
@@ -60,17 +60,17 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
       return out
     }
     case "fromEntries": {
-      if (args[0] instanceof SandboxMap) {
+      if (args[0] instanceof CodeModeMap) {
         const out: Record<string, unknown> = Object.create(null)
         for (const [key, item] of args[0].map.entries()) addEntry(out, key, item)
         return out
       }
-      if (args[0] instanceof SandboxURLSearchParams) {
+      if (args[0] instanceof CodeModeURLSearchParams) {
         const out: Record<string, unknown> = Object.create(null)
         for (const [key, value] of args[0].params.entries()) guardedSet(out, key, value)
         return out
       }
-      const pairs = args[0] instanceof SandboxSet ? Array.from(args[0].set.values()) : args[0]
+      const pairs = args[0] instanceof CodeModeSet ? Array.from(args[0].set.values()) : args[0]
       if (!Array.isArray(pairs)) {
         boundedData(args[0], "Object.fromEntries input")
         throw new InterpreterRuntimeError("Object.fromEntries expects an array of [key, value] pairs.", node)
@@ -78,7 +78,7 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
       const out: Record<string, unknown> = Object.create(null)
       for (const pair of pairs) {
         const validated = boundedData(pair, "Object.fromEntries entry")
-        if (validated === null || typeof validated !== "object" || isSandboxValue(validated))
+        if (validated === null || typeof validated !== "object" || isCodeModeValue(validated))
           throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node)
         const entry = pair as Record<string, unknown>
         addEntry(out, entry[0], entry[1])

+ 3 - 3
packages/codemode/src/stdlib/regexp.ts

@@ -19,7 +19,7 @@ export const escapeRegexHint =
   'To match special characters like ( ) [ ] { } + * ? . literally, escape them with a backslash (e.g. "\\\\(") or test for them with String.includes instead.'
 
 export const toHostRegex = (arg: unknown, method: string, node: AstNode, extraFlags = ""): RegExp => {
-  if (arg instanceof SandboxRegExp) return arg.regex
+  if (arg instanceof CodeModeRegExp) return arg.regex
   if (typeof arg === "string") {
     try {
       return new RegExp(arg, extraFlags)
@@ -50,7 +50,7 @@ export const matchToValue = (match: RegExpMatchArray): Array<unknown> => {
 }
 
 export const invokeRegExpMethod = (
-  value: SandboxRegExp,
+  value: CodeModeRegExp,
   name: string,
   args: Array<unknown>,
   node: AstNode,
@@ -70,5 +70,5 @@ export const invokeRegExpMethod = (
 }
 import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
 import { isBlockedMember, type SafeObject } from "../tool-runtime.js"
-import { SandboxRegExp } from "../values.js"
+import { CodeModeRegExp } from "../values.js"
 import { coerceToString } from "./value.js"

+ 4 - 4
packages/codemode/src/stdlib/url.ts

@@ -66,7 +66,7 @@ export const invokeUriFunction = (ref: UriFunction, args: Array<unknown>, node:
 }
 
 export const urlArgument = (value: unknown, label: string): string =>
-  value instanceof SandboxURL ? value.url.href : uriArgument(value, label)
+  value instanceof CodeModeURL ? value.url.href : uriArgument(value, label)
 
 export const invokeURLStatic = (name: string, args: Array<unknown>, node: AstNode): unknown => {
   if (!urlStatics.has(name)) throw new InterpreterRuntimeError(`URL.${name} is not available in CodeMode.`, node)
@@ -75,16 +75,16 @@ export const invokeURLStatic = (name: string, args: Array<unknown>, node: AstNod
   const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`)
   try {
     const url = new URL(input, base)
-    return name === "canParse" ? true : new SandboxURL(url)
+    return name === "canParse" ? true : new CodeModeURL(url)
   } catch {
     return name === "canParse" ? false : null
   }
 }
 
-export const invokeURLMethod = (value: SandboxURL, name: string, node: AstNode): string => {
+export const invokeURLMethod = (value: CodeModeURL, name: string, node: AstNode): string => {
   if (name === "toString" || name === "toJSON") return value.url.href
   throw new InterpreterRuntimeError(`URL method '${name}' is not available in CodeMode.`, node)
 }
 import { type AstNode, InterpreterRuntimeError, UriFunction } from "../interpreter/model.js"
-import { SandboxURL } from "../values.js"
+import { CodeModeURL } from "../values.js"
 import { boundedData, coerceToString } from "./value.js"

+ 16 - 16
packages/codemode/src/stdlib/value.ts

@@ -34,13 +34,13 @@ export const boundedData = (value: unknown, label: string): unknown => copyIn(va
 export const coerceToString = (value: unknown): string => {
   if (value === null) return "null"
   if (value === undefined) return "undefined"
-  if (value instanceof SandboxDate)
+  if (value instanceof CodeModeDate)
     return Number.isFinite(value.time) ? new Date(value.time).toISOString() : "Invalid Date"
-  if (value instanceof SandboxRegExp) return `/${value.regex.source}/${value.regex.flags}`
-  if (value instanceof SandboxMap) return "[object Map]"
-  if (value instanceof SandboxSet) return "[object Set]"
-  if (value instanceof SandboxURL) return value.url.href
-  if (value instanceof SandboxURLSearchParams) return value.params.toString()
+  if (value instanceof CodeModeRegExp) return `/${value.regex.source}/${value.regex.flags}`
+  if (value instanceof CodeModeMap) return "[object Map]"
+  if (value instanceof CodeModeSet) return "[object Set]"
+  if (value instanceof CodeModeURL) return value.url.href
+  if (value instanceof CodeModeURLSearchParams) return value.params.toString()
   if (typeof value === "object") {
     return Array.isArray(value)
       ? value.map((item) => (item === null || item === undefined ? "" : coerceToString(item))).join(",")
@@ -50,14 +50,14 @@ export const coerceToString = (value: unknown): string => {
 }
 
 export const coerceToNumber = (value: unknown): number => {
-  if (value instanceof SandboxDate) return value.time
-  if (isSandboxValue(value)) return Number.NaN
+  if (value instanceof CodeModeDate) return value.time
+  if (isCodeModeValue(value)) return Number.NaN
   return value !== null && typeof value === "object" && !Array.isArray(value) ? Number.NaN : Number(value)
 }
 
 export const invokeCoercion = (ref: CoercionFunction, args: Array<unknown>, node: AstNode): unknown => {
   const raw = args[0]
-  if (isSandboxValue(raw)) {
+  if (isCodeModeValue(raw)) {
     if (ref.name === "Boolean") return true
     if (ref.name === "Number") return coerceToNumber(raw)
     if (ref.name === "String") return coerceToString(raw)
@@ -80,11 +80,11 @@ export const invokeCoercion = (ref: CoercionFunction, args: Array<unknown>, node
 import { type AstNode, CoercionFunction, InterpreterRuntimeError } from "../interpreter/model.js"
 import { copyIn, type SafeObject } from "../tool-runtime.js"
 import {
-  isSandboxValue,
-  SandboxDate,
-  SandboxMap,
-  SandboxRegExp,
-  SandboxSet,
-  SandboxURL,
-  SandboxURLSearchParams,
+  isCodeModeValue,
+  CodeModeDate,
+  CodeModeMap,
+  CodeModeRegExp,
+  CodeModeSet,
+  CodeModeURL,
+  CodeModeURLSearchParams,
 } from "../values.js"

+ 34 - 34
packages/codemode/src/tool-runtime.ts

@@ -10,13 +10,13 @@ import {
 } from "./tool-schema.js"
 import { isDefinition as isToolDefinition, type Definition } from "./tool.js"
 import {
-  SandboxDate,
-  SandboxMap,
-  SandboxPromise,
-  SandboxRegExp,
-  SandboxSet,
-  SandboxURL,
-  SandboxURLSearchParams,
+  CodeModeDate,
+  CodeModeMap,
+  CodeModePromise,
+  CodeModeRegExp,
+  CodeModeSet,
+  CodeModeURL,
+  CodeModeURLSearchParams,
 } from "./values.js"
 
 const estimateTokens = (input: string) => Math.max(0, Math.round(input.length / 4))
@@ -141,16 +141,16 @@ const blockedMemberNames = new Set(["__proto__", "constructor", "prototype"])
 
 export const isBlockedMember = (name: string): boolean => blockedMemberNames.has(name)
 
-// Checkpoint mode preserves sandbox values; boundary mode JSON-normalizes them.
-export const copyIn = (value: unknown, label: string, preserveSandboxValues = false): unknown =>
-  copyBounded(value, label, 0, new Set(), preserveSandboxValues)
+// Checkpoint mode preserves CodeMode values; boundary mode JSON-normalizes them.
+export const copyIn = (value: unknown, label: string, preserveCodeModeValues = false): unknown =>
+  copyBounded(value, label, 0, new Set(), preserveCodeModeValues)
 
 const copyBounded = (
   value: unknown,
   label: string,
   depth: number,
   seen: Set<object>,
-  preserveSandboxValues: boolean,
+  preserveCodeModeValues: boolean,
 ): unknown => {
   if (depth > MAX_VALUE_DEPTH) {
     throw new ToolRuntimeError("InvalidDataValue", `${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`)
@@ -169,55 +169,55 @@ const copyBounded = (
     throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`)
   }
 
-  if (value instanceof SandboxPromise) {
+  if (value instanceof CodeModePromise) {
     throw new ToolRuntimeError(
       "InvalidDataValue",
       `${label} contains an un-awaited Promise; await tool calls (e.g. \`const result = await tools.ns.tool(...)\`) before using their results.`,
     )
   }
 
-  if (preserveSandboxValues) {
+  if (preserveCodeModeValues) {
     if (
-      value instanceof SandboxDate ||
-      value instanceof SandboxRegExp ||
-      value instanceof SandboxMap ||
-      value instanceof SandboxSet ||
-      value instanceof SandboxURL ||
-      value instanceof SandboxURLSearchParams
+      value instanceof CodeModeDate ||
+      value instanceof CodeModeRegExp ||
+      value instanceof CodeModeMap ||
+      value instanceof CodeModeSet ||
+      value instanceof CodeModeURL ||
+      value instanceof CodeModeURLSearchParams
     ) {
       return value
     }
-    if (value instanceof Date) return new SandboxDate(value.getTime())
-    if (value instanceof RegExp) return new SandboxRegExp(value.source, value.flags)
+    if (value instanceof Date) return new CodeModeDate(value.getTime())
+    if (value instanceof RegExp) return new CodeModeRegExp(value.source, value.flags)
     if (value instanceof Map) {
-      const wrapped = new SandboxMap()
+      const wrapped = new CodeModeMap()
       for (const [key, item] of value.entries()) {
         wrapped.map.set(copyBounded(key, label, depth + 1, seen, true), copyBounded(item, label, depth + 1, seen, true))
       }
       return wrapped
     }
     if (value instanceof Set) {
-      const wrapped = new SandboxSet()
+      const wrapped = new CodeModeSet()
       for (const item of value.values()) wrapped.set.add(copyBounded(item, label, depth + 1, seen, true))
       return wrapped
     }
-    if (value instanceof URL) return new SandboxURL(new URL(value.href))
-    if (value instanceof URLSearchParams) return new SandboxURLSearchParams(new URLSearchParams(value))
+    if (value instanceof URL) return new CodeModeURL(new URL(value.href))
+    if (value instanceof URLSearchParams) return new CodeModeURLSearchParams(new URLSearchParams(value))
   }
 
-  if (value instanceof SandboxDate) {
+  if (value instanceof CodeModeDate) {
     return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null
   }
   if (value instanceof Date) {
     return Number.isFinite(value.getTime()) ? value.toISOString() : null
   }
-  if (value instanceof SandboxURL) return value.url.href
+  if (value instanceof CodeModeURL) return value.url.href
   if (value instanceof URL) return value.href
   if (
-    value instanceof SandboxRegExp ||
-    value instanceof SandboxMap ||
-    value instanceof SandboxSet ||
-    value instanceof SandboxURLSearchParams ||
+    value instanceof CodeModeRegExp ||
+    value instanceof CodeModeMap ||
+    value instanceof CodeModeSet ||
+    value instanceof CodeModeURLSearchParams ||
     value instanceof RegExp ||
     value instanceof Map ||
     value instanceof Set ||
@@ -233,8 +233,8 @@ const copyBounded = (
   seen.add(value)
 
   if (Array.isArray(value)) {
-    const copied = value.map((item) => copyBounded(item, label, depth + 1, seen, preserveSandboxValues))
-    if (preserveSandboxValues) {
+    const copied = value.map((item) => copyBounded(item, label, depth + 1, seen, preserveCodeModeValues))
+    if (preserveCodeModeValues) {
       // Checkpoint copies retain array metadata that boundary copies omit.
       for (const [key, item] of Object.entries(value)) {
         if (Object.hasOwn(copied, key)) continue
@@ -258,7 +258,7 @@ const copyBounded = (
     if (isBlockedMember(key)) {
       throw new ToolRuntimeError("InvalidDataValue", `${label} contains blocked property '${key}'.`)
     }
-    copied[key] = copyBounded(item, label, depth + 1, seen, preserveSandboxValues)
+    copied[key] = copyBounded(item, label, depth + 1, seen, preserveCodeModeValues)
   }
   seen.delete(value)
   return copied

+ 17 - 17
packages/codemode/src/values.ts

@@ -1,45 +1,45 @@
 import type { Fiber } from "effect"
 
-export class SandboxPromise {
+export class CodeModePromise {
   constructor(readonly fiber: Fiber.Fiber<unknown, unknown>) {}
 }
 
-export class SandboxDate {
+export class CodeModeDate {
   constructor(readonly time: number) {}
 }
 
-export class SandboxRegExp {
+export class CodeModeRegExp {
   readonly regex: RegExp
   constructor(pattern: string, flags: string) {
     this.regex = new RegExp(pattern, flags)
   }
 }
 
-export class SandboxMap {
+export class CodeModeMap {
   readonly map = new Map<unknown, unknown>()
 }
 
-export class SandboxSet {
+export class CodeModeSet {
   readonly set = new Set<unknown>()
 }
 
-export class SandboxURLSearchParams {
+export class CodeModeURLSearchParams {
   constructor(readonly params: URLSearchParams) {}
 }
 
-export class SandboxURL {
-  readonly searchParams: SandboxURLSearchParams
+export class CodeModeURL {
+  readonly searchParams: CodeModeURLSearchParams
   constructor(readonly url: URL) {
-    this.searchParams = new SandboxURLSearchParams(url.searchParams)
+    this.searchParams = new CodeModeURLSearchParams(url.searchParams)
   }
 }
 
-export const isSandboxValue = (
+export const isCodeModeValue = (
   value: unknown,
-): value is SandboxDate | SandboxRegExp | SandboxMap | SandboxSet | SandboxURL | SandboxURLSearchParams =>
-  value instanceof SandboxDate ||
-  value instanceof SandboxRegExp ||
-  value instanceof SandboxMap ||
-  value instanceof SandboxSet ||
-  value instanceof SandboxURL ||
-  value instanceof SandboxURLSearchParams
+): value is CodeModeDate | CodeModeRegExp | CodeModeMap | CodeModeSet | CodeModeURL | CodeModeURLSearchParams =>
+  value instanceof CodeModeDate ||
+  value instanceof CodeModeRegExp ||
+  value instanceof CodeModeMap ||
+  value instanceof CodeModeSet ||
+  value instanceof CodeModeURL ||
+  value instanceof CodeModeURLSearchParams

+ 2 - 2
packages/codemode/test/codemode.test.ts

@@ -276,7 +276,7 @@ describe("CodeMode console capture", () => {
     expect(result.logs).toStrictEqual(["NaN", "Infinity -Infinity", '{"ratio":NaN,"bounds":[Infinity]}'])
   })
 
-  test("renders sandbox values nested inside logged containers", async () => {
+  test("renders CodeMode values nested inside logged containers", async () => {
     const result = await Effect.runPromise(
       CodeMode.execute({
         code: `
@@ -311,7 +311,7 @@ describe("CodeMode console capture", () => {
     expect(result.logs).toStrictEqual(['{"box":Map(1) [["self",[Circular]]]}', '{"fn":[CodeMode reference],"ok":1}'])
   })
 
-  test("console.table renders sandbox value cells", async () => {
+  test("console.table renders CodeMode value cells", async () => {
     const result = await Effect.runPromise(
       CodeMode.execute({
         code: `

+ 6 - 6
packages/codemode/test/parity.test.ts

@@ -8,7 +8,7 @@ import { ToolRuntime } from "../src/tool-runtime.js"
 // a strict interpreter would throw but idiomatic JS yields undefined / succeeds.
 //
 // Note on the result boundary: this package normalizes a bare `undefined` result to `null` when
-// it crosses out of the sandbox (results are JSON data), so tests asserting an in-sandbox
+// it crosses out of CodeMode (results are JSON data), so tests asserting an in-CodeMode
 // `undefined` read check `=== undefined` inside the program and `null` at the boundary.
 const run = (code: string) => Effect.runPromise(CodeMode.execute({ code, tools: {} }))
 const value = async (code: string) => {
@@ -108,7 +108,7 @@ describe("H1: NaN/Infinity flow as intermediates and normalize to null at the bo
     expect(await value(`const a = []; return a.length ? a.reduce((s,x)=>s+x,0)/a.length : 0`)).toBe(0)
   })
 
-  test("a non-finite value becomes null when it leaves the sandbox", async () => {
+  test("a non-finite value becomes null when it leaves CodeMode", async () => {
     expect(await value(`return 5/0`)).toBeNull()
     expect(await value(`return 0/0`)).toBeNull()
     expect(await value(`return Math.max()`)).toBeNull()
@@ -116,12 +116,12 @@ describe("H1: NaN/Infinity flow as intermediates and normalize to null at the bo
     expect(await value(`return { a: Number("x"), b: 2, c: [1/0] }`)).toEqual({ a: null, b: 2, c: [null] })
   })
 
-  test("NaN and Infinity are usable identifiers and inspectable in-sandbox", async () => {
+  test("NaN and Infinity are usable identifiers and inspectable in-CodeMode", async () => {
     expect(await value(`return Number.isNaN(NaN)`)).toBe(true)
     expect(await value(`return Infinity > 1e9`)).toBe(true)
     expect(await value(`return Number.isFinite(1/0)`)).toBe(false)
     expect(await value(`return [3,1,2].reduce((a,b)=>Math.max(a,b), -Infinity)`)).toBe(3)
-    // JSON.stringify inside the sandbox matches JS: non-finite serializes to null
+    // JSON.stringify inside CodeMode matches JS: non-finite serializes to null
     expect(await value(`return JSON.stringify({ x: Number("z") })`)).toBe('{"x":null}')
   })
 
@@ -321,12 +321,12 @@ describe("compound assignment matches its binary operator", () => {
     return a
   }
 
-  test("sandbox Date += concatenates its string form, like d = d + 1", async () => {
+  test("CodeMode Date += concatenates its string form, like d = d + 1", async () => {
     const result = await pair(`let d = new Date(1000); d += 1; return d`, `let d = new Date(1000); d = d + 1; return d`)
     expect(result).toBe("1970-01-01T00:00:01.000Z1")
   })
 
-  test("sandbox Date numeric compound ops use its time value", async () => {
+  test("CodeMode Date numeric compound ops use its time value", async () => {
     expect(
       await pair(`let d = new Date(1000); d -= 400; return d`, `let d = new Date(1000); d = d - 400; return d`),
     ).toBe(600)

+ 6 - 6
packages/codemode/test/promise-test262.test.ts

@@ -212,7 +212,7 @@ describe("Test262 Promise statics", () => {
     ])
   })
 
-  test("Promise.resolve adopts values and preserves sandbox-promise identity", async () => {
+  test("Promise.resolve adopts values and preserves CodeMode-promise identity", async () => {
     // Sources:
     // test/built-ins/Promise/resolve/S25.4.4.5_A2.1_T1.js
     // test/built-ins/Promise/resolve/resolve-non-obj.js
@@ -289,7 +289,7 @@ describe("Test262 Promise statics", () => {
     // test/built-ins/Promise/all/reject-immed.js
     // test/built-ins/Promise/allSettled/reject-immed.js
     // test/built-ins/Promise/race/reject-immed.js
-    // (adapted: immediately-rejecting thenables become sandbox promises that settled,
+    // (adapted: immediately-rejecting thenables become CodeMode promises that settled,
     //  and were even observed, before the combinator call)
     expect(
       await value(`
@@ -360,7 +360,7 @@ describe("Test262 Promise statics", () => {
     // test/built-ins/Promise/race/S25.4.4.3_A2.1_T1.js
     // test/built-ins/Promise/race/S25.4.4.3_A5.1_T1.js
     // (adapted: upstream requires Promise.race([]) to never settle; CodeMode intentionally
-    //  rejects with a catchable diagnostic instead of hanging, so this asserts the sandbox
+    //  rejects with a catchable diagnostic instead of hanging, so this asserts CodeMode
     //  divergence rather than the spec never-settles behavior)
     expect(
       await value(`
@@ -375,7 +375,7 @@ describe("Test262 Promise statics", () => {
     ).toEqual([true, true])
   })
 
-  test("Promise.resolve passes the same sandbox promise through nested chains", async () => {
+  test("Promise.resolve passes the same CodeMode promise through nested chains", async () => {
     // Source: test/built-ins/Promise/resolve/S25.4.4.5_A2.2_T1.js
     // (adapted: no executor construction, and identity is observed with Array includes
     //  because promises are not comparable data values in CodeMode)
@@ -1208,7 +1208,7 @@ describe("Test262 AggregateError", () => {
 
   test("coerces a non-string message to a string", async () => {
     // Source: test/built-ins/AggregateError/message-method-prop-cast.js (value coercion only; the
-    // upstream object-with-toString case is omitted because the sandbox has no user toString dispatch)
+    // upstream object-with-toString case is omitted because CodeMode has no user toString dispatch)
     expect(
       await value(`
         return [
@@ -1408,7 +1408,7 @@ describe("Test262 Promise constructor", () => {
 
   test.failing("calling Promise without new throws TypeError", async () => {
     // Source: test/built-ins/Promise/undefined-newtarget.js
-    // The sandbox currently reports a generic Error ("Only tools are callable in CodeMode.").
+    // CodeMode currently reports a generic Error ("Only tools are callable in CodeMode.").
     expect(
       await value(`
         try {

+ 8 - 8
packages/codemode/test/stdlib.test.ts

@@ -3,7 +3,7 @@ import { Effect, Schema } from "effect"
 import { CodeMode, Tool } from "../src/index.js"
 
 // Standard-library value types: Date, RegExp, Map, Set. Programs use them as ordinary JS;
-// intra-sandbox checkpoints (Object.* helpers, spread, coercion inputs) preserve the live
+// intra-CodeMode checkpoints (Object.* helpers, spread, coercion inputs) preserve the live
 // values, while at the host boundary (final result, tool arguments, JSON.stringify) they
 // serialize exactly as JSON.stringify would: Date -> ISO string (invalid -> null),
 // URL -> href, and RegExp/Map/Set/URLSearchParams -> {}.
@@ -69,7 +69,7 @@ describe("Date", () => {
     ).toEqual([2024, 2, 5, 6, 7, 8, 9])
   })
 
-  test("invalid dates yield NaN times, guardable in-sandbox", async () => {
+  test("invalid dates yield NaN times, guardable in-CodeMode", async () => {
     expect(await value(`return Number.isNaN(new Date("garbage").getTime())`)).toBe(true)
     expect(await value(`return new Date("garbage").toJSON()`)).toBeNull()
   })
@@ -702,11 +702,11 @@ describe("stdlib integration", () => {
     expect(await value(`const fn = () => 1; return !fn`)).toBe(false)
   })
 
-  test("object spread of sandbox values is a no-op, like JS", async () => {
+  test("object spread of CodeMode values is a no-op, like JS", async () => {
     expect(await value(`return { ...new Map([["a", 1]]), kept: true }`)).toEqual({ kept: true })
   })
 
-  test("dates inside Map values survive in-sandbox reads", async () => {
+  test("dates inside Map values survive in-CodeMode reads", async () => {
     expect(
       await value(`
       const m = new Map([["start", new Date(1000)]])
@@ -748,7 +748,7 @@ describe("stdlib integration", () => {
   })
 })
 
-describe("sandbox values at intra-sandbox checkpoints", () => {
+describe("CodeMode values at intra-CodeMode checkpoints", () => {
   test("Object.values/entries keep Dates usable", async () => {
     expect(await value(`return Object.values({ d: new Date(0) })[0].getTime()`)).toBe(0)
     expect(await value(`const [key, d] = Object.entries({ d: new Date(0) })[0]; return key + ":" + d.getTime()`)).toBe(
@@ -799,7 +799,7 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
     )
   })
 
-  test("object and array spread keep sandbox values usable", async () => {
+  test("object and array spread keep CodeMode values usable", async () => {
     expect(
       await value(`
       const src = { m: new Map([["a", 1]]) }
@@ -811,7 +811,7 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
     expect(await value(`const list = [new Date(1000)]; const copy = [...list]; return copy[0].getTime()`)).toBe(1000)
   })
 
-  test("Array.from over arrays keeps nested sandbox values usable", async () => {
+  test("Array.from over arrays keeps nested CodeMode values usable", async () => {
     expect(await value(`return Array.from([new Date(5)])[0].getTime()`)).toBe(5)
   })
 
@@ -866,7 +866,7 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
     expect(await value(`return Object.values({ r: /ab+/ })[0].test("abb")`)).toBe(true)
   })
 
-  test("Object.* helpers see sandbox values as empty objects, never internals", async () => {
+  test("Object.* helpers see CodeMode values as empty objects, never internals", async () => {
     expect(await value(`return Object.keys(new Map([["a", 1]]))`)).toEqual([])
     expect(await value(`return Object.values(new Date(0))`)).toEqual([])
     expect(await value(`return Object.entries(new Set([1]))`)).toEqual([])