@ai-sdk/harness-claude-code 1.0.96 → 1.0.97

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-claude-code
2
2
 
3
+ ## 1.0.97
4
+
5
+ ### Patch Changes
6
+
7
+ - cc9f6ce: fix(harness): stop diagnosing caller-initiated aborts as bridge errors, and serialize bridge turns so a start racing an aborted turn's teardown no longer overlaps it (bounded by a teardown grace period, after which the start proceeds as before)
8
+ - 7608210: feat(harness): add `model` parameter to `HarnessAgent` instead of having each harness adapter support it on their own constructor functions
9
+ - f7bd978: chore(harness): update underlying adapter SDKs to their latest versions
10
+ - 14d4fc0: feat(harness): allow changing harness settings between turns via `prepareCall()` support on `HarnessAgent`
11
+ - Updated dependencies [cc9f6ce]
12
+ - Updated dependencies [7608210]
13
+ - Updated dependencies [6f8a2d7]
14
+ - Updated dependencies [14d4fc0]
15
+ - Updated dependencies [90192f1]
16
+ - @ai-sdk/harness@1.0.93
17
+ - @ai-sdk/provider-utils@5.0.33
18
+
3
19
  ## 1.0.96
4
20
 
5
21
  ### Patch Changes
@@ -141,6 +141,7 @@ var ENV_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
141
141
  var WS_OPEN = 1;
142
142
  async function runBridge(options) {
143
143
  const { bridgeType, bridgeStateDir: bridgeStateDir2, onStart, onStop, onDestroy } = options;
144
+ const teardownGraceMs = options.turnTeardownGraceMs ?? 1e4;
144
145
  const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? "";
145
146
  const bridgeWsPort = options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? "0", 10);
146
147
  const bridgeMetaPath = `${bridgeStateDir2}/bridge-meta.json`;
@@ -157,6 +158,7 @@ async function runBridge(options) {
157
158
  let isFirstTurn = true;
158
159
  let turnAbort;
159
160
  let currentUserMessages;
161
+ let activeTurn;
160
162
  let debugConfig;
161
163
  let consoleCaptureInstalled = false;
162
164
  const envDebugEnabled = ENV_TRUTHY.has(
@@ -347,10 +349,28 @@ async function runBridge(options) {
347
349
  const handleInbound = async (msg, ws) => {
348
350
  switch (msg.type) {
349
351
  case "start": {
352
+ for (; ; ) {
353
+ const pendingTurn = activeTurn;
354
+ if (pendingTurn == null) break;
355
+ turnAbort?.abort();
356
+ currentUserMessages?.close(
357
+ new Error("A new bridge turn replaced the active turn.")
358
+ );
359
+ let graceTimer;
360
+ const settled = await Promise.race([
361
+ pendingTurn.then(() => true),
362
+ new Promise((resolve) => {
363
+ graceTimer = setTimeout(() => resolve(false), teardownGraceMs);
364
+ graceTimer.unref?.();
365
+ })
366
+ ]);
367
+ clearTimeout(graceTimer);
368
+ if (!settled) break;
369
+ }
370
+ let turnFinished;
371
+ const thisTurn = new Promise((resolve) => turnFinished = resolve);
372
+ activeTurn = thisTurn;
350
373
  activeSocket = ws;
351
- currentUserMessages?.close(
352
- new Error("A new bridge turn replaced the active turn.")
353
- );
354
374
  const firstTurn = isFirstTurn;
355
375
  isFirstTurn = false;
356
376
  eventLog = [];
@@ -407,8 +427,12 @@ async function runBridge(options) {
407
427
  if (currentUserMessages === userMessages) {
408
428
  currentUserMessages = void 0;
409
429
  }
410
- currentTurnState = "waiting";
411
- void writeBridgeMeta("waiting");
430
+ if (activeTurn === thisTurn) {
431
+ activeTurn = void 0;
432
+ currentTurnState = "waiting";
433
+ void writeBridgeMeta("waiting");
434
+ }
435
+ turnFinished();
412
436
  }
413
437
  return;
414
438
  }
@@ -1185,12 +1209,19 @@ function nativeToolRequiresApproval(input) {
1185
1209
  async function runTurn(start, turn) {
1186
1210
  const emit = (msg) => turn.emit(msg);
1187
1211
  const abortCtl = new AbortController();
1212
+ let gracefulAbort;
1213
+ let hardAbortTimer;
1214
+ const onHostAbort = () => {
1215
+ if (gracefulAbort) {
1216
+ gracefulAbort();
1217
+ } else {
1218
+ abortCtl.abort();
1219
+ }
1220
+ };
1188
1221
  if (turn.abortSignal.aborted) {
1189
1222
  abortCtl.abort();
1190
1223
  } else {
1191
- turn.abortSignal.addEventListener("abort", () => abortCtl.abort(), {
1192
- once: true
1193
- });
1224
+ turn.abortSignal.addEventListener("abort", onHostAbort, { once: true });
1194
1225
  }
1195
1226
  const streamEventState = createClaudeStreamEventState();
1196
1227
  const mcpServers = { ...start.mcpServers ?? {} };
@@ -1304,6 +1335,11 @@ async function runTurn(start, turn) {
1304
1335
  abortSignal: abortCtl.signal
1305
1336
  }
1306
1337
  });
1338
+ gracefulAbort = () => {
1339
+ hardAbortTimer = setTimeout(() => abortCtl.abort(), 5e3);
1340
+ hardAbortTimer.unref?.();
1341
+ void Promise.resolve().then(() => q.interrupt()).catch(() => abortCtl.abort());
1342
+ };
1307
1343
  let turnUsage;
1308
1344
  let totalCostUsd;
1309
1345
  let emittedTerminalError = false;
@@ -1313,10 +1349,12 @@ async function runTurn(start, turn) {
1313
1349
  if (!normalized || emittedTerminalError || emittedTerminalFinish) return;
1314
1350
  streamEventState.observedTerminalError = normalized;
1315
1351
  emittedTerminalError = true;
1316
- turn.emitError({
1317
- error: normalized,
1318
- message: "claude-code terminal error"
1319
- });
1352
+ if (!turn.abortSignal.aborted) {
1353
+ turn.emitError({
1354
+ error: normalized,
1355
+ message: "claude-code terminal error"
1356
+ });
1357
+ }
1320
1358
  queryInput.close();
1321
1359
  abortCtl.abort();
1322
1360
  };
@@ -1385,12 +1423,21 @@ async function runTurn(start, turn) {
1385
1423
  }
1386
1424
  }
1387
1425
  } catch (err) {
1388
- if (!(abortCtl.signal.aborted && emittedTerminalError)) {
1426
+ if (!turn.abortSignal.aborted && !(abortCtl.signal.aborted && emittedTerminalError)) {
1389
1427
  turn.emitError({ error: err, message: "claude-code turn failed" });
1390
1428
  }
1391
1429
  return;
1392
1430
  } finally {
1431
+ gracefulAbort = void 0;
1432
+ if (hardAbortTimer != null) clearTimeout(hardAbortTimer);
1433
+ turn.abortSignal.removeEventListener("abort", onHostAbort);
1393
1434
  queryInput.close();
1435
+ try {
1436
+ await q.return?.(
1437
+ void 0
1438
+ );
1439
+ } catch {
1440
+ }
1394
1441
  }
1395
1442
  if (emittedTerminalError) return;
1396
1443
  emittedTerminalFinish = true;