@ai-sdk/harness-claude-code 1.0.68 → 1.0.70

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/dist/index.js CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  writeSkills as writeHarnessSkills
23
23
  } from "@ai-sdk/harness/utils";
24
24
  import {
25
+ safeParseJSON,
25
26
  tool
26
27
  } from "@ai-sdk/provider-utils";
27
28
  import { WebSocket } from "ws";
@@ -151,7 +152,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
151
152
  ]);
152
153
 
153
154
  // src/version.ts
154
- var VERSION = true ? "1.0.68" : "0.0.0-test";
155
+ var VERSION = true ? "1.0.70" : "0.0.0-test";
155
156
 
156
157
  // src/claude-code-harness.ts
157
158
  var CLAUDE_CODE_CLIENT_APP = `ai-sdk/harness-claude-code/${VERSION}`;
@@ -1045,32 +1046,77 @@ async function readBridgeAsset(name) {
1045
1046
  }
1046
1047
  throw lastErr ?? new Error(`bridge asset not found: ${name}`);
1047
1048
  }
1048
- async function waitForBridgeHello({
1049
- ws,
1050
- timeoutMs
1049
+ function openWebSocketAndWaitForBridgeHello({
1050
+ url,
1051
+ openTimeoutMs,
1052
+ getHelloTimeoutMs
1051
1053
  }) {
1052
- await new Promise((resolve, reject) => {
1054
+ return new Promise((resolve, reject) => {
1055
+ const ws = new WebSocket(url);
1056
+ let opened = false;
1057
+ let sawBridgeHello = false;
1053
1058
  let settled = false;
1059
+ let openTimer;
1060
+ let helloTimer;
1054
1061
  const cleanup = () => {
1062
+ if (openTimer) clearTimeout(openTimer);
1063
+ if (helloTimer) clearTimeout(helloTimer);
1064
+ ws.off("open", onOpen);
1055
1065
  ws.off("message", onMessage);
1056
1066
  ws.off("close", onClose);
1057
1067
  ws.off("error", onError);
1058
- if (timer) clearTimeout(timer);
1059
1068
  };
1060
1069
  const settle = (err) => {
1061
1070
  if (settled) return;
1062
1071
  settled = true;
1063
1072
  cleanup();
1064
- if (err) reject(err);
1065
- else resolve();
1073
+ if (err) {
1074
+ try {
1075
+ ws.terminate();
1076
+ } catch {
1077
+ }
1078
+ reject(err);
1079
+ } else {
1080
+ resolve(ws);
1081
+ }
1066
1082
  };
1067
- const onMessage = (raw) => {
1068
- try {
1069
- const text = typeof raw === "string" ? raw : raw.toString ? raw.toString("utf8") : String(raw);
1070
- const parsed = JSON.parse(text);
1071
- if (parsed?.type === "bridge-hello") settle();
1072
- } catch {
1083
+ const tryResolve = () => {
1084
+ if (opened && sawBridgeHello) settle();
1085
+ };
1086
+ const startHelloTimer = () => {
1087
+ if (helloTimer) return;
1088
+ const helloTimeoutMs = getHelloTimeoutMs();
1089
+ helloTimer = setTimeout(
1090
+ () => settle(
1091
+ new Error(
1092
+ `claude-code bridge did not send bridge-hello within ${helloTimeoutMs}ms`
1093
+ )
1094
+ ),
1095
+ helloTimeoutMs
1096
+ );
1097
+ helloTimer.unref?.();
1098
+ };
1099
+ const onOpen = () => {
1100
+ opened = true;
1101
+ if (openTimer) {
1102
+ clearTimeout(openTimer);
1103
+ openTimer = void 0;
1073
1104
  }
1105
+ startHelloTimer();
1106
+ tryResolve();
1107
+ };
1108
+ const onMessage = (raw) => {
1109
+ void (async () => {
1110
+ const parsed = await safeParseJSON({
1111
+ text: webSocketMessageToString(raw)
1112
+ });
1113
+ if (!parsed.success || settled) return;
1114
+ const value = parsed.value;
1115
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && value.type === "bridge-hello") {
1116
+ sawBridgeHello = true;
1117
+ tryResolve();
1118
+ }
1119
+ })();
1074
1120
  };
1075
1121
  const onClose = () => {
1076
1122
  settle(
@@ -1078,15 +1124,12 @@ async function waitForBridgeHello({
1078
1124
  );
1079
1125
  };
1080
1126
  const onError = (err) => settle(err);
1081
- const timer = setTimeout(
1082
- () => settle(
1083
- new Error(
1084
- `claude-code bridge did not send bridge-hello within ${timeoutMs}ms`
1085
- )
1086
- ),
1087
- timeoutMs
1127
+ openTimer = setTimeout(
1128
+ () => settle(new Error(`WebSocket open timed out after ${openTimeoutMs}ms`)),
1129
+ openTimeoutMs
1088
1130
  );
1089
- timer.unref?.();
1131
+ openTimer.unref?.();
1132
+ ws.on("open", onOpen);
1090
1133
  ws.on("message", onMessage);
1091
1134
  ws.on("close", onClose);
1092
1135
  ws.on("error", onError);
@@ -1101,24 +1144,15 @@ async function openBridgeWebSocket({
1101
1144
  let lastError;
1102
1145
  while (Date.now() < deadline) {
1103
1146
  attempt++;
1104
- let ws;
1105
1147
  try {
1106
1148
  const remaining = Math.max(1, deadline - Date.now());
1107
- ws = await openWebSocket({
1149
+ return await openWebSocketAndWaitForBridgeHello({
1108
1150
  url: wsUrl,
1109
- timeoutMs: Math.min(1e4, remaining)
1110
- });
1111
- await waitForBridgeHello({
1112
- ws,
1113
- timeoutMs: Math.min(5e3, Math.max(1, deadline - Date.now()))
1151
+ openTimeoutMs: Math.min(1e4, remaining),
1152
+ getHelloTimeoutMs: () => Math.min(5e3, Math.max(1, deadline - Date.now()))
1114
1153
  });
1115
- return ws;
1116
1154
  } catch (err) {
1117
1155
  lastError = err;
1118
- try {
1119
- ws?.close();
1120
- } catch {
1121
- }
1122
1156
  const remaining = deadline - Date.now();
1123
1157
  if (remaining <= 0) break;
1124
1158
  await sleep(Math.min(250 * attempt, 1e3, remaining));
@@ -1128,37 +1162,17 @@ async function openBridgeWebSocket({
1128
1162
  `claude-code bridge did not complete WebSocket handshake within ${timeoutMs}ms after ${attempt} attempt(s). Last error: ${formatUnknownError(lastError)}`
1129
1163
  );
1130
1164
  }
1131
- function openWebSocket({
1132
- url,
1133
- timeoutMs
1134
- }) {
1135
- return new Promise((resolve, reject) => {
1136
- const ws = new WebSocket(url);
1137
- const timer = setTimeout(() => {
1138
- cleanup();
1139
- try {
1140
- ws.terminate();
1141
- } catch {
1142
- }
1143
- reject(new Error(`WebSocket open timed out after ${timeoutMs}ms`));
1144
- }, timeoutMs);
1145
- timer.unref?.();
1146
- const cleanup = () => {
1147
- clearTimeout(timer);
1148
- ws.off("open", onOpen);
1149
- ws.off("error", onError);
1150
- };
1151
- const onOpen = () => {
1152
- cleanup();
1153
- resolve(ws);
1154
- };
1155
- const onError = (err) => {
1156
- cleanup();
1157
- reject(err);
1158
- };
1159
- ws.once("open", onOpen);
1160
- ws.once("error", onError);
1161
- });
1165
+ function webSocketMessageToString(raw) {
1166
+ if (typeof raw === "string") return raw;
1167
+ if (Buffer.isBuffer(raw)) return raw.toString("utf8");
1168
+ if (Array.isArray(raw)) return Buffer.concat(raw).toString("utf8");
1169
+ if (raw instanceof ArrayBuffer) return Buffer.from(raw).toString("utf8");
1170
+ if (ArrayBuffer.isView(raw)) {
1171
+ return Buffer.from(raw.buffer, raw.byteOffset, raw.byteLength).toString(
1172
+ "utf8"
1173
+ );
1174
+ }
1175
+ return String(raw);
1162
1176
  }
1163
1177
  function sleep(ms) {
1164
1178
  return new Promise((resolve) => {