@opencode/codemode 0.0.0-reserved → 2.0.1
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 +184 -2
- package/dist/codemode.d.ts +145 -0
- package/dist/codemode.js +66 -0
- package/dist/data.d.ts +25 -0
- package/dist/data.js +158 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/interpreter/errors.d.ts +10 -0
- package/dist/interpreter/errors.js +112 -0
- package/dist/interpreter/execute.d.ts +5 -0
- package/dist/interpreter/execute.js +171 -0
- package/dist/interpreter/globals.d.ts +13 -0
- package/dist/interpreter/globals.js +64 -0
- package/dist/interpreter/host.d.ts +41 -0
- package/dist/interpreter/host.js +44 -0
- package/dist/interpreter/intrinsics.d.ts +10 -0
- package/dist/interpreter/intrinsics.js +41 -0
- package/dist/interpreter/methods.d.ts +4 -0
- package/dist/interpreter/methods.js +837 -0
- package/dist/interpreter/model.d.ts +89 -0
- package/dist/interpreter/model.js +90 -0
- package/dist/interpreter/objects.d.ts +37 -0
- package/dist/interpreter/objects.js +154 -0
- package/dist/interpreter/promises.d.ts +31 -0
- package/dist/interpreter/promises.js +270 -0
- package/dist/interpreter/references.d.ts +7 -0
- package/dist/interpreter/references.js +93 -0
- package/dist/interpreter/runner.d.ts +26 -0
- package/dist/interpreter/runner.js +45 -0
- package/dist/interpreter/runtime.d.ts +19 -0
- package/dist/interpreter/runtime.js +1942 -0
- package/dist/interpreter/scope.d.ts +15 -0
- package/dist/interpreter/scope.js +79 -0
- package/dist/interpreter/transpile.node.d.ts +5 -0
- package/dist/interpreter/transpile.node.js +19 -0
- package/dist/interpreter/transpile.workerd.d.ts +5 -0
- package/dist/interpreter/transpile.workerd.js +6 -0
- package/dist/namespace.d.ts +15 -0
- package/dist/namespace.js +7 -0
- package/dist/openapi/index.d.ts +7 -0
- package/dist/openapi/index.js +101 -0
- package/dist/openapi/runtime.d.ts +4 -0
- package/dist/openapi/runtime.js +283 -0
- package/dist/openapi/spec.d.ts +20 -0
- package/dist/openapi/spec.js +583 -0
- package/dist/openapi/types.d.ts +122 -0
- package/dist/openapi/types.js +2 -0
- package/dist/stdlib/array.d.ts +3 -0
- package/dist/stdlib/array.js +68 -0
- package/dist/stdlib/collections.d.ts +9 -0
- package/dist/stdlib/collections.js +173 -0
- package/dist/stdlib/console.d.ts +3 -0
- package/dist/stdlib/console.js +137 -0
- package/dist/stdlib/date.d.ts +8 -0
- package/dist/stdlib/date.js +208 -0
- package/dist/stdlib/json.d.ts +3 -0
- package/dist/stdlib/json.js +101 -0
- package/dist/stdlib/math.d.ts +8 -0
- package/dist/stdlib/math.js +89 -0
- package/dist/stdlib/number.d.ts +4 -0
- package/dist/stdlib/number.js +69 -0
- package/dist/stdlib/object.d.ts +7 -0
- package/dist/stdlib/object.js +106 -0
- package/dist/stdlib/regexp.d.ts +10 -0
- package/dist/stdlib/regexp.js +120 -0
- package/dist/stdlib/string.d.ts +2 -0
- package/dist/stdlib/string.js +51 -0
- package/dist/stdlib/url.d.ts +16 -0
- package/dist/stdlib/url.js +161 -0
- package/dist/stdlib/value.d.ts +8 -0
- package/dist/stdlib/value.js +98 -0
- package/dist/stdlib/web.d.ts +4 -0
- package/dist/stdlib/web.js +21 -0
- package/dist/tool-error.d.ts +11 -0
- package/dist/tool-error.js +9 -0
- package/dist/tool-runtime.d.ts +69 -0
- package/dist/tool-runtime.js +254 -0
- package/dist/tool-schema.d.ts +16 -0
- package/dist/tool-schema.js +263 -0
- package/dist/tool.d.ts +66 -0
- package/dist/tool.js +16 -0
- package/dist/tools.d.ts +5 -0
- package/dist/tools.js +1 -0
- package/dist/values.d.ts +37 -0
- package/dist/values.js +56 -0
- package/package.json +37 -6
package/README.md
CHANGED
|
@@ -1,5 +1,187 @@
|
|
|
1
1
|
# @opencode/codemode
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This is our take on code mode: a lightweight, pure interpreter for a JavaScript-like language built around calling
|
|
4
|
+
tools. It supports familiar JavaScript syntax with a few key differences and limitations. See the
|
|
5
|
+
[interpreter support checklist](./interpreter-support.md) for more details.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Rather than trying to sandbox arbitrary JavaScript, CodeMode only runs the language features we implement. Programs
|
|
8
|
+
cannot directly access the network, filesystem, processes, or application APIs. They can interact with the outside
|
|
9
|
+
world only through tools provided by the host, which can also limit execution time, tool calls, output size, and data.
|
|
10
|
+
|
|
11
|
+
The idea of code mode was originally introduced by Cloudflare. See
|
|
12
|
+
[their post](https://blog.cloudflare.com/code-mode/) to learn more about the concept and their isolate-based approach.
|
|
13
|
+
|
|
14
|
+
## How it differs from JavaScript
|
|
15
|
+
|
|
16
|
+
- **Only supported APIs are available.** Programs can use the provided tools and supported JavaScript built-ins. APIs
|
|
17
|
+
such as `fetch`, timers, `process`, filesystem access, imports, and modules are unavailable.
|
|
18
|
+
- **Unfinished work is interrupted.** Tool calls and async functions start when called. When the program finishes,
|
|
19
|
+
anything still running is interrupted. Unhandled rejections from un-awaited promises are returned as warnings.
|
|
20
|
+
- **REPL-style results.** Without an explicit `return`, the final top-level expression becomes the result. `undefined`
|
|
21
|
+
becomes `null`.
|
|
22
|
+
|
|
23
|
+
Unsupported syntax returns an `UnsupportedSyntax` diagnostic with a source location. Current gaps are tracked in the
|
|
24
|
+
[interpreter support checklist](./interpreter-support.md).
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { CodeMode, Namespace, Tool } from "@opencode/codemode"
|
|
30
|
+
import { Effect, Schema } from "effect"
|
|
31
|
+
|
|
32
|
+
const lookupOrder = Tool.make({
|
|
33
|
+
description: "Look up an order by ID",
|
|
34
|
+
input: Schema.Struct({ id: Schema.String }),
|
|
35
|
+
output: Schema.Struct({ id: Schema.String, status: Schema.String }),
|
|
36
|
+
execute: ({ id }) => Effect.succeed({ id, status: "open" }),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const runtime = CodeMode.make({
|
|
40
|
+
tools: { orders: { lookup: lookupOrder } },
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const result = await Effect.runPromise(
|
|
44
|
+
runtime.execute(`
|
|
45
|
+
const order = await tools.orders.lookup({ id: "order_42" })
|
|
46
|
+
return { id: order.id, needsAttention: order.status !== "complete" }
|
|
47
|
+
`),
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`result` is always a [`CodeMode.Result`](#results).
|
|
52
|
+
|
|
53
|
+
## API
|
|
54
|
+
|
|
55
|
+
### `Tool.make`
|
|
56
|
+
|
|
57
|
+
`input` and `output` accept either an Effect Schema or a render-only JSON Schema document. Effect Schema input is
|
|
58
|
+
decoded before `execute`; Effect Schema output is decoded and safely copied before the program sees it. JSON Schemas
|
|
59
|
+
only shape the model-visible signature. Without `output`, the signature uses `Promise<void>`.
|
|
60
|
+
|
|
61
|
+
Descriptions and schemas are model-visible contracts. Authorization belongs in `execute`.
|
|
62
|
+
|
|
63
|
+
Nested records are the shorthand for ordinary namespaces. Use `Namespace.make` when a namespace needs a description:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const runtime = CodeMode.make({
|
|
67
|
+
tools: {
|
|
68
|
+
orders: Namespace.make({
|
|
69
|
+
description: "Purchases, fulfillment, and shipment tracking",
|
|
70
|
+
tools: { lookup: lookupOrder },
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Namespace descriptions are optional and participate in search matching for every descendant tool. Names still come
|
|
77
|
+
from record keys, so the wrapper does not repeat `orders`. Dots in keys create nested paths; other characters use
|
|
78
|
+
bracket notation, such as `tools.context7["resolve-library-id"](...)`.
|
|
79
|
+
|
|
80
|
+
### `CodeMode.execute` and `CodeMode.make`
|
|
81
|
+
|
|
82
|
+
`CodeMode.execute({ ...options, code })` runs once. `CodeMode.make(options)` creates a reusable runtime:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const runtime = CodeMode.make({ tools, limits: { timeoutMs: 30_000 } })
|
|
86
|
+
|
|
87
|
+
runtime.catalog() // structured tool descriptions
|
|
88
|
+
runtime.execute(source) // Effect<CodeMode.Result, never, ToolServices>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The Effect environment is inferred from the supplied tools. `onToolCallStart` observes admitted calls with decoded
|
|
92
|
+
input; `onToolCallEnd` observes settled outcomes and duration. Both hooks return Effects and must not fail.
|
|
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
|
+
|
|
100
|
+
### OpenAPI tools
|
|
101
|
+
|
|
102
|
+
`OpenAPI.fromSpec` converts an OpenAPI 3.x document into one tool per supported operation. Dotted `operationId` values
|
|
103
|
+
create namespaces:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const api = OpenAPI.fromSpec({ spec, auth: { resolve } })
|
|
107
|
+
const runtime = CodeMode.make({ tools: { opencode: api.tools } })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The synchronous result is `{ tools, skipped }`. Operations with unsupported parameter encodings, request bodies
|
|
111
|
+
without JSON content, WebSocket or SSE semantics, or binary responses are reported in `skipped`.
|
|
112
|
+
|
|
113
|
+
Authentication is resolved by the host and never shown to the model. Generated tools require `HttpClient.HttpClient`.
|
|
114
|
+
Request signatures omit `readOnly` properties; response signatures omit `writeOnly` properties. These JSON Schemas
|
|
115
|
+
shape model-visible signatures but do not filter runtime values: nested JSON body properties and decoded server
|
|
116
|
+
responses pass through unchanged. See `src/openapi/types.ts` for option details.
|
|
117
|
+
|
|
118
|
+
## Results
|
|
119
|
+
|
|
120
|
+
Every execution returns:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
type Result =
|
|
124
|
+
| {
|
|
125
|
+
readonly ok: true
|
|
126
|
+
readonly value: CodeMode.DataValue
|
|
127
|
+
readonly warnings?: ReadonlyArray<CodeMode.Diagnostic>
|
|
128
|
+
readonly logs?: ReadonlyArray<string>
|
|
129
|
+
readonly truncated?: boolean
|
|
130
|
+
readonly toolCalls: ReadonlyArray<CodeMode.ToolCall>
|
|
131
|
+
}
|
|
132
|
+
| {
|
|
133
|
+
readonly ok: false
|
|
134
|
+
readonly error: CodeMode.Diagnostic
|
|
135
|
+
readonly logs?: ReadonlyArray<string>
|
|
136
|
+
readonly truncated?: boolean
|
|
137
|
+
readonly toolCalls: ReadonlyArray<CodeMode.ToolCall>
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`value` is JSON-safe. `warnings` are non-fatal diagnostics, `logs` contain program console output, and `truncated`
|
|
142
|
+
indicates that retained output was cut by `maxOutputBytes`. `toolCalls` retains admitted calls in order, including after
|
|
143
|
+
failure.
|
|
144
|
+
|
|
145
|
+
Diagnostic kinds:
|
|
146
|
+
|
|
147
|
+
| Kind | Meaning |
|
|
148
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------- |
|
|
149
|
+
| `ParseError` | Source is empty or cannot be parsed. |
|
|
150
|
+
| `UnsupportedSyntax` | Parsed JavaScript is outside the supported subset. |
|
|
151
|
+
| `UnknownTool` | The program referenced an unavailable tool. |
|
|
152
|
+
| `InvalidToolInput` | Tool input failed schema decoding or safe-data copying. |
|
|
153
|
+
| `InvalidToolOutput` | Tool output failed schema decoding or safe-data copying. |
|
|
154
|
+
| `InvalidDataValue` | Program data violated the plain-data contract. |
|
|
155
|
+
| `ToolCallLimitExceeded` | The program exceeded `maxToolCalls`. |
|
|
156
|
+
| `TimeoutExceeded` | Execution timed out; as a warning, background work was interrupted after the program returned. |
|
|
157
|
+
| `ToolFailure` | A tool refused or failed. |
|
|
158
|
+
| `ExecutionFailure` | The program threw or another execution error occurred. |
|
|
159
|
+
| `Truncated` | Warning only: additional warnings were omitted by `maxOutputBytes`. |
|
|
160
|
+
|
|
161
|
+
Host failures and defects report their messages and underlying causes. Invalid outputs include the validation or
|
|
162
|
+
copying error. Interruption propagates without becoming an error diagnostic.
|
|
163
|
+
|
|
164
|
+
## Discovery
|
|
165
|
+
|
|
166
|
+
`runtime.catalog()` returns structured descriptors — exact path, description, and generated TypeScript signature — for
|
|
167
|
+
every visible tool. Hosts render their own model-facing instructions from these descriptors; `CodeMode.searchSignature`
|
|
168
|
+
and `CodeMode.toolExpression(path)` supply the exact callable forms.
|
|
169
|
+
|
|
170
|
+
The synchronous `search(...)` built-in is always available. It supports exact-path lookup, namespace-scoped search,
|
|
171
|
+
empty-query browsing, and pagination, and returns callable paths with full signatures. Search counts toward
|
|
172
|
+
`maxToolCalls`. Search also matches descriptions from enclosing `Namespace` values.
|
|
173
|
+
|
|
174
|
+
## Execution Limits
|
|
175
|
+
|
|
176
|
+
| Limit | Default | Controls |
|
|
177
|
+
| ---------------- | --------- | ------------------------------- |
|
|
178
|
+
| `timeoutMs` | unlimited | Total execution time. |
|
|
179
|
+
| `maxToolCalls` | unlimited | Admitted tool calls. |
|
|
180
|
+
| `maxOutputBytes` | unlimited | Retained result value and logs. |
|
|
181
|
+
|
|
182
|
+
Execution limits have no default values.
|
|
183
|
+
|
|
184
|
+
Invalid limit configuration throws `RangeError`. Warnings receive a separate budget equal to `maxOutputBytes`.
|
|
185
|
+
Truncation does not fail execution; an oversized value becomes a string with an in-band marker. Timeouts interrupt
|
|
186
|
+
tool calls and busy loops, while a result returned before cleanup times out remains successful with a
|
|
187
|
+
`TimeoutExceeded` warning. Tool-call concurrency is unrestricted. Boundary data is limited to 32 nested levels.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { type Services, type ToolDescription, ToolRuntime } from "./tool-runtime.js";
|
|
3
|
+
import type { Tools } from "./tools.js";
|
|
4
|
+
/** A tool call admitted during an execution. */
|
|
5
|
+
export type { ToolCall, ToolCallEnded, ToolCallHooks, ToolCallStarted, ToolDescription } from "./tool-runtime.js";
|
|
6
|
+
/** Signature-construction helpers for host-owned catalog instructions. */
|
|
7
|
+
export { searchSignature, toolExpression } from "./tool-runtime.js";
|
|
8
|
+
/** Resource budgets enforced independently during each CodeMode program execution. */
|
|
9
|
+
export type ExecutionLimits = {
|
|
10
|
+
/**
|
|
11
|
+
* Wall-clock milliseconds before interruption. Result delivery waits for tool cleanup.
|
|
12
|
+
* No default: absent means no timeout.
|
|
13
|
+
*/
|
|
14
|
+
readonly timeoutMs?: number;
|
|
15
|
+
/** Maximum number of tool calls admitted by the runtime. No default: absent means unlimited. */
|
|
16
|
+
readonly maxToolCalls?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum UTF-8 bytes retained from the result and logs. Warnings have a separate equal budget;
|
|
19
|
+
* truncation notices and host formatting are additional.
|
|
20
|
+
*/
|
|
21
|
+
readonly maxOutputBytes?: number;
|
|
22
|
+
};
|
|
23
|
+
export type ResolvedExecutionLimits = {
|
|
24
|
+
readonly timeoutMs: number | undefined;
|
|
25
|
+
readonly maxToolCalls: number | undefined;
|
|
26
|
+
readonly maxOutputBytes: number | undefined;
|
|
27
|
+
};
|
|
28
|
+
/** Configuration shared by `CodeMode.make` and `CodeMode.execute`. */
|
|
29
|
+
export type Options<Provided extends Record<string, unknown> = {}> = ToolRuntime.ToolCallHooks<Services<Provided>> & {
|
|
30
|
+
/** Explicit tools exposed to the program as `tools`. */
|
|
31
|
+
tools?: Provided & Tools<Services<Provided>>;
|
|
32
|
+
/** Resource limits enforced on each execution. */
|
|
33
|
+
limits?: ExecutionLimits;
|
|
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;
|
|
39
|
+
};
|
|
40
|
+
/** A JSON value that can cross the confined interpreter boundary. */
|
|
41
|
+
export type DataValue = Schema.Json;
|
|
42
|
+
/** Schema for a host tool input containing CodeMode source. */
|
|
43
|
+
export declare const Input: Schema.Struct<{
|
|
44
|
+
readonly code: Schema.String;
|
|
45
|
+
}>;
|
|
46
|
+
export type Input = typeof Input.Type;
|
|
47
|
+
export declare const DiagnosticKind: Schema.Literals<readonly ["ParseError", "UnsupportedSyntax", "UnknownTool", "InvalidToolInput", "InvalidToolOutput", "InvalidDataValue", "ToolCallLimitExceeded", "TimeoutExceeded", "ToolFailure", "ExecutionFailure", "Truncated"]>;
|
|
48
|
+
/** Stable categories produced by program, schema, tool, limit, and truncation diagnostics. */
|
|
49
|
+
export type DiagnosticKind = typeof DiagnosticKind.Type;
|
|
50
|
+
export declare const Diagnostic: Schema.Struct<{
|
|
51
|
+
readonly kind: Schema.Literals<readonly ["ParseError", "UnsupportedSyntax", "UnknownTool", "InvalidToolInput", "InvalidToolOutput", "InvalidDataValue", "ToolCallLimitExceeded", "TimeoutExceeded", "ToolFailure", "ExecutionFailure", "Truncated"]>;
|
|
52
|
+
readonly message: Schema.String;
|
|
53
|
+
readonly location: Schema.optionalKey<Schema.Struct<{
|
|
54
|
+
readonly line: Schema.Number;
|
|
55
|
+
readonly column: Schema.Number;
|
|
56
|
+
}>>;
|
|
57
|
+
readonly suggestions: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
58
|
+
}>;
|
|
59
|
+
/** A normalized program diagnostic safe to return across an agent tool boundary. */
|
|
60
|
+
export type Diagnostic = typeof Diagnostic.Type;
|
|
61
|
+
export declare const Success: Schema.Struct<{
|
|
62
|
+
readonly ok: Schema.Literal<true>;
|
|
63
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
64
|
+
readonly warnings: Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
65
|
+
readonly kind: Schema.Literals<readonly ["ParseError", "UnsupportedSyntax", "UnknownTool", "InvalidToolInput", "InvalidToolOutput", "InvalidDataValue", "ToolCallLimitExceeded", "TimeoutExceeded", "ToolFailure", "ExecutionFailure", "Truncated"]>;
|
|
66
|
+
readonly message: Schema.String;
|
|
67
|
+
readonly location: Schema.optionalKey<Schema.Struct<{
|
|
68
|
+
readonly line: Schema.Number;
|
|
69
|
+
readonly column: Schema.Number;
|
|
70
|
+
}>>;
|
|
71
|
+
readonly suggestions: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
72
|
+
}>>>;
|
|
73
|
+
readonly logs: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
74
|
+
readonly truncated: Schema.optionalKey<Schema.Boolean>;
|
|
75
|
+
readonly toolCalls: Schema.$Array<Schema.Struct<{
|
|
76
|
+
readonly name: Schema.String;
|
|
77
|
+
}>>;
|
|
78
|
+
}>;
|
|
79
|
+
/** Successful execution after the result has crossed the plain-data boundary. */
|
|
80
|
+
export type Success = typeof Success.Type;
|
|
81
|
+
export declare const Failure: Schema.Struct<{
|
|
82
|
+
readonly ok: Schema.Literal<false>;
|
|
83
|
+
readonly error: Schema.Struct<{
|
|
84
|
+
readonly kind: Schema.Literals<readonly ["ParseError", "UnsupportedSyntax", "UnknownTool", "InvalidToolInput", "InvalidToolOutput", "InvalidDataValue", "ToolCallLimitExceeded", "TimeoutExceeded", "ToolFailure", "ExecutionFailure", "Truncated"]>;
|
|
85
|
+
readonly message: Schema.String;
|
|
86
|
+
readonly location: Schema.optionalKey<Schema.Struct<{
|
|
87
|
+
readonly line: Schema.Number;
|
|
88
|
+
readonly column: Schema.Number;
|
|
89
|
+
}>>;
|
|
90
|
+
readonly suggestions: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
91
|
+
}>;
|
|
92
|
+
readonly logs: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
93
|
+
readonly truncated: Schema.optionalKey<Schema.Boolean>;
|
|
94
|
+
readonly toolCalls: Schema.$Array<Schema.Struct<{
|
|
95
|
+
readonly name: Schema.String;
|
|
96
|
+
}>>;
|
|
97
|
+
}>;
|
|
98
|
+
/** Failed execution with calls admitted before the diagnostic was produced. */
|
|
99
|
+
export type Failure = typeof Failure.Type;
|
|
100
|
+
/** Schema for the structured success or diagnostic returned by CodeMode execution. */
|
|
101
|
+
export declare const Result: Schema.Union<readonly [Schema.Struct<{
|
|
102
|
+
readonly ok: Schema.Literal<true>;
|
|
103
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
104
|
+
readonly warnings: Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
105
|
+
readonly kind: Schema.Literals<readonly ["ParseError", "UnsupportedSyntax", "UnknownTool", "InvalidToolInput", "InvalidToolOutput", "InvalidDataValue", "ToolCallLimitExceeded", "TimeoutExceeded", "ToolFailure", "ExecutionFailure", "Truncated"]>;
|
|
106
|
+
readonly message: Schema.String;
|
|
107
|
+
readonly location: Schema.optionalKey<Schema.Struct<{
|
|
108
|
+
readonly line: Schema.Number;
|
|
109
|
+
readonly column: Schema.Number;
|
|
110
|
+
}>>;
|
|
111
|
+
readonly suggestions: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
112
|
+
}>>>;
|
|
113
|
+
readonly logs: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
114
|
+
readonly truncated: Schema.optionalKey<Schema.Boolean>;
|
|
115
|
+
readonly toolCalls: Schema.$Array<Schema.Struct<{
|
|
116
|
+
readonly name: Schema.String;
|
|
117
|
+
}>>;
|
|
118
|
+
}>, Schema.Struct<{
|
|
119
|
+
readonly ok: Schema.Literal<false>;
|
|
120
|
+
readonly error: Schema.Struct<{
|
|
121
|
+
readonly kind: Schema.Literals<readonly ["ParseError", "UnsupportedSyntax", "UnknownTool", "InvalidToolInput", "InvalidToolOutput", "InvalidDataValue", "ToolCallLimitExceeded", "TimeoutExceeded", "ToolFailure", "ExecutionFailure", "Truncated"]>;
|
|
122
|
+
readonly message: Schema.String;
|
|
123
|
+
readonly location: Schema.optionalKey<Schema.Struct<{
|
|
124
|
+
readonly line: Schema.Number;
|
|
125
|
+
readonly column: Schema.Number;
|
|
126
|
+
}>>;
|
|
127
|
+
readonly suggestions: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
128
|
+
}>;
|
|
129
|
+
readonly logs: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
130
|
+
readonly truncated: Schema.optionalKey<Schema.Boolean>;
|
|
131
|
+
readonly toolCalls: Schema.$Array<Schema.Struct<{
|
|
132
|
+
readonly name: Schema.String;
|
|
133
|
+
}>>;
|
|
134
|
+
}>]>;
|
|
135
|
+
/** Result of executing a CodeMode program. Program failures are data, not Effect failures. */
|
|
136
|
+
export type Result = typeof Result.Type;
|
|
137
|
+
/** Reusable confined runtime over explicit tools. */
|
|
138
|
+
export type Runtime<R = never> = {
|
|
139
|
+
readonly catalog: ReadonlyArray<ToolDescription>;
|
|
140
|
+
readonly execute: (code: string) => Effect.Effect<Result, never, R>;
|
|
141
|
+
};
|
|
142
|
+
/** Executes one Effect-native CodeMode program without constructing a reusable runtime. */
|
|
143
|
+
export declare const execute: <const Provided extends Record<string, unknown>>(options: ExecuteOptions<Provided>) => Effect.Effect<Result, never, Services<Provided>>;
|
|
144
|
+
/** Creates an Effect-native runtime over explicit, schema-described tools. */
|
|
145
|
+
export declare const make: <const Provided extends Record<string, unknown> = {}>(options?: Options<Provided>) => Runtime<Services<Provided>>;
|
package/dist/codemode.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { executeProgram } from "./interpreter/execute.js";
|
|
3
|
+
import { ToolRuntime } from "./tool-runtime.js";
|
|
4
|
+
/** Signature-construction helpers for host-owned catalog instructions. */
|
|
5
|
+
export { searchSignature, toolExpression } from "./tool-runtime.js";
|
|
6
|
+
/** Schema for a host tool input containing CodeMode source. */
|
|
7
|
+
export const Input = Schema.Struct({ code: Schema.String });
|
|
8
|
+
export const DiagnosticKind = Schema.Literals([
|
|
9
|
+
"ParseError",
|
|
10
|
+
"UnsupportedSyntax",
|
|
11
|
+
"UnknownTool",
|
|
12
|
+
"InvalidToolInput",
|
|
13
|
+
"InvalidToolOutput",
|
|
14
|
+
"InvalidDataValue",
|
|
15
|
+
"ToolCallLimitExceeded",
|
|
16
|
+
"TimeoutExceeded",
|
|
17
|
+
"ToolFailure",
|
|
18
|
+
"ExecutionFailure",
|
|
19
|
+
"Truncated",
|
|
20
|
+
]);
|
|
21
|
+
export const Diagnostic = Schema.Struct({
|
|
22
|
+
kind: DiagnosticKind,
|
|
23
|
+
message: Schema.String,
|
|
24
|
+
location: Schema.optionalKey(Schema.Struct({ line: Schema.Number, column: Schema.Number })),
|
|
25
|
+
suggestions: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
26
|
+
});
|
|
27
|
+
const ToolCallSchema = Schema.Struct({ name: Schema.String });
|
|
28
|
+
export const Success = Schema.Struct({
|
|
29
|
+
ok: Schema.Literal(true),
|
|
30
|
+
value: Schema.Json,
|
|
31
|
+
warnings: Schema.optionalKey(Schema.Array(Diagnostic)),
|
|
32
|
+
logs: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
33
|
+
truncated: Schema.optionalKey(Schema.Boolean),
|
|
34
|
+
toolCalls: Schema.Array(ToolCallSchema),
|
|
35
|
+
});
|
|
36
|
+
export const Failure = Schema.Struct({
|
|
37
|
+
ok: Schema.Literal(false),
|
|
38
|
+
error: Diagnostic,
|
|
39
|
+
logs: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
40
|
+
truncated: Schema.optionalKey(Schema.Boolean),
|
|
41
|
+
toolCalls: Schema.Array(ToolCallSchema),
|
|
42
|
+
});
|
|
43
|
+
/** Schema for the structured success or diagnostic returned by CodeMode execution. */
|
|
44
|
+
export const Result = Schema.Union([Success, Failure]);
|
|
45
|
+
const validateLimit = (name, value, minimum) => {
|
|
46
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < minimum)) {
|
|
47
|
+
throw new RangeError(`${name} must be a safe integer greater than or equal to ${minimum}.`);
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
};
|
|
51
|
+
const resolveExecutionLimits = (limits) => ({
|
|
52
|
+
timeoutMs: validateLimit("timeoutMs", limits?.timeoutMs, 1),
|
|
53
|
+
maxToolCalls: validateLimit("maxToolCalls", limits?.maxToolCalls, 0),
|
|
54
|
+
maxOutputBytes: validateLimit("maxOutputBytes", limits?.maxOutputBytes, 0),
|
|
55
|
+
});
|
|
56
|
+
/** Executes one Effect-native CodeMode program without constructing a reusable runtime. */
|
|
57
|
+
export const execute = (options) => make(options).execute(options.code);
|
|
58
|
+
/** Creates an Effect-native runtime over explicit, schema-described tools. */
|
|
59
|
+
export const make = (options = {}) => {
|
|
60
|
+
const prepared = ToolRuntime.prepare((options.tools ?? {}));
|
|
61
|
+
const limits = resolveExecutionLimits(options.limits);
|
|
62
|
+
return {
|
|
63
|
+
catalog: prepared.catalog,
|
|
64
|
+
execute: (code) => executeProgram(code, prepared, limits, options),
|
|
65
|
+
};
|
|
66
|
+
};
|
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * as Data from "./data.js";
|
|
2
|
+
import type { DiagnosticKind } from "./codemode.js";
|
|
3
|
+
export declare class ToolRuntimeError extends Error {
|
|
4
|
+
readonly kind: Extract<DiagnosticKind, "UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded">;
|
|
5
|
+
readonly suggestions: ReadonlyArray<string>;
|
|
6
|
+
constructor(kind: Extract<DiagnosticKind, "UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded">, message: string, suggestions?: ReadonlyArray<string>);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Brings a host-produced value into the program: program and runtime values pass through, their
|
|
10
|
+
* host counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and host objects
|
|
11
|
+
* and arrays are copied.
|
|
12
|
+
*/
|
|
13
|
+
export declare const toProgram: (value: unknown, label: string) => unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Brings host data into the program: Date and URL become strings, other host collections become
|
|
16
|
+
* empty objects, and objects become program copies. Used for tool results and parsed JSON.
|
|
17
|
+
*/
|
|
18
|
+
export declare const fromData: (value: unknown, label: string) => unknown;
|
|
19
|
+
/**
|
|
20
|
+
* Takes a program value out as plain JSON: runtime values serialize like `JSON.stringify` would,
|
|
21
|
+
* non-finite numbers become null, and array holes become null. `undefined` object properties are
|
|
22
|
+
* dropped ("json") or become null ("result", for program results where the consumer must never see
|
|
23
|
+
* undefined); a bare `undefined` follows the same rule.
|
|
24
|
+
*/
|
|
25
|
+
export declare const toData: (value: unknown, label: string, undefinedAs?: "json" | "result") => unknown;
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
export * as Data from "./data.js";
|
|
2
|
+
import { get, ownEntries, parseArrayIndex, ProgramArray, ProgramError, ProgramFunction, ProgramObject, set, } from "./interpreter/objects.js";
|
|
3
|
+
import { Values } from "./values.js";
|
|
4
|
+
const MAX_VALUE_DEPTH = 32;
|
|
5
|
+
export class ToolRuntimeError extends Error {
|
|
6
|
+
kind;
|
|
7
|
+
suggestions;
|
|
8
|
+
constructor(kind, message, suggestions = []) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.kind = kind;
|
|
11
|
+
this.suggestions = suggestions;
|
|
12
|
+
this.name = "ToolRuntimeError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Brings a host-produced value into the program: program and runtime values pass through, their
|
|
17
|
+
* host counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and host objects
|
|
18
|
+
* and arrays are copied.
|
|
19
|
+
*/
|
|
20
|
+
export const toProgram = (value, label) => copy(value, label, "program", 0, new Set());
|
|
21
|
+
/**
|
|
22
|
+
* Brings host data into the program: Date and URL become strings, other host collections become
|
|
23
|
+
* empty objects, and objects become program copies. Used for tool results and parsed JSON.
|
|
24
|
+
*/
|
|
25
|
+
export const fromData = (value, label) => copy(value, label, "data", 0, new Set());
|
|
26
|
+
/**
|
|
27
|
+
* Takes a program value out as plain JSON: runtime values serialize like `JSON.stringify` would,
|
|
28
|
+
* non-finite numbers become null, and array holes become null. `undefined` object properties are
|
|
29
|
+
* dropped ("json") or become null ("result", for program results where the consumer must never see
|
|
30
|
+
* undefined); a bare `undefined` follows the same rule.
|
|
31
|
+
*/
|
|
32
|
+
export const toData = (value, label, undefinedAs = "json") => copy(value, label, undefinedAs, 0, new Set());
|
|
33
|
+
const copy = (value, label, mode, depth, seen) => {
|
|
34
|
+
if (depth > MAX_VALUE_DEPTH) {
|
|
35
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`);
|
|
36
|
+
}
|
|
37
|
+
if (value === undefined)
|
|
38
|
+
return mode === "result" ? null : undefined;
|
|
39
|
+
if (typeof value === "number")
|
|
40
|
+
return (mode === "json" || mode === "result") && !Number.isFinite(value) ? null : value;
|
|
41
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
42
|
+
return value;
|
|
43
|
+
if (typeof value !== "object") {
|
|
44
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`);
|
|
45
|
+
}
|
|
46
|
+
if (value instanceof Values.Promise) {
|
|
47
|
+
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.`);
|
|
48
|
+
}
|
|
49
|
+
if (value instanceof ProgramFunction && mode !== "program") {
|
|
50
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`);
|
|
51
|
+
}
|
|
52
|
+
const plain = mode === "program" || mode === "data";
|
|
53
|
+
if (mode === "program") {
|
|
54
|
+
if (value instanceof ProgramObject || Values.isValue(value))
|
|
55
|
+
return value;
|
|
56
|
+
if (value instanceof Date)
|
|
57
|
+
return new Values.Date(value.getTime());
|
|
58
|
+
if (value instanceof RegExp)
|
|
59
|
+
return new Values.RegExp(value.source, value.flags);
|
|
60
|
+
if (value instanceof Map) {
|
|
61
|
+
const wrapped = new Values.Map();
|
|
62
|
+
for (const [key, item] of value.entries()) {
|
|
63
|
+
wrapped.map.set(copy(key, label, mode, depth + 1, seen), copy(item, label, mode, depth + 1, seen));
|
|
64
|
+
}
|
|
65
|
+
return wrapped;
|
|
66
|
+
}
|
|
67
|
+
if (value instanceof Set) {
|
|
68
|
+
const wrapped = new Values.Set();
|
|
69
|
+
for (const item of value.values())
|
|
70
|
+
wrapped.set.add(copy(item, label, mode, depth + 1, seen));
|
|
71
|
+
return wrapped;
|
|
72
|
+
}
|
|
73
|
+
if (value instanceof URL)
|
|
74
|
+
return new Values.URL(new URL(value.href));
|
|
75
|
+
if (value instanceof URLSearchParams)
|
|
76
|
+
return new Values.URLSearchParams(new URLSearchParams(value));
|
|
77
|
+
}
|
|
78
|
+
if (value instanceof Values.Date)
|
|
79
|
+
return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null;
|
|
80
|
+
if (value instanceof Date)
|
|
81
|
+
return Number.isFinite(value.getTime()) ? value.toISOString() : null;
|
|
82
|
+
if (value instanceof Values.URL)
|
|
83
|
+
return value.url.href;
|
|
84
|
+
if (value instanceof URL)
|
|
85
|
+
return value.href;
|
|
86
|
+
// Remaining runtime values and their host counterparts serialize as empty objects, like JSON.stringify.
|
|
87
|
+
if (Values.isValue(value) ||
|
|
88
|
+
value instanceof RegExp ||
|
|
89
|
+
value instanceof Map ||
|
|
90
|
+
value instanceof Set ||
|
|
91
|
+
value instanceof URLSearchParams) {
|
|
92
|
+
return plain ? new ProgramObject() : {};
|
|
93
|
+
}
|
|
94
|
+
if (seen.has(value)) {
|
|
95
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} contains a circular value.`);
|
|
96
|
+
}
|
|
97
|
+
seen.add(value);
|
|
98
|
+
if (value instanceof ProgramArray) {
|
|
99
|
+
const copied = Array.from(value.items, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
|
|
100
|
+
seen.delete(value);
|
|
101
|
+
return copied;
|
|
102
|
+
}
|
|
103
|
+
if (value instanceof ProgramObject) {
|
|
104
|
+
const copied = {};
|
|
105
|
+
// Errors serialize as { name, message, ...own }: both may be inherited, and neither is enumerable in JS.
|
|
106
|
+
if (value instanceof ProgramError) {
|
|
107
|
+
define(copied, "name", copy(get(value, "name"), label, mode, depth + 1, seen));
|
|
108
|
+
define(copied, "message", copy(get(value, "message"), label, mode, depth + 1, seen));
|
|
109
|
+
}
|
|
110
|
+
for (const [key, item] of ownEntries(value)) {
|
|
111
|
+
const next = copy(item, label, mode, depth + 1, seen);
|
|
112
|
+
if (next === undefined && mode === "json")
|
|
113
|
+
continue;
|
|
114
|
+
define(copied, key, next);
|
|
115
|
+
}
|
|
116
|
+
seen.delete(value);
|
|
117
|
+
return copied;
|
|
118
|
+
}
|
|
119
|
+
if (Array.isArray(value)) {
|
|
120
|
+
if (plain) {
|
|
121
|
+
const copied = new ProgramArray(value.map((item) => copy(item, label, mode, depth + 1, seen)));
|
|
122
|
+
for (const [key, item] of Object.entries(value)) {
|
|
123
|
+
if (parseArrayIndex(key) === undefined)
|
|
124
|
+
set(copied, key, copy(item, label, mode, depth + 1, seen));
|
|
125
|
+
}
|
|
126
|
+
seen.delete(value);
|
|
127
|
+
return copied;
|
|
128
|
+
}
|
|
129
|
+
const copied = Array.from(value, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
|
|
130
|
+
seen.delete(value);
|
|
131
|
+
return copied;
|
|
132
|
+
}
|
|
133
|
+
const prototype = Object.getPrototypeOf(value);
|
|
134
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
135
|
+
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain plain objects only.`);
|
|
136
|
+
}
|
|
137
|
+
if (plain) {
|
|
138
|
+
const copied = new ProgramObject();
|
|
139
|
+
for (const [key, item] of Object.entries(value))
|
|
140
|
+
set(copied, key, copy(item, label, mode, depth + 1, seen));
|
|
141
|
+
seen.delete(value);
|
|
142
|
+
return copied;
|
|
143
|
+
}
|
|
144
|
+
const copied = {};
|
|
145
|
+
for (const [key, item] of Object.entries(value)) {
|
|
146
|
+
const next = copy(item, label, mode, depth + 1, seen);
|
|
147
|
+
if (next === undefined && mode === "json")
|
|
148
|
+
continue;
|
|
149
|
+
define(copied, key, next);
|
|
150
|
+
}
|
|
151
|
+
seen.delete(value);
|
|
152
|
+
return copied;
|
|
153
|
+
};
|
|
154
|
+
// Own data property regardless of the target's prototype, so a "__proto__" key on a host object
|
|
155
|
+
// never reaches the Object.prototype setter.
|
|
156
|
+
const define = (target, key, value) => {
|
|
157
|
+
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
158
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as CodeMode from "./codemode.js";
|
|
2
|
+
export * as Namespace from "./namespace.js";
|
|
3
|
+
export * as Tool from "./tool.js";
|
|
4
|
+
export * as OpenAPI from "./openapi/index.js";
|
|
5
|
+
export { Values } from "./values.js";
|
|
6
|
+
export { searchSignature, toolExpression } from "./codemode.js";
|
|
7
|
+
export { ToolError, toolError } from "./tool-error.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as CodeMode from "./codemode.js";
|
|
2
|
+
export * as Namespace from "./namespace.js";
|
|
3
|
+
export * as Tool from "./tool.js";
|
|
4
|
+
export * as OpenAPI from "./openapi/index.js";
|
|
5
|
+
export { Values } from "./values.js";
|
|
6
|
+
export { searchSignature, toolExpression } from "./codemode.js";
|
|
7
|
+
export { ToolError, toolError } from "./tool-error.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Diagnostic } from "../codemode.js";
|
|
2
|
+
import { HostFunction } from "./host.js";
|
|
3
|
+
import { type ErrorType } from "./intrinsics.js";
|
|
4
|
+
import { ProgramError } from "./objects.js";
|
|
5
|
+
import { type Runner } from "./runner.js";
|
|
6
|
+
export declare const normalizeError: (error: unknown) => Diagnostic;
|
|
7
|
+
export declare const caughtErrorValue: <R>(runner: Runner<R>, thrown: unknown) => unknown;
|
|
8
|
+
export declare const createAggregateErrorValue: <R>(runner: Runner<R>, errors: Array<unknown>, message: string) => ProgramError;
|
|
9
|
+
/** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */
|
|
10
|
+
export declare const errorGlobal: <R>(type: ErrorType, runner: Runner<R>) => HostFunction<R>;
|