@neurocode-ai/codemode 1.18.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +23 -0
- package/README.md +369 -0
- package/codemode.md +176 -0
- package/package.json +25 -0
- package/src/codemode.ts +159 -0
- package/src/index.ts +4 -0
- package/src/interpreter/model.ts +201 -0
- package/src/interpreter/runtime.ts +3465 -0
- package/src/openapi/TODO.md +19 -0
- package/src/openapi/index.ts +130 -0
- package/src/openapi/runtime.ts +326 -0
- package/src/openapi/spec.ts +511 -0
- package/src/openapi/types.ts +112 -0
- package/src/stdlib/collections.ts +51 -0
- package/src/stdlib/console.ts +4 -0
- package/src/stdlib/date.ts +94 -0
- package/src/stdlib/json.ts +42 -0
- package/src/stdlib/math.ts +65 -0
- package/src/stdlib/number.ts +66 -0
- package/src/stdlib/object.ts +77 -0
- package/src/stdlib/promise.ts +6 -0
- package/src/stdlib/regexp.ts +74 -0
- package/src/stdlib/string.ts +52 -0
- package/src/stdlib/url.ts +90 -0
- package/src/stdlib/value.ts +86 -0
- package/src/tool-error.ts +11 -0
- package/src/tool-runtime.ts +806 -0
- package/src/tool-schema.ts +301 -0
- package/src/tool.ts +96 -0
- package/src/values.ts +49 -0
- package/sst-env.d.ts +10 -0
- package/test/codemode.test.ts +1163 -0
- package/test/enumeration.test.ts +159 -0
- package/test/fixtures/openapi-happy-path.json +230 -0
- package/test/fixtures/opencode-v2-openapi.json +23730 -0
- package/test/openapi.test.ts +964 -0
- package/test/parity.test.ts +425 -0
- package/test/promise.test.ts +456 -0
- package/test/signature.test.ts +449 -0
- package/test/stdlib.test.ts +715 -0
- package/tsconfig.json +7 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @opencode-ai/codemode
|
|
2
|
+
|
|
3
|
+
- This local package owns confined execution over explicit schema-described tools. Applications own authorization, persistence, external authority, and tool-specific delivery semantics.
|
|
4
|
+
- Do not add a speculative generic permission or approval policy. A host omits tools it does not expose and enforces domain authorization inside each provided tool.
|
|
5
|
+
- Keep Code Mode unaware of host session, channel, and conversation models. The hosting application supplies trusted execution scope around it.
|
|
6
|
+
- Tool schemas are the model-facing Interface. Keep arguments minimal and natural to the operation; never add unrelated IDs as ambient capability tokens.
|
|
7
|
+
|
|
8
|
+
## OpenAPI
|
|
9
|
+
|
|
10
|
+
- Generate an operation only when its transport semantics are supported; otherwise return a precise `skipped` reason.
|
|
11
|
+
- Never guess parameter serialization or malformed security semantics. Unsupported serialization is skipped and malformed security fails closed.
|
|
12
|
+
- Render unresolved schema constructs as `unknown`, never as invented TypeScript names.
|
|
13
|
+
- Keep network reads bounded and map expected encoding, transport, and decoding failures to model-safe `ToolError` values.
|
|
14
|
+
- Test supported behavior directly; do not reproduce adapter algorithms in tests.
|
|
15
|
+
|
|
16
|
+
## Future Design Notes
|
|
17
|
+
|
|
18
|
+
- If a captured user-visible output channel returns (an earlier `output.text`/`output.file`/`output.image` API was removed from v1), keep `output` as its name, distinct from the program return value: `return` stays the structured result for the model, while `output.*` describes artifacts the host may render into a conversation or UI after execution. Keep this host-neutral and let applications decide how captured output is delivered. In v1, hosts collect media host-side (outside the sandbox) instead.
|
|
19
|
+
- Improve the sandbox failure taxonomy. Distinguish parse/compile mistakes, unsupported syntax, user-thrown errors, invalid returned data, tool refusal, tool internal failure, timeout, and genuine runtime defects so agents can recover accurately instead of treating everything as a generic execution failure.
|
|
20
|
+
- Preserve the public/private error split. Tool authors should be able to return a safe model-visible message while retaining a private cause for host diagnostics. Unknown host failures must remain sanitized by default.
|
|
21
|
+
- Think deliberately about richer binary boundaries before allowing `Blob`, `File`, `ArrayBuffer`, streams, or typed arrays beyond today's JSON-like values. If CodeMode supports binary tool args/results, use explicit tagged data shapes and clear size limits rather than relying on ambient runtime serialization.
|
|
22
|
+
- Keep host capabilities explicit. Globals such as `fetch`, `crypto`, filesystem handles, extra modules, or network clients should be opt-in runtime capabilities with obvious policy defaults, not ambient authority. Default to unavailable unless a host deliberately provides the capability.
|
|
23
|
+
- If `fetch` is added, model it as a host-provided outbound capability with policy controls: allowed origins, methods, headers, response size, timeout, and whether response bodies may be returned, emitted, or only summarized through a tool.
|
package/README.md
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# @opencode-ai/codemode
|
|
2
|
+
|
|
3
|
+
Effect-native confined code execution over explicit, schema-described tools.
|
|
4
|
+
|
|
5
|
+
CodeMode lets a model write a small JavaScript program that can call only the tools supplied by the host. The program can sequence calls, transform plain data, branch, loop, and run independent calls in parallel without receiving ambient filesystem, process, network, module, or application authority.
|
|
6
|
+
|
|
7
|
+
The package is currently private to this workspace. Its API is designed around one-shot and reusable execution:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// One execution
|
|
11
|
+
yield * CodeMode.execute({ tools, code })
|
|
12
|
+
|
|
13
|
+
// A reusable runtime
|
|
14
|
+
const runtime = CodeMode.make({ tools, limits })
|
|
15
|
+
yield * runtime.execute(code)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
Within this workspace:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@opencode-ai/codemode": "workspace:*"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Hosts interact with CodeMode through `effect` (tool `run` implementations, `Effect`-typed results), so they should depend on `effect` themselves.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
Define tools with Effect Schema, then place them in the object tree exposed to programs as `tools`:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { CodeMode, Tool } from "@opencode-ai/codemode"
|
|
38
|
+
import { Effect, Schema } from "effect"
|
|
39
|
+
|
|
40
|
+
const lookupOrder = Tool.make({
|
|
41
|
+
description: "Look up an order by ID",
|
|
42
|
+
input: Schema.Struct({ id: Schema.String }),
|
|
43
|
+
output: Schema.Struct({ id: Schema.String, status: Schema.String }),
|
|
44
|
+
run: ({ id }) => Effect.succeed({ id, status: "open" }),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const runtime = CodeMode.make({
|
|
48
|
+
tools: {
|
|
49
|
+
orders: {
|
|
50
|
+
lookup: lookupOrder,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const result =
|
|
56
|
+
yield *
|
|
57
|
+
runtime.execute(`
|
|
58
|
+
const order = await tools.orders.lookup({ id: "order_42" })
|
|
59
|
+
return { id: order.id, needsAttention: order.status !== "complete" }
|
|
60
|
+
`)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`result` is always a `CodeMode.Result`. Program, validation, limit, and tool failures are returned as diagnostics rather than failing the Effect. Host interruption remains interruption.
|
|
64
|
+
|
|
65
|
+
Successful result values are JSON-safe data. A program that returns `undefined`, including by reaching the end without `return`, produces `null`; nested `undefined` values are normalized to `null` as well.
|
|
66
|
+
|
|
67
|
+
## API
|
|
68
|
+
|
|
69
|
+
### `Tool.make`
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const tool = Tool.make({
|
|
73
|
+
description,
|
|
74
|
+
input, // Effect Schema (validating) or JSON Schema (render-only)
|
|
75
|
+
output, // optional; same choice
|
|
76
|
+
run,
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`input` and `output` each accept a validating Effect Schema or a render-only JSON Schema document (the natural shape for adapter-provided tools whose schemas arrive as JSON Schema, e.g. MCP definitions). Effect Schema input is decoded before `run` is invoked, and `run` returns the encoded representation of an Effect Schema `output`, which CodeMode decodes and copies before exposing it to the program. JSON Schemas only shape the model-visible signature; values pass through unvalidated (they still cross the plain-data boundary).
|
|
81
|
+
|
|
82
|
+
`output` is optional. Without it the tool's signature advertises `Promise<unknown>` and the host result is exposed as-is.
|
|
83
|
+
|
|
84
|
+
The description and schemas are part of the model-visible tool contract. Keep descriptions concrete and put authorization in `run` or in the service it calls.
|
|
85
|
+
|
|
86
|
+
Public tool types are grouped under the same namespace: `Tool.Definition`, `Tool.Options`, `Tool.SchemaType`, and `Tool.JsonSchema`.
|
|
87
|
+
|
|
88
|
+
### `CodeMode.execute`
|
|
89
|
+
|
|
90
|
+
Use `CodeMode.execute` for a single execution:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const result =
|
|
94
|
+
yield *
|
|
95
|
+
CodeMode.execute({
|
|
96
|
+
tools: { orders: { lookup: lookupOrder } },
|
|
97
|
+
code: `return await tools.orders.lookup({ id: "order_42" })`,
|
|
98
|
+
limits: { maxToolCalls: 10 },
|
|
99
|
+
onToolCallStart: (call) => Effect.logDebug("CodeMode tool started", call),
|
|
100
|
+
onToolCallEnd: (call) => Effect.logDebug("CodeMode tool settled", call),
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The Effect environment is inferred from the supplied tools. CodeMode does not erase service requirements introduced by tool implementations.
|
|
105
|
+
|
|
106
|
+
### `CodeMode.make`
|
|
107
|
+
|
|
108
|
+
Use `CodeMode.make` when the tool set and execution policy are reused:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const runtime = CodeMode.make({
|
|
112
|
+
tools: { orders: { lookup: lookupOrder } },
|
|
113
|
+
limits: { timeoutMs: 30_000 },
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
runtime.catalog() // structured tool descriptions
|
|
117
|
+
runtime.instructions() // model-facing syntax and tool guide
|
|
118
|
+
runtime.execute(source) // CodeMode.Result
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`CodeMode.Input`, `CodeMode.Result`, `CodeMode.Success`, `CodeMode.Failure`, `CodeMode.Diagnostic`, and `CodeMode.DiagnosticKind` are both Effect schemas and their inferred TypeScript types. Hosts can combine `CodeMode.Input` and `CodeMode.Result` with `runtime.instructions()` and `runtime.execute()` when constructing a framework-specific agent tool.
|
|
122
|
+
|
|
123
|
+
All other CodeMode types use the same namespace: `CodeMode.Options`, `CodeMode.ExecuteOptions`, `CodeMode.Runtime`, `CodeMode.ExecutionLimits`, `CodeMode.DiscoveryOptions`, `CodeMode.DataValue`, `CodeMode.ToolDescription`, and the `CodeMode.ToolCall*` observation types.
|
|
124
|
+
|
|
125
|
+
### Results
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
type Result = Success | Failure
|
|
129
|
+
|
|
130
|
+
interface Success {
|
|
131
|
+
readonly ok: true
|
|
132
|
+
readonly value: CodeMode.DataValue
|
|
133
|
+
readonly logs?: ReadonlyArray<string>
|
|
134
|
+
readonly truncated?: boolean
|
|
135
|
+
readonly toolCalls: ReadonlyArray<CodeMode.ToolCall>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
interface Failure {
|
|
139
|
+
readonly ok: false
|
|
140
|
+
readonly error: CodeMode.Diagnostic
|
|
141
|
+
readonly logs?: ReadonlyArray<string>
|
|
142
|
+
readonly truncated?: boolean
|
|
143
|
+
readonly toolCalls: ReadonlyArray<CodeMode.ToolCall>
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`toolCalls` contains the names of calls admitted by the runtime in call order. It is retained on failure so hosts can audit partial execution without exposing inputs or host failures. `truncated` is present when the value or logs were cut to fit `maxOutputBytes` (see Execution Limits).
|
|
148
|
+
|
|
149
|
+
### Tool-call hooks
|
|
150
|
+
|
|
151
|
+
`onToolCallStart` receives `{ index, name, input }` after input decoding and before tool execution. The input is decoded host-side data and may include values produced by schema transformations; applications should avoid logging sensitive tool arguments indiscriminately.
|
|
152
|
+
|
|
153
|
+
`onToolCallEnd` receives `{ index, name, input, durationMs, outcome, message? }` when an admitted call settles. `outcome` is `"success"` or `"failure"`; `message` is the model-safe failure message and is present only on failure. Interrupted calls (for example when the execution timeout fires) do not produce an end event. Both hooks are Effect-returning and must not fail.
|
|
154
|
+
|
|
155
|
+
### OpenAPI tools
|
|
156
|
+
|
|
157
|
+
`OpenAPI.fromSpec` turns an OpenAPI 3.x document into a tool subtree - one tool per operation. Dotted `operationId` values form namespaces such as `v2.session.get`. Missing IDs receive a flat method/path fallback such as `getUsersById`; names are sanitized and deduplicated. The host places the subtree under a key in its `tools` tree; that key is the model-visible namespace.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { CodeMode, OpenAPI } from "@opencode-ai/codemode"
|
|
161
|
+
import { Effect } from "effect"
|
|
162
|
+
import { FetchHttpClient } from "effect/unstable/http"
|
|
163
|
+
|
|
164
|
+
const api = OpenAPI.fromSpec({
|
|
165
|
+
spec: await Bun.file("openapi.json").json(), // parsed document (no YAML)
|
|
166
|
+
auth: {
|
|
167
|
+
resolve: ({ name, scopes, operation }) =>
|
|
168
|
+
name === "BearerAuth" ? Effect.succeed({ type: "bearer", token }) : Effect.succeed(undefined),
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const runtime = CodeMode.make({ tools: { opencode: api.tools } })
|
|
173
|
+
const result = await Effect.runPromise(runtime.execute(code).pipe(Effect.provide(FetchHttpClient.layer)))
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`fromSpec` is synchronous and returns `{ tools, skipped }`. The initial adapter supports query `form`/`deepObject`, path/header `simple`, JSON request bodies, JSON responses, and text responses; unsupported parameter encodings, non-JSON request bodies, binary responses, and streaming operations land in `skipped` instead of producing broken tools. Operation and path servers take precedence over document servers unless `baseUrl` explicitly overrides all of them. Tool inputs flatten path, query, header, and closed object-body fields into one model-facing object while retaining their HTTP locations internally. Cross-location name collisions receive a location prefix such as `path_id` and `query_id`; composed, nullable, dictionary, conditionally-required, and non-object JSON bodies remain under `body`. Auth is never model-visible. Responses are limited to 50 MiB, and non-2xx responses become safe tool failures carrying the status and a size-capped body summary. Deferred capabilities are tracked in `src/openapi/TODO.md`.
|
|
177
|
+
|
|
178
|
+
Supported bearer, basic, header, and query authentication follows OpenAPI `security` semantics and is resolved host-side via `auth.resolve` - credential storage, OAuth flows, and token refresh never enter the compiler. Cookie authentication alternatives are discarded; an operation is skipped when it has no supported alternative. See the option docstrings in `src/openapi/types.ts` for the full semantics. Generated tools require `HttpClient.HttpClient` (from `effect/unstable/http`) in the Effect environment - provide `FetchHttpClient.layer` or a custom/test client layer at execution. The supplied client owns redirect policy; credentialed hosts should reject redirects or strip credentials when the origin changes.
|
|
179
|
+
|
|
180
|
+
## Discovery
|
|
181
|
+
|
|
182
|
+
The agent-tool instructions use a budgeted catalog. Every tool namespace is always listed with its tool count regardless of budget, and as many complete, JSDoc-annotated tool signatures (each with a one-line description) as fit an estimated-token budget are inlined. Schema field descriptions and tags are part of each signature's measured cost. Selection is round-robin across namespaces for fairness: in each round (namespaces alphabetical), every namespace still holding un-inlined tools attempts to place its next-cheapest signature against the shared budget, and a namespace whose next signature does not fit drops out while the others keep going - so every namespace gets some representation before any namespace gets everything. The instructions state exactly how comprehensive the list is, both overall (`COMPLETE list` vs `PARTIAL - N of M shown`) and per namespace (`(3 tools)`, `(3 tools, 1 shown)`, `(3 tools, none shown)`).
|
|
183
|
+
|
|
184
|
+
The catalog-entry budget defaults to 2,000 estimated tokens (characters / 4, the same heuristic OpenCode uses). It applies only to full tool entries shown in the catalog; fixed instructions and namespace summaries are not counted. Override it when constructing a runtime:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
const runtime = CodeMode.make({
|
|
188
|
+
tools,
|
|
189
|
+
discovery: { catalogBudget: 6_000 },
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The budget must be a non-negative safe integer.
|
|
194
|
+
|
|
195
|
+
The runtime search tool is always registered - including when the catalog is fully inlined - so a speculative `tools.$codemode.search` call never fails as an unknown tool. It is only advertised in the instructions when the inlined list is partial:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
const matches = await tools.$codemode.search({
|
|
199
|
+
query: "order status",
|
|
200
|
+
namespace: "orders", // optional: scope to one top-level namespace
|
|
201
|
+
limit: 10,
|
|
202
|
+
offset: 0,
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`search` performs deterministic, additive field-weighted matching. The query is tokenized (camelCase boundaries split; every non-alphanumeric character is a separator; empties and `*` are dropped), and each term scores every tool: exact path or path-segment match (20), path substring (8), description substring (4), and searchable-text substring (2). Each term also carries naive singular variants (trailing `s`/`es` stripped), and a field check passes when the term or any variant matches - so a plural query term (`issues`) still finds a tool whose text only says `issue`, without changing the weights. The searchable text also includes the input schema's property names and their description strings, so a query naming a parameter finds its tool, and substring matching means partial words match. Scores sum across terms; matches are sorted by score (ties broken alphabetically by path), then sliced from the zero-based `offset` (default 0) to the configured `limit` (default 10). `remaining` counts matches after the current page. `next` is `{ offset }` when another page exists and `null` on the final page; spread it into the original request to preserve its query, namespace, and limit.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
const request = { query: "order status", namespace: "orders", limit: 10 }
|
|
210
|
+
const page = await tools.$codemode.search(request)
|
|
211
|
+
const nextPage = page.next ? await tools.$codemode.search({ ...request, ...page.next }) : undefined
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Each result contains the path, description, and the same generated TypeScript signature used by the inline catalog, so no second lookup is needed. Signatures use the JSDoc-annotated multiline form: each described input/output field carries its schema `description` as a `/** ... */` comment, and constraints TypeScript cannot express ride along as tags (`@deprecated`, `@default`, `@format`, `@minItems`, `@maxItems`).
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
tools.github.list_issues(input: {
|
|
218
|
+
/** Repository owner */
|
|
219
|
+
owner: string,
|
|
220
|
+
/** Cursor from the previous response's pageInfo */
|
|
221
|
+
after?: string,
|
|
222
|
+
/**
|
|
223
|
+
* Results per page
|
|
224
|
+
* @default 30
|
|
225
|
+
*/
|
|
226
|
+
perPage?: number,
|
|
227
|
+
}): Promise<unknown>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Result paths are rendered as JavaScript expressions rooted at `tools` (`tools.orders.lookup`, or `tools.context7["resolve-library-id"]` for non-identifier segments), so each `path` is directly usable as the call site. An empty query browses the catalog alphabetically by path; combined with `namespace` (`{ query: "", namespace: "orders" }`) it lists everything in that namespace. A query that names one tool path exactly (canonical path, `tools.`-prefixed path, or rendered JavaScript expression) is treated as a lookup and returns that tool alone.
|
|
231
|
+
|
|
232
|
+
The instructions are structured markdown, ordered so the workflow sits at the top and the catalog at the bottom: a `## Workflow` section with numbered steps (find a tool via search when the catalog is partial, or pick from the inlined list when it is complete; call the exact path as-is; return only the needed fields), a `## Rules` section holding only guidance the workflow does not already cover (only listed/search-result Code Mode tools and internal runtime tools exist inside `tools`; filter and aggregate collections in code; narrow `Promise<unknown>` results at runtime; run independent calls through `Promise.all`; enumerate `tools` with `Object.keys`/`for...in`; browse a namespace and paginate search results when search is advertised), a short `## Language` section that identifies the runtime as a restricted JavaScript orchestration language and names its major unavailable capabilities, and the budgeted `## Available tools` catalog. Example call forms use explicit `<namespace>.<tool>`/`<field>` placeholders - never a real or fabricated tool name.
|
|
233
|
+
|
|
234
|
+
A host cannot define its own `$codemode` top-level namespace.
|
|
235
|
+
|
|
236
|
+
## Supported Programs
|
|
237
|
+
|
|
238
|
+
CodeMode executes a deliberately bounded JavaScript subset. It supports:
|
|
239
|
+
|
|
240
|
+
- Plain data literals, property access, assignment, and destructuring.
|
|
241
|
+
- `if`, conditional expressions, `switch`, `for`, `for...of` (arrays, strings, Maps, Sets), `for...in` (own keys of plain objects, index strings of arrays, and namespace/tool names of `tools` references - anything else is an error suggesting `for...of` or `Object.keys`, rather than real JS's surprising behavior of indices for strings and zero iterations for Maps/Sets), `while`, and `do...while`.
|
|
242
|
+
- Arrow functions and function declarations with closures, defaults, rest parameters, and destructuring.
|
|
243
|
+
- Optional chaining, nullish coalescing, templates, spread (arrays, strings, Maps, Sets), and `try`/`catch`.
|
|
244
|
+
- Common array, string, number, `Object`, `Math`, and `JSON` operations. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
|
|
245
|
+
- `Date` - `Date.now()`/`Date.parse()`/`Date.UTC()`, `new Date(...)`, the getter methods, and date arithmetic/comparison via the time value. Dates stringify as ISO (`toString` included, for determinism across host timezones).
|
|
246
|
+
- Regular expressions - `/literals/` and `new RegExp(...)` with `test`/`exec` (stateful `lastIndex` for `g`), plus string `match`/`matchAll`/`replace`/`replaceAll`/`split`/`search` with patterns. Match results are arrays carrying `index` and named `groups` as own properties (`input` is omitted). `replace` and `replaceAll` accept function replacers with captures, offset, input, and named groups; callbacks run sequentially, may await tool calls, and have their results coerced to strings. Invalid patterns, invalid flags, and missing-`g` calls fail with catchable errors that say what was wrong and how to fix it (escaping hints, the exact `/pattern/g` to write). Patterns run on the host engine, so pathological backtracking is bounded only by the execution timeout.
|
|
247
|
+
- `Map` and `Set` - construction from entries/arrays/strings, `get`/`set`/`add`/`has`/`delete`/`clear`/`size`/`forEach`, and `keys`/`values`/`entries` returning **arrays** (not iterators).
|
|
248
|
+
- URL helpers - `URL` resolution and mutation, linked `URLSearchParams`, `URL.canParse`/`URL.parse`, URI and URI-component encoding/decoding, and query parameter construction, lookup, mutation, sorting, callbacks, and materialization. URLSearchParams iteration methods return arrays, matching the Map/Set convention.
|
|
249
|
+
- First-class promises - an un-awaited `tools.ns.tool(...)` is a promise value whose call starts immediately on a supervised fiber; `await` resolves it (awaiting a non-promise value is a no-op, and `return tools.ns.tool(...)` resolves like an async-function return). `Promise.all`, `Promise.allSettled`, and `Promise.race` accept any array mixing promises and plain values (built inline, beforehand, or via spread); `Promise.resolve`/`Promise.reject` construct settled promises. `Promise.allSettled` rejection reasons are the same plain `{ name?, message }` data a `catch` binding sees, and `Promise.race` interrupts its losing in-flight calls. At most 8 tool calls run concurrently. When a program completes, still-running un-awaited calls are awaited before the execution ends; a failure from a call that was never awaited surfaces as an unhandled-rejection diagnostic.
|
|
250
|
+
- `throw value` and `throw new Error(message)` for explicit program failure. `Error` (and `TypeError`/`RangeError`/`SyntaxError`/`ReferenceError`/`EvalError`/`URIError`) are real constructors, callable with or without `new`; error values are plain `{ name, message }` data that additionally satisfy `instanceof Error` (a specific type matches itself and `Error`, as in JS). Every caught failure - thrown errors, interpreter runtime errors, and tool failures - is `instanceof Error` in a `catch` block; a thrown non-error value (`throw "text"`) is not, matching JS. Caught failures carry the `name` the equivalent real-JS failure would have - `JSON.parse` and invalid regex patterns produce a `SyntaxError` (satisfying `instanceof SyntaxError`), an unknown identifier a `ReferenceError`, assigning to a constant a `TypeError`, a bad `normalize` form a `RangeError`; failures with no specific analogue (including tool failures) are named `"Error"`. `instanceof` also recognizes `Date`, `RegExp`, `Map`, `Set`, `URL`, `URLSearchParams`, `Array`, `Object`, and `Promise`; any other right-hand side is a catchable error.
|
|
251
|
+
|
|
252
|
+
Inside a program, standard-library values stay live everywhere: the internal data checkpoints (`Object.*` helpers, spread, coercion inputs) preserve the instances, so `Object.values({ d: date })[0].getTime()` and a spread copy of an object holding a Map keep working. Only at the host boundary (final result, tool arguments, `JSON.stringify`) do they serialize exactly as `JSON.stringify` would: Date and URL become strings (an invalid Date becomes `null`), while RegExp, Map, Set, and URLSearchParams become `{}`. Promise values never cross a data boundary: an un-awaited promise in a result or tool argument produces a diagnostic that says to await it, instead of serializing to `{}`.
|
|
253
|
+
|
|
254
|
+
It does not expose `eval`, dynamic imports, modules, classes, generators, timers, host globals, prototype mutation, custom promise constructors (`new Promise`), promise chaining (`.then`/`.catch`/`.finally` - `await` with `try`/`catch` is the supported style), or arbitrary method calls. Unsupported syntax returns an `UnsupportedSyntax` diagnostic with a source location when available.
|
|
255
|
+
|
|
256
|
+
CodeMode is an orchestration language, not a general JavaScript runtime.
|
|
257
|
+
|
|
258
|
+
## Execution Limits
|
|
259
|
+
|
|
260
|
+
The limits are exactly three knobs:
|
|
261
|
+
|
|
262
|
+
| Limit | Default | Bounds |
|
|
263
|
+
| ---------------- | -------------------: | -------------------------------------------------------------------- |
|
|
264
|
+
| `timeoutMs` | none - no timeout | Wall-clock execution time. |
|
|
265
|
+
| `maxToolCalls` | none - unlimited | Tool calls admitted during the execution. |
|
|
266
|
+
| `maxOutputBytes` | none - no truncation | Model-facing output: the serialized result value plus captured logs. |
|
|
267
|
+
|
|
268
|
+
No limit has a default, on purpose: execution budgets are host policy, not library policy - a host that wants a bound sets one; a host that can interrupt the execution fiber (as OpenCode does on user cancel) may set no timeout, and a host with its own tool-output truncation (as OpenCode has) may leave `maxOutputBytes` unset. A host with neither should set `maxOutputBytes`, or oversized results silently flood model context.
|
|
269
|
+
|
|
270
|
+
Pass only the overrides you need:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
const runtime = CodeMode.make({
|
|
274
|
+
tools,
|
|
275
|
+
limits: {
|
|
276
|
+
maxToolCalls: 20,
|
|
277
|
+
timeoutMs: 60_000,
|
|
278
|
+
},
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Limits are safe integers. `timeoutMs` must be at least `1`; the others may be `0`. Invalid configuration throws a `RangeError` when `CodeMode.make` or `CodeMode.execute` is called. An explicitly `undefined` value is the same as leaving the limit unset.
|
|
283
|
+
|
|
284
|
+
Exceeding a configured `maxOutputBytes` never fails the execution. An oversized result value is replaced by its truncated serialized text plus an explanatory marker, logs are kept from the start until the remaining budget is exhausted (with a final marker line noting the cut), and the result carries `truncated: true`.
|
|
285
|
+
|
|
286
|
+
When configured, the timeout interrupts in-flight tool Effects, including eagerly started calls the program has not awaited (their fibers are supervised by the execution). The interpreter yields cooperatively between steps, so the timeout also interrupts pure busy loops (`while (true) {}`) - no separate work budget exists. Tool implementations remain responsible for making their external operations interruptible or independently bounded.
|
|
287
|
+
|
|
288
|
+
Two interpreter internals are fixed constants rather than knobs: at most 8 tool calls run concurrently, and values crossing a data boundary may nest at most 32 levels deep (deeper values fail as `InvalidDataValue`, which reads better than a native stack-overflow error). Neither is part of the public contract.
|
|
289
|
+
|
|
290
|
+
## Diagnostics
|
|
291
|
+
|
|
292
|
+
Failures are data:
|
|
293
|
+
|
|
294
|
+
| Kind | Meaning |
|
|
295
|
+
| ----------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
296
|
+
| `ParseError` | Source is empty or cannot be parsed. |
|
|
297
|
+
| `UnsupportedSyntax` | Parsed JavaScript is outside the supported subset. |
|
|
298
|
+
| `UnknownTool` | A program referenced a tool the host did not provide. |
|
|
299
|
+
| `InvalidToolInput` | Tool input failed schema decoding or safe-data copying. |
|
|
300
|
+
| `InvalidToolOutput` | Tool output failed schema decoding or safe-data copying. |
|
|
301
|
+
| `InvalidDataValue` | Program data violated the plain-data contract (depth, circularity, blocked properties, non-data values). |
|
|
302
|
+
| `ToolCallLimitExceeded` | Calls exceeded `maxToolCalls`. |
|
|
303
|
+
| `TimeoutExceeded` | Execution exceeded `timeoutMs`. |
|
|
304
|
+
| `ToolFailure` | A tool refused or failed. |
|
|
305
|
+
| `ExecutionFailure` | The program threw or another execution error occurred. |
|
|
306
|
+
|
|
307
|
+
Unknown host failures, defects, invalid outputs, and copying failures are sanitized. To return a safe operational refusal, fail with `toolError`:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
import { toolError } from "@opencode-ai/codemode"
|
|
311
|
+
|
|
312
|
+
run: ({ id }) => (authorized(id) ? loadOrder(id) : Effect.fail(toolError("Order is unavailable")))
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Only the supplied message is model-visible. The optional cause is never returned in `CodeMode.Result`; hosts should perform any required internal logging before crossing this boundary.
|
|
316
|
+
|
|
317
|
+
## Authority Boundary
|
|
318
|
+
|
|
319
|
+
CodeMode confines programs to the supplied tool tree, but it does not decide what those tools may do.
|
|
320
|
+
|
|
321
|
+
The host owns:
|
|
322
|
+
|
|
323
|
+
- Authentication and authorization.
|
|
324
|
+
- Tool selection and immutable scope.
|
|
325
|
+
- Credentials and network clients.
|
|
326
|
+
- Persistence, idempotency, approval, and durable side effects.
|
|
327
|
+
- Logging and redaction policy.
|
|
328
|
+
|
|
329
|
+
CodeMode owns:
|
|
330
|
+
|
|
331
|
+
- Parsing and interpreting the supported subset without `eval`.
|
|
332
|
+
- Schema boundaries around tool calls.
|
|
333
|
+
- Plain-data copying and blocked prototype members.
|
|
334
|
+
- Resource limits, call accounting, and normalized diagnostics.
|
|
335
|
+
- Model-facing tool discovery and instructions.
|
|
336
|
+
|
|
337
|
+
A program cannot gain authority through prose or generated code. It can only exercise authority already present in the supplied tools. Do not expose a broad tool and expect the prompt to restrict it.
|
|
338
|
+
|
|
339
|
+
## Laws
|
|
340
|
+
|
|
341
|
+
The public contract is guided by these equivalences:
|
|
342
|
+
|
|
343
|
+
- `CodeMode.execute({ ...options, code })` is equivalent to `CodeMode.make(options).execute(code)`.
|
|
344
|
+
- A tool implementation is not invoked unless its input has decoded successfully.
|
|
345
|
+
- A tool result is not visible to the program unless its output has decoded and crossed the plain-data boundary successfully.
|
|
346
|
+
- Unknown host failures do not become model-visible diagnostics; `ToolError` is the explicit safe-message channel.
|
|
347
|
+
- Host interruption remains interruption rather than a `CodeMode.Failure`.
|
|
348
|
+
|
|
349
|
+
## Non-Goals
|
|
350
|
+
|
|
351
|
+
- Generic permission prompts or approval workflows.
|
|
352
|
+
- Durable pause/resume, replay, or storage adapters.
|
|
353
|
+
- Exactly-once external side effects.
|
|
354
|
+
- Application authorization or product policy.
|
|
355
|
+
- A filesystem or process sandbox for arbitrary JavaScript.
|
|
356
|
+
- Compatibility with the full JavaScript language or npm ecosystem.
|
|
357
|
+
|
|
358
|
+
Applications that need approval or durable consequences should model those above CodeMode and expose only the currently authorized tools.
|
|
359
|
+
|
|
360
|
+
## Testing
|
|
361
|
+
|
|
362
|
+
From the package directory:
|
|
363
|
+
|
|
364
|
+
```sh
|
|
365
|
+
bun test
|
|
366
|
+
bun run typecheck
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
The direct suite covers public projections, discovery, schema boundaries, diagnostic sanitization, resource limits, tool-call observation, and interruption.
|
package/codemode.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# CodeMode Design and Status
|
|
2
|
+
|
|
3
|
+
This is the living design and status document for `@opencode-ai/codemode` and its existing V2 OpenCode adapter.
|
|
4
|
+
It records current behavior, intentional boundaries, durable rationale, and material remaining work.
|
|
5
|
+
|
|
6
|
+
Completed implementation history, branch names, test counts, and closed findings belong in git, not here. Remove
|
|
7
|
+
completed work instead of preserving checked-off chronology.
|
|
8
|
+
|
|
9
|
+
Detailed package API documentation lives in [README.md](./README.md). OpenAPI-specific follow-ups live in
|
|
10
|
+
[src/openapi/TODO.md](./src/openapi/TODO.md).
|
|
11
|
+
|
|
12
|
+
## How CodeMode Works
|
|
13
|
+
|
|
14
|
+
### Purpose
|
|
15
|
+
|
|
16
|
+
CodeMode gives a model one `execute` tool backed by a confined JavaScript interpreter. Inside the program, the model
|
|
17
|
+
can call an explicit tree of schema-described tools, sequence dependent work, run independent calls concurrently,
|
|
18
|
+
and filter or aggregate results before returning them to the agent loop.
|
|
19
|
+
|
|
20
|
+
The goals are:
|
|
21
|
+
|
|
22
|
+
- Reduce model context consumed by large tool catalogs.
|
|
23
|
+
- Avoid an agent round-trip between every dependent tool call.
|
|
24
|
+
- Keep large intermediate results inside the program instead of sending them through model context.
|
|
25
|
+
- Give generated code only the authority explicitly supplied by the host.
|
|
26
|
+
|
|
27
|
+
CodeMode is an orchestration language, not a general JavaScript runtime or an application authorization system.
|
|
28
|
+
|
|
29
|
+
### Runtime
|
|
30
|
+
|
|
31
|
+
The generic runtime lives in `packages/codemode` and is host-neutral:
|
|
32
|
+
|
|
33
|
+
1. The host builds a tree of `Tool.make(...)` definitions and calls `CodeMode.make(...)` or `CodeMode.execute(...)`.
|
|
34
|
+
2. CodeMode generates model instructions, a budgeted inline catalog, and the internal `$codemode.search` tool.
|
|
35
|
+
3. TypeScript syntax is transpiled away, Acorn parses the resulting JavaScript, and an owned tree-walking interpreter
|
|
36
|
+
executes it without `eval`.
|
|
37
|
+
4. Tool inputs and outputs cross schema and plain-data boundaries before they become visible on either side.
|
|
38
|
+
5. Execution returns `CodeMode.Result`. Expected program and tool failures are diagnostic data; host interruption
|
|
39
|
+
remains Effect interruption.
|
|
40
|
+
|
|
41
|
+
Effect Schemas validate and transform tool inputs and outputs. JSON Schemas render model-facing signatures but do not
|
|
42
|
+
validate values; adapter-provided values still cross the plain-data boundary. A tool without an output schema is
|
|
43
|
+
advertised as `Promise<unknown>`.
|
|
44
|
+
|
|
45
|
+
### Discovery and model workflow
|
|
46
|
+
|
|
47
|
+
The model sees a token-budgeted catalog. Every namespace remains visible, and complete signatures are selected
|
|
48
|
+
round-robin across namespaces so one large namespace cannot starve the others. `$codemode.search` is always callable
|
|
49
|
+
and is advertised when the inline catalog is partial.
|
|
50
|
+
|
|
51
|
+
The intended workflow is:
|
|
52
|
+
|
|
53
|
+
1. Pick an exact signature from the inline catalog, or return `$codemode.search(...)` results and use a selected path
|
|
54
|
+
in the next execution.
|
|
55
|
+
2. Call the exact returned path without guessing or normalizing segments.
|
|
56
|
+
3. Narrow `Promise<unknown>` results before reading fields.
|
|
57
|
+
4. Start independent calls together and await them with `Promise.all`.
|
|
58
|
+
5. Filter and aggregate inside the program, then return only the data needed by the model.
|
|
59
|
+
|
|
60
|
+
Search returns directly usable JavaScript paths, descriptions, and complete TypeScript signatures. It supports exact
|
|
61
|
+
path lookup, namespace browsing, deterministic ranking, and pagination.
|
|
62
|
+
|
|
63
|
+
### Tool execution
|
|
64
|
+
|
|
65
|
+
Calling a tool starts its Effect eagerly on a supervised fiber. The returned sandbox promise is run-once and can be
|
|
66
|
+
awaited directly or through the supported `Promise` combinators. At most eight tool calls execute concurrently.
|
|
67
|
+
Unfinished calls are drained before successful program completion, and an unhandled call failure becomes a diagnostic.
|
|
68
|
+
|
|
69
|
+
The public execution-policy knobs are `timeoutMs`, `maxToolCalls`, and `maxOutputBytes`. The package supplies no
|
|
70
|
+
defaults because budgets are host policy. The interpreter also enforces fixed internal boundaries for tool-call
|
|
71
|
+
concurrency and data nesting depth.
|
|
72
|
+
|
|
73
|
+
### Data, files, and failures
|
|
74
|
+
|
|
75
|
+
Program results and tool arguments are JSON-like data. Dates become ISO strings at host boundaries; RegExp, Map, and
|
|
76
|
+
Set values become `{}` as they do under JSON serialization. Promise and runtime reference values cannot cross the
|
|
77
|
+
boundary.
|
|
78
|
+
|
|
79
|
+
Unknown host failures and invalid outputs are sanitized. `ToolError` is the explicit channel for a safe message that a
|
|
80
|
+
tool wants the model to see. Diagnostic categories distinguish parsing, unsupported syntax, unknown tools, invalid
|
|
81
|
+
data, tool failures, limits, timeouts, and execution failures.
|
|
82
|
+
|
|
83
|
+
Files and other attachment content stay outside the interpreter. A host may collect them while child tools execute and
|
|
84
|
+
attach them to the outer result, but the program receives only the structured tool output.
|
|
85
|
+
|
|
86
|
+
### V2 OpenCode adapter
|
|
87
|
+
|
|
88
|
+
This section describes the `v2` branch integration. On `dev`, CodeMode is integrated through
|
|
89
|
+
`packages/opencode/src/tool/code-mode.ts`, where nested MCP calls run the `tool.execute.before` and
|
|
90
|
+
`tool.execute.after` plugin hooks.
|
|
91
|
+
|
|
92
|
+
CodeMode is integrated into V2 through `packages/core/src/tool/registry.ts` and
|
|
93
|
+
`packages/core/src/tool/execute.ts`:
|
|
94
|
+
|
|
95
|
+
- Core has one canonical `Tool` representation. Location-scoped producers register direct or deferred tools through
|
|
96
|
+
`Tools.Service`.
|
|
97
|
+
- Each model step snapshots effective registrations, applies catalog visibility filtering, and exposes direct tools
|
|
98
|
+
normally.
|
|
99
|
+
- When visible deferred tools exist, Core reserves and materializes one `execute` tool. Grouped deferred tools become
|
|
100
|
+
CodeMode namespaces instead of flattened model-facing names.
|
|
101
|
+
- Each nested call checks that its captured registration is still current before dispatching it.
|
|
102
|
+
- Authorization and side-effect ordering remain responsibilities of the leaf tool. Catalog visibility is not execution
|
|
103
|
+
authorization.
|
|
104
|
+
- Structured child output enters the interpreter. File parts are collected host-side and attached to the outer result.
|
|
105
|
+
- Nested call statuses are returned as final `execute` metadata for the TUI.
|
|
106
|
+
- `execute` is the one model-facing tool invocation. Nested calls reuse its invocation context and do not independently
|
|
107
|
+
run registry hooks or model-output bounding; this keeps complete intermediate structured values available for
|
|
108
|
+
in-program filtering. The outer `execute` settlement is the single model-output bounding boundary.
|
|
109
|
+
- Core supplies no CodeMode timeout or tool-call limit. User cancellation interrupts the outer invocation and its
|
|
110
|
+
supervised children; the outer settlement applies Core's normal output-retention policy.
|
|
111
|
+
|
|
112
|
+
MCP tools use this canonical path: they register as grouped tools and are deferred while CodeMode is enabled. Existing
|
|
113
|
+
output schemas are preserved in generated signatures. Direct Core tools remain direct and are not ambient globals
|
|
114
|
+
inside CodeMode.
|
|
115
|
+
|
|
116
|
+
## Intentionally Unsupported
|
|
117
|
+
|
|
118
|
+
These are product boundaries rather than DSL backlog:
|
|
119
|
+
|
|
120
|
+
- Ambient filesystem, process, environment, network, credential, or application access. External work must go through
|
|
121
|
+
supplied tools.
|
|
122
|
+
- Modules, imports, dynamic imports, `eval`, arbitrary host globals, npm packages, and prototype mutation.
|
|
123
|
+
- Generic permission prompts, authorization policy, durable pause/resume, replay, storage, or exactly-once external
|
|
124
|
+
side effects. Hosts and tools own those concerns.
|
|
125
|
+
- Heuristic parsing of text tool results as JSON. A result should not silently change type based on its contents.
|
|
126
|
+
|
|
127
|
+
The OpenAPI adapter may gain more transports and encodings, but it must continue skipping operations it cannot
|
|
128
|
+
represent accurately rather than guessing semantics.
|
|
129
|
+
|
|
130
|
+
## Decisions and Rationale
|
|
131
|
+
|
|
132
|
+
| Decision | Rationale |
|
|
133
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
134
|
+
| Keep an owned tree-walking interpreter. | The product need is bounded tool orchestration, not arbitrary JavaScript. Owning the language surface keeps authority and behavior explicit. |
|
|
135
|
+
| Treat schemas as the model-facing interface. | Signatures drive correct calls; Effect Schema also provides the runtime validation boundary, while JSON Schema supports adapter interoperability. |
|
|
136
|
+
| Keep authority host-owned. | CodeMode can only confine programs to supplied tools. The host chooses those tools, and each tool enforces its own authorization and side-effect policy. |
|
|
137
|
+
| Use progressive catalog disclosure plus search. | Large tool sets should not consume the prompt, but every namespace must remain discoverable and speculative search calls should remain valid. |
|
|
138
|
+
| Start tool promises eagerly and supervise them. | This preserves normal call-time parallelism while giving each call run-once settlement and interruption safety. |
|
|
139
|
+
| Keep files outside the sandbox value space. | Models should compose structured data without routing binary payloads through generated code or context. |
|
|
140
|
+
| Treat `execute` as the model-facing invocation boundary. | Nested calls are implementation details of one orchestration program. Reusing the outer context and bounding only the final result preserves complete intermediate data without inventing durable child-call identities. |
|
|
141
|
+
| Return expected failures as data. | Models need actionable diagnostics without exposing private host causes; host interruption and defects must still propagate correctly. |
|
|
142
|
+
| Leave execution-limit defaults to hosts. | Appropriate budgets depend on the surrounding product and its own cancellation, retention, and output-bounding policies. |
|
|
143
|
+
| Skip unsupported OpenAPI operations. | Incorrect parameter encoding, authentication, or transport behavior is worse than a precise `skipped` reason. |
|
|
144
|
+
|
|
145
|
+
## Remaining Work
|
|
146
|
+
|
|
147
|
+
Keep only material unresolved work here. Small isolated defects should be GitHub issues; adapter-only work belongs in
|
|
148
|
+
the adapter TODO. Delete entries when completed.
|
|
149
|
+
|
|
150
|
+
### DSL expansion
|
|
151
|
+
|
|
152
|
+
The supported JavaScript subset should grow when common model-generated code improves tool orchestration. These are
|
|
153
|
+
current omissions to implement, not intentional product boundaries.
|
|
154
|
+
|
|
155
|
+
- [ ] Design proper multi-stage promise pipelines. Supporting `.then`, `.catch`, and `.finally` should preserve promise
|
|
156
|
+
assimilation, cancellation, failure handling, and concurrent per-item pipelines rather than adding syntax-only
|
|
157
|
+
shims. Consider `Promise.any` in the same pass.
|
|
158
|
+
- [ ] Support async iteration and `for await...of`. Define behavior first for the runtime's supported promise and
|
|
159
|
+
collection values, then extend it to bounded host streams when a stream boundary exists.
|
|
160
|
+
- [ ] Support callback-bearing standard-library variants that models commonly generate: the mapper argument to
|
|
161
|
+
`Array.from(...)` and replacers for `JSON.stringify(...)`, including Effect-aware callbacks where needed.
|
|
162
|
+
- [ ] Close basic `Object` parity gaps: let `Object.values`/`Object.entries` accept arrays, make `Object.assign` validate
|
|
163
|
+
and mutate its target, add `Object.is`, and let `Object.fromEntries` consume every supported iterable.
|
|
164
|
+
- [ ] Add deterministic modern collection conveniences where they improve orchestration: `Object.groupBy`, Set
|
|
165
|
+
composition methods, and `Array.prototype.toSpliced`.
|
|
166
|
+
- [ ] Complete the deterministic `Math` surface beyond the current arithmetic, rounding, root, power, and logarithm
|
|
167
|
+
helpers. Decide separately whether nondeterministic `Math.random` belongs in the runtime.
|
|
168
|
+
- [ ] Refine diagnostics so user throws, expected tool failures, unexpected host/tool defects, and genuine interpreter
|
|
169
|
+
defects are distinguishable without leaking private causes.
|
|
170
|
+
|
|
171
|
+
### Tool and result contracts
|
|
172
|
+
|
|
173
|
+
- [ ] Design explicit tagged representations and size rules before allowing Blob, File, ArrayBuffer, typed arrays, or
|
|
174
|
+
host streams to cross the sandbox boundary.
|
|
175
|
+
- [ ] Define one consistent policy for tool path segments named `__proto__`, `constructor`, or `prototype`. They must
|
|
176
|
+
either be safely callable, rejected before catalog generation, or use one documented escaping rule.
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "@neurocode-ai/codemode",
|
|
4
|
+
"version": "1.18.8",
|
|
5
|
+
"description": "Effect-native confined code execution over schema-described tools",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.ts"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"typecheck": "tsgo --noEmit",
|
|
13
|
+
"test": "bun test"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"acorn": "8.15.0",
|
|
17
|
+
"effect": "4.0.0-beta.83",
|
|
18
|
+
"typescript": "5.8.2"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@tsconfig/bun": "1.0.9",
|
|
22
|
+
"@types/bun": "1.3.13",
|
|
23
|
+
"@typescript/native-preview": "7.0.0-dev.20251207.1"
|
|
24
|
+
}
|
|
25
|
+
}
|