@ai-sdk/harness-opencode 1.0.36 → 1.0.38

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # @ai-sdk/harness-opencode
2
2
 
3
+ ## 1.0.38
4
+
5
+ ### Patch Changes
6
+
7
+ - ab7f97a: fix(harness-opencode): route OpenCode reasoning deltas to the reasoning part instead of duplicating them as text
8
+ - Updated dependencies [b460541]
9
+ - Updated dependencies [079591e]
10
+ - @ai-sdk/harness@1.0.37
11
+
12
+ ## 1.0.37
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [cd06458]
17
+ - @ai-sdk/provider-utils@5.0.11
18
+ - @ai-sdk/harness@1.0.36
19
+
3
20
  ## 1.0.36
4
21
 
5
22
  ### Patch Changes
@@ -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 (!isRecord(part) || part.type !== "step-finish") return void 0;
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 isRecord(value) {
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 (isRecord2(error)) {
1225
- const message = stringValue(error.message) ?? (isRecord2(error.data) ? stringValue(error.data.message) : void 0);
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 (isRecord2(info)) {
1527
- const id = stringValue(info.id);
1528
- const role = stringValue(info.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
- const field = String(props.field ?? "");
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 (isRecord2(error) && error.isRetryable === false) {
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 = isRecord2(part) ? stringValue(part.id) : void 0;
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
- ...isRecord2(part.metadata) ? part.metadata : {},
1942
- ...isRecord2(state?.metadata) ? state.metadata : {},
1943
- ...isRecord2(state?.input) ? state.input : {}
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 isRecord2(value) {
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 (isRecord2(assistant.metadata)) {
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 (!isRecord2(data)) return void 0;
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 (!isRecord2(value)) return void 0;
2293
- const providerID = stringValue(value.providerID);
2294
- const modelID = stringValue(value.modelID ?? value.id);
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 stringValue(value) {
2319
+ function stringValue2(value) {
2299
2320
  return typeof value === "string" && value.length > 0 ? value : void 0;
2300
2321
  }
2301
2322
  function stripWorkDir(file) {