@ai-sdk/harness-opencode 1.0.51 → 1.0.53

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,21 @@
1
1
  # @ai-sdk/harness-opencode
2
2
 
3
+ ## 1.0.53
4
+
5
+ ### Patch Changes
6
+
7
+ - 2d9c434: fix(harness-opencode): avoid bridge import from provider-utils and rely on more granular types
8
+ - @ai-sdk/harness@1.0.52
9
+
10
+ ## 1.0.52
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [fa95504]
15
+ - Updated dependencies [226a679]
16
+ - @ai-sdk/provider-utils@5.0.17
17
+ - @ai-sdk/harness@1.0.51
18
+
3
19
  ## 1.0.51
4
20
 
5
21
  ### Patch Changes
@@ -461,7 +461,6 @@ function serialiseError(err) {
461
461
  }
462
462
 
463
463
  // src/bridge/index.ts
464
- import { isRecord as isRecord5 } from "@ai-sdk/provider-utils";
465
464
  import { randomUUID } from "crypto";
466
465
  import { mkdirSync } from "fs";
467
466
  import path2 from "path";
@@ -471,8 +470,12 @@ import {
471
470
  createOpencodeServer
472
471
  } from "@opencode-ai/sdk/v2";
473
472
 
473
+ // src/bridge/opencode-types.ts
474
+ function asOpenCodeObject(value) {
475
+ return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
476
+ }
477
+
474
478
  // src/bridge/opencode-events.ts
