@ai-sdk/harness-opencode 1.0.114 → 1.0.116
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 +23 -0
- package/README.md +11 -0
- package/dist/bridge/index.mjs +123 -31
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/bridge/create-emit-stream-event.ts +81 -0
- package/src/bridge/index.ts +60 -34
- package/src/bridge/opencode-types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @ai-sdk/harness-opencode
|
|
2
2
|
|
|
3
|
+
## 1.0.116
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [215b25e]
|
|
8
|
+
- Updated dependencies [d4d96bf]
|
|
9
|
+
- Updated dependencies [3456e2c]
|
|
10
|
+
- Updated dependencies [c4e76de]
|
|
11
|
+
- @ai-sdk/provider-utils@5.0.43
|
|
12
|
+
- @ai-sdk/harness@1.0.114
|
|
13
|
+
|
|
14
|
+
## 1.0.115
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 0185c4d: fix(harness-opencode): refresh warm runtimes when native configuration changes
|
|
19
|
+
- b3c82fd: fix(harness-opencode): preserve native compaction events without leaking internal summary text
|
|
20
|
+
- Updated dependencies [91c2128]
|
|
21
|
+
- Updated dependencies [2cd80b3]
|
|
22
|
+
- Updated dependencies [2fa5e0e]
|
|
23
|
+
- @ai-sdk/provider-utils@5.0.42
|
|
24
|
+
- @ai-sdk/harness@1.0.113
|
|
25
|
+
|
|
3
26
|
## 1.0.114
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -27,4 +27,15 @@ Adapter-managed settings take precedence when the same key is present in
|
|
|
27
27
|
`openCodeConfig`. Agent-local `permission` and deprecated `tools` settings are
|
|
28
28
|
ignored so they cannot bypass harness permissions or built-in tool filtering.
|
|
29
29
|
|
|
30
|
+
## Compaction events
|
|
31
|
+
|
|
32
|
+
Native automatic and manual compaction emit a `compaction` stream part on
|
|
33
|
+
completion. Internal summary text is available on that part, rather than
|
|
34
|
+
streamed as assistant answer text. Summary model usage is preserved.
|
|
35
|
+
|
|
36
|
+
With `includeRawChunks` enabled, `opencode.compaction` raw events expose
|
|
37
|
+
`messageId` and `status` (`started` or `failed`). Successful completion uses
|
|
38
|
+
the normalized `compaction` part, whose `harnessMetadata.opencode.messageId`
|
|
39
|
+
identifies the same operation.
|
|
40
|
+
|
|
30
41
|
See the AI SDK documentation for usage.
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -648,6 +648,7 @@ function serialiseError(err) {
|
|
|
648
648
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
649
649
|
import path2 from "path";
|
|
650
650
|
import { argv, env as procEnv2 } from "process";
|
|
651
|
+
import { isDeepStrictEqual } from "util";
|
|
651
652
|
import {
|
|
652
653
|
createOpencodeClient,
|
|
653
654
|
createOpencodeServer
|
|
@@ -1036,6 +1037,30 @@ function createEmitStreamEvent({
|
|
|
1036
1037
|
stripWorkDir: stripWorkDir2,
|
|
1037
1038
|
formatError: formatError2
|
|
1038
1039
|
}) {
|
|
1040
|
+
const compactionMessages = /* @__PURE__ */ new Set();
|
|
1041
|
+
let compactionTrigger = "auto";
|
|
1042
|
+
let compaction;
|
|
1043
|
+
const finishCompaction = (failed = false) => {
|
|
1044
|
+
if (!compaction) return;
|
|
1045
|
+
if (failed) {
|
|
1046
|
+
emit({
|
|
1047
|
+
type: "raw",
|
|
1048
|
+
rawValue: {
|
|
1049
|
+
type: "opencode.compaction",
|
|
1050
|
+
messageId: compaction.id,
|
|
1051
|
+
status: "failed"
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
1054
|
+
} else {
|
|
1055
|
+
emit({
|
|
1056
|
+
type: "compaction",
|
|
1057
|
+
trigger: compaction.trigger,
|
|
1058
|
+
summary: [...compaction.text.values()].join(""),
|
|
1059
|
+
harnessMetadata: { opencode: { messageId: compaction.id } }
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
compaction = void 0;
|
|
1063
|
+
};
|
|
1039
1064
|
return (event) => {
|
|
1040
1065
|
const type = event.type;
|
|
1041
1066
|
const props = event.properties ?? {};
|
|
@@ -1045,9 +1070,56 @@ function createEmitStreamEvent({
|
|
|
1045
1070
|
const id = stringValue2(info.id);
|
|
1046
1071
|
const role = stringValue2(info.role);
|
|
1047
1072
|
if (id && role) state.messageRoles.set(id, role);
|
|
1073
|
+
if (id && info.summary === true) {
|
|
1074
|
+
if (!compactionMessages.has(id)) {
|
|
1075
|
+
compactionMessages.add(id);
|
|
1076
|
+
compaction = { id, trigger: compactionTrigger, text: /* @__PURE__ */ new Map() };
|
|
1077
|
+
emitOpenCodeStreamStart({ info, state, emit });
|
|
1078
|
+
emit({
|
|
1079
|
+
type: "raw",
|
|
1080
|
+
rawValue: {
|
|
1081
|
+
type: "opencode.compaction",
|
|
1082
|
+
messageId: id,
|
|
1083
|
+
status: "started"
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
if (info.error && compaction?.id === id) finishCompaction(true);
|
|
1088
|
+
}
|
|
1048
1089
|
}
|
|
1049
1090
|
return;
|
|
1050
1091
|
}
|
|
1092
|
+
if (type === "session.compacted") {
|
|
1093
|
+
finishCompaction();
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
if (type === "message.part.updated" || type === "message.part.delta") {
|
|
1097
|
+
const part = asOpenCodeObject(props.part);
|
|
1098
|
+
if (type === "message.part.updated" && part?.type === "compaction") {
|
|
1099
|
+
compactionTrigger = part.auto === false ? "manual" : "auto";
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const messageId = stringValue2(
|
|
1103
|
+
type === "message.part.updated" ? part?.messageID : props.messageID
|
|
1104
|
+
);
|
|
1105
|
+
if (messageId && compactionMessages.has(messageId)) {
|
|
1106
|
+
if (type === "message.part.updated") {
|
|
1107
|
+
if (messageId === compaction?.id && part?.type === "text") {
|
|
1108
|
+
const id = stringValue2(part.id);
|
|
1109
|
+
if (id && typeof part.text === "string") {
|
|
1110
|
+
compaction.text.set(id, part.text);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
emitLegacyStepFinishPart({ part: props.part, state, emit });
|
|
1114
|
+
} else if (messageId === compaction?.id && props.field === "text") {
|
|
1115
|
+
const id = stringValue2(props.partID);
|
|
1116
|
+
if (id && compaction.text.has(id) && typeof props.delta === "string") {
|
|
1117
|
+
compaction.text.set(id, compaction.text.get(id) + props.delta);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1051
1123
|
if (type === "message.part.delta") {
|
|
1052
1124
|
emitLegacyPartDelta({ props, state, emit });
|
|
1053
1125
|
return;
|
|
@@ -1268,6 +1340,7 @@ function createEmitStreamEvent({
|
|
|
1268
1340
|
return;
|
|
1269
1341
|
}
|
|
1270
1342
|
if (type === "session.error" || type === "session.next.step.failed") {
|
|
1343
|
+
finishCompaction(true);
|
|
1271
1344
|
const error = props.error ?? event;
|
|
1272
1345
|
emitError({
|
|
1273
1346
|
error,
|
|
@@ -1835,38 +1908,57 @@ async function ensureRuntime({
|
|
|
1835
1908
|
turn,
|
|
1836
1909
|
emit
|
|
1837
1910
|
}) {
|
|
1838
|
-
if (runtime.client)
|
|
1839
|
-
|
|
1840
|
-
runtime.toolNames = new Set(start.tools.map((tool) => tool.name));
|
|
1841
|
-
runtime.relay = await startToolRelay({
|
|
1842
|
-
tools: start.tools,
|
|
1843
|
-
emit,
|
|
1844
|
-
requestToolResult: turn.requestToolResult
|
|
1845
|
-
});
|
|
1911
|
+
if (runtime.client && isDeepStrictEqual(runtime.openCodeConfig, start.openCodeConfig)) {
|
|
1912
|
+
return;
|
|
1846
1913
|
}
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1914
|
+
closeRuntime();
|
|
1915
|
+
try {
|
|
1916
|
+
if (start.tools && start.tools.length > 0) {
|
|
1917
|
+
runtime.toolNames = new Set(start.tools.map((tool) => tool.name));
|
|
1918
|
+
runtime.relay = await startToolRelay({
|
|
1919
|
+
tools: start.tools,
|
|
1920
|
+
emit,
|
|
1921
|
+
requestToolResult: turn.requestToolResult
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
const serverAuthHeaders = configureOpenCodeServerAuth({ env: procEnv2 });
|
|
1925
|
+
const server = await createOpencodeServer({
|
|
1926
|
+
hostname: "127.0.0.1",
|
|
1927
|
+
port: 0,
|
|
1928
|
+
timeout: 3e4,
|
|
1929
|
+
config: buildOpenCodeConfig({
|
|
1930
|
+
start,
|
|
1931
|
+
relayPort: runtime.relay?.port
|
|
1932
|
+
})
|
|
1933
|
+
});
|
|
1934
|
+
runtime.server = server;
|
|
1935
|
+
runtime.client = createOpencodeClient({
|
|
1936
|
+
baseUrl: server.url,
|
|
1937
|
+
directory: workdir,
|
|
1938
|
+
headers: serverAuthHeaders
|
|
1939
|
+
});
|
|
1940
|
+
const mcpStatus = await runtime.client.mcp.status();
|
|
1941
|
+
const mcpServers = asOpenCodeObject(mcpStatus.data) ?? {};
|
|
1942
|
+
runtime.mcpToolPrefixes = new Set(
|
|
1943
|
+
Object.entries(mcpServers).filter(
|
|
1944
|
+
([serverName, status]) => serverName !== "harness-tools" && asOpenCodeObject(status)?.status === "connected"
|
|
1945
|
+
).map(([serverName]) => `${sanitizeMcpToolName(serverName)}_`)
|
|
1946
|
+
);
|
|
1947
|
+
runtime.openCodeConfig = structuredClone(start.openCodeConfig);
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
closeRuntime();
|
|
1950
|
+
throw error;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
function closeRuntime() {
|
|
1954
|
+
runtime.relay?.close();
|
|
1955
|
+
runtime.server?.close();
|
|
1956
|
+
runtime.server = void 0;
|
|
1957
|
+
runtime.client = void 0;
|
|
1958
|
+
runtime.relay = void 0;
|
|
1959
|
+
runtime.openCodeConfig = void 0;
|
|
1960
|
+
runtime.toolNames = /* @__PURE__ */ new Set();
|
|
1961
|
+
runtime.mcpToolPrefixes = /* @__PURE__ */ new Set();
|
|
1870
1962
|
}
|
|
1871
1963
|
function buildOpenCodeConfig({
|
|
1872
1964
|
start,
|