@opencode/codemode 0.0.0-beta-19296 → 0.0.0-beta-19378
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.
- package/README.md +6 -0
- package/dist/codemode.d.ts +8 -11
- package/dist/codemode.js +4 -8
- package/dist/data.d.ts +28 -0
- package/dist/data.js +130 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interpreter/errors.d.ts +4 -6
- package/dist/interpreter/errors.js +22 -6
- package/dist/interpreter/execute.d.ts +3 -3
- package/dist/interpreter/execute.js +11 -18
- package/dist/interpreter/globals.d.ts +13 -0
- package/dist/interpreter/globals.js +59 -0
- package/dist/interpreter/host.d.ts +41 -0
- package/dist/interpreter/host.js +44 -0
- package/dist/interpreter/methods.d.ts +3 -16
- package/dist/interpreter/methods.js +42 -239
- package/dist/interpreter/model.d.ts +12 -73
- package/dist/interpreter/model.js +0 -87
- package/dist/interpreter/promises.d.ts +12 -13
- package/dist/interpreter/promises.js +49 -26
- package/dist/interpreter/references.js +23 -70
- package/dist/interpreter/runner.d.ts +23 -0
- package/dist/interpreter/runner.js +42 -0
- package/dist/interpreter/runtime.d.ts +13 -95
- package/dist/interpreter/runtime.js +290 -723
- package/dist/openapi/spec.js +1 -1
- package/dist/stdlib/array.d.ts +3 -0
- package/dist/stdlib/array.js +73 -0
- package/dist/stdlib/collections.d.ts +6 -1
- package/dist/stdlib/collections.js +120 -1
- package/dist/stdlib/console.d.ts +3 -2
- package/dist/stdlib/console.js +25 -16
- package/dist/stdlib/date.d.ts +5 -4
- package/dist/stdlib/date.js +28 -12
- package/dist/stdlib/json.d.ts +4 -4
- package/dist/stdlib/json.js +23 -28
- package/dist/stdlib/math.d.ts +3 -7
- package/dist/stdlib/math.js +85 -153
- package/dist/stdlib/number.d.ts +2 -4
- package/dist/stdlib/number.js +30 -37
- package/dist/stdlib/object.d.ts +4 -6
- package/dist/stdlib/object.js +106 -75
- package/dist/stdlib/regexp.d.ts +4 -6
- package/dist/stdlib/regexp.js +35 -12
- package/dist/stdlib/string.d.ts +1 -3
- package/dist/stdlib/string.js +15 -17
- package/dist/stdlib/url.d.ts +10 -6
- package/dist/stdlib/url.js +102 -25
- package/dist/stdlib/value.d.ts +6 -5
- package/dist/stdlib/value.js +33 -32
- package/dist/tool-runtime.d.ts +16 -15
- package/dist/tool-runtime.js +13 -150
- package/dist/values.d.ts +22 -16
- package/dist/values.js +23 -17
- package/package.json +1 -1
- package/dist/interpreter/iterator.d.ts +0 -13
- package/dist/interpreter/iterator.js +0 -4
- package/dist/stdlib/promise.d.ts +0 -2
- package/dist/stdlib/promise.js +0 -1
package/README.md
CHANGED
|
@@ -91,6 +91,12 @@ runtime.execute(source) // Effect<CodeMode.Result, never, ToolServices>
|
|
|
91
91
|
The Effect environment is inferred from the supplied tools. `onToolCallStart` observes admitted calls with decoded
|
|
92
92
|
input; `onToolCallEnd` observes settled outcomes and duration. Both hooks return Effects and must not fail.
|
|
93
93
|
|
|
94
|
+
### `Values`
|
|
95
|
+
|
|
96
|
+
`Values` exports the runtime's non-JSON value classes: `Values.URL`, `Values.URLSearchParams`, `Values.Date`,
|
|
97
|
+
`Values.RegExp`, `Values.Map`, `Values.Set`, and `Values.Promise`. The interpreter recognizes these by class; a
|
|
98
|
+
program's `new URL(...)` is a `Values.URL` wrapping the host `URL`. `Values.isValue` narrows to the data-like kinds.
|
|
99
|
+
|
|
94
100
|
### OpenAPI tools
|
|
95
101
|
|
|
96
102
|
`OpenAPI.fromSpec` converts an OpenAPI 3.x document into one tool per supported operation. Dotted `operationId` values
|
package/dist/codemode.d.ts
CHANGED
|
@@ -25,23 +25,20 @@ export type ResolvedExecutionLimits = {
|
|
|
25
25
|
readonly maxToolCalls: number | undefined;
|
|
26
26
|
readonly maxOutputBytes: number | undefined;
|
|
27
27
|
};
|
|
28
|
-
/**
|
|
29
|
-
export type
|
|
30
|
-
/** Source for one program in the supported JavaScript subset. */
|
|
31
|
-
code: string;
|
|
28
|
+
/** Configuration shared by `CodeMode.make` and `CodeMode.execute`. */
|
|
29
|
+
export type Options<Provided extends Record<string, unknown> = {}> = ToolRuntime.ToolCallHooks<Services<Provided>> & {
|
|
32
30
|
/** Explicit tools exposed to the program as `tools`. */
|
|
33
31
|
tools?: Provided & Tools<Services<Provided>>;
|
|
34
|
-
/**
|
|
32
|
+
/** Resource limits enforced on each execution. */
|
|
35
33
|
limits?: ExecutionLimits;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
};
|
|
35
|
+
/** Options for one CodeMode execution. */
|
|
36
|
+
export type ExecuteOptions<Provided extends Record<string, unknown> = {}> = Options<Provided> & {
|
|
37
|
+
/** Source for one program in the supported JavaScript subset. */
|
|
38
|
+
code: string;
|
|
40
39
|
};
|
|
41
40
|
/** A JSON value that can cross the confined interpreter boundary. */
|
|
42
41
|
export type DataValue = Schema.Json;
|
|
43
|
-
/** Configuration shared by `CodeMode.make` and `CodeMode.execute`. */
|
|
44
|
-
export type Options<Provided extends Record<string, unknown> = {}> = Omit<ExecuteOptions<Provided>, "code">;
|
|
45
42
|
/** Schema for a host tool input containing CodeMode source. */
|
|
46
43
|
export declare const Input: Schema.Struct<{
|
|
47
44
|
readonly code: Schema.String;
|
package/dist/codemode.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
|
-
import {
|
|
2
|
+
import { executeProgram } from "./interpreter/execute.js";
|
|
3
3
|
import { ToolRuntime } from "./tool-runtime.js";
|
|
4
4
|
/** Signature-construction helpers for host-owned catalog instructions. */
|
|
5
5
|
export { searchSignature, toolExpression } from "./tool-runtime.js";
|
|
@@ -54,17 +54,13 @@ const resolveExecutionLimits = (limits) => ({
|
|
|
54
54
|
maxOutputBytes: validateLimit("maxOutputBytes", limits?.maxOutputBytes, 0),
|
|
55
55
|
});
|
|
56
56
|
/** Executes one Effect-native CodeMode program without constructing a reusable runtime. */
|
|
57
|
-
export const execute = (options) =>
|
|
58
|
-
const tools = (options.tools ?? {});
|
|
59
|
-
return executeWithLimits(options, resolveExecutionLimits(options.limits), ToolRuntime.searchIndex(tools));
|
|
60
|
-
};
|
|
57
|
+
export const execute = (options) => make(options).execute(options.code);
|
|
61
58
|
/** Creates an Effect-native runtime over explicit, schema-described tools. */
|
|
62
59
|
export const make = (options = {}) => {
|
|
63
|
-
const
|
|
60
|
+
const prepared = ToolRuntime.prepare((options.tools ?? {}));
|
|
64
61
|
const limits = resolveExecutionLimits(options.limits);
|
|
65
|
-
const prepared = ToolRuntime.prepare(tools);
|
|
66
62
|
return {
|
|
67
63
|
catalog: () => prepared.catalog,
|
|
68
|
-
execute: (code) =>
|
|
64
|
+
execute: (code) => executeProgram(code, prepared, limits, options),
|
|
69
65
|
};
|
|
70
66
|
};
|
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export * as Data from "./data.js";
|
|
2
|
+
import type { DiagnosticKind } from "./codemode.js";
|
|
3
|
+
/** A null-prototype object owned by the program. */
|
|
4
|
+
export type SafeObject = Record<string, unknown>;
|
|
5
|
+
export declare class ToolRuntimeError extends Error {
|
|
6
|
+
readonly kind: Extract<DiagnosticKind, "UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded">;
|
|
7
|
+
readonly suggestions: ReadonlyArray<string>;
|
|
8
|
+
constructor(kind: Extract<DiagnosticKind, "UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded">, message: string, suggestions?: ReadonlyArray<string>);
|
|
9
|
+
}
|
|
10
|
+
export declare const isBlockedMember: (name: string) => boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Brings a host-produced runtime value into the program: runtime values pass through, their host
|
|
13
|
+
* counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and objects become
|
|
14
|
+
* null-prototype copies. Arrays keep extra enumerable properties such as `index` and `groups`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const toProgram: (value: unknown, label: string) => unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Brings host data into the program: Date and URL become strings, other host collections become
|
|
19
|
+
* empty objects, and objects become null-prototype copies. Used for tool results and parsed JSON.
|
|
20
|
+
*/
|
|
21
|
+
export declare const fromData: (value: unknown, label: string) => unknown;
|
|
22
|
+
/**
|
|
23
|
+
* Takes a program value out as plain JSON: runtime values serialize like `JSON.stringify` would,
|
|
24
|
+
* non-finite numbers become null, and array holes become null. `undefined` object properties are
|
|
25
|
+
* dropped ("json") or become null ("result", for program results where the consumer must never see
|
|
26
|
+
* undefined); a bare `undefined` follows the same rule.
|
|
27
|
+
*/
|
|
28
|
+
export declare const toData: (value: unknown, label: string, undefinedAs?: "json" | "result") => unknown;
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export * as Data from "./data.js";
|
|
2
|
+
import { Values } from "./values.js";
|
|
3
|
+
const MAX_VALUE_DEPTH = 32;
|
|
4
|
+
export class ToolRuntimeError extends Error {
|
|
5
|
+
kind;
|
|
6
|
+
suggestions;
|
|
7
|
+
constructor(kind, message, suggestions = []) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.kind = kind;
|
|
10
|
+
this.suggestions = suggestions;
|
|
11
|
+
this.name = "ToolRuntimeError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const blockedMemberNames = new Set(["__proto__", "constructor", "prototype"]);
|
|
15
|
+
export const isBlockedMember = (name) => blockedMemberNames.has(name);
|
|
16
|
+
/**
|
|
17
|
+
* Brings a host-produced runtime value into the program: runtime values pass through, their host
|
|
18
|
+
* counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and objects become
|
|
19
|
+
* null-prototype copies. Arrays keep extra enumerable properties such as `index` and `groups`.
|
|
20
|
+
*/
|
|
21
|
+
export const toProgram = (value, label) => copy(value, label, "program", 0, new Set());
|
|
22
|
+
/**
|
|
23
|
+
* Brings host data into the program: Date and URL become strings, other host collections become
|
|
24
|
+
* empty objects, and objects become null-prototype copies. Used for tool results and parsed JSON.
|
|
25
|
+
*/
|
|
26
|
+
export const fromData = (value, label) => copy(value, label, "data", 0, new Set());
|
|
27
|
+
/**
|
|
28
|
+
* Takes a program value out as plain JSON: runtime values serialize like `JSON.stringify` would,
|
|
29
|
+
* non-finite numbers become null, and array holes become null. `undefined` object properties are
|
|
30
|
+
* dropped ("json") or become null ("result", for program results where the consumer must never see
|
|
31
|
+
* undefined); a bare `undefined` follows the same rule.
|
|
32
|
+
*/
|
|
33
|
+
export const toData = (value, label, undefinedAs = "json") => copy(value, label, undefinedAs, 0, new Set());
|
|
34
|
+
const copy = (value, label, mode, depth, seen) => {
|
|
35
|
+
if (depth > MAX_VALUE_DEPTH) {
|
|
36
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`);
|
|
37
|
+
}
|
|
38
|
+
if (value === undefined)
|
|
39
|
+
return mode === "result" ? null : undefined;
|
|
40
|
+
if (typeof value === "number")
|
|
41
|
+
return (mode === "json" || mode === "result") && !Number.isFinite(value) ? null : value;
|
|
42
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
43
|
+
return value;
|
|
44
|
+
if (typeof value !== "object") {
|
|
45
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`);
|
|
46
|
+
}
|
|
47
|
+
if (value instanceof Values.Promise) {
|
|
48
|
+
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.`);
|
|
49
|
+
}
|
|
50
|
+
if (mode === "program") {
|
|
51
|
+
if (Values.isValue(value))
|
|
52
|
+
return value;
|
|
53
|
+
if (value instanceof Date)
|
|
54
|
+
return new Values.Date(value.getTime());
|
|
55
|
+
if (value instanceof RegExp)
|
|
56
|
+
return new Values.RegExp(value.source, value.flags);
|
|
57
|
+
if (value instanceof Map) {
|
|
58
|
+
const wrapped = new Values.Map();
|
|
59
|
+
for (const [key, item] of value.entries()) {
|
|
60
|
+
wrapped.map.set(copy(key, label, mode, depth + 1, seen), copy(item, label, mode, depth + 1, seen));
|
|
61
|
+
}
|
|
62
|
+
return wrapped;
|
|
63
|
+
}
|
|
64
|
+
if (value instanceof Set) {
|
|
65
|
+
const wrapped = new Values.Set();
|
|
66
|
+
for (const item of value.values())
|
|
67
|
+
wrapped.set.add(copy(item, label, mode, depth + 1, seen));
|
|
68
|
+
return wrapped;
|
|
69
|
+
}
|
|
70
|
+
if (value instanceof URL)
|
|
71
|
+
return new Values.URL(new URL(value.href));
|
|
72
|
+
if (value instanceof URLSearchParams)
|
|
73
|
+
return new Values.URLSearchParams(new URLSearchParams(value));
|
|
74
|
+
}
|
|
75
|
+
const plain = mode === "program" || mode === "data";
|
|
76
|
+
if (value instanceof Values.Date)
|
|
77
|
+
return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null;
|
|
78
|
+
if (value instanceof Date)
|
|
79
|
+
return Number.isFinite(value.getTime()) ? value.toISOString() : null;
|
|
80
|
+
if (value instanceof Values.URL)
|
|
81
|
+
return value.url.href;
|
|
82
|
+
if (value instanceof URL)
|
|
83
|
+
return value.href;
|
|
84
|
+
// Remaining runtime values and their host counterparts serialize as empty objects, like JSON.stringify.
|
|
85
|
+
if (Values.isValue(value) ||
|
|
86
|
+
value instanceof RegExp ||
|
|
87
|
+
value instanceof Map ||
|
|
88
|
+
value instanceof Set ||
|
|
89
|
+
value instanceof URLSearchParams) {
|
|
90
|
+
return plain ? Object.create(null) : {};
|
|
91
|
+
}
|
|
92
|
+
if (seen.has(value)) {
|
|
93
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} contains a circular value.`);
|
|
94
|
+
}
|
|
95
|
+
seen.add(value);
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
// Host output densifies holes to null like JSON; program copies keep them.
|
|
98
|
+
const copied = plain
|
|
99
|
+
? value.map((item) => copy(item, label, mode, depth + 1, seen))
|
|
100
|
+
: Array.from(value, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
|
|
101
|
+
if (mode === "program") {
|
|
102
|
+
for (const [key, item] of Object.entries(value)) {
|
|
103
|
+
if (Object.hasOwn(copied, key))
|
|
104
|
+
continue;
|
|
105
|
+
if (isBlockedMember(key)) {
|
|
106
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} contains blocked property '${key}'.`);
|
|
107
|
+
}
|
|
108
|
+
Reflect.set(copied, key, copy(item, label, mode, depth + 1, seen));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
seen.delete(value);
|
|
112
|
+
return copied;
|
|
113
|
+
}
|
|
114
|
+
const prototype = Object.getPrototypeOf(value);
|
|
115
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
116
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain plain objects only.`);
|
|
117
|
+
}
|
|
118
|
+
const copied = plain ? Object.create(null) : {};
|
|
119
|
+
for (const [key, item] of Object.entries(value)) {
|
|
120
|
+
if (isBlockedMember(key)) {
|
|
121
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} contains blocked property '${key}'.`);
|
|
122
|
+
}
|
|
123
|
+
const next = copy(item, label, mode, depth + 1, seen);
|
|
124
|
+
if (next === undefined && mode === "json")
|
|
125
|
+
continue;
|
|
126
|
+
copied[key] = next;
|
|
127
|
+
}
|
|
128
|
+
seen.delete(value);
|
|
129
|
+
return copied;
|
|
130
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,6 @@ export * as CodeMode from "./codemode.js";
|
|
|
2
2
|
export * as Namespace from "./namespace.js";
|
|
3
3
|
export * as Tool from "./tool.js";
|
|
4
4
|
export * as OpenAPI from "./openapi/index.js";
|
|
5
|
+
export { Values } from "./values.js";
|
|
5
6
|
export { searchSignature, toolExpression } from "./codemode.js";
|
|
6
7
|
export { ToolError, toolError } from "./tool-error.js";
|
package/dist/index.js
CHANGED
|
@@ -2,5 +2,6 @@ export * as CodeMode from "./codemode.js";
|
|
|
2
2
|
export * as Namespace from "./namespace.js";
|
|
3
3
|
export * as Tool from "./tool.js";
|
|
4
4
|
export * as OpenAPI from "./openapi/index.js";
|
|
5
|
+
export { Values } from "./values.js";
|
|
5
6
|
export { searchSignature, toolExpression } from "./codemode.js";
|
|
6
7
|
export { ToolError, toolError } from "./tool-error.js";
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
1
|
import type { Diagnostic } from "../codemode.js";
|
|
3
|
-
import {
|
|
4
|
-
import { type
|
|
5
|
-
import { type SyncIteratorRunner } from "./iterator.js";
|
|
2
|
+
import { HostFunction } from "./host.js";
|
|
3
|
+
import { type Runner } from "./runner.js";
|
|
6
4
|
export declare const normalizeError: (error: unknown) => Diagnostic;
|
|
7
5
|
export declare const caughtErrorValue: (thrown: unknown) => unknown;
|
|
8
|
-
|
|
9
|
-
export declare const
|
|
6
|
+
/** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */
|
|
7
|
+
export declare const errorGlobal: <R>(name: string, runner: Runner<R>) => HostFunction<R>;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { ToolError } from "../tool-error.js";
|
|
3
|
-
import {
|
|
3
|
+
import { toData, ToolRuntimeError } from "../data.js";
|
|
4
4
|
import { formatLocation, InterpreterRuntimeError, ProgramThrow, sourceLocation } from "./model.js";
|
|
5
5
|
import { containsRuntimeReference } from "./references.js";
|
|
6
|
-
import {} from "./
|
|
7
|
-
import {
|
|
6
|
+
import { HostFunction } from "./host.js";
|
|
7
|
+
import {} from "./runner.js";
|
|
8
|
+
import { coerceToString, createAggregateErrorValue, createErrorValue, errorBrandName, errorConstructors, } from "../stdlib/value.js";
|
|
8
9
|
export const normalizeError = (error) => {
|
|
9
10
|
if (error instanceof InterpreterRuntimeError) {
|
|
10
11
|
return {
|
|
@@ -41,7 +42,7 @@ export const normalizeError = (error) => {
|
|
|
41
42
|
}
|
|
42
43
|
else {
|
|
43
44
|
try {
|
|
44
|
-
message = JSON.stringify(
|
|
45
|
+
message = JSON.stringify(toData(value, "Thrown value")) ?? String(value);
|
|
45
46
|
}
|
|
46
47
|
catch {
|
|
47
48
|
message = String(value);
|
|
@@ -74,8 +75,8 @@ export const caughtErrorValue = (thrown) => {
|
|
|
74
75
|
const name = thrown instanceof Error && errorConstructors.has(thrown.name) ? thrown.name : "Error";
|
|
75
76
|
return createErrorValue(name, normalizeError(thrown).message);
|
|
76
77
|
};
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
const constructErrorValue = (name, args) => createErrorValue(name, args[0] === undefined ? "" : coerceToString(args[0]));
|
|
79
|
+
const constructAggregateErrorValue = (runner, args, node) => Effect.gen(function* () {
|
|
79
80
|
const cursor = yield* runner.syncIterator(args[0], node);
|
|
80
81
|
if (cursor === undefined) {
|
|
81
82
|
throw new InterpreterRuntimeError("new AggregateError(...) expects a synchronous iterable of errors.", node).as("TypeError");
|
|
@@ -89,3 +90,18 @@ export const constructAggregateErrorValue = (runner, args, node) => Effect.gen(f
|
|
|
89
90
|
errors.push(step.value);
|
|
90
91
|
}
|
|
91
92
|
});
|
|
93
|
+
/** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */
|
|
94
|
+
export const errorGlobal = (name, runner) => {
|
|
95
|
+
const construct = (args, node) => name === "AggregateError"
|
|
96
|
+
? constructAggregateErrorValue(runner, args, node)
|
|
97
|
+
: Effect.sync(() => constructErrorValue(name, args));
|
|
98
|
+
return new HostFunction({
|
|
99
|
+
name,
|
|
100
|
+
call: construct,
|
|
101
|
+
construct,
|
|
102
|
+
instanceOf: (value) => {
|
|
103
|
+
const brand = errorBrandName(value);
|
|
104
|
+
return brand !== undefined && (name === "Error" || brand === name);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import type {
|
|
3
|
-
import { ToolRuntime
|
|
4
|
-
export declare const
|
|
2
|
+
import type { ResolvedExecutionLimits, Result } from "../codemode.js";
|
|
3
|
+
import { ToolRuntime } from "../tool-runtime.js";
|
|
4
|
+
export declare const executeProgram: <R>(code: string, prepared: ToolRuntime.Prepared<R>, limits: ResolvedExecutionLimits, hooks: ToolRuntime.ToolCallHooks<R>) => Effect.Effect<Result, never, R>;
|
|
@@ -3,13 +3,14 @@ import { Cause, Effect, Scope } from "effect";
|
|
|
3
3
|
// #transpile: conditional import — full typescript on node/bun, an identity
|
|
4
4
|
// pass-through on workerd (the compiler is ~11 MiB and can't init there).
|
|
5
5
|
import { transpile } from "#transpile";
|
|
6
|
-
import {
|
|
6
|
+
import { toData } from "../data.js";
|
|
7
|
+
import { ToolRuntime } from "../tool-runtime.js";
|
|
7
8
|
import { normalizeError } from "./errors.js";
|
|
8
|
-
import { InterpreterRuntimeError
|
|
9
|
+
import { InterpreterRuntimeError } from "./model.js";
|
|
9
10
|
import { PromiseRuntime } from "./promises.js";
|
|
10
|
-
import {
|
|
11
|
-
export const
|
|
12
|
-
if (
|
|
11
|
+
import { Runtime } from "./runtime.js";
|
|
12
|
+
export const executeProgram = (code, prepared, limits, hooks) => {
|
|
13
|
+
if (code.trim().length === 0) {
|
|
13
14
|
return Effect.succeed({
|
|
14
15
|
ok: false,
|
|
15
16
|
error: { kind: "ParseError", message: "Code cannot be empty." },
|
|
@@ -18,20 +19,16 @@ export const executeWithLimits = (options, limits, searchIndex) => {
|
|
|
18
19
|
}
|
|
19
20
|
// Allocate execution state inside suspension so reused Effects never share it.
|
|
20
21
|
return Effect.suspend(() => {
|
|
21
|
-
const tools = ToolRuntime.make(
|
|
22
|
-
onToolCallStart: options.onToolCallStart,
|
|
23
|
-
onToolCallEnd: options.onToolCallEnd,
|
|
24
|
-
});
|
|
22
|
+
const tools = ToolRuntime.make(prepared, limits.maxToolCalls, hooks);
|
|
25
23
|
const logs = [];
|
|
26
24
|
const logged = () => (logs.length > 0 ? { logs: [...logs] } : {});
|
|
27
25
|
// Set only after copy-out so timeouts cannot report invalid values as completed.
|
|
28
26
|
let returned;
|
|
29
27
|
const base = Effect.acquireUseRelease(Scope.make("parallel"), (scope) => Effect.gen(function* () {
|
|
30
|
-
const program = parseProgram(
|
|
28
|
+
const program = parseProgram(code);
|
|
31
29
|
const promises = new PromiseRuntime(scope);
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const result = copyOut(copyIn(value, "Execution result"), "nullify");
|
|
30
|
+
const value = yield* new Runtime(tools.execute, tools.search, tools.keys, promises, logs).run(program);
|
|
31
|
+
const result = toData(value, "Execution result", "result");
|
|
35
32
|
returned = { value: result, promises };
|
|
36
33
|
const warnings = yield* promises.interrupt();
|
|
37
34
|
return {
|
|
@@ -90,17 +87,13 @@ const parseProgram = (code) => {
|
|
|
90
87
|
const bodyStart = transpiled.outputText.indexOf("{") + 1;
|
|
91
88
|
const bodyEnd = transpiled.outputText.lastIndexOf("}");
|
|
92
89
|
const executableCode = transpiled.outputText.slice(bodyStart, bodyEnd);
|
|
93
|
-
|
|
90
|
+
return parse(executableCode, {
|
|
94
91
|
ecmaVersion: "latest",
|
|
95
92
|
sourceType: "script",
|
|
96
93
|
allowReturnOutsideFunction: true,
|
|
97
94
|
allowAwaitOutsideFunction: true,
|
|
98
95
|
locations: true,
|
|
99
96
|
});
|
|
100
|
-
if (!isRecord(parsed) || parsed.type !== "Program" || !Array.isArray(parsed.body)) {
|
|
101
|
-
throw new InterpreterRuntimeError("Failed to parse script as a Program node.");
|
|
102
|
-
}
|
|
103
|
-
return parsed;
|
|
104
97
|
};
|
|
105
98
|
const utf8ByteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
106
99
|
// Drop a replacement character produced by truncating inside a UTF-8 sequence.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { type PromiseRuntime } from "./promises.js";
|
|
3
|
+
import type { Runner } from "./runner.js";
|
|
4
|
+
/** What the built-in globals need from the interpreter that owns them. */
|
|
5
|
+
export type Host<R> = {
|
|
6
|
+
readonly runner: Runner<R>;
|
|
7
|
+
readonly promises: PromiseRuntime<R>;
|
|
8
|
+
readonly search: (args: Array<unknown>) => Effect.Effect<unknown, unknown, R>;
|
|
9
|
+
readonly toolKeys: (path: ReadonlyArray<string>) => ReadonlyArray<string>;
|
|
10
|
+
readonly logs: Array<string>;
|
|
11
|
+
};
|
|
12
|
+
/** The immutable global bindings of every program, in declaration order. */
|
|
13
|
+
export declare const globals: <R>(host: Host<R>) => ReadonlyArray<readonly [string, unknown]>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { arrayGlobal } from "../stdlib/array.js";
|
|
3
|
+
import { mapGlobal, setGlobal } from "../stdlib/collections.js";
|
|
4
|
+
import { consoleGlobal } from "../stdlib/console.js";
|
|
5
|
+
import { dateGlobal } from "../stdlib/date.js";
|
|
6
|
+
import { jsonGlobal } from "../stdlib/json.js";
|
|
7
|
+
import { mathGlobal } from "../stdlib/math.js";
|
|
8
|
+
import { numberGlobal } from "../stdlib/number.js";
|
|
9
|
+
import { objectGlobal } from "../stdlib/object.js";
|
|
10
|
+
import { regexpGlobal } from "../stdlib/regexp.js";
|
|
11
|
+
import { stringGlobal } from "../stdlib/string.js";
|
|
12
|
+
import { uriGlobal, urlGlobal, urlSearchParamsGlobal } from "../stdlib/url.js";
|
|
13
|
+
import { coercion, errorConstructors } from "../stdlib/value.js";
|
|
14
|
+
import { ToolReference } from "../tool-runtime.js";
|
|
15
|
+
import { errorGlobal } from "./errors.js";
|
|
16
|
+
import { HostFunction } from "./host.js";
|
|
17
|
+
import { AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol } from "./model.js";
|
|
18
|
+
import { promiseGlobal } from "./promises.js";
|
|
19
|
+
const symbolGlobal = new HostFunction({
|
|
20
|
+
name: "Symbol",
|
|
21
|
+
call: (_, node) => Effect.sync(() => {
|
|
22
|
+
throw new InterpreterRuntimeError("Symbol is not callable; only Symbol.asyncIterator and Symbol.iterator are available.", node).as("TypeError");
|
|
23
|
+
}),
|
|
24
|
+
callback: false,
|
|
25
|
+
members: { asyncIterator: AsyncIteratorSymbol, iterator: IteratorSymbol },
|
|
26
|
+
});
|
|
27
|
+
/** The immutable global bindings of every program, in declaration order. */
|
|
28
|
+
export const globals = (host) => [
|
|
29
|
+
["tools", new ToolReference([])],
|
|
30
|
+
["search", new HostFunction({ name: "search", call: (args) => host.search(args), callback: false })],
|
|
31
|
+
["undefined", undefined],
|
|
32
|
+
["NaN", NaN],
|
|
33
|
+
["Infinity", Infinity],
|
|
34
|
+
["Object", objectGlobal(host.runner, host.toolKeys)],
|
|
35
|
+
["Array", arrayGlobal(host.runner)],
|
|
36
|
+
["Math", mathGlobal(host.runner)],
|
|
37
|
+
["JSON", jsonGlobal(host.runner)],
|
|
38
|
+
["console", consoleGlobal(host.logs)],
|
|
39
|
+
["Promise", promiseGlobal(host.runner, host.promises)],
|
|
40
|
+
["Symbol", symbolGlobal],
|
|
41
|
+
["Number", numberGlobal],
|
|
42
|
+
["String", stringGlobal],
|
|
43
|
+
["Boolean", coercion("Boolean", { instanceOf: () => false })],
|
|
44
|
+
["parseInt", coercion("parseInt")],
|
|
45
|
+
["parseFloat", coercion("parseFloat")],
|
|
46
|
+
["isFinite", coercion("isFinite")],
|
|
47
|
+
["isNaN", coercion("isNaN")],
|
|
48
|
+
["Date", dateGlobal(host.runner)],
|
|
49
|
+
["RegExp", regexpGlobal],
|
|
50
|
+
["Map", mapGlobal(host.runner)],
|
|
51
|
+
["Set", setGlobal(host.runner)],
|
|
52
|
+
["URL", urlGlobal],
|
|
53
|
+
["URLSearchParams", urlSearchParamsGlobal(host.runner)],
|
|
54
|
+
["encodeURI", uriGlobal("encodeURI")],
|
|
55
|
+
["encodeURIComponent", uriGlobal("encodeURIComponent")],
|
|
56
|
+
["decodeURI", uriGlobal("decodeURI")],
|
|
57
|
+
["decodeURIComponent", uriGlobal("decodeURIComponent")],
|
|
58
|
+
...[...errorConstructors].map((name) => [name, errorGlobal(name, host.runner)]),
|
|
59
|
+
];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { type AstNode } from "./model.js";
|
|
3
|
+
export type HostCall<R> = (args: Array<unknown>, node: AstNode) => Effect.Effect<unknown, unknown, R>;
|
|
4
|
+
type HostMember = (key: PropertyKey, node: AstNode) => unknown;
|
|
5
|
+
type HostFunctionOptions<R> = {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly call: HostCall<R>;
|
|
8
|
+
/** `new name(...)`; without it `new` is unsupported syntax. */
|
|
9
|
+
readonly construct?: HostCall<R>;
|
|
10
|
+
/** Static members read through `name.key`; unknown keys read as `undefined` unless the function decides otherwise. */
|
|
11
|
+
readonly members?: Record<string, unknown> | HostMember;
|
|
12
|
+
/** `value instanceof name`; without it the operator rejects this right-hand side. */
|
|
13
|
+
readonly instanceOf?: (value: unknown) => boolean;
|
|
14
|
+
/** Whether callback sites (array methods, replacers, promise reactions) admit this function. Defaults to true. */
|
|
15
|
+
readonly callback?: boolean;
|
|
16
|
+
};
|
|
17
|
+
/** A host-implemented function value. `typeof` is "function". */
|
|
18
|
+
export declare class HostFunction<R = never> {
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly call: HostCall<R>;
|
|
21
|
+
readonly construct: HostCall<R> | undefined;
|
|
22
|
+
readonly member: HostMember;
|
|
23
|
+
readonly instanceOf: ((value: unknown) => boolean) | undefined;
|
|
24
|
+
readonly callback: boolean;
|
|
25
|
+
constructor(options: HostFunctionOptions<R>);
|
|
26
|
+
}
|
|
27
|
+
/** A host-implemented object of static members. `typeof` is "object" and it is not callable. */
|
|
28
|
+
export declare class HostNamespace {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly member: HostMember;
|
|
31
|
+
constructor(name: string, members: Record<string, unknown> | HostMember);
|
|
32
|
+
}
|
|
33
|
+
export type SyncOptions = Omit<HostFunctionOptions<never>, "name" | "call">;
|
|
34
|
+
type SyncImpl = (args: Array<unknown>, node: AstNode) => unknown;
|
|
35
|
+
/** Lifts a synchronous implementation, which may throw `InterpreterRuntimeError`, into a host call. */
|
|
36
|
+
export declare const syncCall: (impl: SyncImpl) => HostCall<never>;
|
|
37
|
+
/** A synchronous host function. */
|
|
38
|
+
export declare const sync: (name: string, impl: SyncImpl, options?: SyncOptions) => HostFunction<never>;
|
|
39
|
+
/** The `call` of a constructor that JS requires to be invoked with `new`. */
|
|
40
|
+
export declare const requiresNew: (name: string) => HostCall<never>;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { InterpreterRuntimeError } from "./model.js";
|
|
3
|
+
/** A host-implemented function value. `typeof` is "function". */
|
|
4
|
+
export class HostFunction {
|
|
5
|
+
name;
|
|
6
|
+
call;
|
|
7
|
+
construct;
|
|
8
|
+
member;
|
|
9
|
+
instanceOf;
|
|
10
|
+
callback;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.name = options.name;
|
|
13
|
+
this.call = options.call;
|
|
14
|
+
this.construct = options.construct;
|
|
15
|
+
this.member = memberLookup(options.members);
|
|
16
|
+
this.instanceOf = options.instanceOf;
|
|
17
|
+
this.callback = options.callback ?? true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** A host-implemented object of static members. `typeof` is "object" and it is not callable. */
|
|
21
|
+
export class HostNamespace {
|
|
22
|
+
name;
|
|
23
|
+
member;
|
|
24
|
+
constructor(name, members) {
|
|
25
|
+
this.name = name;
|
|
26
|
+
this.member = memberLookup(members);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const memberLookup = (members) => {
|
|
30
|
+
if (members === undefined)
|
|
31
|
+
return () => undefined;
|
|
32
|
+
if (typeof members === "function")
|
|
33
|
+
return members;
|
|
34
|
+
const table = new Map(Object.entries(members));
|
|
35
|
+
return (key) => (typeof key === "string" ? table.get(key) : undefined);
|
|
36
|
+
};
|
|
37
|
+
/** Lifts a synchronous implementation, which may throw `InterpreterRuntimeError`, into a host call. */
|
|
38
|
+
export const syncCall = (impl) => (args, node) => Effect.sync(() => impl(args, node));
|
|
39
|
+
/** A synchronous host function. */
|
|
40
|
+
export const sync = (name, impl, options = {}) => new HostFunction({ name, call: syncCall(impl), ...options });
|
|
41
|
+
/** The `call` of a constructor that JS requires to be invoked with `new`. */
|
|
42
|
+
export const requiresNew = (name) => (_, node) => Effect.sync(() => {
|
|
43
|
+
throw new InterpreterRuntimeError(`Constructor ${name} requires 'new'.`, node).as("TypeError");
|
|
44
|
+
});
|
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { type AstNode,
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export type CallbackRunner<R> = {
|
|
6
|
-
readonly invokeFunction: (fn: CodeModeFunction, args: Array<unknown>) => Effect.Effect<unknown, unknown, R>;
|
|
7
|
-
readonly invokeCallable: (callable: unknown, args: Array<unknown>, node: AstNode) => Effect.Effect<unknown, unknown, R>;
|
|
8
|
-
readonly settlePromise: (promise: CodeModePromise) => Effect.Effect<unknown, unknown, never>;
|
|
9
|
-
};
|
|
10
|
-
export type SupportedCallback = CodeModeFunction | CoercionFunction | UriFunction | PromiseCapabilityFunction | GlobalMethodReference | JsonMethodReference | IntrinsicReference | ErrorConstructorReference | GlobalNamespace | PromiseNamespace;
|
|
11
|
-
export declare const isSupportedCallback: (value: unknown) => value is SupportedCallback;
|
|
12
|
-
export declare const invokeIntrinsic: <R>(runner: CallbackRunner<R>, ref: IntrinsicReference, args: Array<unknown>, node: AstNode) => Effect.Effect<unknown, unknown, R>;
|
|
13
|
-
export declare const invokeGlobalMethod: (ref: GlobalMethodReference, args: Array<unknown>, node: AstNode) => unknown;
|
|
14
|
-
export declare const arrayStatics: Set<string>;
|
|
15
|
-
export declare const invokeArrayFrom: <R>(runner: CallbackRunner<R> & SyncIteratorRunner<R>, args: Array<unknown>, node: AstNode) => Effect.Effect<unknown, unknown, R>;
|
|
16
|
-
export declare const invokeGroupBy: <R>(runner: CallbackRunner<R> & SyncIteratorRunner<R>, namespace: "Map" | "Object", args: Array<unknown>, node: AstNode) => Effect.Effect<unknown, unknown, R>;
|
|
17
|
-
export declare const applyCollectionCallback: <R>(runner: CallbackRunner<R>, callback: unknown, name: string, node: AstNode) => ((args: Array<unknown>) => Effect.Effect<unknown, unknown, R>);
|
|
2
|
+
import { type AstNode, IntrinsicReference } from "./model.js";
|
|
3
|
+
import { type Runner } from "./runner.js";
|
|
4
|
+
export declare const invokeIntrinsic: <R>(runner: Runner<R>, ref: IntrinsicReference, args: Array<unknown>, node: AstNode) => Effect.Effect<unknown, unknown, R>;
|