@mousedev/harness-opencode 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pete McGrath and Mouse contributors
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.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @mousedev/harness-opencode
2
+
3
+ The OpenCode engine for the Mouse harness. It owns everything Mouse says to OpenCode: the agent configuration (one OpenCode agent per mode, each with its own permission block and Mouse's prompt), the `local` and `bench` config profiles, the `opencode run --format=json` transport that implements core's `Engine` interface, the compat manifest that records which OpenCode versions Mouse has been run against, the tool-output prune plugin, and the logic that locates the `opencode` binary. It depends on `@mousedev/harness-core` and on `@opencode-ai/sdk` (pinned to 1.14.22) for the `Config` type.
4
+
5
+ This is the package the hosted product consumes as well as the CLI, so its output is pinned byte for byte by `test/golden.test.ts`: the same profile, permission mode, and model must produce the same config, and the same round kind the same continue prompt. Never update that snapshot to make a refactor pass. Built on OpenCode; Mouse is an independent project and is not affiliated with or endorsed by the OpenCode project or Anomaly.
6
+
7
+ ## Main API
8
+
9
+ ```ts
10
+ import {
11
+ buildOpencodeConfig, mergeConfig, MODE_PERMISSIONS, DISABLED_TOOLS,
12
+ OpencodeRunEngine, locateOpencode, opencodeVersion,
13
+ loadCompatManifest, checkCompat, parseOpencodeVersion,
14
+ prunePluginSource, PRUNE_PLUGIN_FILENAME, versionLine,
15
+ } from "@mousedev/harness-opencode";
16
+ ```
17
+
18
+ - `buildOpencodeConfig({ profile, model, permissions, permissionMode?, smallModel?, extraModes? })` returns the OpenCode `Config`. `local` (what `mouse run` sends through `OPENCODE_CONFIG_CONTENT`) carries the agents, Mouse's prompt, the tools-off list, compaction, and cache keys for the model's provider. `bench` is the complete file the benchmark writes: the same plus `$schema`, `autoupdate: false`, `share: "disabled"`, `small_model` set to the run model, and OpenRouter pinned to Fireworks. `product` is the delta a host pushes (agents and tools only). Only the `bash` deny patterns from `permissions` cross into the config; [docs/policy.md](../../docs/policy.md) explains why.
19
+ - `mergeConfig(base, over)` deep-merges objects (arrays and scalars replace), used to layer the bench config over a runner's existing `opencode.json`.
20
+ - `new OpencodeRunEngine({ cwd, model, bin?, agent?, env?, idleTimeoutMs?, maxAttempts?, skipPermissions?, passthrough?, passthroughErr?, trace?, extraArgs? })` spawns one `opencode run` per `prompt()` call, continues the session with `--session`, passes every stdout line through unchanged, counts steps and tokens, retries turns that produced no steps with backoff, and kills a turn that goes idle. `argsFor(prompt)` shows the argv.
21
+ - `locateOpencode({ flag?, env?, cwd? })` resolves the binary (flag, `MOUSE_OPENCODE_BIN`, `PATH`, nearest `node_modules/.bin/opencode`); `opencodeVersion(bin)` runs `--version`.
22
+ - `checkCompat(versionOutput)` returns `supported`, `untested`, or `unknown` against `compat/opencode-compat.json` ([docs/opencode-compat.md](../../docs/opencode-compat.md)).
23
+ - `prunePluginSource(policy.context.prune)` is the plugin source the CLI writes when pruning is enabled.
24
+ - `versionLine(opencodeVersion)` is `mouse/<version> opencode/<version>`, the line benchmark runners record.
25
+
26
+ Requires Node 22 or newer. MIT.
@@ -0,0 +1,19 @@
1
+ {
2
+ "$comment": "OpenCode versions the Mouse harness has been run against. `supported` versions are exercised in CI (mouse doctor + mouse config --profile bench); `tested` carries what was verified on that version. Anything else prints a warning from `mouse doctor` and fails `--strict-compat`.",
3
+ "supported": ["1.18.27", "1.14.22"],
4
+ "tested": {
5
+ "1.18.27": {
6
+ "benchmark": "FrontierHarness Eval, Kimi K3 via Fireworks, under the benchmark's unmodified run-trials.sh on golden checkpoint fh-golden-mouse-v3",
7
+ "result": "25/30 (datacurve 8/9, terminal-bench 17/21), run 2026-09-08-mouse-c, harness commit 315e2b8, all 30 trials valid; an earlier self-run through Harbor on 2026-09-03 scored 24/30",
8
+ "notes": "The version both runs used: the adapter installed opencode-ai@latest, which was 1.18.27 on 2026-09-03, and the adapters have pinned it since."
9
+ },
10
+ "1.14.22": {
11
+ "benchmark": "none",
12
+ "result": "CI compat job only: mouse doctor --strict-compat and a bench config write succeed",
13
+ "notes": "The version the hosted product's SDK pins. No benchmark run on it."
14
+ }
15
+ },
16
+ "notes": {
17
+ "1.18.19": "FrontierHarness pinned 1.18.19 for its stock OpenCode control. Not run; 1.18.27 is the same minor line."
18
+ }
19
+ }
@@ -0,0 +1,26 @@
1
+ export interface CompatManifest {
2
+ supported: string[];
3
+ tested: Record<string, {
4
+ benchmark: string;
5
+ result: string;
6
+ notes?: string;
7
+ }>;
8
+ notes?: Record<string, string>;
9
+ }
10
+ /** The manifest ships inside the package, so the bundled CLI carries it too. */
11
+ export declare function loadCompatManifest(): CompatManifest;
12
+ export type CompatVerdict = {
13
+ level: "supported";
14
+ version: string;
15
+ } | {
16
+ level: "untested";
17
+ version: string;
18
+ supported: string[];
19
+ } | {
20
+ level: "unknown";
21
+ version: null;
22
+ };
23
+ /** `opencode --version` prints a bare semver; some builds prefix it. */
24
+ export declare function parseOpencodeVersion(output: string): string | null;
25
+ export declare function checkCompat(versionOutput: string | null, manifest?: CompatManifest): CompatVerdict;
26
+ //# sourceMappingURL=compat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,IAAI,cAAc,CAEnD;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAC3D;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAExC,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGlE;AAED,wBAAgB,WAAW,CACzB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,QAAQ,iBAAuB,GAC9B,aAAa,CAKf"}
package/dist/compat.js ADDED
@@ -0,0 +1,19 @@
1
+ import manifest from "./compat/opencode-compat.json" with { type: "json" };
2
+ /** The manifest ships inside the package, so the bundled CLI carries it too. */
3
+ export function loadCompatManifest() {
4
+ return manifest;
5
+ }
6
+ /** `opencode --version` prints a bare semver; some builds prefix it. */
7
+ export function parseOpencodeVersion(output) {
8
+ const m = /(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/.exec(output);
9
+ return m?.[1] ?? null;
10
+ }
11
+ export function checkCompat(versionOutput, manifest = loadCompatManifest()) {
12
+ const version = versionOutput ? parseOpencodeVersion(versionOutput) : null;
13
+ if (!version)
14
+ return { level: "unknown", version: null };
15
+ if (manifest.supported.includes(version))
16
+ return { level: "supported", version };
17
+ return { level: "untested", version, supported: manifest.supported };
18
+ }
19
+ //# sourceMappingURL=compat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compat.js","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,+BAA+B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAQ3E,gFAAgF;AAChF,MAAM,UAAU,kBAAkB;IAChC,OAAO,QAA0B,CAAC;AACpC,CAAC;AAOD,wEAAwE;AACxE,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,CAAC,GAAG,qCAAqC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,aAA4B,EAC5B,QAAQ,GAAG,kBAAkB,EAAE;IAE/B,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,oBAAoB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3E,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACzD,IAAI,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IACjF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC;AACvE,CAAC"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Mouse's OpenCode engine config.
3
+ *
4
+ * Mouse owns the engine configuration; the user owns the repo. Every mode is
5
+ * an OpenCode agent carrying its own `permission` block, and agent-level
6
+ * permission overrides global, so a read-only mode cannot edit a file even
7
+ * if the model ignores the prompt.
8
+ *
9
+ * Profiles:
10
+ * - `product`: the per-turn `config.update` delta a host pushes. Agents and
11
+ * the tools-off list only.
12
+ * - `bench`: the complete file the headless harness writes: Mouse's prompt,
13
+ * compaction, cache keys, the run model as `small_model`, autoupdate off.
14
+ * Frozen; a byte change here is a benchmark change.
15
+ * - `local`: what `mouse run` hands OpenCode through `OPENCODE_CONFIG_CONTENT`
16
+ * so nothing under `~/.config/opencode` is written. Same agents, prompt,
17
+ * tools-off list, and compaction as bench; no OpenRouter pin, no
18
+ * `small_model` override.
19
+ */
20
+ import { type Mode, type PermissionAction, type PermissionsPolicy, type Profile } from "@mousedev/harness-core";
21
+ import type { Config } from "@opencode-ai/sdk";
22
+ /**
23
+ * Providers whose OpenCode adapter takes a `setCacheKey` option. A provider
24
+ * added for a benchmark gets cache keys everywhere.
25
+ */
26
+ export declare const SET_CACHE_KEY_PROVIDER_IDS: readonly ["anthropic", "openrouter", "fireworks-ai"];
27
+ /**
28
+ * Providers whose API rejects the cache key outright when they serve the run.
29
+ * Fireworks' OpenAI-compatible endpoint answers 400 "Extra inputs are not
30
+ * permitted, field: 'promptCacheKey'" to every request that carries it (seen
31
+ * on the 2026-09-07 Runta run), and it caches prefixes on its own. The entry
32
+ * stays in the list for runs on other providers, where it is inert, so those
33
+ * configs keep the bytes the 2026-09-03 run used.
34
+ */
35
+ export declare const NO_CACHE_KEY_WHEN_SERVING: readonly ["fireworks-ai"];
36
+ export declare function providerCacheOptions(extra?: readonly string[]): Record<string, {
37
+ options: {
38
+ setCacheKey: boolean;
39
+ };
40
+ }>;
41
+ /** Cache options for a run served by `modelProvider`, minus any entry that provider rejects. */
42
+ export declare function runProviderCacheOptions(modelProvider: string | undefined): Record<string, {
43
+ options: Record<string, unknown>;
44
+ }>;
45
+ /** Bench compaction: auto at the window, prune stale tool outputs, 10k reserve. */
46
+ export declare const BENCH_COMPACTION: {
47
+ readonly auto: true;
48
+ readonly prune: true;
49
+ readonly reserved: 10000;
50
+ };
51
+ /**
52
+ * OpenRouter request body pinning Kimi K3 to the backend FrontierHarness used.
53
+ * An implicit prefix cache lives on one backend; a session that hops
54
+ * providers between calls pays full price on every hop.
55
+ */
56
+ export declare const OPENROUTER_PIN_FIREWORKS: {
57
+ readonly provider: {
58
+ readonly order: readonly ["Fireworks"];
59
+ readonly allow_fallbacks: false;
60
+ };
61
+ };
62
+ export interface ModePermission {
63
+ edit: PermissionAction;
64
+ bash: PermissionAction;
65
+ }
66
+ /**
67
+ * Per-mode posture. `plan` keeps bash at `ask` because that is what
68
+ * OpenCode's built-in plan agent does; plan-mode research legitimately
69
+ * reaches for `git log`. `debug` may run shell freely but asks before edits.
70
+ */
71
+ export declare const MODE_PERMISSIONS: Record<"ask" | "plan" | "debug" | "build", ModePermission>;
72
+ /**
73
+ * OpenCode built-ins Mouse turns off. A host replaces them with metered,
74
+ * event-emitting equivalents; the CLI keeps the run to the repo and the
75
+ * model. Names a given OpenCode version does not define are ignored.
76
+ */
77
+ export declare const DISABLED_TOOLS: readonly ["webfetch", "websearch", "task", "skill"];
78
+ export interface OpencodeConfigInput {
79
+ /** `.mouse/policy.json -> permissions`, already parsed. */
80
+ permissions?: PermissionsPolicy;
81
+ /**
82
+ * Effective permission mode. Only `bypass` widens anything, and only where
83
+ * the mode already allowed writes.
84
+ */
85
+ permissionMode?: "interactive" | "auto" | "bypass";
86
+ profile?: Profile;
87
+ /** `provider/model` the run uses. Bench also makes it `small_model`. */
88
+ model?: string;
89
+ /** Model for cheap internal calls (titles). Ignored by `bench`, which uses `model`. */
90
+ smallModel?: string;
91
+ /**
92
+ * Host-defined modes appended after the built-in four, in this order.
93
+ * Prompts come from `buildAgentPrompt`, so a host mode named `overnight`
94
+ * gets build's prompt.
95
+ */
96
+ extraModes?: Record<string, ModePermission>;
97
+ }
98
+ /** The OpenCode agent name Mouse selects for a mode. */
99
+ export declare function agentNameForMode(mode: Mode): string;
100
+ export declare function buildOpencodeConfig(input: OpencodeConfigInput): Config;
101
+ /**
102
+ * Deep-merge `over` into `base`, objects only; arrays and scalars replace.
103
+ * Used to layer Mouse's config over whatever a runner already registered.
104
+ */
105
+ export declare function mergeConfig(base: Record<string, unknown>, over: Record<string, unknown>): Record<string, unknown>;
106
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAEL,KAAK,IAAI,EACT,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,OAAO,EAEb,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,0BAA0B,YAAI,WAAW,EAAE,YAAY,EAAE,cAAc,CAAU,CAAC;AAE/F;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,YAAI,cAAc,CAAU,CAAC;AAEnE,wBAAgB,oBAAoB,CAClC,KAAK,GAAE,SAAS,MAAM,EAAO,GAC5B,MAAM,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAAC,CAMvD;AAED,gGAAgG;AAChG,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC,MAAM,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAQtD;AAED,mFAAmF;AACnF,eAAO,MAAM,gBAAgB;aAAK,IAAI;aAAQ,KAAK;aAAQ,QAAQ,EAAE,KAAM;CAAW,CAAC;AAEvF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;aACnC,QAAQ;iBAAI,KAAK,YAAG,WAAW;iBAAG,eAAe;;CACzC,CAAC;AAEX,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,EAAE,cAAc,CAKvF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAAI,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAElF,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC;;;OAGG;IACH,cAAc,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;IACnD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC7C;AA8CD,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEnD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAuDtE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAezB"}
package/dist/config.js ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Mouse's OpenCode engine config.
3
+ *
4
+ * Mouse owns the engine configuration; the user owns the repo. Every mode is
5
+ * an OpenCode agent carrying its own `permission` block, and agent-level
6
+ * permission overrides global, so a read-only mode cannot edit a file even
7
+ * if the model ignores the prompt.
8
+ *
9
+ * Profiles:
10
+ * - `product`: the per-turn `config.update` delta a host pushes. Agents and
11
+ * the tools-off list only.
12
+ * - `bench`: the complete file the headless harness writes: Mouse's prompt,
13
+ * compaction, cache keys, the run model as `small_model`, autoupdate off.
14
+ * Frozen; a byte change here is a benchmark change.
15
+ * - `local`: what `mouse run` hands OpenCode through `OPENCODE_CONFIG_CONTENT`
16
+ * so nothing under `~/.config/opencode` is written. Same agents, prompt,
17
+ * tools-off list, and compaction as bench; no OpenRouter pin, no
18
+ * `small_model` override.
19
+ */
20
+ import { buildAgentPrompt, permissionRulesForTool, } from "@mousedev/harness-core";
21
+ /**
22
+ * Providers whose OpenCode adapter takes a `setCacheKey` option. A provider
23
+ * added for a benchmark gets cache keys everywhere.
24
+ */
25
+ export const SET_CACHE_KEY_PROVIDER_IDS = ["anthropic", "openrouter", "fireworks-ai"];
26
+ /**
27
+ * Providers whose API rejects the cache key outright when they serve the run.
28
+ * Fireworks' OpenAI-compatible endpoint answers 400 "Extra inputs are not
29
+ * permitted, field: 'promptCacheKey'" to every request that carries it (seen
30
+ * on the 2026-09-07 Runta run), and it caches prefixes on its own. The entry
31
+ * stays in the list for runs on other providers, where it is inert, so those
32
+ * configs keep the bytes the 2026-09-03 run used.
33
+ */
34
+ export const NO_CACHE_KEY_WHEN_SERVING = ["fireworks-ai"];
35
+ export function providerCacheOptions(extra = []) {
36
+ const out = {};
37
+ for (const id of [...SET_CACHE_KEY_PROVIDER_IDS, ...extra]) {
38
+ out[id] = { options: { setCacheKey: true } };
39
+ }
40
+ return out;
41
+ }
42
+ /** Cache options for a run served by `modelProvider`, minus any entry that provider rejects. */
43
+ export function runProviderCacheOptions(modelProvider) {
44
+ const provider = providerCacheOptions(modelProvider ? [modelProvider] : []);
45
+ const skip = NO_CACHE_KEY_WHEN_SERVING;
46
+ if (modelProvider && skip.includes(modelProvider))
47
+ delete provider[modelProvider];
48
+ return provider;
49
+ }
50
+ /** Bench compaction: auto at the window, prune stale tool outputs, 10k reserve. */
51
+ export const BENCH_COMPACTION = { auto: true, prune: true, reserved: 10_000 };
52
+ /**
53
+ * OpenRouter request body pinning Kimi K3 to the backend FrontierHarness used.
54
+ * An implicit prefix cache lives on one backend; a session that hops
55
+ * providers between calls pays full price on every hop.
56
+ */
57
+ export const OPENROUTER_PIN_FIREWORKS = {
58
+ provider: { order: ["Fireworks"], allow_fallbacks: false },
59
+ };
60
+ /**
61
+ * Per-mode posture. `plan` keeps bash at `ask` because that is what
62
+ * OpenCode's built-in plan agent does; plan-mode research legitimately
63
+ * reaches for `git log`. `debug` may run shell freely but asks before edits.
64
+ */
65
+ export const MODE_PERMISSIONS = {
66
+ ask: { edit: "deny", bash: "deny" },
67
+ plan: { edit: "deny", bash: "ask" },
68
+ debug: { edit: "ask", bash: "allow" },
69
+ build: { edit: "allow", bash: "allow" },
70
+ };
71
+ /**
72
+ * OpenCode built-ins Mouse turns off. A host replaces them with metered,
73
+ * event-emitting equivalents; the CLI keeps the run to the repo and the
74
+ * model. Names a given OpenCode version does not define are ignored.
75
+ */
76
+ export const DISABLED_TOOLS = ["webfetch", "websearch", "task", "skill"];
77
+ /**
78
+ * Fold the user's `deny` globs for a tool into an OpenCode `permission.bash`
79
+ * object, on top of the mode default.
80
+ *
81
+ * Only `deny` rules cross over, deliberately. Mouse's policy is documented as
82
+ * first-match-wins; OpenCode's pattern matching is last-match-wins. Feeding
83
+ * both allow and deny rules through would silently invert precedence when a
84
+ * user writes overlapping patterns. Denies are safe under either reading.
85
+ */
86
+ function bashPermission(base, policy) {
87
+ const denies = policy
88
+ ? permissionRulesForTool(policy.bash)
89
+ .filter(([, action]) => action === "deny")
90
+ .map(([pattern]) => pattern)
91
+ : [];
92
+ if (denies.length === 0)
93
+ return base;
94
+ // `*` first so it is the weakest rule under last-match-wins.
95
+ const out = { "*": base };
96
+ for (const pattern of denies)
97
+ out[pattern] = "deny";
98
+ return out;
99
+ }
100
+ /** `bypass` only turns an `ask` into `allow`; a read-only mode stays read-only. */
101
+ function editPermission(base, permissionMode) {
102
+ if (permissionMode === "bypass" && base === "ask")
103
+ return "allow";
104
+ return base;
105
+ }
106
+ /**
107
+ * Plan mode's one writable path: the plan file. OpenCode's `permission.edit`
108
+ * accepts a glob map at runtime (last match wins) though the SDK type says
109
+ * scalar, hence the cast.
110
+ */
111
+ const PLAN_EDIT = {
112
+ "*": "deny",
113
+ ".mouse/plans/*.md": "allow",
114
+ };
115
+ /** The OpenCode agent name Mouse selects for a mode. */
116
+ export function agentNameForMode(mode) {
117
+ return mode;
118
+ }
119
+ export function buildOpencodeConfig(input) {
120
+ const profile = input.profile ?? "product";
121
+ const withPrompt = profile !== "product";
122
+ const modes = [
123
+ ...Object.entries(MODE_PERMISSIONS),
124
+ ...Object.entries(input.extraModes ?? {}),
125
+ ];
126
+ const agent = {};
127
+ for (const [mode, posture] of modes) {
128
+ const prompt = withPrompt ? buildAgentPrompt(mode, { profile }) : undefined;
129
+ agent[agentNameForMode(mode)] = {
130
+ mode: "primary",
131
+ ...(prompt ? { prompt } : {}),
132
+ permission: {
133
+ edit: mode === "plan" ? PLAN_EDIT : editPermission(posture.edit, input.permissionMode),
134
+ bash: bashPermission(posture.bash, input.permissions),
135
+ // Native fetch is off; belt-and-braces alongside `tools.webfetch: false`.
136
+ webfetch: "deny",
137
+ // Hosts run their own doom-loop detector; OpenCode's would double-prompt.
138
+ doom_loop: "allow",
139
+ },
140
+ };
141
+ }
142
+ const tools = {};
143
+ for (const name of DISABLED_TOOLS)
144
+ tools[name] = false;
145
+ const config = { agent, tools };
146
+ const smallModel = input.smallModel?.trim();
147
+ if (smallModel)
148
+ config.small_model = smallModel;
149
+ if (profile === "bench") {
150
+ // The bench config is the whole file OpenCode reads, not a delta.
151
+ // `compaction` is in the server schema but not the SDK's Config type.
152
+ const bench = config;
153
+ bench.$schema = "https://opencode.ai/config.json";
154
+ bench.autoupdate = false;
155
+ bench.share = "disabled";
156
+ bench.compaction = { ...BENCH_COMPACTION };
157
+ const modelProvider = input.model?.includes("/") ? input.model.split("/")[0] : undefined;
158
+ const provider = runProviderCacheOptions(modelProvider);
159
+ if (modelProvider === "openrouter") {
160
+ provider.openrouter.options.extraBody = OPENROUTER_PIN_FIREWORKS;
161
+ }
162
+ bench.provider = provider;
163
+ if (input.model)
164
+ bench.small_model = input.model;
165
+ }
166
+ else if (profile === "local") {
167
+ const local = config;
168
+ local.compaction = { ...BENCH_COMPACTION };
169
+ const modelProvider = input.model?.includes("/") ? input.model.split("/")[0] : undefined;
170
+ local.provider = runProviderCacheOptions(modelProvider);
171
+ }
172
+ return config;
173
+ }
174
+ /**
175
+ * Deep-merge `over` into `base`, objects only; arrays and scalars replace.
176
+ * Used to layer Mouse's config over whatever a runner already registered.
177
+ */
178
+ export function mergeConfig(base, over) {
179
+ const out = { ...base };
180
+ for (const [k, v] of Object.entries(over)) {
181
+ const prev = out[k];
182
+ out[k] =
183
+ v &&
184
+ typeof v === "object" &&
185
+ !Array.isArray(v) &&
186
+ prev &&
187
+ typeof prev === "object" &&
188
+ !Array.isArray(prev)
189
+ ? mergeConfig(prev, v)
190
+ : v;
191
+ }
192
+ return out;
193
+ }
194
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from "./compat.js";
2
+ export * from "./config.js";
3
+ export * from "./locate.js";
4
+ export * from "./plugins/prune.js";
5
+ export * from "./run-engine.js";
6
+ export * from "./version.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./compat.js";
2
+ export * from "./config.js";
3
+ export * from "./locate.js";
4
+ export * from "./plugins/prune.js";
5
+ export * from "./run-engine.js";
6
+ export * from "./version.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface LocateOptions {
2
+ flag?: string;
3
+ env?: NodeJS.ProcessEnv;
4
+ cwd?: string;
5
+ /** Defaults to `process.platform`; tests pass `win32` to exercise PATHEXT. */
6
+ platform?: NodeJS.Platform;
7
+ }
8
+ export declare function locateOpencode(o?: LocateOptions): string | null;
9
+ /** `opencode --version` output, or null when the binary cannot run. */
10
+ export declare function opencodeVersion(bin: string, timeoutMs?: number): string | null;
11
+ //# sourceMappingURL=locate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.d.ts","sourceRoot":"","sources":["../src/locate.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B;AAED,wBAAgB,cAAc,CAAC,CAAC,GAAE,aAAkB,GAAG,MAAM,GAAG,IAAI,CAkBnE;AAyBD,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,SAAS,GAAG,MAAM,GAAG,IAAI,CAM9E"}
package/dist/locate.js ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Find the `opencode` binary: explicit flag, then `MOUSE_OPENCODE_BIN`, then
3
+ * PATH, then the nearest `node_modules/.bin/opencode` above the workspace.
4
+ */
5
+ import { execFileSync } from "node:child_process";
6
+ import { existsSync } from "node:fs";
7
+ import path from "node:path";
8
+ export function locateOpencode(o = {}) {
9
+ const env = o.env ?? process.env;
10
+ if (o.flag) {
11
+ // A path must exist; a bare name is looked up on PATH like any other.
12
+ if (o.flag.includes(path.sep))
13
+ return existsSync(o.flag) ? o.flag : null;
14
+ return whichNamed(o.flag, env, o.platform) ?? o.flag;
15
+ }
16
+ if (env.MOUSE_OPENCODE_BIN?.trim())
17
+ return env.MOUSE_OPENCODE_BIN.trim();
18
+ const onPath = whichNamed("opencode", env, o.platform);
19
+ if (onPath)
20
+ return onPath;
21
+ let dir = path.resolve(o.cwd ?? process.cwd());
22
+ for (;;) {
23
+ const candidate = path.join(dir, "node_modules", ".bin", "opencode");
24
+ if (existsSync(candidate))
25
+ return candidate;
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir)
28
+ return null;
29
+ dir = parent;
30
+ }
31
+ }
32
+ /**
33
+ * PATH lookup. On Windows a bare name resolves through PATHEXT (`opencode.cmd`
34
+ * is what npm installs there); elsewhere the name is taken as-is.
35
+ */
36
+ function whichNamed(name, env, platform = process.platform) {
37
+ const suffixes = platform === "win32" && !path.extname(name)
38
+ ? ["", ...(env.PATHEXT ?? ".EXE;.CMD;.BAT").toLowerCase().split(";").filter(Boolean)]
39
+ : [""];
40
+ for (const dir of (env.PATH ?? "").split(path.delimiter)) {
41
+ if (!dir)
42
+ continue;
43
+ for (const ext of suffixes) {
44
+ const candidate = path.join(dir, name + ext);
45
+ if (existsSync(candidate))
46
+ return candidate;
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+ /** `opencode --version` output, or null when the binary cannot run. */
52
+ export function opencodeVersion(bin, timeoutMs = 20_000) {
53
+ try {
54
+ return execFileSync(bin, ["--version"], { encoding: "utf8", timeout: timeoutMs }).trim();
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ //# sourceMappingURL=locate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.js","sourceRoot":"","sources":["../src/locate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Per-result tool-output cap, DSH's pruner shape: anything over the threshold
3
+ * keeps its head and tail. Applied when the result is produced, never by
4
+ * rewriting history, so the cached prefix stays intact. Parameters come from
5
+ * `.mouse/policy.json -> context.prune`.
6
+ */
7
+ import type { PrunePolicy } from "@mousedev/harness-core";
8
+ export declare const PRUNE_PLUGIN_FILENAME = "mouse-prune.mjs";
9
+ export declare function prunePluginSource(p: PrunePolicy): string;
10
+ //# sourceMappingURL=prune.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../../src/plugins/prune.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAexD"}
@@ -0,0 +1,18 @@
1
+ export const PRUNE_PLUGIN_FILENAME = "mouse-prune.mjs";
2
+ export function prunePluginSource(p) {
3
+ return `// Written by the Mouse harness from .mouse/policy.json (context.prune).
4
+ const THRESHOLD = ${p.thresholdChars}, HEAD = ${p.headChars}, TAIL = ${p.tailChars};
5
+ export default {
6
+ name: "mouse-prune",
7
+ events: {
8
+ "tool.execute.after": async (_input, output) => {
9
+ if (!output || typeof output.output !== "string") return;
10
+ const s = output.output;
11
+ if (s.length <= THRESHOLD) return;
12
+ output.output = s.slice(0, HEAD) + "\\n\\n[... " + (s.length - HEAD - TAIL) + " chars omitted by mouse-prune ...]\\n\\n" + s.slice(-TAIL);
13
+ },
14
+ },
15
+ };
16
+ `;
17
+ }
18
+ //# sourceMappingURL=prune.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune.js","sourceRoot":"","sources":["../../src/plugins/prune.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AAEvD,MAAM,UAAU,iBAAiB,CAAC,CAAc;IAC9C,OAAO;oBACW,CAAC,CAAC,cAAc,YAAY,CAAC,CAAC,SAAS,YAAY,CAAC,CAAC,SAAS;;;;;;;;;;;;CAYjF,CAAC;AACF,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { type Engine, type PromptOptions, type PromptResult, type TokenTotals } from "@mousedev/harness-core";
2
+ export interface OpencodeRunEngineOptions {
3
+ bin?: string;
4
+ cwd: string;
5
+ /** `provider/model`, as `opencode --model` expects. */
6
+ model: string;
7
+ agent?: string;
8
+ env?: NodeJS.ProcessEnv;
9
+ idleTimeoutMs?: number;
10
+ maxAttempts?: number;
11
+ /** Pass `--dangerously-skip-permissions`. The run engine has no one to ask, so this defaults on. */
12
+ skipPermissions?: boolean;
13
+ /** Every stdout line, verbatim. */
14
+ passthrough?: (line: string) => void;
15
+ /** Every stderr line. */
16
+ passthroughErr?: (line: string) => void;
17
+ /** Structured engine records (attempts, retries). */
18
+ trace?: (record: Record<string, unknown>) => void;
19
+ /** Extra argv appended before `--` (e.g. `--title`). */
20
+ extraArgs?: string[];
21
+ }
22
+ export declare class OpencodeRunEngine implements Engine {
23
+ private readonly opts;
24
+ sessionId: string | null;
25
+ steps: number;
26
+ lastText: string;
27
+ tokens: TokenTotals;
28
+ private title;
29
+ private child;
30
+ constructor(opts: OpencodeRunEngineOptions);
31
+ /**
32
+ * `opencode run` creates the session on the first prompt, so `open` only
33
+ * records what to name it. Pass `sessionId` to continue an existing one.
34
+ */
35
+ open(opts?: {
36
+ sessionId?: string;
37
+ title?: string;
38
+ }): Promise<string>;
39
+ prompt(_sessionId: string, text: string, signal: AbortSignal, opts?: PromptOptions): Promise<PromptResult>;
40
+ abort(): Promise<void>;
41
+ /** The argv for one turn; exported for tests and for `mouse doctor`. */
42
+ argsFor(prompt: string, model?: string): string[];
43
+ private spawnOnce;
44
+ }
45
+ //# sourceMappingURL=run-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-engine.d.ts","sourceRoot":"","sources":["../src/run-engine.ts"],"names":[],"mappings":"AAiBA,OAAO,EAEL,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,wBAAwB;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oGAAoG;IACpG,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mCAAmC;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,yBAAyB;IACzB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,qDAAqD;IACrD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClD,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAyBD,qBAAa,iBAAkB,YAAW,MAAM;IAQlC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPjC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChC,KAAK,SAAK;IACV,QAAQ,SAAM;IACd,MAAM,EAAE,WAAW,CAAiE;IACpF,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAAyC;IAEtD,YAA6B,IAAI,EAAE,wBAAwB,EAAI;IAE/D;;;OAGG;IACG,IAAI,CAAC,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7E;IAEK,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,IAAI,CAAC,EAAE,aAAa,GACnB,OAAO,CAAC,YAAY,CAAC,CAiCvB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED,wEAAwE;IACxE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAkB,GAAG,MAAM,EAAE,CAgBzD;IAED,OAAO,CAAC,SAAS;CA+FlB"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * `Engine` over the `opencode run --format=json` child process.
3
+ *
4
+ * This is the transport benchmark adapters use for stock OpenCode, so a Mouse
5
+ * run and an OpenCode control run differ only in Mouse's config, prompt, and
6
+ * completion loop. Every JSON line the engine prints is passed through
7
+ * unchanged (Harbor's trajectory parser and `tee` see one continuous log);
8
+ * the engine only reads them to learn the session id, count steps, sum
9
+ * tokens, and collect the final text.
10
+ *
11
+ * Zero-step watchdog: a turn that prints nothing for `idleTimeoutMs`, or dies
12
+ * with a transient provider error before its first step, is killed and
13
+ * re-run on the same session. That is the 0-turn hang FrontierHarness
14
+ * recorded for OpenCode on python-statemachine.
15
+ */
16
+ import { spawn } from "node:child_process";
17
+ import { createInterface } from "node:readline";
18
+ import { classifyInferenceFailure, } from "@mousedev/harness-core";
19
+ const DEFAULT_IDLE_MS = 600_000;
20
+ const DEFAULT_ATTEMPTS = 3;
21
+ const RETRY_BASE_MS = 2_000;
22
+ const RETRY_MAX_MS = 30_000;
23
+ /** Backoff between empty-turn retries: 2 s, 4 s, 8 s, capped at 30 s. */
24
+ function retryDelayMs(attempt) {
25
+ return Math.min(RETRY_MAX_MS, RETRY_BASE_MS * 2 ** (attempt - 1));
26
+ }
27
+ function num(v) {
28
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
29
+ }
30
+ export class OpencodeRunEngine {
31
+ opts;
32
+ sessionId = null;
33
+ steps = 0;
34
+ lastText = "";
35
+ tokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
36
+ title;
37
+ child = null;
38
+ constructor(opts) {
39
+ this.opts = opts;
40
+ }
41
+ /**
42
+ * `opencode run` creates the session on the first prompt, so `open` only
43
+ * records what to name it. Pass `sessionId` to continue an existing one.
44
+ */
45
+ async open(opts = {}) {
46
+ if (opts.sessionId)
47
+ this.sessionId = opts.sessionId;
48
+ this.title = opts.title;
49
+ return this.sessionId ?? "";
50
+ }
51
+ async prompt(_sessionId, text, signal, opts) {
52
+ const max = this.opts.maxAttempts ?? DEFAULT_ATTEMPTS;
53
+ const model = opts?.model ?? this.opts.model;
54
+ let steps = 0;
55
+ for (let attempt = 1; attempt <= max; attempt++) {
56
+ if (signal.aborted)
57
+ throw Object.assign(new Error("Aborted"), { name: "AbortError" });
58
+ const r = await this.spawnOnce(text, model, signal);
59
+ this.steps += r.steps;
60
+ steps += r.steps;
61
+ if (r.text)
62
+ this.lastText = r.text;
63
+ this.opts.trace?.({
64
+ type: "mouse.attempt",
65
+ attempt,
66
+ steps: r.steps,
67
+ exitCode: r.exitCode,
68
+ timedOut: r.timedOut,
69
+ transientError: r.transientError,
70
+ error: r.errorMessage,
71
+ sessionId: this.sessionId,
72
+ });
73
+ // A turn that did real work is done even if the process exited non-zero
74
+ // (the engine reports late errors that way). Only an empty turn retries.
75
+ if (r.steps > 0)
76
+ return { steps, text: this.lastText };
77
+ const retryable = r.timedOut || r.transientError || r.exitCode !== 0;
78
+ if (!retryable)
79
+ return { steps, text: this.lastText };
80
+ if (attempt === max) {
81
+ throw new Error(`opencode run produced no steps after ${max} attempts${r.errorMessage ? `: ${r.errorMessage}` : ""}`);
82
+ }
83
+ await new Promise((res) => setTimeout(res, retryDelayMs(attempt)));
84
+ }
85
+ return { steps, text: this.lastText };
86
+ }
87
+ async abort() {
88
+ this.child?.kill("SIGKILL");
89
+ }
90
+ /** The argv for one turn; exported for tests and for `mouse doctor`. */
91
+ argsFor(prompt, model = this.opts.model) {
92
+ return [
93
+ "run",
94
+ "--format=json",
95
+ "--agent",
96
+ this.opts.agent ?? "build",
97
+ "--model",
98
+ model,
99
+ ...(this.opts.skipPermissions === false ? [] : ["--dangerously-skip-permissions"]),
100
+ ...(this.sessionId ? ["--session", this.sessionId] : []),
101
+ // Passed on every turn, continuation included, as the benchmark run did.
102
+ ...(this.title ? ["--title", this.title] : []),
103
+ ...(this.opts.extraArgs ?? []),
104
+ "--",
105
+ prompt,
106
+ ];
107
+ }
108
+ spawnOnce(prompt, model, signal) {
109
+ return new Promise((resolve, reject) => {
110
+ const child = spawn(this.opts.bin ?? "opencode", this.argsFor(prompt, model), {
111
+ cwd: this.opts.cwd,
112
+ env: this.opts.env ?? process.env,
113
+ stdio: ["ignore", "pipe", "pipe"],
114
+ });
115
+ this.child = child;
116
+ const attempt = {
117
+ exitCode: null,
118
+ steps: 0,
119
+ text: "",
120
+ timedOut: false,
121
+ transientError: false,
122
+ errorMessage: null,
123
+ };
124
+ const texts = [];
125
+ const idleMs = this.opts.idleTimeoutMs ?? DEFAULT_IDLE_MS;
126
+ let idle = setTimeout(onIdle, idleMs);
127
+ function onIdle() {
128
+ attempt.timedOut = true;
129
+ child.kill("SIGKILL");
130
+ }
131
+ const touch = () => {
132
+ clearTimeout(idle);
133
+ idle = setTimeout(onIdle, idleMs);
134
+ };
135
+ const onAbort = () => child.kill("SIGKILL");
136
+ signal.addEventListener("abort", onAbort, { once: true });
137
+ const out = createInterface({ input: child.stdout });
138
+ out.on("line", (line) => {
139
+ touch();
140
+ this.opts.passthrough?.(line);
141
+ let ev;
142
+ try {
143
+ ev = JSON.parse(line);
144
+ }
145
+ catch {
146
+ return;
147
+ }
148
+ if (!ev || typeof ev !== "object")
149
+ return;
150
+ if (!this.sessionId && typeof ev.sessionID === "string" && ev.sessionID) {
151
+ this.sessionId = ev.sessionID;
152
+ }
153
+ const part = (ev.part ?? {});
154
+ switch (ev.type) {
155
+ case "step_finish": {
156
+ attempt.steps += 1;
157
+ const tokens = (part.tokens ?? {});
158
+ const cache = (tokens.cache ?? {});
159
+ this.tokens.input += num(tokens.input);
160
+ this.tokens.output += num(tokens.output);
161
+ this.tokens.cacheRead += num(cache.read);
162
+ this.tokens.cacheWrite += num(cache.write);
163
+ this.tokens.cost += num(part.cost);
164
+ break;
165
+ }
166
+ case "text": {
167
+ if (typeof part.text === "string" && part.text.trim())
168
+ texts.push(part.text);
169
+ break;
170
+ }
171
+ case "error": {
172
+ const message = JSON.stringify(ev.error ?? "");
173
+ attempt.errorMessage = message.slice(0, 500);
174
+ if (classifyInferenceFailure(message, ev.error) === "transient") {
175
+ attempt.transientError = true;
176
+ }
177
+ break;
178
+ }
179
+ default:
180
+ break;
181
+ }
182
+ });
183
+ const err = createInterface({ input: child.stderr });
184
+ err.on("line", (line) => {
185
+ touch();
186
+ this.opts.passthroughErr?.(line);
187
+ });
188
+ child.on("error", (e) => {
189
+ clearTimeout(idle);
190
+ signal.removeEventListener("abort", onAbort);
191
+ this.child = null;
192
+ reject(e);
193
+ });
194
+ child.on("close", (code) => {
195
+ clearTimeout(idle);
196
+ signal.removeEventListener("abort", onAbort);
197
+ this.child = null;
198
+ attempt.exitCode = code;
199
+ attempt.text = texts.join("\n");
200
+ resolve(attempt);
201
+ });
202
+ });
203
+ }
204
+ }
205
+ //# sourceMappingURL=run-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-engine.js","sourceRoot":"","sources":["../src/run-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** The line benchmark runners record as the harness version. */
2
+ export declare function versionLine(opencode: string | null): string;
3
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE3D"}
@@ -0,0 +1,6 @@
1
+ import { MOUSE_VERSION } from "@mousedev/harness-core";
2
+ /** The line benchmark runners record as the harness version. */
3
+ export function versionLine(opencode) {
4
+ return `mouse/${MOUSE_VERSION} opencode/${opencode ?? "unavailable"}`;
5
+ }
6
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,gEAAgE;AAChE,MAAM,UAAU,WAAW,CAAC,QAAuB;IACjD,OAAO,SAAS,aAAa,aAAa,QAAQ,IAAI,aAAa,EAAE,CAAC;AACxE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@mousedev/harness-opencode",
3
+ "version": "0.1.1",
4
+ "description": "OpenCode engine adapter for the Mouse harness: engine config, the `opencode run` engine, compat manifest, and the tool-output prune plugin.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/mousedev/mouse-harness.git",
9
+ "directory": "packages/opencode"
10
+ },
11
+ "homepage": "https://github.com/mousedev/mouse-harness#readme",
12
+ "type": "module",
13
+ "engines": {
14
+ "node": ">=22"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md"
27
+ ],
28
+ "sideEffects": false,
29
+ "dependencies": {
30
+ "@opencode-ai/sdk": "1.14.22",
31
+ "@mousedev/harness-core": "0.1.1"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "7.0.2",
35
+ "vitest": "5.0.0"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "typecheck": "tsc -p tsconfig.json --noEmit",
40
+ "test": "vitest run"
41
+ }
42
+ }