@opencode/codemode 0.0.0-dev-19643 → 0.0.0-dev-19645
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/dist/codemode.d.ts +1 -1
- package/dist/extension.d.ts +3 -4
- package/dist/extension.js +1 -1
- package/dist/interpreter/extensions.d.ts +3 -3
- package/dist/interpreter/extensions.js +29 -150
- package/dist/interpreter/objects.d.ts +0 -5
- package/dist/interpreter/objects.js +0 -8
- package/dist/interpreter/references.js +1 -4
- package/package.json +1 -1
package/dist/codemode.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export type ResolvedExecutionLimits = {
|
|
|
30
30
|
export type Options<Provided extends Record<string, unknown> = {}> = ToolRuntime.ToolCallHooks<Services<Provided>> & {
|
|
31
31
|
/** Explicit tools exposed to the program as `tools`. */
|
|
32
32
|
tools?: Provided & Tools<Services<Provided>>;
|
|
33
|
-
/** Host
|
|
33
|
+
/** Host functions exposed as globals; see `Extension.make`. */
|
|
34
34
|
extensions?: ReadonlyArray<Extension>;
|
|
35
35
|
/** Resource limits enforced on each execution. */
|
|
36
36
|
limits?: ExecutionLimits;
|
package/dist/extension.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
export * as Extension from "./extension.js";
|
|
2
2
|
/**
|
|
3
|
-
* Host
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Host functions a program calls directly as globals. Values crossing in either direction are converted, never
|
|
4
|
+
* shared: arguments come in as copies, results go out as copies, and a function inside a result is callable the
|
|
5
|
+
* same way. Extension calls are not tool calls.
|
|
6
6
|
*/
|
|
7
7
|
export type Extension = {
|
|
8
8
|
readonly name: string;
|
|
9
|
-
/** Each value is a class or a function; everything on a class is exposed, including statics and accessors. */
|
|
10
9
|
readonly globals: Readonly<Record<string, Function>>;
|
|
11
10
|
};
|
|
12
11
|
export declare const make: (options: Extension) => Extension;
|
package/dist/extension.js
CHANGED
|
@@ -2,7 +2,7 @@ export * as Extension from "./extension.js";
|
|
|
2
2
|
export const make = (options) => {
|
|
3
3
|
for (const [name, value] of Object.entries(options.globals)) {
|
|
4
4
|
if (typeof value !== "function") {
|
|
5
|
-
throw new TypeError(`Extension "${options.name}" global "${name}" must be a
|
|
5
|
+
throw new TypeError(`Extension "${options.name}" global "${name}" must be a function.`);
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
return { name: options.name, globals: { ...options.globals } };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Extension } from "../extension.js";
|
|
2
2
|
import type { Interpreter } from "./interpreter.js";
|
|
3
3
|
/**
|
|
4
|
-
* The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data
|
|
5
|
-
*
|
|
6
|
-
* a program promise.
|
|
4
|
+
* The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data and
|
|
5
|
+
* built-in wrappers are copied, a host function becomes a program function whose calls cross the same way, and a
|
|
6
|
+
* host Promise becomes a program promise.
|
|
7
7
|
*/
|
|
8
8
|
export declare const extensionGlobals: <R>(ctx: Interpreter<R>, extensions: ReadonlyArray<Extension>) => ReadonlyArray<readonly [string, unknown]>;
|
|
@@ -3,26 +3,16 @@ import { coerceToString } from "../stdlib/value.js";
|
|
|
3
3
|
import { createErrorValue, isErrorType } from "./intrinsics.js";
|
|
4
4
|
import { MAX_VALUE_DEPTH } from "./limits.js";
|
|
5
5
|
import { Throw, typeError } from "./model.js";
|
|
6
|
-
import {
|
|
7
|
-
import { Callable, define,
|
|
6
|
+
import { fn } from "./native.js";
|
|
7
|
+
import { Callable, define, entries, get, Arr, Bytes, DateObj, ErrorObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "./objects.js";
|
|
8
8
|
import { describeValue } from "./references.js";
|
|
9
|
-
const isClass = (value) => typeof value === "function" && typeof value.prototype === "object" && value.prototype !== null;
|
|
10
|
-
// Own keys the native function already carries.
|
|
11
|
-
const ownFunctionKeys = new Set(["length", "name", "prototype"]);
|
|
12
|
-
const ownPrototypeKeys = new Set(["constructor"]);
|
|
13
9
|
/**
|
|
14
|
-
* The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data
|
|
15
|
-
*
|
|
16
|
-
* a program promise.
|
|
10
|
+
* The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data and
|
|
11
|
+
* built-in wrappers are copied, a host function becomes a program function whose calls cross the same way, and a
|
|
12
|
+
* host Promise becomes a program promise.
|
|
17
13
|
*/
|
|
18
14
|
export const extensionGlobals = (ctx, extensions) => {
|
|
19
15
|
const builtins = ctx.builtins;
|
|
20
|
-
const classes = new Set(extensions.flatMap((extension) => Object.values(extension.globals)).filter(isClass));
|
|
21
|
-
// Host prototype object → this run's program prototype, so an instance wraps as its most-derived exposed class.
|
|
22
|
-
const protoOf = new Map();
|
|
23
|
-
const exposed = new Map();
|
|
24
|
-
const classOf = new Map();
|
|
25
|
-
const handles = new WeakMap();
|
|
26
16
|
const toHost = (value, label, depth = 0, seen = new Set()) => {
|
|
27
17
|
if (depth > MAX_VALUE_DEPTH)
|
|
28
18
|
throw typeError(`${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`);
|
|
@@ -31,8 +21,6 @@ export const extensionGlobals = (ctx, extensions) => {
|
|
|
31
21
|
return value;
|
|
32
22
|
throw typeError(`${label} contains ${describeValue(value)}, which cannot be passed to an extension.`);
|
|
33
23
|
}
|
|
34
|
-
if (value instanceof Handle)
|
|
35
|
-
return value.instance;
|
|
36
24
|
if (value instanceof Bytes)
|
|
37
25
|
return new Uint8Array(value.bytes);
|
|
38
26
|
if (value instanceof DateObj)
|
|
@@ -58,7 +46,7 @@ export const extensionGlobals = (ctx, extensions) => {
|
|
|
58
46
|
const name = coerceToString(get(value, "name"));
|
|
59
47
|
const message = get(value, "message");
|
|
60
48
|
const text = message === undefined ? "" : coerceToString(message);
|
|
61
|
-
return name === "AggregateError" ? new AggregateError([], text) : new (hostErrors
|
|
49
|
+
return name === "AggregateError" ? new AggregateError([], text) : new (hostErrors.get(name) ?? Error)(text);
|
|
62
50
|
}
|
|
63
51
|
if (seen.has(value))
|
|
64
52
|
throw typeError(`${label} contains a circular value.`);
|
|
@@ -76,16 +64,9 @@ export const extensionGlobals = (ctx, extensions) => {
|
|
|
76
64
|
throw typeError(`${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`);
|
|
77
65
|
if (isPrimitive(value))
|
|
78
66
|
return value;
|
|
67
|
+
if (typeof value === "function")
|
|
68
|
+
return wrap(value, label);
|
|
79
69
|
if (value !== null && typeof value === "object") {
|
|
80
|
-
const existing = handles.get(value);
|
|
81
|
-
if (existing !== undefined)
|
|
82
|
-
return existing;
|
|
83
|
-
const proto = handlePrototype(value);
|
|
84
|
-
if (proto !== undefined) {
|
|
85
|
-
const handle = new Handle(proto, value);
|
|
86
|
-
handles.set(value, handle);
|
|
87
|
-
return handle;
|
|
88
|
-
}
|
|
89
70
|
if (value instanceof Date)
|
|
90
71
|
return new DateObj(builtins.Date, value.getTime());
|
|
91
72
|
if (value instanceof RegExp)
|
|
@@ -102,24 +83,24 @@ export const extensionGlobals = (ctx, extensions) => {
|
|
|
102
83
|
if (value instanceof URLSearchParams) {
|
|
103
84
|
return new URLSearchParamsObj(builtins.URLSearchParams, new URLSearchParams(value));
|
|
104
85
|
}
|
|
105
|
-
const next = (item) => fromHost(item,
|
|
86
|
+
const next = (item, path) => fromHost(item, path, depth + 1, seen);
|
|
106
87
|
if (value instanceof Map) {
|
|
107
88
|
const wrapped = new MapObj(builtins.Map);
|
|
108
89
|
for (const [key, item] of value)
|
|
109
|
-
wrapped.map.set(next(key), next(item));
|
|
90
|
+
wrapped.map.set(next(key, label), next(item, label));
|
|
110
91
|
return wrapped;
|
|
111
92
|
}
|
|
112
93
|
if (value instanceof Set) {
|
|
113
94
|
const wrapped = new SetObj(builtins.Set);
|
|
114
95
|
for (const item of value)
|
|
115
|
-
wrapped.set.add(next(item));
|
|
96
|
+
wrapped.set.add(next(item, label));
|
|
116
97
|
return wrapped;
|
|
117
98
|
}
|
|
118
99
|
if (seen.has(value))
|
|
119
100
|
throw typeError(`${label} produced a circular value.`);
|
|
120
101
|
seen.add(value);
|
|
121
102
|
if (Array.isArray(value)) {
|
|
122
|
-
const copied = new Arr(builtins.Array, value.map(next));
|
|
103
|
+
const copied = new Arr(builtins.Array, value.map((item, index) => next(item, `${label}[${index}]`)));
|
|
123
104
|
seen.delete(value);
|
|
124
105
|
return copied;
|
|
125
106
|
}
|
|
@@ -127,139 +108,39 @@ export const extensionGlobals = (ctx, extensions) => {
|
|
|
127
108
|
if (prototype === Object.prototype || prototype === null) {
|
|
128
109
|
const copied = new Obj(builtins.Object);
|
|
129
110
|
for (const [key, item] of Object.entries(value))
|
|
130
|
-
define(copied, key, next(item));
|
|
111
|
+
define(copied, key, next(item, `${label}.${key}`));
|
|
131
112
|
seen.delete(value);
|
|
132
113
|
return copied;
|
|
133
114
|
}
|
|
134
115
|
}
|
|
135
116
|
throw typeError(`${label} produced ${describeHost(value)}, which the program cannot hold.`);
|
|
136
117
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return proto;
|
|
142
|
-
}
|
|
143
|
-
return undefined;
|
|
144
|
-
};
|
|
145
|
-
// Runs host code with already-converted inputs. Whatever it returns, resolves, throws, or rejects with crosses
|
|
146
|
-
// the same way, so the program catches what the author threw.
|
|
147
|
-
const invoke = (run, label) => {
|
|
118
|
+
// A host function as a program function: arguments cross in, and whatever it returns, resolves, throws, or
|
|
119
|
+
// rejects with crosses out, so the program catches what the author threw.
|
|
120
|
+
const wrap = (value, label) => fn(builtins, value.name, value.length, (_, values) => {
|
|
121
|
+
const converted = values.map((item, index) => toHost(item, `Argument ${index + 1} to ${label}`));
|
|
148
122
|
const thrown = (reason) => new Throw(fromHost(reason, label));
|
|
149
123
|
let result;
|
|
150
124
|
try {
|
|
151
|
-
result =
|
|
125
|
+
result = value.apply(undefined, converted);
|
|
152
126
|
}
|
|
153
127
|
catch (reason) {
|
|
154
128
|
return Effect.fail(thrown(reason));
|
|
155
129
|
}
|
|
156
130
|
if (!(result instanceof Promise))
|
|
157
|
-
return
|
|
131
|
+
return fromHost(result, label);
|
|
158
132
|
return ctx.pending.create(Effect.map(Effect.tryPromise({ try: () => result, catch: thrown }), (settled) => fromHost(settled, label)));
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Own members of each level from `from` up to (excluding) `root`, child first, as JS resolves them.
|
|
162
|
-
const members = (target, from, root, skip, label, receiver) => {
|
|
163
|
-
for (let level = from; level !== null && level !== root; level = Object.getPrototypeOf(level)) {
|
|
164
|
-
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(level))) {
|
|
165
|
-
if (skip.has(key) || target.props.has(key))
|
|
166
|
-
continue;
|
|
167
|
-
const name = `${label}.${key}`;
|
|
168
|
-
if (typeof descriptor.value === "function") {
|
|
169
|
-
const method = descriptor.value;
|
|
170
|
-
const impl = (thisValue, values) => {
|
|
171
|
-
const self = receiver(thisValue, name);
|
|
172
|
-
const converted = args(values, name);
|
|
173
|
-
return invoke(() => method.apply(self, converted), name);
|
|
174
|
-
};
|
|
175
|
-
define(target, key, fn(builtins, key, method.length, impl), hidden);
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
// Data properties stay host-side: a program write to one would change the host class itself.
|
|
179
|
-
if ("value" in descriptor)
|
|
180
|
-
continue;
|
|
181
|
-
const get = descriptor.get;
|
|
182
|
-
const set = descriptor.set;
|
|
183
|
-
defineAccessor(target, key, get === undefined
|
|
184
|
-
? undefined
|
|
185
|
-
: (thisValue) => {
|
|
186
|
-
const value = get.call(receiver(thisValue, name));
|
|
187
|
-
if (value instanceof Promise)
|
|
188
|
-
throw typeError(`${name} returned a Promise; a getter must be synchronous.`);
|
|
189
|
-
return fromHost(value, name);
|
|
190
|
-
}, set === undefined
|
|
191
|
-
? undefined
|
|
192
|
-
: (thisValue, value) => {
|
|
193
|
-
set.call(receiver(thisValue, name), toHost(value, `${name} value`));
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
const expose = (cls) => {
|
|
199
|
-
const existing = exposed.get(cls);
|
|
200
|
-
if (existing !== undefined)
|
|
201
|
-
return existing;
|
|
202
|
-
const ancestor = exposedAncestor(cls);
|
|
203
|
-
const base = ancestor === undefined ? undefined : expose(ancestor);
|
|
204
|
-
const proto = new Obj(base === undefined ? builtins.Object : base.proto);
|
|
205
|
-
protoOf.set(cls.prototype, proto);
|
|
206
|
-
const name = cls.name;
|
|
207
|
-
const ctor = constructor(builtins, proto, {
|
|
208
|
-
name,
|
|
209
|
-
length: cls.length,
|
|
210
|
-
call: (_, values) => {
|
|
211
|
-
const converted = args(values, name);
|
|
212
|
-
return invoke(() => cls.apply(undefined, converted), name);
|
|
213
|
-
},
|
|
214
|
-
construct: (values) => {
|
|
215
|
-
const label = `new ${name}`;
|
|
216
|
-
const construct = cls;
|
|
217
|
-
const converted = args(values, label);
|
|
218
|
-
return invoke(() => new construct(...converted), label);
|
|
219
|
-
},
|
|
220
|
-
});
|
|
221
|
-
if (base !== undefined)
|
|
222
|
-
ctor.proto = base.ctor;
|
|
223
|
-
const entry = { ctor, proto };
|
|
224
|
-
exposed.set(cls, entry);
|
|
225
|
-
classOf.set(ctor, cls);
|
|
226
|
-
// A static called through an exposed subclass sees that subclass as `this`, like JS.
|
|
227
|
-
members(ctor, cls, ancestor ?? Function.prototype, ownFunctionKeys, name, (thisValue) => {
|
|
228
|
-
const called = classOf.get(thisValue);
|
|
229
|
-
return called !== undefined && (called === cls || called.prototype instanceof cls) ? called : cls;
|
|
230
|
-
});
|
|
231
|
-
members(proto, cls.prototype, ancestor?.prototype ?? Object.prototype, ownPrototypeKeys, `${name}.prototype`, (thisValue, member) => {
|
|
232
|
-
if (thisValue instanceof Handle && thisValue.instance instanceof cls)
|
|
233
|
-
return thisValue.instance;
|
|
234
|
-
throw typeError(`Illegal invocation: ${member} called on ${describeValue(thisValue)}.`);
|
|
235
|
-
});
|
|
236
|
-
return entry;
|
|
237
|
-
};
|
|
238
|
-
const exposedAncestor = (cls) => {
|
|
239
|
-
for (let level = Object.getPrototypeOf(cls); isClass(level); level = Object.getPrototypeOf(level)) {
|
|
240
|
-
if (classes.has(level))
|
|
241
|
-
return level;
|
|
242
|
-
}
|
|
243
|
-
return undefined;
|
|
244
|
-
};
|
|
245
|
-
return extensions.flatMap((extension) => Object.entries(extension.globals).map(([name, value]) => {
|
|
246
|
-
if (isClass(value))
|
|
247
|
-
return [name, expose(value).ctor];
|
|
248
|
-
const impl = (_, values) => {
|
|
249
|
-
const converted = args(values, name);
|
|
250
|
-
return invoke(() => value.apply(undefined, converted), name);
|
|
251
|
-
};
|
|
252
|
-
return [name, fn(builtins, name, value.length, impl)];
|
|
253
|
-
}));
|
|
254
|
-
};
|
|
255
|
-
const hostErrors = {
|
|
256
|
-
TypeError,
|
|
257
|
-
RangeError,
|
|
258
|
-
SyntaxError,
|
|
259
|
-
ReferenceError,
|
|
260
|
-
EvalError,
|
|
261
|
-
URIError,
|
|
133
|
+
});
|
|
134
|
+
return extensions.flatMap((extension) => Object.entries(extension.globals).map(([name, value]) => [name, wrap(value, name)]));
|
|
262
135
|
};
|
|
136
|
+
const hostErrors = new Map([
|
|
137
|
+
["TypeError", TypeError],
|
|
138
|
+
["RangeError", RangeError],
|
|
139
|
+
["SyntaxError", SyntaxError],
|
|
140
|
+
["ReferenceError", ReferenceError],
|
|
141
|
+
["EvalError", EvalError],
|
|
142
|
+
["URIError", URIError],
|
|
143
|
+
]);
|
|
263
144
|
// The primitives the interpreter operates on; symbols and BigInts are not among them.
|
|
264
145
|
const isPrimitive = (value) => value === null ||
|
|
265
146
|
value === undefined ||
|
|
@@ -267,8 +148,6 @@ const isPrimitive = (value) => value === null ||
|
|
|
267
148
|
typeof value === "number" ||
|
|
268
149
|
typeof value === "boolean";
|
|
269
150
|
const describeHost = (value) => {
|
|
270
|
-
if (typeof value === "function")
|
|
271
|
-
return "a function";
|
|
272
151
|
if (typeof value !== "object" || value === null)
|
|
273
152
|
return `a ${typeof value}`;
|
|
274
153
|
const name = value.constructor?.name;
|
|
@@ -109,11 +109,6 @@ export declare class Bytes extends Obj {
|
|
|
109
109
|
readonly bytes: Uint8Array;
|
|
110
110
|
constructor(proto: Obj, bytes: Uint8Array);
|
|
111
111
|
}
|
|
112
|
-
/** An instance of an extension class: the host object lives in a field no property path reaches. */
|
|
113
|
-
export declare class Handle extends Obj {
|
|
114
|
-
readonly instance: object;
|
|
115
|
-
constructor(proto: Obj, instance: object);
|
|
116
|
-
}
|
|
117
112
|
/** Built-in objects that wrap a host value; data-like, but never plain data. */
|
|
118
113
|
export declare const isWrapper: (value: unknown) => value is DateObj | RegExpObj | MapObj | SetObj | URLObj | URLSearchParamsObj | Bytes;
|
|
119
114
|
export declare const parseArrayIndex: (key: string | number) => number | undefined;
|
|
@@ -123,14 +123,6 @@ export class Bytes extends Obj {
|
|
|
123
123
|
this.bytes = bytes;
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
/** An instance of an extension class: the host object lives in a field no property path reaches. */
|
|
127
|
-
export class Handle extends Obj {
|
|
128
|
-
instance;
|
|
129
|
-
constructor(proto, instance) {
|
|
130
|
-
super(proto);
|
|
131
|
-
this.instance = instance;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
126
|
/** Built-in objects that wrap a host value; data-like, but never plain data. */
|
|
135
127
|
export const isWrapper = (value) => value instanceof DateObj ||
|
|
136
128
|
value instanceof RegExpObj ||
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { ToolReference } from "../tool-runtime.js";
|
|
2
2
|
import { invalidData } from "./model.js";
|
|
3
|
-
import { Callable, getOwn, isWrapper, ownKeys, Arr, Bytes, DateObj, GeneratorObj,
|
|
3
|
+
import { Callable, getOwn, isWrapper, ownKeys, Arr, Bytes, DateObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "./objects.js";
|
|
4
4
|
/** Values that cannot cross the data boundary. */
|
|
5
5
|
export const isRuntimeReference = (value) => value instanceof Callable ||
|
|
6
6
|
value instanceof GeneratorObj ||
|
|
7
|
-
value instanceof Handle ||
|
|
8
7
|
value instanceof ToolReference ||
|
|
9
8
|
value instanceof PromiseObj ||
|
|
10
9
|
isWrapper(value);
|
|
@@ -68,8 +67,6 @@ export const describeValue = (value) => {
|
|
|
68
67
|
return "a Uint8Array";
|
|
69
68
|
if (value instanceof GeneratorObj)
|
|
70
69
|
return "a generator";
|
|
71
|
-
if (value instanceof Handle)
|
|
72
|
-
return `a ${value.instance.constructor.name}`;
|
|
73
70
|
if (isRuntimeReference(value))
|
|
74
71
|
return "a function";
|
|
75
72
|
if (typeof value === "object")
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@opencode/codemode",
|
|
4
|
-
"version": "0.0.0-dev-
|
|
4
|
+
"version": "0.0.0-dev-19645",
|
|
5
5
|
"description": "Effect-native confined code execution over schema-described tools",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|