refactor(codemode): simplify model-facing wording (#38042)

This commit is contained in:
Aiden Cline
2026-07-21 10:19:53 -05:00
committed by GitHub
parent 3ea8895299
commit 8d80365ef4
14 changed files with 70 additions and 98 deletions
+1
View File
@@ -4,6 +4,7 @@
- Do not add a speculative generic permission or approval policy. A host omits tools it does not expose and enforces domain authorization inside each provided tool.
- Keep Code Mode unaware of host session, channel, and conversation models. The hosting application supplies trusted execution scope around it.
- Tool schemas are the model-facing Interface. Keep arguments minimal and natural to the operation; never add unrelated IDs as ambient capability tokens.
- State model-visible diagnostics, logs, tool descriptions, and instructions directly. The execution context is already clear; do not repeat `Code Mode` or `CodeMode` unless the distinction is necessary.
- When interpreter behavior or support changes, update `interpreter-support.md` and direct tests in the same PR.
## OpenAPI
+11 -12
View File
@@ -126,7 +126,7 @@ export const invokeIntrinsic = <R>(
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)
throw new InterpreterRuntimeError(`Method '${ref.name}' is not available.`, node)
}
const coerceNumericArgument = <R>(
@@ -157,8 +157,7 @@ const coerceNumericArgument = <R>(
}
export const invokeGlobalMethod = (ref: GlobalMethodReference, args: Array<unknown>, node: AstNode): unknown => {
if (ref.namespace === "console")
throw new InterpreterRuntimeError(`console.${ref.name} is not available in CodeMode.`, node)
if (ref.namespace === "console") throw new InterpreterRuntimeError(`console.${ref.name} is not available.`, node)
if (ref.namespace === "Object") return invokeObjectMethod(ref.name, args, node)
if (ref.namespace === "Math") return invokeMathMethod(ref.name, args, node)
if (ref.namespace === "Array") return invokeArrayStatic(ref.name, args, node)
@@ -168,9 +167,9 @@ export const invokeGlobalMethod = (ref: GlobalMethodReference, args: Array<unkno
if (ref.namespace === "Date") return invokeDateStatic(ref.name, args, node)
if (ref.namespace === "RegExp") return invokeRegExpStatic(ref.name, args, node)
if (ref.namespace === "Map" || ref.namespace === "Set" || ref.namespace === "URLSearchParams") {
throw new InterpreterRuntimeError(`${ref.namespace}.${ref.name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`${ref.namespace}.${ref.name} is not available.`, node)
}
throw new InterpreterRuntimeError(`${ref.namespace}.${ref.name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`${ref.namespace}.${ref.name} is not available.`, node)
}
const requireDataArgument = (name: string, index: number, arg: unknown, node: AstNode): unknown => {
@@ -348,7 +347,7 @@ const invokeStringMethod = (value: string, name: string, args: Array<unknown>, n
break
}
default:
throw new InterpreterRuntimeError(`String method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`String method '${name}' is not available.`, node)
}
return boundedData(result, `String.${name} result`)
}
@@ -364,7 +363,7 @@ const invokeArrayStatic = (name: string, args: Array<unknown>, node: AstNode): u
case "from":
return arrayFromItems(args[0], node)
default:
throw new InterpreterRuntimeError(`Array.${name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Array.${name} is not available.`, node)
}
}
@@ -449,7 +448,7 @@ export const invokeGroupBy = <R>(
for (const item of items) {
const key = yield* coerceGroupByPropertyKey(runner, yield* apply([item, index]), node)
if (isBlockedMember(key)) {
throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Property '${key}' is not available.`, node)
}
const group = result[key]
if (group === undefined) result[key] = [item]
@@ -619,7 +618,7 @@ const invokeMapMethod = <R>(
})
}
default:
throw new InterpreterRuntimeError(`Map method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Map method '${name}' is not available.`, node)
}
}
@@ -666,7 +665,7 @@ const invokeSetMethod = <R>(
case "isDisjointFrom":
return invokeSetOperation(runner, target, name, args[0], node)
default:
throw new InterpreterRuntimeError(`Set method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Set method '${name}' is not available.`, node)
}
}
@@ -861,7 +860,7 @@ const invokeURLSearchParamsMethod = <R>(
})
}
default:
throw new InterpreterRuntimeError(`URLSearchParams method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`URLSearchParams method '${name}' is not available.`, node)
}
}
@@ -1104,7 +1103,7 @@ const invokeArrayMethod = <R>(
}
return -1
}
throw new InterpreterRuntimeError(`Array method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Array method '${name}' is not available.`, node)
})
}
+1 -1
View File
@@ -166,7 +166,7 @@ export class InterpreterRuntimeError extends Error {
export const unsupportedSyntax = (kind: string, node: AstNode): InterpreterRuntimeError =>
new InterpreterRuntimeError(
`Syntax '${kind}' is not supported in CodeMode. ${supportedSyntaxMessage}`,
`Syntax '${kind}' is not supported. ${supportedSyntaxMessage}`,
node,
"UnsupportedSyntax",
[supportedSyntaxMessage],
+31 -59
View File
@@ -166,7 +166,7 @@ const instanceofValue = (lhs: unknown, rhs: unknown, node: AstNode): boolean =>
return false
}
throw new InterpreterRuntimeError(
"The right-hand side of 'instanceof' must be a constructor CodeMode knows: Error (or a specific error type like TypeError), Date, RegExp, Map, Set, URL, URLSearchParams, Array, Object, or Promise.",
"The right-hand side of 'instanceof' must be a supported constructor: Error (or a specific error type like TypeError), Date, RegExp, Map, Set, URL, URLSearchParams, Array, Object, or Promise.",
node,
)
}
@@ -393,12 +393,9 @@ export class Interpreter<R> {
private createFunction(node: AstNode): CodeModeFunction {
if (node.generator === true) {
throw new InterpreterRuntimeError(
"Generator functions are not supported in CodeMode.",
node,
"UnsupportedSyntax",
[supportedSyntaxMessage],
)
throw new InterpreterRuntimeError("Generator functions are not supported.", node, "UnsupportedSyntax", [
supportedSyntaxMessage,
])
}
return new CodeModeFunction(
getArray(node, "params").map((parameter, index) => asNode(parameter, `params[${index}]`)),
@@ -454,11 +451,7 @@ export class Interpreter<R> {
return Effect.gen(function* () {
const discriminant = yield* self.evaluateExpression(getNode(node, "discriminant"))
if (containsOpaqueReference(discriminant)) {
throw new InterpreterRuntimeError(
"Switch discriminants must be data values in CodeMode.",
node,
"InvalidDataValue",
)
throw new InterpreterRuntimeError("Switch discriminants must be data values.", node, "InvalidDataValue")
}
self.scopes.push()
return yield* Effect.gen(function* () {
@@ -474,11 +467,7 @@ export class Interpreter<R> {
}
const candidate = yield* self.evaluateExpression(test)
if (containsOpaqueReference(candidate)) {
throw new InterpreterRuntimeError(
"Switch case values must be data values in CodeMode.",
test,
"InvalidDataValue",
)
throw new InterpreterRuntimeError("Switch case values must be data values.", test, "InvalidDataValue")
}
if (candidate === discriminant) {
selected = index
@@ -649,7 +638,7 @@ export class Interpreter<R> {
const iterable = spreadItems(right)
if (iterable === undefined) {
throw new InterpreterRuntimeError(
`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams value in CodeMode.`,
`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams value.`,
node,
)
}
@@ -746,7 +735,7 @@ export class Interpreter<R> {
const keys = self.enumerableKeys(right)
if (keys === undefined) {
throw new InterpreterRuntimeError(
"for...in requires a plain object, array, or tools reference in CodeMode. Use for...of for arrays/strings/Maps/Sets, or Object.keys(value) for a key list.",
"for...in requires a plain object, array, or tools reference. Use for...of for arrays/strings/Maps/Sets, or Object.keys(value) for a key list.",
node,
)
}
@@ -948,7 +937,7 @@ export class Interpreter<R> {
const key = yield* self.destructuringPropertyKey(property)
if (isBlockedMember(String(key))) {
throw new InterpreterRuntimeError(`Property '${String(key)}' is not available in CodeMode.`, property)
throw new InterpreterRuntimeError(`Property '${String(key)}' is not available.`, property)
}
consumed.add(String(key))
yield* self.declarePattern(
@@ -1026,7 +1015,7 @@ export class Interpreter<R> {
}
const key = yield* self.destructuringPropertyKey(property)
if (isBlockedMember(String(key))) {
throw new InterpreterRuntimeError(`Property '${String(key)}' is not available in CodeMode.`, property)
throw new InterpreterRuntimeError(`Property '${String(key)}' is not available.`, property)
}
consumed.add(String(key))
yield* self.assignPattern(getNode(property, "value"), self.destructuringPropertyValue(source, key), property)
@@ -1202,7 +1191,7 @@ export class Interpreter<R> {
if (first === null || first === undefined) return {}
if (typeof first === "object") return first
throw new InterpreterRuntimeError(
`Object(${typeof first}) wrapper objects are not supported in CodeMode; use the primitive value directly.`,
`Object(${typeof first}) wrapper objects are not supported; use the primitive value directly.`,
node,
)
}
@@ -1379,7 +1368,7 @@ export class Interpreter<R> {
private applyBinaryOperator(operator: string, lhs: unknown, rhs: unknown, node: AstNode): unknown {
if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
throw new InterpreterRuntimeError("Binary operators require data values in CodeMode.", node, "InvalidDataValue")
throw new InterpreterRuntimeError("Binary operators require data values.", node, "InvalidDataValue")
}
// Null-prototype data needs explicit primitive coercion; identity and `in` retain raw objects.
// Dates use their default string hint for addition and loose equality, and epoch time elsewhere.
@@ -1470,7 +1459,7 @@ export class Interpreter<R> {
if (operator === "!") return !value
if (operator === "void") return undefined
if (containsOpaqueReference(value)) {
throw new InterpreterRuntimeError("Unary operators require data values in CodeMode.", node, "InvalidDataValue")
throw new InterpreterRuntimeError("Unary operators require data values.", node, "InvalidDataValue")
}
const operand =
value instanceof CodeModeDate
@@ -1585,11 +1574,7 @@ export class Interpreter<R> {
// the host throw during ToPrimitive, and opaque runtime references must reject clearly.
const operand = (current: unknown): number => {
if (containsOpaqueReference(current)) {
throw new InterpreterRuntimeError(
`'${operator}' requires a data value in CodeMode.`,
argument,
"InvalidDataValue",
)
throw new InterpreterRuntimeError(`'${operator}' requires a data value.`, argument, "InvalidDataValue")
}
return coerceToNumber(current)
}
@@ -1712,7 +1697,7 @@ export class Interpreter<R> {
if (callable === undefined || callable === null) {
throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee).as("TypeError")
}
throw new InterpreterRuntimeError("Only tools are callable in CodeMode.", callee)
throw new InterpreterRuntimeError("Only tools are callable here.", callee)
})
}
@@ -1728,8 +1713,7 @@ export class Interpreter<R> {
}
private invokeConsole(name: string, args: Array<unknown>, node: AstNode): undefined {
if (!consoleMethods.has(name))
throw new InterpreterRuntimeError(`console.${name} is not available in CodeMode.`, node)
if (!consoleMethods.has(name)) throw new InterpreterRuntimeError(`console.${name} is not available.`, node)
this.logs.push(formatConsoleMessage(name, args))
return undefined
}
@@ -1744,10 +1728,7 @@ export class Interpreter<R> {
const spread = yield* self.evaluateExpression(getNode(argNode, "argument"))
const items = spreadItems(spread)
if (items === undefined)
throw new InterpreterRuntimeError(
"Spread arguments require an array, string, Map, or Set in CodeMode.",
argNode,
)
throw new InterpreterRuntimeError("Spread arguments require an array, string, Map, or Set.", argNode)
args.push(...items)
} else {
args.push(yield* self.evaluateExpression(argNode))
@@ -1813,15 +1794,10 @@ export class Interpreter<R> {
const spread = yield* self.evaluateExpression(getNode(property, "argument"))
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.",
property,
"InvalidDataValue",
)
throw new InterpreterRuntimeError("Object spread requires a data object.", property, "InvalidDataValue")
}
for (const [key, value] of Object.entries(spread)) {
if (isBlockedMember(key))
throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, property)
if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available.`, property)
objectValue[key] = value
}
continue
@@ -1852,7 +1828,7 @@ export class Interpreter<R> {
}
if (isBlockedMember(String(key))) {
throw new InterpreterRuntimeError(`Property '${String(key)}' is not available in CodeMode.`, keyNode)
throw new InterpreterRuntimeError(`Property '${String(key)}' is not available.`, keyNode)
}
objectValue[String(key)] = yield* self.evaluateExpression(valueNode)
}
@@ -1878,10 +1854,7 @@ export class Interpreter<R> {
const spread = yield* self.evaluateExpression(getNode(element, "argument"))
const items = spreadItems(spread)
if (items === undefined)
throw new InterpreterRuntimeError(
"Array spread requires an array, string, Map, or Set in CodeMode.",
element,
)
throw new InterpreterRuntimeError("Array spread requires an array, string, Map, or Set.", element)
values.push(...items)
} else {
values.push(yield* self.evaluateExpression(element))
@@ -1977,14 +1950,14 @@ export class Interpreter<R> {
return new PromiseMethodReference(key as PromiseMethodName)
}
throw new InterpreterRuntimeError(
`Promise.${String(key)} is not available in CodeMode. Available: Promise.all, Promise.allSettled, Promise.race, Promise.any, Promise.resolve, and Promise.reject; consume promises with await.`,
`Promise.${String(key)} is not available. Available: Promise.all, Promise.allSettled, Promise.race, Promise.any, Promise.resolve, and Promise.reject; consume promises with await.`,
propertyNode,
)
}
if (objectValue instanceof GlobalNamespace) {
if (typeof key === "string" && isBlockedMember(key)) {
throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available in CodeMode.`, propertyNode)
throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available.`, propertyNode)
}
if (typeof key !== "string") return new ComputedValue(undefined)
if (objectValue.name === "Math" && mathConstants.has(key)) {
@@ -2016,7 +1989,7 @@ export class Interpreter<R> {
if (objectValue instanceof CoercionFunction) {
if (typeof key === "string" && isBlockedMember(key)) {
throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available in CodeMode.`, propertyNode)
throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available.`, propertyNode)
}
if (typeof key !== "string") return new ComputedValue(undefined)
if (objectValue.name === "Number" && numberConstants.has(key)) {
@@ -2083,7 +2056,7 @@ export class Interpreter<R> {
if (isRuntimeReference(objectValue)) {
throw new InterpreterRuntimeError(
"CodeMode runtime references are opaque and do not expose properties.",
"Runtime references are opaque and do not expose properties.",
objectNode,
"InvalidDataValue",
)
@@ -2094,7 +2067,7 @@ export class Interpreter<R> {
}
if (typeof key === "string" && isBlockedMember(key)) {
throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, propertyNode)
throw new InterpreterRuntimeError(`Property '${key}' is not available.`, propertyNode)
}
if (Array.isArray(objectValue)) {
@@ -2147,7 +2120,7 @@ export class Interpreter<R> {
private evaluateDeleteExpression(argument: AstNode): Effect.Effect<boolean, unknown, R> {
const target = argument.type === "ChainExpression" ? getNode(argument, "expression") : argument
if (target.type !== "MemberExpression") {
throw new InterpreterRuntimeError("Only data fields may be deleted in CodeMode.", argument)
throw new InterpreterRuntimeError("Only data fields may be deleted.", argument)
}
return Effect.map(this.getMemberReference(target, "delete"), (reference) => {
if (reference === OptionalShortCircuit) return true
@@ -2162,7 +2135,7 @@ export class Interpreter<R> {
reference instanceof JsonMethodReference ||
reference.target instanceof CodeModeURL
) {
throw new InterpreterRuntimeError("Only data fields may be deleted in CodeMode.", target, "InvalidDataValue")
throw new InterpreterRuntimeError("Only data fields may be deleted.", target, "InvalidDataValue")
}
if (reference.target instanceof CodeModeRegExp) {
return Reflect.deleteProperty(reference.target.regex, reference.key)
@@ -2190,13 +2163,12 @@ export class Interpreter<R> {
reference instanceof GlobalMethodReference ||
reference instanceof JsonMethodReference
) {
throw new InterpreterRuntimeError("Only data fields may be assigned in CodeMode.", node)
throw new InterpreterRuntimeError("Only data fields may be assigned.", node)
}
if (Array.isArray(reference.target)) {
if (reference.key === "length")
throw new InterpreterRuntimeError("Array length cannot be assigned in CodeMode.", node)
if (reference.key === "length") throw new InterpreterRuntimeError("Array length cannot be assigned.", node)
if (typeof reference.key === "string" && arrayMethods.has(reference.key)) {
throw new InterpreterRuntimeError("Array methods cannot be assigned in CodeMode.", node)
throw new InterpreterRuntimeError("Array methods cannot be assigned.", node)
}
}
const key = Array.isArray(reference.target) ? reference.key : String(reference.key)
+2 -2
View File
@@ -58,7 +58,7 @@ const formatConsoleValue = (value: unknown, seen: Set<object>, depth: number): s
seen.delete(value)
}
}
if (isRuntimeReference(value)) return "[CodeMode reference]"
if (isRuntimeReference(value)) return "[opaque reference]"
seen.add(value)
try {
if (Array.isArray(value)) {
@@ -74,7 +74,7 @@ const formatConsoleValue = (value: unknown, seen: Set<object>, depth: number): s
const formatConsoleTable = (value: unknown, columnsArgument: unknown): string => {
if (value === undefined) return "undefined"
if (containsOpaqueReference(value)) return "[CodeMode reference]"
if (containsOpaqueReference(value)) return "[opaque reference]"
const data = boundedData(value, "console.table argument")
const columns = consoleTableColumns(columnsArgument)
const rows = consoleTableRows(data, columns)
+2 -2
View File
@@ -59,7 +59,7 @@ export const invokeDateStatic = (name: string, args: Array<unknown>, node: AstNo
case "UTC":
return Date.UTC(...(args.map((arg) => coerceToNumber(arg)) as Parameters<typeof Date.UTC>))
default:
throw new InterpreterRuntimeError(`Date.${name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Date.${name} is not available.`, node)
}
}
@@ -170,7 +170,7 @@ export const invokeDateMethod = (
if (args.length < 3) return updateDate(value, hosted.setUTCFullYear(args[0], args[1]))
return updateDate(value, hosted.setUTCFullYear(args[0], args[1], args[2]))
default:
throw new InterpreterRuntimeError(`Date method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Date method '${name}' is not available.`, node)
}
}
+2 -2
View File
@@ -51,7 +51,7 @@ export const mathMethods = new Set([
])
export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNode): number => {
if (!mathMethods.has(name)) throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
if (!mathMethods.has(name)) throw new InterpreterRuntimeError(`Math.${name} is not available.`, node)
if (name === "random") return Math.random()
if (name === "sumPrecise") {
const items = spreadItems(args[0])
@@ -151,5 +151,5 @@ export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNo
case "imul":
return Math.imul(a, b())
}
throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Math.${name} is not available.`, node)
}
+2 -2
View File
@@ -45,7 +45,7 @@ export const invokeNumberMethod = (value: number, name: string, args: Array<unkn
result = value
break
default:
throw new InterpreterRuntimeError(`Number method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Number method '${name}' is not available.`, node)
}
return boundedData(result, `Number.${name} result`)
}
@@ -71,7 +71,7 @@ export const invokeNumberStatic = (name: string, args: Array<unknown>, node: Ast
case "parseFloat":
return parseFloat(coerceToString(value))
default:
throw new InterpreterRuntimeError(`Number.${name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Number.${name} is not available.`, node)
}
}
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
+3 -3
View File
@@ -30,7 +30,7 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
return input as Record<string, unknown>
}
const guardedSet = (out: Record<string, unknown>, key: string, item: unknown): void => {
if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available.`, node)
out[key] = item
}
const addEntry = (out: Record<string, unknown>, key: unknown, item: unknown): void => {
@@ -49,7 +49,7 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
return Object.hasOwn(requireObject(), String(args[1]))
case "is":
if (containsOpaqueReference(args[0]) || containsOpaqueReference(args[1])) {
throw new InterpreterRuntimeError("Object.is requires data values in CodeMode.", node, "InvalidDataValue")
throw new InterpreterRuntimeError("Object.is requires data values.", node, "InvalidDataValue")
}
return Object.is(args[0], args[1])
case "assign": {
@@ -94,5 +94,5 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
return out
}
}
throw new InterpreterRuntimeError(`Object.${name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`Object.${name} is not available.`, node)
}
+2 -2
View File
@@ -72,7 +72,7 @@ export const matchToValue = (match: RegExpMatchArray): Array<unknown> => {
}
export const invokeRegExpStatic = (name: string, args: Array<unknown>, node: AstNode): string => {
if (name !== "escape") throw new InterpreterRuntimeError(`RegExp.${name} is not available in CodeMode.`, node)
if (name !== "escape") throw new InterpreterRuntimeError(`RegExp.${name} is not available.`, node)
if (typeof args[0] !== "string") {
throw new InterpreterRuntimeError("RegExp.escape expects a string.", node).as("TypeError")
}
@@ -104,7 +104,7 @@ export const invokeRegExpMethod = (
case "toString":
return coerceToString(value)
default:
throw new InterpreterRuntimeError(`RegExp method '${name}' is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`RegExp method '${name}' is not available.`, node)
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ export const invokeStringStatic = (name: string, args: Array<unknown>, node: Ast
case "fromCodePoint":
return String.fromCodePoint(...codes)
default:
throw new InterpreterRuntimeError(`String.${name} is not available in CodeMode.`, node)
throw new InterpreterRuntimeError(`String.${name} is not available.`, node)
}
}
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
+2 -2
View File
@@ -69,7 +69,7 @@ export const urlArgument = (value: unknown, label: string): string =>
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)
if (!urlStatics.has(name)) throw new InterpreterRuntimeError(`URL.${name} is not available.`, node)
if (args.length === 0) throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node).as("TypeError")
const input = urlArgument(args[0], `URL.${name} input`)
const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`)
@@ -83,7 +83,7 @@ export const invokeURLStatic = (name: string, args: Array<unknown>, node: AstNod
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)
throw new InterpreterRuntimeError(`URL method '${name}' is not available.`, node)
}
import { type AstNode, InterpreterRuntimeError, UriFunction } from "../interpreter/model.js"
import { CodeModeURL } from "../values.js"
+6 -6
View File
@@ -363,7 +363,7 @@ const termForms = (term: string): Array<string> => {
const makeSearchTool = (searchIndex: ReadonlyArray<SearchEntry>): Definition => ({
_tag: "CodeModeTool",
description: "Search available Code Mode tools",
description: "Search available tools",
input: SearchInput,
output: SearchOutput,
run: (input) =>
@@ -501,8 +501,8 @@ export const prepare = <R>(tools: Tools<R>, catalogBudget = defaultCatalogBudget
empty
? "This is a restricted JavaScript language for calling tools, not a general-purpose runtime."
: complete
? "This is a restricted JavaScript language for calling tools, not a general-purpose runtime. Inside the confined interpreter, `tools` contains the Code Mode tools listed below; surrounding agent tools are not available."
: "This is a restricted JavaScript language for calling tools, not a general-purpose runtime. Inside the confined interpreter, `tools` contains the Code Mode tools listed or searchable below; surrounding agent tools are not available.",
? "This is a restricted JavaScript language for calling tools, not a general-purpose runtime. Inside the confined interpreter, `tools` contains the tools listed below; surrounding agent tools are not available."
: "This is a restricted JavaScript language for calling tools, not a general-purpose runtime. Inside the confined interpreter, `tools` contains the tools listed or searchable below; surrounding agent tools are not available.",
...(empty
? []
: ["Do not infer or normalize tool names; use only exact signatures shown below or returned by search."]),
@@ -533,8 +533,8 @@ export const prepare = <R>(tools: Tools<R>, catalogBudget = defaultCatalogBudget
"## Rules",
"",
complete
? "- Only Code Mode tools listed here are available; surrounding agent tools are not implicitly exposed."
: "- Only Code Mode tools listed here or returned by the built-in `search` function are available; surrounding agent tools are not implicitly exposed.",
? "- Only tools listed here are available; surrounding agent tools are not implicitly exposed."
: "- Only tools listed here or returned by the built-in `search` function are available; surrounding agent tools are not implicitly exposed.",
"- Filter, aggregate, and transform collections in code - never return them raw or call a tool per item across messages.",
"- A result typed `Promise<unknown>` may be structured data or text. Before reading fields, check that it is a non-null object and not an array; otherwise handle the returned text or primitive directly.",
'- Run independent calls in parallel: `await Promise.all(items.map((item) => tools.<namespace>.<tool>(item)))`, or use `tools.<namespace>["tool-name"](item)` when the listed signature uses bracket notation.',
@@ -553,7 +553,7 @@ export const prepare = <R>(tools: Tools<R>, catalogBudget = defaultCatalogBudget
"## Language",
"",
"Use common JavaScript data operations, functions, control flow, selected standard-library methods, and awaited tool calls. Built-ins include Date, RegExp, Map, Set, URL, URLSearchParams, and URI encoding helpers.",
"Modules/imports, classes, generators, timers, fetch, eval, prototype access, and unlisted methods are unavailable. Use Code Mode tools for external operations. Use await with try/catch.",
"Modules/imports, classes, generators, timers, fetch, eval, prototype access, and unlisted methods are unavailable. Use tools for external operations. Use await with try/catch.",
"Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
"Dates and URLs serialize to strings at data boundaries; Map/Set/RegExp/URLSearchParams serialize to `{}`.",
]
+4 -4
View File
@@ -308,7 +308,7 @@ describe("CodeMode console capture", () => {
)
expect(result.ok).toBe(true)
expect(result.logs).toStrictEqual(['{"box":Map(1) [["self",[Circular]]]}', '{"fn":[CodeMode reference],"ok":1}'])
expect(result.logs).toStrictEqual(['{"box":Map(1) [["self",[Circular]]]}', '{"fn":[opaque reference],"ok":1}'])
})
test("console.table renders CodeMode value cells", async () => {
@@ -696,7 +696,7 @@ describe("CodeMode public contract", () => {
expect(instructions).toContain("Do not infer or normalize tool names")
expect(instructions).toContain("bracket notation and quotes are part of the path")
expect(instructions).toContain("surrounding agent tools are not available")
expect(instructions).toContain("Only Code Mode tools listed here are available")
expect(instructions).toContain("Only tools listed here are available")
// Placeholders use generic namespace/tool/field names only - no fabricated real tools
// and no real catalog tools cherry-picked into example lines.
expect(instructions).toContain("`const result = await tools.<namespace>.<tool>(input)`")
@@ -718,7 +718,7 @@ describe("CodeMode public contract", () => {
'1. If needed, discover tools with the built-in search function: `return search({ query: "<intent + key nouns>" })`.',
)
expect(partial).toContain("In the next execution, copy a returned path exactly")
expect(partial).toContain("Only Code Mode tools listed here or returned by the built-in `search` function")
expect(partial).toContain("Only tools listed here or returned by the built-in `search` function")
expect(partial).toContain('- Browse one namespace: `search({ query: "", namespace: "<name>" })`.')
expect(partial).toContain("repeat the same search with `offset: next.offset`")
expect(partial).toContain(" limit?: number,\n offset?: number,")
@@ -739,7 +739,7 @@ describe("CodeMode public contract", () => {
expect(instructions).not.toContain("promise chaining")
expect(instructions).toContain("URL, URLSearchParams, and URI encoding helpers")
expect(instructions).not.toContain("host globals")
expect(instructions).toContain("Use Code Mode tools for external operations")
expect(instructions).toContain("Use tools for external operations")
expect(instructions).toContain(
"Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
)