@ai-sdk/harness-opencode 1.0.37 → 1.0.39
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 +19 -0
- package/dist/bridge/index.mjs +141 -120
- 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/index.js +1 -1
- package/package.json +4 -4
- package/src/bridge/index.ts +5 -142
- package/src/bridge/opencode-events.ts +182 -0
- package/src/bridge/package.json +2 -2
- package/src/bridge/pnpm-lock.yaml +59 -59
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @ai-sdk/harness-opencode
|
|
2
2
|
|
|
3
|
+
## 1.0.39
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f5cdb2d: chore(harness): update primary SDK dependencies
|
|
8
|
+
- Updated dependencies [02ffdcb]
|
|
9
|
+
- Updated dependencies [76cb673]
|
|
10
|
+
- @ai-sdk/provider-utils@5.0.12
|
|
11
|
+
- @ai-sdk/harness@1.0.38
|
|
12
|
+
|
|
13
|
+
## 1.0.38
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- ab7f97a: fix(harness-opencode): route OpenCode reasoning deltas to the reasoning part instead of duplicating them as text
|
|
18
|
+
- Updated dependencies [b460541]
|
|
19
|
+
- Updated dependencies [079591e]
|
|
20
|
+
- @ai-sdk/harness@1.0.37
|
|
21
|
+
|
|
3
22
|
## 1.0.37
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -488,6 +488,23 @@ import {
|
|
|
488
488
|
} from "@opencode-ai/sdk/v2";
|
|
489
489
|
|
|
490
490
|
// src/bridge/opencode-events.ts
|
|
491
|
+
function createTranslationState() {
|
|
492
|
+
return {
|
|
493
|
+
textDeltas: /* @__PURE__ */ new Map(),
|
|
494
|
+
reasoningDeltas: /* @__PURE__ */ new Map(),
|
|
495
|
+
toolInputs: /* @__PURE__ */ new Map(),
|
|
496
|
+
toolNames: /* @__PURE__ */ new Map(),
|
|
497
|
+
toolCallsEmitted: /* @__PURE__ */ new Set(),
|
|
498
|
+
toolResultsEmitted: /* @__PURE__ */ new Set(),
|
|
499
|
+
hostToolCallsAuthorized: /* @__PURE__ */ new Set(),
|
|
500
|
+
shellCommands: /* @__PURE__ */ new Map(),
|
|
501
|
+
messageRoles: /* @__PURE__ */ new Map(),
|
|
502
|
+
turnUsage: void 0,
|
|
503
|
+
legacyTextPartIds: /* @__PURE__ */ new Set(),
|
|
504
|
+
legacyReasoningPartIds: /* @__PURE__ */ new Set(),
|
|
505
|
+
legacyStepFinishPartIds: /* @__PURE__ */ new Set()
|
|
506
|
+
};
|
|
507
|
+
}
|
|
491
508
|
function unwrapOpenCodeEvent(rawEvent) {
|
|
492
509
|
if (!rawEvent || typeof rawEvent !== "object") return void 0;
|
|
493
510
|
const raw = rawEvent;
|
|
@@ -534,6 +551,85 @@ function emitMissingFinalDelta({
|
|
|
534
551
|
}
|
|
535
552
|
emit({ type, id, delta: fullText.slice(emittedText.length) });
|
|
536
553
|
}
|
|
554
|
+
function emitLegacyPartDelta({
|
|
555
|
+
props,
|
|
556
|
+
state,
|
|
557
|
+
emit
|
|
558
|
+
}) {
|
|
559
|
+
const field = String(props.field ?? "");
|
|
560
|
+
const delta = String(props.delta ?? "");
|
|
561
|
+
if (!delta) return;
|
|
562
|
+
const messageID = stringValue(props.messageID);
|
|
563
|
+
if (messageID && state.messageRoles.get(messageID) === "user") return;
|
|
564
|
+
if (field === "text") {
|
|
565
|
+
const id = legacyPartId({ value: props, fallback: "legacy-text" });
|
|
566
|
+
if (state.legacyReasoningPartIds.has(id)) {
|
|
567
|
+
state.reasoningDeltas.set(
|
|
568
|
+
id,
|
|
569
|
+
`${state.reasoningDeltas.get(id) ?? ""}${delta}`
|
|
570
|
+
);
|
|
571
|
+
emit({ type: "reasoning-delta", id, delta });
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
startLegacyPart({ ids: state.legacyTextPartIds, id, emit, type: "text" });
|
|
575
|
+
state.textDeltas.set(id, `${state.textDeltas.get(id) ?? ""}${delta}`);
|
|
576
|
+
emit({ type: "text-delta", id, delta });
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (field === "reasoning") {
|
|
580
|
+
const id = legacyPartId({ value: props, fallback: "legacy-reasoning" });
|
|
581
|
+
startLegacyPart({
|
|
582
|
+
ids: state.legacyReasoningPartIds,
|
|
583
|
+
id,
|
|
584
|
+
emit,
|
|
585
|
+
type: "reasoning"
|
|
586
|
+
});
|
|
587
|
+
state.reasoningDeltas.set(
|
|
588
|
+
id,
|
|
589
|
+
`${state.reasoningDeltas.get(id) ?? ""}${delta}`
|
|
590
|
+
);
|
|
591
|
+
emit({ type: "reasoning-delta", id, delta });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
function emitLegacyTextPartUpdate({
|
|
595
|
+
part,
|
|
596
|
+
state,
|
|
597
|
+
emit
|
|
598
|
+
}) {
|
|
599
|
+
if (!isRecord(part)) return false;
|
|
600
|
+
if (part.type !== "text" && part.type !== "reasoning") return false;
|
|
601
|
+
const id = stringValue(part.id);
|
|
602
|
+
if (!id) return true;
|
|
603
|
+
const messageID = stringValue(part.messageID);
|
|
604
|
+
if (messageID && state.messageRoles.get(messageID) === "user") return true;
|
|
605
|
+
const isReasoning = part.type === "reasoning";
|
|
606
|
+
const ids = isReasoning ? state.legacyReasoningPartIds : state.legacyTextPartIds;
|
|
607
|
+
const deltaMap = isReasoning ? state.reasoningDeltas : state.textDeltas;
|
|
608
|
+
const deltaType = isReasoning ? "reasoning-delta" : "text-delta";
|
|
609
|
+
const text = typeof part.text === "string" ? part.text : void 0;
|
|
610
|
+
startLegacyPart({
|
|
611
|
+
ids,
|
|
612
|
+
id,
|
|
613
|
+
emit,
|
|
614
|
+
type: isReasoning ? "reasoning" : "text"
|
|
615
|
+
});
|
|
616
|
+
if (text !== void 0) {
|
|
617
|
+
emitMissingFinalDelta({
|
|
618
|
+
id,
|
|
619
|
+
fullText: text,
|
|
620
|
+
emittedText: deltaMap.get(id) ?? "",
|
|
621
|
+
emit,
|
|
622
|
+
type: deltaType
|
|
623
|
+
});
|
|
624
|
+
deltaMap.set(id, text);
|
|
625
|
+
}
|
|
626
|
+
if (legacyPartEnded(part)) {
|
|
627
|
+
ids.delete(id);
|
|
628
|
+
deltaMap.delete(id);
|
|
629
|
+
emit({ type: isReasoning ? "reasoning-end" : "text-end", id });
|
|
630
|
+
}
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
537
633
|
function stripSyncVersion(type) {
|
|
538
634
|
return type.replace(/\.\d+$/, "");
|
|
539
635
|
}
|
|
@@ -542,6 +638,31 @@ function asRecord(value) {
|
|
|
542
638
|
return void 0;
|
|
543
639
|
return value;
|
|
544
640
|
}
|
|
641
|
+
function isRecord(value) {
|
|
642
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
643
|
+
}
|
|
644
|
+
function stringValue(value) {
|
|
645
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
646
|
+
}
|
|
647
|
+
function legacyPartId({
|
|
648
|
+
value,
|
|
649
|
+
fallback
|
|
650
|
+
}) {
|
|
651
|
+
return stringValue(value.partID) ?? stringValue(value.id) ?? fallback;
|
|
652
|
+
}
|
|
653
|
+
function startLegacyPart({
|
|
654
|
+
ids,
|
|
655
|
+
id,
|
|
656
|
+
emit,
|
|
657
|
+
type
|
|
658
|
+
}) {
|
|
659
|
+
if (ids.has(id)) return;
|
|
660
|
+
ids.add(id);
|
|
661
|
+
emit({ type: `${type}-start`, id });
|
|
662
|
+
}
|
|
663
|
+
function legacyPartEnded(part) {
|
|
664
|
+
return isRecord(part.time) && part.time.end != null;
|
|
665
|
+
}
|
|
545
666
|
|
|
546
667
|
// src/bridge/opencode-usage.ts
|
|
547
668
|
function mapUsage(tokens) {
|
|
@@ -673,7 +794,7 @@ function mapOpenCodeFinishReason(reason) {
|
|
|
673
794
|
return "other";
|
|
674
795
|
}
|
|
675
796
|
function legacyStepFinishPartToFinishStep(part) {
|
|
676
|
-
if (!
|
|
797
|
+
if (!isRecord2(part) || part.type !== "step-finish") return void 0;
|
|
677
798
|
const rawFinish = typeof part.reason === "string" ? part.reason : "stop";
|
|
678
799
|
return {
|
|
679
800
|
type: "finish-step",
|
|
@@ -685,7 +806,7 @@ function legacyStepFinishPartToFinishStep(part) {
|
|
|
685
806
|
...typeof part.cost === "number" ? { harnessMetadata: { opencode: { cost: part.cost } } } : {}
|
|
686
807
|
};
|
|
687
808
|
}
|
|
688
|
-
function
|
|
809
|
+
function isRecord2(value) {
|
|
689
810
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
690
811
|
}
|
|
691
812
|
|
|
@@ -1221,8 +1342,8 @@ function nextRetryEventMessage(event) {
|
|
|
1221
1342
|
details.push(`attempt ${props.attempt}`);
|
|
1222
1343
|
}
|
|
1223
1344
|
const error = props.error;
|
|
1224
|
-
if (
|
|
1225
|
-
const message =
|
|
1345
|
+
if (isRecord3(error)) {
|
|
1346
|
+
const message = stringValue2(error.message) ?? (isRecord3(error.data) ? stringValue2(error.data.message) : void 0);
|
|
1226
1347
|
const statusCode = error.statusCode;
|
|
1227
1348
|
if (typeof statusCode === "number") {
|
|
1228
1349
|
details.push(`HTTP ${statusCode}`);
|
|
@@ -1492,23 +1613,6 @@ async function consumeEvents({
|
|
|
1492
1613
|
if (onEvent?.(event)) break;
|
|
1493
1614
|
}
|
|
1494
1615
|
}
|
|
1495
|
-
function createTranslationState() {
|
|
1496
|
-
return {
|
|
1497
|
-
textDeltas: /* @__PURE__ */ new Map(),
|
|
1498
|
-
reasoningDeltas: /* @__PURE__ */ new Map(),
|
|
1499
|
-
toolInputs: /* @__PURE__ */ new Map(),
|
|
1500
|
-
toolNames: /* @__PURE__ */ new Map(),
|
|
1501
|
-
toolCallsEmitted: /* @__PURE__ */ new Set(),
|
|
1502
|
-
toolResultsEmitted: /* @__PURE__ */ new Set(),
|
|
1503
|
-
hostToolCallsAuthorized: /* @__PURE__ */ new Set(),
|
|
1504
|
-
shellCommands: /* @__PURE__ */ new Map(),
|
|
1505
|
-
messageRoles: /* @__PURE__ */ new Map(),
|
|
1506
|
-
turnUsage: void 0,
|
|
1507
|
-
legacyTextPartIds: /* @__PURE__ */ new Set(),
|
|
1508
|
-
legacyReasoningPartIds: /* @__PURE__ */ new Set(),
|
|
1509
|
-
legacyStepFinishPartIds: /* @__PURE__ */ new Set()
|
|
1510
|
-
};
|
|
1511
|
-
}
|
|
1512
1616
|
async function translateAndEmit({
|
|
1513
1617
|
event,
|
|
1514
1618
|
state,
|
|
@@ -1523,40 +1627,15 @@ async function translateAndEmit({
|
|
|
1523
1627
|
const props = event.properties ?? {};
|
|
1524
1628
|
if (type === "message.updated") {
|
|
1525
1629
|
const info = props.info;
|
|
1526
|
-
if (
|
|
1527
|
-
const id =
|
|
1528
|
-
const role =
|
|
1630
|
+
if (isRecord3(info)) {
|
|
1631
|
+
const id = stringValue2(info.id);
|
|
1632
|
+
const role = stringValue2(info.role);
|
|
1529
1633
|
if (id && role) state.messageRoles.set(id, role);
|
|
1530
1634
|
}
|
|
1531
1635
|
return;
|
|
1532
1636
|
}
|
|
1533
1637
|
if (type === "message.part.delta") {
|
|
1534
|
-
|
|
1535
|
-
const delta = String(props.delta ?? "");
|
|
1536
|
-
if (!delta) return;
|
|
1537
|
-
const messageID = stringValue(props.messageID);
|
|
1538
|
-
if (messageID && state.messageRoles.get(messageID) === "user") return;
|
|
1539
|
-
if (field === "text") {
|
|
1540
|
-
const id = legacyPartId({ value: props, fallback: "legacy-text" });
|
|
1541
|
-
startLegacyPart({ ids: state.legacyTextPartIds, id, emit, type: "text" });
|
|
1542
|
-
state.textDeltas.set(id, `${state.textDeltas.get(id) ?? ""}${delta}`);
|
|
1543
|
-
emit({ type: "text-delta", id, delta });
|
|
1544
|
-
return;
|
|
1545
|
-
}
|
|
1546
|
-
if (field === "reasoning") {
|
|
1547
|
-
const id = legacyPartId({ value: props, fallback: "legacy-reasoning" });
|
|
1548
|
-
startLegacyPart({
|
|
1549
|
-
ids: state.legacyReasoningPartIds,
|
|
1550
|
-
id,
|
|
1551
|
-
emit,
|
|
1552
|
-
type: "reasoning"
|
|
1553
|
-
});
|
|
1554
|
-
state.reasoningDeltas.set(
|
|
1555
|
-
id,
|
|
1556
|
-
`${state.reasoningDeltas.get(id) ?? ""}${delta}`
|
|
1557
|
-
);
|
|
1558
|
-
emit({ type: "reasoning-delta", id, delta });
|
|
1559
|
-
}
|
|
1638
|
+
emitLegacyPartDelta({ props, state, emit });
|
|
1560
1639
|
return;
|
|
1561
1640
|
}
|
|
1562
1641
|
if (type === "message.part.updated") {
|
|
@@ -1711,7 +1790,7 @@ async function translateAndEmit({
|
|
|
1711
1790
|
}
|
|
1712
1791
|
if (type === "session.next.retried") {
|
|
1713
1792
|
const error = props.error ?? event;
|
|
1714
|
-
if (
|
|
1793
|
+
if (isRecord3(error) && error.isRetryable === false) {
|
|
1715
1794
|
turn.emitError({
|
|
1716
1795
|
error,
|
|
1717
1796
|
message: "OpenCode session retry failed"
|
|
@@ -1788,64 +1867,6 @@ async function translateAndEmit({
|
|
|
1788
1867
|
});
|
|
1789
1868
|
}
|
|
1790
1869
|
}
|
|
1791
|
-
function legacyPartId({
|
|
1792
|
-
value,
|
|
1793
|
-
fallback
|
|
1794
|
-
}) {
|
|
1795
|
-
return stringValue(value.partID) ?? stringValue(value.id) ?? fallback;
|
|
1796
|
-
}
|
|
1797
|
-
function startLegacyPart({
|
|
1798
|
-
ids,
|
|
1799
|
-
id,
|
|
1800
|
-
emit,
|
|
1801
|
-
type
|
|
1802
|
-
}) {
|
|
1803
|
-
if (ids.has(id)) return;
|
|
1804
|
-
ids.add(id);
|
|
1805
|
-
emit({ type: `${type}-start`, id });
|
|
1806
|
-
}
|
|
1807
|
-
function emitLegacyTextPartUpdate({
|
|
1808
|
-
part,
|
|
1809
|
-
state,
|
|
1810
|
-
emit
|
|
1811
|
-
}) {
|
|
1812
|
-
if (!isRecord2(part)) return false;
|
|
1813
|
-
if (part.type !== "text" && part.type !== "reasoning") return false;
|
|
1814
|
-
const id = stringValue(part.id);
|
|
1815
|
-
if (!id) return true;
|
|
1816
|
-
const messageID = stringValue(part.messageID);
|
|
1817
|
-
if (messageID && state.messageRoles.get(messageID) === "user") return true;
|
|
1818
|
-
const isReasoning = part.type === "reasoning";
|
|
1819
|
-
const ids = isReasoning ? state.legacyReasoningPartIds : state.legacyTextPartIds;
|
|
1820
|
-
const deltaMap = isReasoning ? state.reasoningDeltas : state.textDeltas;
|
|
1821
|
-
const deltaType = isReasoning ? "reasoning-delta" : "text-delta";
|
|
1822
|
-
const text = typeof part.text === "string" ? part.text : void 0;
|
|
1823
|
-
startLegacyPart({
|
|
1824
|
-
ids,
|
|
1825
|
-
id,
|
|
1826
|
-
emit,
|
|
1827
|
-
type: isReasoning ? "reasoning" : "text"
|
|
1828
|
-
});
|
|
1829
|
-
if (text !== void 0) {
|
|
1830
|
-
emitMissingFinalDelta({
|
|
1831
|
-
id,
|
|
1832
|
-
fullText: text,
|
|
1833
|
-
emittedText: deltaMap.get(id) ?? "",
|
|
1834
|
-
emit,
|
|
1835
|
-
type: deltaType
|
|
1836
|
-
});
|
|
1837
|
-
deltaMap.set(id, text);
|
|
1838
|
-
}
|
|
1839
|
-
if (legacyPartEnded(part)) {
|
|
1840
|
-
ids.delete(id);
|
|
1841
|
-
deltaMap.delete(id);
|
|
1842
|
-
emit({ type: isReasoning ? "reasoning-end" : "text-end", id });
|
|
1843
|
-
}
|
|
1844
|
-
return true;
|
|
1845
|
-
}
|
|
1846
|
-
function legacyPartEnded(part) {
|
|
1847
|
-
return isRecord2(part.time) && part.time.end != null;
|
|
1848
|
-
}
|
|
1849
1870
|
function closeLegacyOpenParts({
|
|
1850
1871
|
state,
|
|
1851
1872
|
emit
|
|
@@ -1868,7 +1889,7 @@ function emitLegacyStepFinishPart({
|
|
|
1868
1889
|
}) {
|
|
1869
1890
|
const event = legacyStepFinishPartToFinishStep(part);
|
|
1870
1891
|
if (!event) return false;
|
|
1871
|
-
const id =
|
|
1892
|
+
const id = isRecord3(part) ? stringValue2(part.id) : void 0;
|
|
1872
1893
|
if (id) {
|
|
1873
1894
|
if (state.legacyStepFinishPartIds.has(id)) return true;
|
|
1874
1895
|
state.legacyStepFinishPartIds.add(id);
|
|
@@ -1938,9 +1959,9 @@ function legacyToolPartStatus(part) {
|
|
|
1938
1959
|
function legacyToolPartInput(part) {
|
|
1939
1960
|
const state = typeof part.state === "object" && part.state !== null ? part.state : void 0;
|
|
1940
1961
|
return {
|
|
1941
|
-
...
|
|
1942
|
-
...
|
|
1943
|
-
...
|
|
1962
|
+
...isRecord3(part.metadata) ? part.metadata : {},
|
|
1963
|
+
...isRecord3(state?.metadata) ? state.metadata : {},
|
|
1964
|
+
...isRecord3(state?.input) ? state.input : {}
|
|
1944
1965
|
};
|
|
1945
1966
|
}
|
|
1946
1967
|
function legacyToolPartOutput(part) {
|
|
@@ -1950,7 +1971,7 @@ function legacyToolPartOutput(part) {
|
|
|
1950
1971
|
}
|
|
1951
1972
|
return state?.output ?? state?.result ?? state?.structured ?? state?.content ?? null;
|
|
1952
1973
|
}
|
|
1953
|
-
function
|
|
1974
|
+
function isRecord3(value) {
|
|
1954
1975
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1955
1976
|
}
|
|
1956
1977
|
function parseToolInput(state, props) {
|
|
@@ -2271,13 +2292,13 @@ function modelRefFromAssistantSnapshot(assistant) {
|
|
|
2271
2292
|
if (model) return model;
|
|
2272
2293
|
const direct = modelRefFromValue(assistant);
|
|
2273
2294
|
if (direct) return direct;
|
|
2274
|
-
if (
|
|
2295
|
+
if (isRecord3(assistant.metadata)) {
|
|
2275
2296
|
return modelRefFromValue(assistant.metadata.assistant);
|
|
2276
2297
|
}
|
|
2277
2298
|
return void 0;
|
|
2278
2299
|
}
|
|
2279
2300
|
function modelRefFromSessionInfo(data) {
|
|
2280
|
-
if (!
|
|
2301
|
+
if (!isRecord3(data)) return void 0;
|
|
2281
2302
|
return modelRefFromValue(data.model) ?? modelRefFromValue(data);
|
|
2282
2303
|
}
|
|
2283
2304
|
function modelRefFromStart(start) {
|
|
@@ -2289,13 +2310,13 @@ function modelRefFromStart(start) {
|
|
|
2289
2310
|
};
|
|
2290
2311
|
}
|
|
2291
2312
|
function modelRefFromValue(value) {
|
|
2292
|
-
if (!
|
|
2293
|
-
const providerID =
|
|
2294
|
-
const modelID =
|
|
2313
|
+
if (!isRecord3(value)) return void 0;
|
|
2314
|
+
const providerID = stringValue2(value.providerID);
|
|
2315
|
+
const modelID = stringValue2(value.modelID ?? value.id);
|
|
2295
2316
|
if (!providerID || !modelID) return void 0;
|
|
2296
2317
|
return { providerID, modelID };
|
|
2297
2318
|
}
|
|
2298
|
-
function
|
|
2319
|
+
function stringValue2(value) {
|
|
2299
2320
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
2300
2321
|
}
|
|
2301
2322
|
function stripWorkDir(file) {
|