@ai-sdk/harness-opencode 1.0.93 → 1.0.95
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/CHANGELOG.md +24 -0
- package/README.md +24 -0
- package/dist/bridge/index.mjs +51 -7
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/bridge/package.json +2 -2
- package/dist/bridge/pnpm-lock.yaml +59 -59
- package/dist/bridge/pnpm-workspace.yaml +1 -1
- package/dist/index.d.ts +13 -32
- package/dist/index.js +73 -103
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/bridge/index.ts +25 -3
- package/src/bridge/package.json +2 -2
- package/src/bridge/pnpm-lock.yaml +59 -59
- package/src/bridge/pnpm-workspace.yaml +1 -1
- package/src/index.ts +1 -4
- package/src/opencode-auth.ts +36 -129
- package/src/opencode-bridge-protocol.ts +2 -0
- package/src/opencode-harness.ts +58 -37
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @ai-sdk/harness-opencode
|
|
2
2
|
|
|
3
|
+
## 1.0.95
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b1fd1bb: feat(harness-opencode): pass native OpenCode configuration through the adapter
|
|
8
|
+
- 7608210: feat(harness): add `model` parameter to `HarnessAgent` instead of having each harness adapter support it on their own constructor functions
|
|
9
|
+
- f7bd978: chore(harness): update underlying adapter SDKs to their latest versions
|
|
10
|
+
- 14d4fc0: feat(harness): allow changing harness settings between turns via `prepareCall()` support on `HarnessAgent`
|
|
11
|
+
- Updated dependencies [cc9f6ce]
|
|
12
|
+
- Updated dependencies [7608210]
|
|
13
|
+
- Updated dependencies [6f8a2d7]
|
|
14
|
+
- Updated dependencies [14d4fc0]
|
|
15
|
+
- Updated dependencies [90192f1]
|
|
16
|
+
- @ai-sdk/harness@1.0.93
|
|
17
|
+
- @ai-sdk/provider-utils@5.0.33
|
|
18
|
+
|
|
19
|
+
## 1.0.94
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- e0d7cfb: feat(harness): allow harness sessions to optionally authenticate from an isolated environment supplied through the `auth` option, and remove support for the formerly deprecated legacy auth options types
|
|
24
|
+
- Updated dependencies [e0d7cfb]
|
|
25
|
+
- @ai-sdk/harness@1.0.92
|
|
26
|
+
|
|
3
27
|
## 1.0.93
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,4 +3,28 @@
|
|
|
3
3
|
The OpenCode harness connects `HarnessAgent` to OpenCode through a sandboxed
|
|
4
4
|
bridge.
|
|
5
5
|
|
|
6
|
+
## Native OpenCode configuration
|
|
7
|
+
|
|
8
|
+
Use `openCodeConfig` to pass native OpenCode settings that do not have a
|
|
9
|
+
dedicated adapter option. For example, OpenCode supports selecting a model for
|
|
10
|
+
an individual agent:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { createOpenCode } from '@ai-sdk/harness-opencode';
|
|
14
|
+
|
|
15
|
+
const adapter = createOpenCode({
|
|
16
|
+
openCodeConfig: {
|
|
17
|
+
agent: {
|
|
18
|
+
general: {
|
|
19
|
+
model: 'provider/model-id',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Adapter-managed settings take precedence when the same key is present in
|
|
27
|
+
`openCodeConfig`. Agent-local `permission` and deprecated `tools` settings are
|
|
28
|
+
ignored so they cannot bypass harness permissions or built-in tool filtering.
|
|
29
|
+
|
|
6
30
|
See the AI SDK documentation for usage.
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -141,6 +141,7 @@ var ENV_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
|
141
141
|
var WS_OPEN = 1;
|
|
142
142
|
async function runBridge(options) {
|
|
143
143
|
const { bridgeType, bridgeStateDir: bridgeStateDir2, onStart, onStop, onDestroy } = options;
|
|
144
|
+
const teardownGraceMs = options.turnTeardownGraceMs ?? 1e4;
|
|
144
145
|
const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? "";
|
|
145
146
|
const bridgeWsPort = options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? "0", 10);
|
|
146
147
|
const bridgeMetaPath = `${bridgeStateDir2}/bridge-meta.json`;
|
|
@@ -157,6 +158,7 @@ async function runBridge(options) {
|
|
|
157
158
|
let isFirstTurn = true;
|
|
158
159
|
let turnAbort;
|
|
159
160
|
let currentUserMessages;
|
|
161
|
+
let activeTurn;
|
|
160
162
|
let debugConfig;
|
|
161
163
|
let consoleCaptureInstalled = false;
|
|
162
164
|
const envDebugEnabled = ENV_TRUTHY.has(
|
|
@@ -347,10 +349,28 @@ async function runBridge(options) {
|
|
|
347
349
|
const handleInbound = async (msg, ws) => {
|
|
348
350
|
switch (msg.type) {
|
|
349
351
|
case "start": {
|
|
352
|
+
for (; ; ) {
|
|
353
|
+
const pendingTurn = activeTurn;
|
|
354
|
+
if (pendingTurn == null) break;
|
|
355
|
+
turnAbort?.abort();
|
|
356
|
+
currentUserMessages?.close(
|
|
357
|
+
new Error("A new bridge turn replaced the active turn.")
|
|
358
|
+
);
|
|
359
|
+
let graceTimer;
|
|
360
|
+
const settled = await Promise.race([
|
|
361
|
+
pendingTurn.then(() => true),
|
|
362
|
+
new Promise((resolve) => {
|
|
363
|
+
graceTimer = setTimeout(() => resolve(false), teardownGraceMs);
|
|
364
|
+
graceTimer.unref?.();
|
|
365
|
+
})
|
|
366
|
+
]);
|
|
367
|
+
clearTimeout(graceTimer);
|
|
368
|
+
if (!settled) break;
|
|
369
|
+
}
|
|
370
|
+
let turnFinished;
|
|
371
|
+
const thisTurn = new Promise((resolve) => turnFinished = resolve);
|
|
372
|
+
activeTurn = thisTurn;
|
|
350
373
|
activeSocket = ws;
|
|
351
|
-
currentUserMessages?.close(
|
|
352
|
-
new Error("A new bridge turn replaced the active turn.")
|
|
353
|
-
);
|
|
354
374
|
const firstTurn = isFirstTurn;
|
|
355
375
|
isFirstTurn = false;
|
|
356
376
|
eventLog = [];
|
|
@@ -407,8 +427,12 @@ async function runBridge(options) {
|
|
|
407
427
|
if (currentUserMessages === userMessages) {
|
|
408
428
|
currentUserMessages = void 0;
|
|
409
429
|
}
|
|
410
|
-
|
|
411
|
-
|
|
430
|
+
if (activeTurn === thisTurn) {
|
|
431
|
+
activeTurn = void 0;
|
|
432
|
+
currentTurnState = "waiting";
|
|
433
|
+
void writeBridgeMeta("waiting");
|
|
434
|
+
}
|
|
435
|
+
turnFinished();
|
|
412
436
|
}
|
|
413
437
|
return;
|
|
414
438
|
}
|
|
@@ -592,7 +616,6 @@ function serialiseError(err) {
|
|
|
592
616
|
|
|
593
617
|
// src/bridge/index.ts
|
|
594
618
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
595
|
-
import { mkdirSync } from "fs";
|
|
596
619
|
import path2 from "path";
|
|
597
620
|
import { argv, env as procEnv2 } from "process";
|
|
598
621
|
import {
|
|
@@ -1674,7 +1697,6 @@ var runtime = {
|
|
|
1674
1697
|
mcpToolPrefixes: /* @__PURE__ */ new Set()
|
|
1675
1698
|
};
|
|
1676
1699
|
prependOpenCodeBinToPath({ bootstrapDir, env: procEnv2 });
|
|
1677
|
-
mkdirSync(process.env.HOME ?? "/tmp/opencode-home", { recursive: true });
|
|
1678
1700
|
await runBridge({
|
|
1679
1701
|
bridgeType: "opencode",
|
|
1680
1702
|
bridgeStateDir,
|
|
@@ -1687,6 +1709,9 @@ async function runTurn(start, turn) {
|
|
|
1687
1709
|
try {
|
|
1688
1710
|
await ensureRuntime({ start, turn, emit });
|
|
1689
1711
|
const client = runtime.client;
|
|
1712
|
+
if (start.skillsChanged) {
|
|
1713
|
+
await client.instance.dispose({ directory: workdir });
|
|
1714
|
+
}
|
|
1690
1715
|
const sessionId = await ensureSession({ client, start, emit });
|
|
1691
1716
|
if (start.operation === "compact") {
|
|
1692
1717
|
await runCompaction({ client, sessionId, start, turn, emit });
|
|
@@ -1747,6 +1772,7 @@ function buildOpenCodeConfig({
|
|
|
1747
1772
|
relayPort
|
|
1748
1773
|
}) {
|
|
1749
1774
|
const config = {
|
|
1775
|
+
...withoutAgentPolicyOverrides(start.openCodeConfig),
|
|
1750
1776
|
share: "disabled",
|
|
1751
1777
|
autoupdate: false,
|
|
1752
1778
|
permission: {
|
|
@@ -1800,6 +1826,24 @@ function buildOpenCodeConfig({
|
|
|
1800
1826
|
if (Object.keys(mcp).length > 0) config.mcp = mcp;
|
|
1801
1827
|
return config;
|
|
1802
1828
|
}
|
|
1829
|
+
function withoutAgentPolicyOverrides(input) {
|
|
1830
|
+
const config = { ...input };
|
|
1831
|
+
for (const key of ["agent", "mode"]) {
|
|
1832
|
+
const agents = asOpenCodeObject(config[key]);
|
|
1833
|
+
if (!agents) continue;
|
|
1834
|
+
config[key] = Object.fromEntries(
|
|
1835
|
+
Object.entries(agents).map(([name, value]) => {
|
|
1836
|
+
const agent = asOpenCodeObject(value);
|
|
1837
|
+
if (!agent) return [name, value];
|
|
1838
|
+
const safeAgent = { ...agent };
|
|
1839
|
+
delete safeAgent.permission;
|
|
1840
|
+
delete safeAgent.tools;
|
|
1841
|
+
return [name, safeAgent];
|
|
1842
|
+
})
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
return config;
|
|
1846
|
+
}
|
|
1803
1847
|
function buildProviderConfig(start) {
|
|
1804
1848
|
const model = splitModel(start.model, start.provider);
|
|
1805
1849
|
const providerID = model.providerID ?? start.provider ?? procEnv2.OPENAI_NAME ?? "anthropic";
|