mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-22 18:25:32 -04:00
refactor(codemode): simplify model-facing wording (#38042)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 `{}`.",
|
||||
]
|
||||
|
||||
@@ -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.",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user