@neurocode-ai/codemode 1.18.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,94 @@
1
+ export const dateMethods = new Set([
2
+ "getTime",
3
+ "valueOf",
4
+ "toISOString",
5
+ "toJSON",
6
+ "toString",
7
+ "getFullYear",
8
+ "getMonth",
9
+ "getDate",
10
+ "getDay",
11
+ "getHours",
12
+ "getMinutes",
13
+ "getSeconds",
14
+ "getMilliseconds",
15
+ "getUTCFullYear",
16
+ "getUTCMonth",
17
+ "getUTCDate",
18
+ "getUTCDay",
19
+ "getUTCHours",
20
+ "getUTCMinutes",
21
+ "getUTCSeconds",
22
+ "getUTCMilliseconds",
23
+ "getTimezoneOffset",
24
+ ])
25
+
26
+ export const dateStatics = new Set(["now", "parse", "UTC"])
27
+
28
+ export const invokeDateStatic = (name: string, args: Array<unknown>, node: AstNode): number => {
29
+ switch (name) {
30
+ case "now":
31
+ return Date.now()
32
+ case "parse":
33
+ return Date.parse(coerceToString(args[0]))
34
+ case "UTC":
35
+ return Date.UTC(...(args.map((arg) => coerceToNumber(arg)) as Parameters<typeof Date.UTC>))
36
+ default:
37
+ throw new InterpreterRuntimeError(`Date.${name} is not available in CodeMode.`, node)
38
+ }
39
+ }
40
+
41
+ export const invokeDateMethod = (value: SandboxDate, name: string, node: AstNode): unknown => {
42
+ const hosted = new Date(value.time)
43
+ switch (name) {
44
+ case "getTime":
45
+ case "valueOf":
46
+ return value.time
47
+ case "toISOString":
48
+ if (!Number.isFinite(value.time)) throw new InterpreterRuntimeError("Invalid time value.", node)
49
+ return hosted.toISOString()
50
+ case "toJSON":
51
+ return Number.isFinite(value.time) ? hosted.toISOString() : null
52
+ case "toString":
53
+ return coerceToString(value)
54
+ case "getFullYear":
55
+ return hosted.getFullYear()
56
+ case "getMonth":
57
+ return hosted.getMonth()
58
+ case "getDate":
59
+ return hosted.getDate()
60
+ case "getDay":
61
+ return hosted.getDay()
62
+ case "getHours":
63
+ return hosted.getHours()
64
+ case "getMinutes":
65
+ return hosted.getMinutes()
66
+ case "getSeconds":
67
+ return hosted.getSeconds()
68
+ case "getMilliseconds":
69
+ return hosted.getMilliseconds()
70
+ case "getUTCFullYear":
71
+ return hosted.getUTCFullYear()
72
+ case "getUTCMonth":
73
+ return hosted.getUTCMonth()
74
+ case "getUTCDate":
75
+ return hosted.getUTCDate()
76
+ case "getUTCDay":
77
+ return hosted.getUTCDay()
78
+ case "getUTCHours":
79
+ return hosted.getUTCHours()
80
+ case "getUTCMinutes":
81
+ return hosted.getUTCMinutes()
82
+ case "getUTCSeconds":
83
+ return hosted.getUTCSeconds()
84
+ case "getUTCMilliseconds":
85
+ return hosted.getUTCMilliseconds()
86
+ case "getTimezoneOffset":
87
+ return hosted.getTimezoneOffset()
88
+ default:
89
+ throw new InterpreterRuntimeError(`Date method '${name}' is not available in CodeMode.`, node)
90
+ }
91
+ }
92
+ import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
93
+ import { SandboxDate } from "../values.js"
94
+ import { coerceToNumber, coerceToString } from "./value.js"
@@ -0,0 +1,42 @@
1
+ import {
2
+ type AstNode,
3
+ CodeModeFunction,
4
+ InterpreterRuntimeError,
5
+ supportedSyntaxMessage,
6
+ } from "../interpreter/model.js"
7
+ import { copyIn, copyOut } from "../tool-runtime.js"
8
+
9
+ export const jsonStatics = new Set(["stringify", "parse"])
10
+
11
+ export const invokeJsonMethod = (name: string, args: Array<unknown>, node: AstNode): unknown => {
12
+ if (!jsonStatics.has(name)) throw new InterpreterRuntimeError(`JSON.${name} is not available in CodeMode.`, node)
13
+ switch (name) {
14
+ case "stringify": {
15
+ const replacer = args[1]
16
+ if (Array.isArray(replacer) || replacer instanceof CodeModeFunction) {
17
+ throw new InterpreterRuntimeError(
18
+ "JSON.stringify replacers are not supported in CodeMode.",
19
+ node,
20
+ "UnsupportedSyntax",
21
+ [supportedSyntaxMessage],
22
+ )
23
+ }
24
+ const space = args[2]
25
+ const indent = typeof space === "number" || typeof space === "string" ? space : undefined
26
+ return JSON.stringify(copyOut(copyIn(args[0], "JSON.stringify value")), null, indent)
27
+ }
28
+ case "parse": {
29
+ const text = args[0]
30
+ if (typeof text !== "string") throw new InterpreterRuntimeError("JSON.parse expects a string.", node)
31
+ try {
32
+ return copyIn(JSON.parse(text), "JSON.parse result")
33
+ } catch (error) {
34
+ throw new InterpreterRuntimeError(
35
+ `JSON.parse received invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
36
+ node,
37
+ ).as("SyntaxError")
38
+ }
39
+ }
40
+ }
41
+ throw new InterpreterRuntimeError(`JSON.${name} is not available in CodeMode.`, node)
42
+ }
@@ -0,0 +1,65 @@
1
+ export const mathConstants = new Set(["PI", "E", "LN2", "LN10", "LOG2E", "LOG10E", "SQRT2", "SQRT1_2"])
2
+
3
+ export const mathMethods = new Set([
4
+ "max",
5
+ "min",
6
+ "abs",
7
+ "floor",
8
+ "ceil",
9
+ "round",
10
+ "trunc",
11
+ "sign",
12
+ "sqrt",
13
+ "cbrt",
14
+ "pow",
15
+ "hypot",
16
+ "log",
17
+ "log2",
18
+ "log10",
19
+ "exp",
20
+ ])
21
+
22
+ export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNode): number => {
23
+ if (!mathMethods.has(name)) throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
24
+ const nums = args.map((arg) => {
25
+ if (typeof arg !== "number") throw new InterpreterRuntimeError(`Math.${name} expects number arguments.`, node)
26
+ return arg
27
+ })
28
+ const [a = Number.NaN, b = Number.NaN] = nums
29
+ switch (name) {
30
+ case "max":
31
+ return Math.max(...nums)
32
+ case "min":
33
+ return Math.min(...nums)
34
+ case "abs":
35
+ return Math.abs(a)
36
+ case "floor":
37
+ return Math.floor(a)
38
+ case "ceil":
39
+ return Math.ceil(a)
40
+ case "round":
41
+ return Math.round(a)
42
+ case "trunc":
43
+ return Math.trunc(a)
44
+ case "sign":
45
+ return Math.sign(a)
46
+ case "sqrt":
47
+ return Math.sqrt(a)
48
+ case "cbrt":
49
+ return Math.cbrt(a)
50
+ case "pow":
51
+ return Math.pow(a, b)
52
+ case "hypot":
53
+ return Math.hypot(...nums)
54
+ case "log":
55
+ return Math.log(a)
56
+ case "log2":
57
+ return Math.log2(a)
58
+ case "log10":
59
+ return Math.log10(a)
60
+ case "exp":
61
+ return Math.exp(a)
62
+ }
63
+ throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
64
+ }
65
+ import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
@@ -0,0 +1,66 @@
1
+ export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString"])
2
+
3
+ export const numberConstants = new Set(["MAX_SAFE_INTEGER", "MIN_SAFE_INTEGER", "MAX_VALUE", "MIN_VALUE", "EPSILON"])
4
+
5
+ export const numberStatics = new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseInt", "parseFloat"])
6
+
7
+ export const invokeNumberMethod = (value: number, name: string, args: Array<unknown>, node: AstNode): unknown => {
8
+ const optNum = (index: number): number | undefined => {
9
+ const arg = args[index]
10
+ if (arg === undefined) return undefined
11
+ if (typeof arg !== "number") throw new InterpreterRuntimeError(`Number.${name} expects a number argument.`, node)
12
+ return arg
13
+ }
14
+ let result: unknown
15
+ switch (name) {
16
+ case "toFixed":
17
+ result = value.toFixed(optNum(0))
18
+ break
19
+ case "toExponential":
20
+ result = value.toExponential(optNum(0))
21
+ break
22
+ case "toPrecision": {
23
+ const digits = optNum(0)
24
+ result = digits === undefined ? value.toString() : value.toPrecision(digits)
25
+ break
26
+ }
27
+ case "toString": {
28
+ const radix = optNum(0)
29
+ if (radix !== undefined && (radix < 2 || radix > 36)) {
30
+ throw new InterpreterRuntimeError("Number.toString radix must be between 2 and 36.", node)
31
+ }
32
+ result = value.toString(radix)
33
+ break
34
+ }
35
+ default:
36
+ throw new InterpreterRuntimeError(`Number method '${name}' is not available in CodeMode.`, node)
37
+ }
38
+ return boundedData(result, `Number.${name} result`)
39
+ }
40
+
41
+ export const invokeNumberStatic = (name: string, args: Array<unknown>, node: AstNode): unknown => {
42
+ const value = args[0]
43
+ switch (name) {
44
+ case "isInteger":
45
+ return Number.isInteger(value)
46
+ case "isFinite":
47
+ return Number.isFinite(value)
48
+ case "isNaN":
49
+ return Number.isNaN(value)
50
+ case "isSafeInteger":
51
+ return Number.isSafeInteger(value)
52
+ case "parseInt": {
53
+ const radix = args[1]
54
+ if (radix !== undefined && typeof radix !== "number") {
55
+ throw new InterpreterRuntimeError("Number.parseInt expects a numeric radix.", node)
56
+ }
57
+ return parseInt(coerceToString(value), radix)
58
+ }
59
+ case "parseFloat":
60
+ return parseFloat(coerceToString(value))
61
+ default:
62
+ throw new InterpreterRuntimeError(`Number.${name} is not available in CodeMode.`, node)
63
+ }
64
+ }
65
+ import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
66
+ import { boundedData, coerceToString } from "./value.js"
@@ -0,0 +1,77 @@
1
+ import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
2
+ import { isBlockedMember } from "../tool-runtime.js"
3
+ import { isSandboxValue, SandboxMap, SandboxURLSearchParams } from "../values.js"
4
+ import { boundedData, coerceToString } from "./value.js"
5
+
6
+ export const objectStatics = new Set(["keys", "values", "entries", "hasOwn", "assign", "fromEntries"])
7
+
8
+ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: AstNode): unknown => {
9
+ if (!objectStatics.has(name)) throw new InterpreterRuntimeError(`Object.${name} is not available in CodeMode.`, node)
10
+ const requireObject = (): Record<string, unknown> => {
11
+ const value = boundedData(args[0], `Object.${name} input`)
12
+ if (isSandboxValue(value)) return {}
13
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
14
+ throw new InterpreterRuntimeError(`Object.${name} expects a data object.`, node)
15
+ }
16
+ return value as Record<string, unknown>
17
+ }
18
+ const guardedSet = (out: Record<string, unknown>, key: string, item: unknown): void => {
19
+ if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
20
+ out[key] = item
21
+ }
22
+ switch (name) {
23
+ case "keys": {
24
+ const value = boundedData(args[0], "Object.keys input")
25
+ if (isSandboxValue(value)) return []
26
+ if (Array.isArray(value)) return Object.keys(value)
27
+ if (value === null || typeof value !== "object") {
28
+ throw new InterpreterRuntimeError("Object.keys expects a data object or array.", node)
29
+ }
30
+ return Object.keys(value)
31
+ }
32
+ case "values":
33
+ return Object.values(requireObject())
34
+ case "entries":
35
+ return Object.entries(requireObject()).map(([key, item]) => [key, item])
36
+ case "hasOwn":
37
+ return Object.hasOwn(requireObject(), String(args[1]))
38
+ case "assign": {
39
+ const out: Record<string, unknown> = Object.create(null)
40
+ for (const source of args) {
41
+ if (source === null || source === undefined) continue
42
+ const value = boundedData(source, "Object.assign input")
43
+ if (isSandboxValue(value)) continue
44
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
45
+ throw new InterpreterRuntimeError("Object.assign expects data objects.", node)
46
+ }
47
+ for (const [key, item] of Object.entries(value)) guardedSet(out, key, item)
48
+ }
49
+ return out
50
+ }
51
+ case "fromEntries": {
52
+ if (args[0] instanceof SandboxMap) {
53
+ const out: Record<string, unknown> = Object.create(null)
54
+ for (const [key, item] of args[0].map.entries()) guardedSet(out, coerceToString(key), item)
55
+ return out
56
+ }
57
+ if (args[0] instanceof SandboxURLSearchParams) {
58
+ const out: Record<string, unknown> = Object.create(null)
59
+ for (const [key, value] of args[0].params.entries()) guardedSet(out, key, value)
60
+ return out
61
+ }
62
+ const pairs = boundedData(args[0], "Object.fromEntries input")
63
+ if (!Array.isArray(pairs)) {
64
+ throw new InterpreterRuntimeError("Object.fromEntries expects an array of [key, value] pairs.", node)
65
+ }
66
+ const out: Record<string, unknown> = Object.create(null)
67
+ for (const pair of pairs) {
68
+ if (!Array.isArray(pair)) {
69
+ throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] pairs.", node)
70
+ }
71
+ guardedSet(out, String(pair[0]), pair[1])
72
+ }
73
+ return out
74
+ }
75
+ }
76
+ throw new InterpreterRuntimeError(`Object.${name} is not available in CodeMode.`, node)
77
+ }
@@ -0,0 +1,6 @@
1
+ import type { PromiseMethodName } from "../interpreter/model.js"
2
+
3
+ export const promiseStatics = new Set<PromiseMethodName>(["all", "allSettled", "race", "resolve", "reject"])
4
+
5
+ /** Maximum number of eagerly forked tool calls that may run concurrently. */
6
+ export const TOOL_CALL_CONCURRENCY = 8
@@ -0,0 +1,74 @@
1
+ export const regexpMethods = new Set(["test", "exec", "toString"])
2
+
3
+ export const regexpProperties = new Set([
4
+ "source",
5
+ "flags",
6
+ "lastIndex",
7
+ "global",
8
+ "ignoreCase",
9
+ "multiline",
10
+ "sticky",
11
+ "unicode",
12
+ "dotAll",
13
+ ])
14
+
15
+ export const regexFailureReason = (error: unknown): string =>
16
+ (error instanceof Error ? error.message : String(error)).replace(/^Invalid regular expression:\s*/i, "")
17
+
18
+ export const escapeRegexHint =
19
+ 'To match special characters like ( ) [ ] { } + * ? . literally, escape them with a backslash (e.g. "\\\\(") or test for them with String.includes instead.'
20
+
21
+ export const toHostRegex = (arg: unknown, method: string, node: AstNode, extraFlags = ""): RegExp => {
22
+ if (arg instanceof SandboxRegExp) return arg.regex
23
+ if (typeof arg === "string") {
24
+ try {
25
+ return new RegExp(arg, extraFlags)
26
+ } catch (error) {
27
+ throw new InterpreterRuntimeError(
28
+ `String.${method} received the string ${JSON.stringify(arg)}, which is not a valid regular expression pattern (${regexFailureReason(error)}). ${escapeRegexHint}`,
29
+ node,
30
+ ).as("SyntaxError")
31
+ }
32
+ }
33
+ throw new InterpreterRuntimeError(
34
+ `String.${method} expects a regular expression (a /pattern/flags literal or new RegExp(...)) or a string pattern, not ${arg === null ? "null" : typeof arg}.`,
35
+ node,
36
+ )
37
+ }
38
+
39
+ export const matchToValue = (match: RegExpMatchArray): Array<unknown> => {
40
+ const result: Array<unknown> = Array.from(match, (group) => group)
41
+ if (match.index !== undefined) (result as Record<string, unknown> & Array<unknown>).index = match.index
42
+ if (match.groups) {
43
+ const groups: SafeObject = Object.create(null) as SafeObject
44
+ for (const [key, group] of Object.entries(match.groups)) {
45
+ if (!isBlockedMember(key)) groups[key] = group
46
+ }
47
+ ;(result as Record<string, unknown> & Array<unknown>).groups = groups
48
+ }
49
+ return result
50
+ }
51
+
52
+ export const invokeRegExpMethod = (
53
+ value: SandboxRegExp,
54
+ name: string,
55
+ args: Array<unknown>,
56
+ node: AstNode,
57
+ ): unknown => {
58
+ switch (name) {
59
+ case "test":
60
+ return value.regex.test(coerceToString(args[0]))
61
+ case "exec": {
62
+ const matched = value.regex.exec(coerceToString(args[0]))
63
+ return matched === null ? null : matchToValue(matched)
64
+ }
65
+ case "toString":
66
+ return coerceToString(value)
67
+ default:
68
+ throw new InterpreterRuntimeError(`RegExp method '${name}' is not available in CodeMode.`, node)
69
+ }
70
+ }
71
+ import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
72
+ import { isBlockedMember, type SafeObject } from "../tool-runtime.js"
73
+ import { SandboxRegExp } from "../values.js"
74
+ import { coerceToString } from "./value.js"
@@ -0,0 +1,52 @@
1
+ export const stringMethods = new Set([
2
+ "toLowerCase",
3
+ "toUpperCase",
4
+ "trim",
5
+ "trimStart",
6
+ "trimEnd",
7
+ "trimLeft",
8
+ "trimRight",
9
+ "split",
10
+ "slice",
11
+ "substring",
12
+ "substr",
13
+ "includes",
14
+ "startsWith",
15
+ "endsWith",
16
+ "indexOf",
17
+ "lastIndexOf",
18
+ "replace",
19
+ "replaceAll",
20
+ "repeat",
21
+ "padStart",
22
+ "padEnd",
23
+ "charAt",
24
+ "charCodeAt",
25
+ "codePointAt",
26
+ "at",
27
+ "concat",
28
+ "toString",
29
+ "match",
30
+ "matchAll",
31
+ "search",
32
+ "localeCompare",
33
+ "normalize",
34
+ ])
35
+
36
+ export const stringStatics = new Set(["fromCharCode", "fromCodePoint"])
37
+
38
+ export const invokeStringStatic = (name: string, args: Array<unknown>, node: AstNode): unknown => {
39
+ const codes = args.map((arg) => {
40
+ if (typeof arg !== "number") throw new InterpreterRuntimeError(`String.${name} expects number arguments.`, node)
41
+ return arg
42
+ })
43
+ switch (name) {
44
+ case "fromCharCode":
45
+ return String.fromCharCode(...codes)
46
+ case "fromCodePoint":
47
+ return String.fromCodePoint(...codes)
48
+ default:
49
+ throw new InterpreterRuntimeError(`String.${name} is not available in CodeMode.`, node)
50
+ }
51
+ }
52
+ import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
@@ -0,0 +1,90 @@
1
+ export const urlProperties = new Set([
2
+ "href",
3
+ "origin",
4
+ "protocol",
5
+ "username",
6
+ "password",
7
+ "host",
8
+ "hostname",
9
+ "port",
10
+ "pathname",
11
+ "search",
12
+ "hash",
13
+ ])
14
+
15
+ export const urlWritableProperties = new Set([
16
+ "href",
17
+ "protocol",
18
+ "username",
19
+ "password",
20
+ "host",
21
+ "hostname",
22
+ "port",
23
+ "pathname",
24
+ "search",
25
+ "hash",
26
+ ])
27
+
28
+ export const urlMethods = new Set(["toString", "toJSON"])
29
+ export const urlStatics = new Set(["canParse", "parse"])
30
+ export const urlSearchParamsMethods = new Set([
31
+ "append",
32
+ "delete",
33
+ "get",
34
+ "getAll",
35
+ "has",
36
+ "set",
37
+ "sort",
38
+ "forEach",
39
+ "keys",
40
+ "values",
41
+ "entries",
42
+ "toString",
43
+ ])
44
+
45
+ export const uriArgument = (value: unknown, label: string): string => coerceToString(boundedData(value, label))
46
+
47
+ export const invokeUriFunction = (ref: UriFunction, args: Array<unknown>, node: AstNode): string => {
48
+ const value = uriArgument(args[0], `${ref.name} input`)
49
+ try {
50
+ switch (ref.name) {
51
+ case "encodeURI":
52
+ return encodeURI(value)
53
+ case "encodeURIComponent":
54
+ return encodeURIComponent(value)
55
+ case "decodeURI":
56
+ return decodeURI(value)
57
+ case "decodeURIComponent":
58
+ return decodeURIComponent(value)
59
+ }
60
+ } catch (error) {
61
+ throw new InterpreterRuntimeError(
62
+ `${ref.name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`,
63
+ node,
64
+ ).as("URIError")
65
+ }
66
+ }
67
+
68
+ export const urlArgument = (value: unknown, label: string): string =>
69
+ value instanceof SandboxURL ? value.url.href : uriArgument(value, label)
70
+
71
+ export const invokeURLStatic = (name: string, args: Array<unknown>, node: AstNode): unknown => {
72
+ if (!urlStatics.has(name)) throw new InterpreterRuntimeError(`URL.${name} is not available in CodeMode.`, node)
73
+ if (args.length === 0) throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node).as("TypeError")
74
+ const input = urlArgument(args[0], `URL.${name} input`)
75
+ const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`)
76
+ try {
77
+ const url = new URL(input, base)
78
+ return name === "canParse" ? true : new SandboxURL(url)
79
+ } catch {
80
+ return name === "canParse" ? false : null
81
+ }
82
+ }
83
+
84
+ export const invokeURLMethod = (value: SandboxURL, name: string, node: AstNode): string => {
85
+ if (name === "toString" || name === "toJSON") return value.url.href
86
+ throw new InterpreterRuntimeError(`URL method '${name}' is not available in CodeMode.`, node)
87
+ }
88
+ import { type AstNode, InterpreterRuntimeError, UriFunction } from "../interpreter/model.js"
89
+ import { SandboxURL } from "../values.js"
90
+ import { boundedData, coerceToString } from "./value.js"
@@ -0,0 +1,86 @@
1
+ export const errorConstructors = new Set([
2
+ "Error",
3
+ "TypeError",
4
+ "RangeError",
5
+ "SyntaxError",
6
+ "ReferenceError",
7
+ "EvalError",
8
+ "URIError",
9
+ ])
10
+
11
+ export const valueConstructors = new Set(["Date", "RegExp", "Map", "Set", "URL", "URLSearchParams"])
12
+
13
+ export const compoundOperators = new Set(["+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", ">>>="])
14
+
15
+ const ErrorBrand: unique symbol = Symbol("codemode.error")
16
+
17
+ export const createErrorValue = (name: string, message: string): SafeObject => {
18
+ const value = Object.assign(Object.create(null) as SafeObject, { name, message })
19
+ Object.defineProperty(value, ErrorBrand, { value: name })
20
+ return value
21
+ }
22
+
23
+ export const errorBrandName = (value: unknown): string | undefined =>
24
+ value !== null && typeof value === "object"
25
+ ? ((value as Record<PropertyKey, unknown>)[ErrorBrand] as string | undefined)
26
+ : undefined
27
+
28
+ export const boundedData = (value: unknown, label: string): unknown => copyIn(value, label, true)
29
+
30
+ export const coerceToString = (value: unknown): string => {
31
+ if (value === null) return "null"
32
+ if (value === undefined) return "undefined"
33
+ if (value instanceof SandboxDate)
34
+ return Number.isFinite(value.time) ? new Date(value.time).toISOString() : "Invalid Date"
35
+ if (value instanceof SandboxRegExp) return `/${value.regex.source}/${value.regex.flags}`
36
+ if (value instanceof SandboxMap) return "[object Map]"
37
+ if (value instanceof SandboxSet) return "[object Set]"
38
+ if (value instanceof SandboxURL) return value.url.href
39
+ if (value instanceof SandboxURLSearchParams) return value.params.toString()
40
+ if (typeof value === "object") {
41
+ return Array.isArray(value)
42
+ ? value.map((item) => (item === null || item === undefined ? "" : coerceToString(item))).join(",")
43
+ : "[object Object]"
44
+ }
45
+ return String(value)
46
+ }
47
+
48
+ export const coerceToNumber = (value: unknown): number => {
49
+ if (value instanceof SandboxDate) return value.time
50
+ if (isSandboxValue(value)) return Number.NaN
51
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? Number.NaN : Number(value)
52
+ }
53
+
54
+ export const invokeCoercion = (ref: CoercionFunction, args: Array<unknown>, node: AstNode): unknown => {
55
+ const raw = args[0]
56
+ if (isSandboxValue(raw)) {
57
+ if (ref.name === "Boolean") return true
58
+ if (ref.name === "Number") return coerceToNumber(raw)
59
+ if (ref.name === "String") return coerceToString(raw)
60
+ if (ref.name === "parseInt") return parseInt(coerceToString(raw))
61
+ return parseFloat(coerceToString(raw))
62
+ }
63
+ const value = boundedData(args[0], `${ref.name} input`)
64
+ if (ref.name === "Number") return coerceToNumber(value)
65
+ if (ref.name === "Boolean") return Boolean(value)
66
+ if (ref.name === "parseInt") {
67
+ const radix = args[1]
68
+ if (radix !== undefined && typeof radix !== "number") {
69
+ throw new InterpreterRuntimeError("parseInt expects a numeric radix.", node)
70
+ }
71
+ return parseInt(coerceToString(value), radix)
72
+ }
73
+ if (ref.name === "parseFloat") return parseFloat(coerceToString(value))
74
+ return coerceToString(value)
75
+ }
76
+ import { type AstNode, CoercionFunction, InterpreterRuntimeError } from "../interpreter/model.js"
77
+ import { copyIn, type SafeObject } from "../tool-runtime.js"
78
+ import {
79
+ isSandboxValue,
80
+ SandboxDate,
81
+ SandboxMap,
82
+ SandboxRegExp,
83
+ SandboxSet,
84
+ SandboxURL,
85
+ SandboxURLSearchParams,
86
+ } from "../values.js"
@@ -0,0 +1,11 @@
1
+ import { Schema } from "effect"
2
+
3
+ /** Safe operational refusal from a standard tool pack, reported as `ToolFailure`. */
4
+ export class ToolError extends Schema.TaggedErrorClass<ToolError>()("ToolError", {
5
+ message: Schema.String,
6
+ cause: Schema.optionalKey(Schema.Defect()),
7
+ }) {}
8
+
9
+ /** Creates a tool refusal whose message is safe to include in an execution diagnostic. */
10
+ export const toolError = (message: string, cause?: unknown): ToolError =>
11
+ new ToolError({ message, ...(cause === undefined ? {} : { cause }) })