@amends-dev/codemode-opencode 0.0.0-stage → 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sarib Mahmood
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,52 @@
1
+ /**
2
+ * opencode's code mode as the runtime of `@amends-dev/toolkit`'s code mode. `@opencode/codemode`
3
+ * interprets the code a model writes, a subset of JavaScript that it implements itself, so the
4
+ * code can reach nothing it does not implement: no engine, worker, or serialization stands between
5
+ * the code and the tools, and what the code is given is only `tools`, `console`, and `search`. `make`
6
+ * gives the `CodeRuntime` that `CodeMode.toolkit` takes.
7
+ *
8
+ * @since 0.1.0
9
+ */
10
+ import { CodeMode } from "@amends-dev/toolkit";
11
+ /**
12
+ * What `make` takes.
13
+ *
14
+ * @category options
15
+ * @since 0.1.0
16
+ */
17
+ export interface Options {
18
+ /**
19
+ * The most calls of tools one run may make, which the calls of `search`, the function the
20
+ * interpreter gives the code to find tools by, count in. Unlimited by default.
21
+ */
22
+ readonly maxToolCalls?: number;
23
+ }
24
+ /**
25
+ * Makes the runtime. A run interprets the code with the tools of its execution as `tools`, each
26
+ * called as `await tools.<name>(input)`, and gives back what the code returned, as JSON, with what
27
+ * it logged. A failure of a tool reaches the code as an error whose message names the reason and
28
+ * what happened. A run that does not finish fails with `CodeError`: what the code threw, code the
29
+ * interpreter does not run, a call of a tool the run does not have, or more calls than allowed,
30
+ * each with where in the code when the interpreter knows.
31
+ *
32
+ * **Example** (Code mode over opencode's interpreter)
33
+ *
34
+ * ```ts
35
+ * import { OpencodeCodeRuntime } from "@amends-dev/codemode-opencode"
36
+ * import { Assembly, CodeMode } from "@amends-dev/toolkit"
37
+ * import { Effect } from "effect"
38
+ *
39
+ * declare const members: ReadonlyArray<Assembly.Member>
40
+ *
41
+ * const program = Effect.gen(function* () {
42
+ * const assembled = yield* Assembly.toolkit(members, () => "allow")
43
+ * // The model is shown `execute`, whose code calls the assembled tools on this runtime.
44
+ * return yield* CodeMode.toolkit(assembled, OpencodeCodeRuntime.make({ maxToolCalls: 50 }))
45
+ * })
46
+ * ```
47
+ *
48
+ * @category constructors
49
+ * @since 0.1.0
50
+ */
51
+ export declare const make: (options?: Options) => CodeMode.CodeRuntime;
52
+ //# sourceMappingURL=OpencodeCodeRuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpencodeCodeRuntime.d.ts","sourceRoot":"","sources":["../src/OpencodeCodeRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAK9C;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,IAAI,aAAa,OAAO,KAAQ,QAAQ,CAAC,WAQpD,CAAA"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * opencode's code mode as the runtime of `@amends-dev/toolkit`'s code mode. `@opencode/codemode`
3
+ * interprets the code a model writes, a subset of JavaScript that it implements itself, so the
4
+ * code can reach nothing it does not implement: no engine, worker, or serialization stands between
5
+ * the code and the tools, and what the code is given is only `tools`, `console`, and `search`. `make`
6
+ * gives the `CodeRuntime` that `CodeMode.toolkit` takes.
7
+ *
8
+ * @since 0.1.0
9
+ */
10
+ import { CodeMode } from "@amends-dev/toolkit";
11
+ import * as Codemode from "@opencode/codemode";
12
+ import * as Effect from "effect/Effect";
13
+ /**
14
+ * Makes the runtime. A run interprets the code with the tools of its execution as `tools`, each
15
+ * called as `await tools.<name>(input)`, and gives back what the code returned, as JSON, with what
16
+ * it logged. A failure of a tool reaches the code as an error whose message names the reason and
17
+ * what happened. A run that does not finish fails with `CodeError`: what the code threw, code the
18
+ * interpreter does not run, a call of a tool the run does not have, or more calls than allowed,
19
+ * each with where in the code when the interpreter knows.
20
+ *
21
+ * **Example** (Code mode over opencode's interpreter)
22
+ *
23
+ * ```ts
24
+ * import { OpencodeCodeRuntime } from "@amends-dev/codemode-opencode"
25
+ * import { Assembly, CodeMode } from "@amends-dev/toolkit"
26
+ * import { Effect } from "effect"
27
+ *
28
+ * declare const members: ReadonlyArray<Assembly.Member>
29
+ *
30
+ * const program = Effect.gen(function* () {
31
+ * const assembled = yield* Assembly.toolkit(members, () => "allow")
32
+ * // The model is shown `execute`, whose code calls the assembled tools on this runtime.
33
+ * return yield* CodeMode.toolkit(assembled, OpencodeCodeRuntime.make({ maxToolCalls: 50 }))
34
+ * })
35
+ * ```
36
+ *
37
+ * @category constructors
38
+ * @since 0.1.0
39
+ */
40
+ export const make = (options = {}) => ({
41
+ run: ({ code, tools, call }) => {
42
+ const runtime = Codemode.CodeMode.make({
43
+ tools: Object.fromEntries(tools.map((name) => [name, toolOf(name, call)])),
44
+ limits: options.maxToolCalls === undefined ? {} : { maxToolCalls: options.maxToolCalls }
45
+ });
46
+ return Effect.flatMap(runtime.execute(code), outcomeOf);
47
+ }
48
+ });
49
+ /** The tool `name` of the execution as the interpreter calls it: an object in, JSON out. */
50
+ const toolOf = (name, call) => Codemode.Tool.make({
51
+ // What the model reads of the tools is `execute`'s own description, so the interpreter's
52
+ // `search` tells the code only their names.
53
+ description: "",
54
+ // The execution checks the input against the tool's parameters; the interpreter checks nothing,
55
+ // so what the code gave arrives as it is, an object or not.
56
+ input: { type: "object" },
57
+ output: {},
58
+ execute: (input) => call(name, input).pipe(
59
+ // The code reads a failure as an error whose message names the reason and what happened.
60
+ Effect.mapError((failure) => Codemode.toolError(`${failure.code}: ${failure.message}`)))
61
+ });
62
+ /** What a run gave back, as the interpreter reports it: the value and the logs, or why it did not finish. */
63
+ const outcomeOf = (result) => {
64
+ const logs = result.logs ?? [];
65
+ if (!result.ok)
66
+ return Effect.fail(new CodeMode.CodeError({ description: describe(result.error), logs }));
67
+ // A rejection the code did not await, and work it left running when it returned, are warnings,
68
+ // which the model reads after the logs.
69
+ const warnings = (result.warnings ?? []).map((warning) => `Warning: ${warning.message}`);
70
+ return Effect.succeed({ result: result.value, logs: [...logs, ...warnings] });
71
+ };
72
+ /** A diagnostic on one line: its kind, where in the code, what happened, and what to do about it. */
73
+ const describe = ({ kind, message, location, suggestions }) => [
74
+ location === undefined
75
+ ? `${kind}: ${message}`
76
+ : `${kind} at line ${location.line}, column ${location.column}: ${message}`,
77
+ ...(suggestions ?? [])
78
+ ].join(" ");
79
+ //# sourceMappingURL=OpencodeCodeRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpencodeCodeRuntime.js","sourceRoot":"","sources":["../src/OpencodeCodeRuntime.ts"],"names":[],"mappings":"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"}
package/README.md CHANGED
@@ -1,3 +1,79 @@
1
- # Temporary Holding Version
1
+ # @amends-dev/codemode-opencode
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ opencode's code mode as the runtime of [`@amends-dev/toolkit`](../toolkit)'s code mode.
4
+ Code mode presents a toolkit to a model as one tool, `execute`, that runs code the model writes, and
5
+ leaves running the code to a `CodeRuntime`. This runtime is
6
+ [`@opencode/codemode`](https://github.com/anomalyco/opencode/tree/dev/packages/codemode), an
7
+ interpreter of a subset of JavaScript that opencode wrote for the purpose: it implements the
8
+ language itself, so the code can reach nothing the interpreter does not give it, and no engine,
9
+ worker, or serialization stands between the code and the tools. The package depends on Effect,
10
+ the toolkit, and `@opencode/codemode`.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ bun add @amends-dev/codemode-opencode @amends-dev/toolkit effect
16
+ ```
17
+
18
+ `@opencode/codemode` names `effect` as a dependency rather than a peer, so a project on another
19
+ Effect 4.0 release gets a second copy of Effect under it, which its types then disagree with. Until
20
+ [the change that makes it a peer](https://github.com/anomalyco/opencode/compare/dev...saribmah:opencode:codemode-effect-peer)
21
+ is published, dedupe it in your manifest:
22
+
23
+ ```json
24
+ { "overrides": { "effect": "4.0.5" } }
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ `OpencodeCodeRuntime.make` gives the runtime, and `CodeMode.toolkit` presents a toolkit over it.
30
+
31
+ ```ts
32
+ import { OpencodeCodeRuntime } from "@amends-dev/codemode-opencode"
33
+ import { Assembly, CodeMode } from "@amends-dev/toolkit"
34
+ import { Effect } from "effect"
35
+
36
+ declare const members: ReadonlyArray<Assembly.Member> // a workspace's sources, from the toolkit
37
+
38
+ const program = Effect.gen(function* () {
39
+ const assembled = yield* Assembly.toolkit(members, () => "allow")
40
+ // The model is shown `execute`, whose code calls the assembled tools on this runtime.
41
+ return yield* CodeMode.toolkit(assembled, OpencodeCodeRuntime.make({ maxToolCalls: 50 }))
42
+ })
43
+ ```
44
+
45
+ The toolkit's [code mode](../toolkit/README.md#code-mode) says what the model is shown and what a
46
+ run gives back; a [harness](https://github.com/amends-dev/harness/tree/main/packages/harness#host-tools)
47
+ session runs `execute` as it runs any host tool.
48
+
49
+ ## What the code can do
50
+
51
+ The code is the body of an async function in the JavaScript the interpreter implements, which its
52
+ [support checklist](https://github.com/anomalyco/opencode/blob/dev/packages/codemode/interpreter-support.md)
53
+ lists: the usual statements, functions, promises, and built-ins such as `JSON`, `Math`, `Date`,
54
+ `Map`, `Set`, `RegExp`, and `URL`. It has no modules, imports, timers, `process`, or file system,
55
+ and nothing of the host but what the runtime gives it:
56
+
57
+ - `tools.<name>(input)`, one function for each tool of the run, which returns a promise of the
58
+ tool's answer as JSON. The input crosses as `JSON.stringify` would write it, and the answer as
59
+ `JSON.parse` would read it; nothing is shared. The execution checks the input against the tool's
60
+ parameters, and a failure reaches the code as an error whose message names the reason and what
61
+ happened, `CallFailedError: No such repository`, which the code can catch.
62
+ - `console.log`, whose lines come back as the run's logs.
63
+ - `search({ query })`, which finds the run's tools by name, counted as a call of a tool.
64
+
65
+ What the code returns comes back as JSON, `null` when it returns nothing, with the logs. A
66
+ rejection the code did not await, and work it left running when it returned, are interrupted and
67
+ come back as warnings after the logs. A run fails with the toolkit's `CodeError` when the code
68
+ throws, cannot be parsed or uses syntax the interpreter does not run, calls a tool the run does not
69
+ have, or makes more calls than `maxToolCalls` allows, saying where in the code when the interpreter
70
+ knows. The toolkit's own `timeout` interrupts a run, a busy loop included, since the interpreter
71
+ yields between its steps; one step is bounded in what it may build, so no built-in runs away.
72
+
73
+ ## Development
74
+
75
+ Run the package's tests with `bun run test packages/codemode-opencode`: the runtime over fake
76
+ tools, and `execute` as `CodeMode.toolkit` presents it.
77
+ `bun run dist check packages/codemode-opencode` builds the package as npm gets it and checks it as
78
+ a consumer. In this repository, the root manifest's
79
+ `overrides` keeps `@opencode/codemode` on the catalog's Effect.
package/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * opencode's code mode as the runtime of `@amends-dev/toolkit`'s code mode: the code a model writes
3
+ * runs on an interpreter that reaches nothing but the tools it is given.
4
+ *
5
+ * @since 0.1.0
6
+ */
7
+ /**
8
+ * @since 0.1.0
9
+ */
10
+ export * as OpencodeCodeRuntime from "./OpencodeCodeRuntime.ts";
11
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAA"}
package/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * opencode's code mode as the runtime of `@amends-dev/toolkit`'s code mode: the code a model writes
3
+ * runs on an interpreter that reaches nothing but the tools it is given.
4
+ *
5
+ * @since 0.1.0
6
+ */
7
+ // @barrel: Auto-generated exports. Do not edit manually.
8
+ /**
9
+ * @since 0.1.0
10
+ */
11
+ export * as OpencodeCodeRuntime from "./OpencodeCodeRuntime.js";
12
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yDAAyD;AAEzD;;GAEG;AACH,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "@amends-dev/codemode-opencode",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "opencode's code mode as the runtime of @amends-dev/toolkit's code mode: the code a model writes runs on an interpreter that reaches nothing but the tools it is given.",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/amends-dev/toolkit/tree/main/packages/codemode-opencode#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/amends-dev/toolkit.git",
10
+ "directory": "packages/codemode-opencode"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/amends-dev/toolkit/issues"
14
+ },
15
+ "keywords": [
16
+ "agent",
17
+ "code mode",
18
+ "effect",
19
+ "opencode",
20
+ "tools"
21
+ ],
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "exports": {
25
+ ".": "./index.js",
26
+ "./*": "./*.js"
27
+ },
28
+ "dependencies": {
29
+ "@amends-dev/toolkit": "^0.2.1",
30
+ "@opencode/codemode": "^2.0.32"
31
+ },
32
+ "peerDependencies": {
33
+ "effect": "^4.0.5"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
38
+ }