475
- import { isRecord } from "@ai-sdk/provider-utils";
476
479
  function createTranslationState() {
477
480
  return {
478
481
  streamStarted: false,
@@ -496,29 +499,32 @@ function emitOpenCodeStreamStart({
496
499
  state,
497
500
  emit
498
501
  }) {
499
- if (state.streamStarted || !isRecord(info)) return;
500
- if (info.role !== "assistant" && info.type !== "assistant") return;
501
- const providerID = stringValue(info.providerID);
502
- const modelID = stringValue(info.modelID);
502
+ if (state.streamStarted) return;
503
+ const message = openCodeMessageInfoFromValue(info);
504
+ if (!message) return;
505
+ if (message.role !== "assistant" && message.type !== "assistant") return;
506
+ const providerID = stringValue(message.providerID);
507
+ const modelID = stringValue(message.modelID);
503
508
  const modelId = providerID && modelID ? `${providerID}/${modelID}` : void 0;
504
509
  state.streamStarted = true;
505
510
  emit({ type: "stream-start", ...modelId ? { modelId } : {} });
506
511
  }
507
512
  function unwrapOpenCodeEvent(rawEvent) {
508
- if (!rawEvent || typeof rawEvent !== "object") return void 0;
509
- const raw = rawEvent;
513
+ const raw = asOpenCodeObject(rawEvent);
514
+ if (!raw) return void 0;
510
515
  if (raw.type === "sync" && raw.syncEvent) {
511
- const sync = raw.syncEvent;
516
+ const sync = asOpenCodeObject(raw.syncEvent);
517
+ if (!sync) return void 0;
512
518
  return {
513
519
  id: String(sync.id ?? raw.id ?? ""),
514
520
  type: stripSyncVersion(String(sync.type ?? "")),
515
- properties: asRecord(sync.data) ?? {}
521
+ properties: openCodeEventPropertiesFromValue(sync.data) ?? {}
516
522
  };
517
523
  }
518
524
  return {
519
525
  id: typeof raw.id === "string" ? raw.id : void 0,
520
526
  type: typeof raw.type === "string" ? stripSyncVersion(raw.type) : void 0,
521
- properties: asRecord(raw.properties) ?? asRecord(raw.data) ?? {}
527
+ properties: openCodeEventPropertiesFromValue(raw.properties) ?? openCodeEventPropertiesFromValue(raw.data) ?? {}
522
528
  };
523
529
  }
524
530
  function getOpenCodeEventSessionId(event) {
@@ -530,9 +536,8 @@ function getOpenCodeEventSessionId(event) {
530
536
  return props.id;
531
537
  }
532
538
  const part = props.part;
533
- if (part && typeof part === "object" && !Array.isArray(part) && typeof part.sessionID === "string") {
534
- return part.sessionID;
535
- }
539
+ const partObject = asOpenCodeObject(part);
540
+ if (typeof partObject?.sessionID === "string") return partObject.sessionID;
536
541
  return void 0;
537
542
  }
538
543
  function isStepSettlementEvent(event) {
@@ -595,17 +600,17 @@ function emitLegacyTextPartUpdate({
595
600
  state,
596
601
  emit
597
602
  }) {
598
- if (!isRecord(part)) return false;
599
- if (part.type !== "text" && part.type !== "reasoning") return false;
600
- const id = stringValue(part.id);
603
+ const textPart = legacyTextPartFromValue(part);
604
+ if (!textPart) return false;
605
+ const id = stringValue(textPart.id);
601
606
  if (!id) return true;
602
- const messageID = stringValue(part.messageID);
607
+ const messageID = stringValue(textPart.messageID);
603
608
  if (messageID && state.messageRoles.get(messageID) === "user") return true;
604
- const isReasoning = part.type === "reasoning";
609
+ const isReasoning = textPart.type === "reasoning";
605
610
  const ids = isReasoning ? state.legacyReasoningPartIds : state.legacyTextPartIds;
606
611
  const deltaMap = isReasoning ? state.reasoningDeltas : state.textDeltas;
607
612
  const deltaType = isReasoning ? "reasoning-delta" : "text-delta";
608
- const text = typeof part.text === "string" ? part.text : void 0;
613
+ const text = typeof textPart.text === "string" ? textPart.text : void 0;
609
614
  startLegacyPart({
610
615
  ids,
611
616
  id,
@@ -622,7 +627,7 @@ function emitLegacyTextPartUpdate({
622
627
  });
623
628
  deltaMap.set(id, text);
624
629
  }
625
- if (legacyPartEnded(part)) {
630
+ if (textPart.time?.end != null) {
626
631
  ids.delete(id);
627
632
  deltaMap.delete(id);
628
633
  emit({ type: isReasoning ? "reasoning-end" : "text-end", id });
@@ -632,9 +637,6 @@ function emitLegacyTextPartUpdate({
632
637
  function stripSyncVersion(type) {
633
638
  return type.replace(/\.\d+$/, "");
634
639
  }
635
- function asRecord(value) {
636
- return isRecord(value) ? value : void 0;
637
- }
638
640
  function stringValue(value) {
639
641
  return typeof value === "string" && value.length > 0 ? value : void 0;
640
642
  }
@@ -654,18 +656,27 @@ function startLegacyPart({
654
656
  ids.add(id);
655
657
  emit({ type: `${type}-start`, id });
656
658
  }
657
- function legacyPartEnded(part) {
658
- return isRecord(part.time) && part.time.end != null;
659
+ function openCodeMessageInfoFromValue(value) {
660
+ return asOpenCodeObject(value);
661
+ }
662
+ function openCodeEventPropertiesFromValue(value) {
663
+ return asOpenCodeObject(value);
664
+ }
665
+ function legacyTextPartFromValue(value) {
666
+ const part = asOpenCodeObject(value);
667
+ if (!part || part.type !== "text" && part.type !== "reasoning") {
668
+ return void 0;
669
+ }
670
+ return {
671
+ type: part.type,
672
+ id: part.id,
673
+ messageID: part.messageID,
674
+ text: part.text,
675
+ time: asOpenCodeObject(part.time)
676
+ };
659
677
  }
660
-
661
- // src/bridge/create-emit-stream-event.ts
662
- import { isRecord as isRecord4 } from "@ai-sdk/provider-utils";
663
-
664
- // src/bridge/opencode-finish-step.ts
665
- import { isRecord as isRecord3 } from "@ai-sdk/provider-utils";
666
678
 
667
679
  // src/bridge/opencode-usage.ts
668
- import { isRecord as isRecord2 } from "@ai-sdk/provider-utils";
669
680
  function mapUsage(tokens) {
670
681
  const value = extractOpenCodeTokens(tokens) ?? zeroOpenCodeTokens();
671
682
  const cacheRead = value.cache.read;
@@ -687,9 +698,9 @@ function defaultUsage() {
687
698
  return mapUsage(zeroOpenCodeTokens());
688
699
  }
689
700
  function extractSessionTokens(value) {
690
- const record = asRecord2(value);
701
+ const record = asOpenCodeObject(value);
691
702
  if (!record) return void 0;
692
- const tokens = extractOpenCodeTokens(record.tokens) ?? extractOpenCodeTokens(asRecord2(record.info)?.tokens) ?? extractOpenCodeTokens(asRecord2(record.data)?.tokens) ?? extractOpenCodeTokens(asRecord2(asRecord2(record.data)?.data)?.tokens);
703
+ const tokens = extractOpenCodeTokens(record.tokens) ?? extractOpenCodeTokens(record.info?.tokens) ?? extractOpenCodeTokens(record.data?.tokens) ?? extractOpenCodeTokens(record.data?.data?.tokens);
693
704
  return tokens;
694
705
  }
695
706
  function subtractSessionTokens({
@@ -739,8 +750,8 @@ function addUsage({
739
750
  };
740
751
  }
741
752
  function extractOpenCodeTokens(value) {
742
- const record = asRecord2(value);
743
- const cache = asRecord2(record?.cache);
753
+ const record = asOpenCodeObject(value);
754
+ const cache = asOpenCodeObject(record?.cache);
744
755
  if (!record || !cache) return void 0;
745
756
  return {
746
757
  input: numberValue(record.input),
@@ -761,10 +772,7 @@ function zeroOpenCodeTokens() {
761
772
  };
762
773
  }
763
774
  function asTokenGroup(value) {
764
- return asRecord2(value) ?? {};
765
- }
766
- function asRecord2(value) {
767
- return isRecord2(value) ? value : void 0;
775
+ return asOpenCodeObject(value) ?? {};
768
776
  }
769
777
  function numberValue(value) {
770
778
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -792,10 +800,11 @@ function mapOpenCodeFinishReason(reason) {
792
800
  if (normalized === "stop" || normalized === "end") return "stop";
793
801
  return "other";
794
802
  }
795
- function legacyStepFinishPartToFinishStep(part) {
796
- if (!isRecord3(part) || part.type !== "step-finish") return void 0;
803
+ function translateLegacyStepFinishPart(value) {
804
+ const part = asOpenCodeObject(value);
805
+ if (!part || part.type !== "step-finish") return void 0;
797
806
  const rawFinish = typeof part.reason === "string" ? part.reason : "stop";
798
- return {
807
+ const event = {
799
808
  type: "finish-step",
800
809
  finishReason: {
801
810
  unified: mapOpenCodeFinishReason(rawFinish),
@@ -804,6 +813,10 @@ function legacyStepFinishPartToFinishStep(part) {
804
813
  usage: mapUsage(part.tokens),
805
814
  ...typeof part.cost === "number" ? { harnessMetadata: { opencode: { cost: part.cost } } } : {}
806
815
  };
816
+ return {
817
+ event,
818
+ ...typeof part.id === "string" ? { partId: part.id } : {}
819
+ };
807
820
  }
808
821
 
809
822
  // src/bridge/create-emit-stream-event.ts
@@ -823,8 +836,8 @@ function createEmitStreamEvent({
823
836
  const type = event.type;
824
837
  const props = event.properties ?? {};
825
838
  if (type === "message.updated") {
826
- const info = props.info;
827
- if (isRecord4(info)) {
839
+ const info = openCodeMessageInfoFromValue(props.info);
840
+ if (info) {
828
841
  const id = stringValue2(info.id);
829
842
  const role = stringValue2(info.role);
830
843
  if (id && role) state.messageRoles.set(id, role);
@@ -994,7 +1007,7 @@ function createEmitStreamEvent({
994
1007
  }
995
1008
  if (type === "session.next.retried") {
996
1009
  const error = props.error ?? event;
997
- if (isRecord4(error) && error.isRetryable === false) {
1010
+ if (openCodeErrorFromValue(error)?.isRetryable === false) {
998
1011
  emitError({
999
1012
  error,
1000
1013
  message: "OpenCode session retry failed"
@@ -1068,9 +1081,9 @@ function emitLegacyStepFinishPart({
1068
1081
  state,
1069
1082
  emit
1070
1083
  }) {
1071
- const event = legacyStepFinishPartToFinishStep(part);
1072
- if (!event) return false;
1073
- const id = isRecord4(part) ? stringValue2(part.id) : void 0;
1084
+ const translated = translateLegacyStepFinishPart(part);
1085
+ if (!translated) return false;
1086
+ const { event, partId: id } = translated;
1074
1087
  if (id) {
1075
1088
  if (state.legacyStepFinishPartIds.has(id)) return true;
1076
1089
  state.legacyStepFinishPartIds.add(id);
@@ -1089,16 +1102,12 @@ function emitLegacyToolPart({
1089
1102
  getHostToolName: getHostToolName2,
1090
1103
  authorizeHostToolCall: authorizeHostToolCall2
1091
1104
  }) {
1092
- if (!part || typeof part !== "object") return;
1093
- const toolPart = part;
1094
- if (toolPart.type !== "tool") return;
1095
- const status = legacyToolPartStatus(toolPart);
1105
+ const toolPart = legacyToolPartFromValue(part);
1106
+ if (!toolPart) return;
1107
+ const status = toolPart.status;
1096
1108
  if (status !== "running" && status !== "completed" && status !== "error") {
1097
1109
  return;
1098
1110
  }
1099
- if (typeof toolPart.tool !== "string" || typeof toolPart.callID !== "string") {
1100
- return;
1101
- }
1102
1111
  const callID = toolPart.callID;
1103
1112
  const rawToolName = toolPart.tool;
1104
1113
  const toolName = toWireToolName2(rawToolName);
@@ -1123,7 +1132,7 @@ function emitLegacyToolPart({
1123
1132
  ...nativeNameField2({ nativeName: rawToolName, toolName }),
1124
1133
  input: JSON.stringify(legacyToolPartInput(toolPart)),
1125
1134
  providerExecuted: true,
1126
- ...toolPart.provider?.metadata ? { providerMetadata: toolPart.provider.metadata } : {}
1135
+ ...toolPart.providerMetadata ? { providerMetadata: toolPart.providerMetadata } : {}
1127
1136
  });
1128
1137
  }
1129
1138
  if ((status === "completed" || status === "error") && !state.toolResultsEmitted.has(callID)) {
@@ -1137,19 +1146,15 @@ function emitLegacyToolPart({
1137
1146
  });
1138
1147
  }
1139
1148
  }
1140
- function legacyToolPartStatus(part) {
1141
- return typeof part.state === "string" ? part.state : typeof part.state === "object" && part.state !== null ? String(part.state.status ?? "") : void 0;
1142
- }
1143
1149
  function legacyToolPartInput(part) {
1144
- const state = typeof part.state === "object" && part.state !== null ? part.state : void 0;
1145
1150
  return {
1146
- ...isRecord4(part.metadata) ? part.metadata : {},
1147
- ...isRecord4(state?.metadata) ? state.metadata : {},
1148
- ...isRecord4(state?.input) ? state.input : {}
1151
+ ...part.metadata ?? {},
1152
+ ...part.state?.metadata ?? {},
1153
+ ...part.state?.input ?? {}
1149
1154
  };
1150
1155
  }
1151
1156
  function legacyToolPartOutput(part) {
1152
- const state = typeof part.state === "object" && part.state !== null ? part.state : void 0;
1157
+ const state = part.state;
1153
1158
  if (state?.status === "error") {
1154
1159
  return state.error ?? part.error ?? state.result ?? "tool failed";
1155
1160
  }
@@ -1174,9 +1179,10 @@ function nextRetryEventMessage({
1174
1179
  details.push(`attempt ${props.attempt}`);
1175
1180
  }
1176
1181
  const error = props.error;
1177
- if (isRecord4(error)) {
1178
- const message = stringValue2(error.message) ?? (isRecord4(error.data) ? stringValue2(error.data.message) : void 0);
1179
- const statusCode = error.statusCode;
1182
+ const errorDetails = openCodeErrorFromValue(error);
1183
+ if (errorDetails) {
1184
+ const message = stringValue2(errorDetails.message) ?? stringValue2(errorDetails.data?.message);
1185
+ const statusCode = errorDetails.statusCode;
1180
1186
  if (typeof statusCode === "number") {
1181
1187
  details.push(`HTTP ${statusCode}`);
1182
1188
  }
@@ -1189,6 +1195,45 @@ function nextRetryEventMessage({
1189
1195
  function stringValue2(value) {
1190
1196
  return typeof value === "string" && value.length > 0 ? value : void 0;
1191
1197
  }
1198
+ function legacyToolPartFromValue(value) {
1199
+ const part = asOpenCodeObject(value);
1200
+ if (!part || part.type !== "tool" || typeof part.tool !== "string" || typeof part.callID !== "string") {
1201
+ return void 0;
1202
+ }
1203
+ const stateObject = asOpenCodeObject(part.state);
1204
+ const provider = asOpenCodeObject(part.provider);
1205
+ const state = stateObject ? {
1206
+ status: stringValue2(stateObject.status),
1207
+ input: asOpenCodeObject(stateObject.input),
1208
+ metadata: asOpenCodeObject(stateObject.metadata),
1209
+ output: stateObject.output,
1210
+ result: stateObject.result,
1211
+ structured: stateObject.structured,
1212
+ content: stateObject.content,
1213
+ error: stateObject.error
1214
+ } : void 0;
1215
+ return {
1216
+ type: "tool",
1217
+ callID: part.callID,
1218
+ tool: part.tool,
1219
+ status: typeof part.state === "string" ? part.state : state?.status ?? void 0,
1220
+ state,
1221
+ metadata: asOpenCodeObject(part.metadata),
1222
+ providerMetadata: provider?.metadata,
1223
+ error: part.error
1224
+ };
1225
+ }
1226
+ function openCodeErrorFromValue(value) {
1227
+ const error = asOpenCodeObject(value);
1228
+ if (!error) return void 0;
1229
+ const data = asOpenCodeObject(error.data);
1230
+ return {
1231
+ isRetryable: error.isRetryable,
1232
+ message: error.message,
1233
+ statusCode: error.statusCode,
1234
+ data: data ? { message: data.message } : void 0
1235
+ };
1236
+ }
1192
1237
 
1193
1238
  // src/bridge/opencode-path.ts
1194
1239
  import path from "path";
@@ -2022,7 +2067,7 @@ async function handlePermissionV2({
2022
2067
  action: String(props.action ?? ""),
2023
2068
  resources: Array.isArray(props.resources) ? props.resources.map(String) : [],
2024
2069
  requestID,
2025
- toolCallId: typeof props.source === "object" && props.source !== null && "callID" in props.source ? String(props.source.callID) : requestID,
2070
+ toolCallId: String(props.source?.callID ?? requestID),
2026
2071
  permissionMode,
2027
2072
  builtinToolFiltering,
2028
2073
  turn,
@@ -2047,11 +2092,12 @@ async function handlePermission({
2047
2092
  const props = event.properties ?? {};
2048
2093
  const requestID = String(props.id ?? "");
2049
2094
  if (!requestID) return;
2095
+ const tool = asOpenCodeObject(props.tool);
2050
2096
  const reply = await selectPermissionReply({
2051
2097
  action: String(props.permission ?? ""),
2052
2098
  resources: Array.isArray(props.patterns) ? props.patterns.map(String) : [],
2053
2099
  requestID,
2054
- toolCallId: typeof props.tool === "object" && props.tool !== null && "callID" in props.tool ? String(props.tool.callID) : requestID,
2100
+ toolCallId: String(tool?.callID ?? requestID),
2055
2101
  permissionMode,
2056
2102
  builtinToolFiltering,
2057
2103
  turn,
@@ -2317,14 +2363,12 @@ function modelRefFromAssistantSnapshot(assistant) {
2317
2363
  if (model) return model;
2318
2364
  const direct = modelRefFromValue(assistant);
2319
2365
  if (direct) return direct;
2320
- if (isRecord5(assistant.metadata)) {
2321
- return modelRefFromValue(assistant.metadata.assistant);
2322
- }
2323
- return void 0;
2366
+ return modelRefFromValue(asOpenCodeObject(assistant.metadata)?.assistant);
2324
2367
  }
2325
2368
  function modelRefFromSessionInfo(data) {
2326
- if (!isRecord5(data)) return void 0;
2327
- return modelRefFromValue(data.model) ?? modelRefFromValue(data);
2369
+ const session = asOpenCodeObject(data);
2370
+ if (!session) return void 0;
2371
+ return modelRefFromValue(session.model) ?? modelRefFromObject(session);
2328
2372
  }
2329
2373
  function modelRefFromStart(start) {
2330
2374
  const model = splitModel(start.model, start.provider);
@@ -2335,7 +2379,10 @@ function modelRefFromStart(start) {
2335
2379
  };
2336
2380
  }
2337
2381
  function modelRefFromValue(value) {
2338
- if (!isRecord5(value)) return void 0;
2382
+ const model = asOpenCodeObject(value);
2383
+ return model ? modelRefFromObject(model) : void 0;
2384
+ }
2385
+ function modelRefFromObject(value) {
2339
2386
  const providerID = stringValue2(value.providerID);
2340
2387
  const modelID = stringValue2(value.modelID ?? value.id);
2341
2388
  if (!providerID || !modelID) return void 0;