@ai-sdk/harness-claude-code 1.0.121 → 1.0.122
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 +12 -0
- package/dist/bridge/index.mjs +22 -13
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +34 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +8 -7
- package/src/bridge/index.ts +6 -5
- package/src/bridge/json-schema-to-zod.ts +16 -3
- package/src/claude-code-harness.ts +48 -10
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';
|
|
|
3
3
|
import { Experimental_SandboxSession } from '@ai-sdk/provider-utils';
|
|
4
4
|
import * as _ai_sdk_harness from '@ai-sdk/harness';
|
|
5
5
|
import { HarnessV1Authentication, HarnessV1CredentialForwarding, HarnessV1PortEndpoint, HarnessV1MintBridgeTokenCallback, HarnessV1, HarnessV1BuiltinTool, HARNESS_V1_BUILTIN_TOOLS } from '@ai-sdk/harness';
|
|
6
|
+
import { SandboxChannelReconnectOptions } from '@ai-sdk/harness/utils';
|
|
6
7
|
|
|
7
8
|
type ClaudeCodeAuthenticationMode = HarnessV1Authentication;
|
|
8
9
|
|
|
@@ -60,6 +61,13 @@ type ClaudeCodeHarnessSettings = {
|
|
|
60
61
|
readonly portEndpoint?: HarnessV1PortEndpoint;
|
|
61
62
|
/** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */
|
|
62
63
|
readonly startupTimeoutMs?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Configures reconnection attempts after an established bridge connection
|
|
66
|
+
* drops. The reconnect window includes connection establishment and
|
|
67
|
+
* backoff delays. Defaults to 30 seconds with exponential backoff from 50
|
|
68
|
+
* milliseconds up to 2 seconds.
|
|
69
|
+
*/
|
|
70
|
+
readonly reconnect?: SandboxChannelReconnectOptions;
|
|
63
71
|
/**
|
|
64
72
|
* Creates the authentication token used by the sandbox bridge. Defaults to
|
|
65
73
|
* a random 32-byte hexadecimal token.
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
resolveSandboxHomeDir,
|
|
23
23
|
SandboxChannel,
|
|
24
24
|
shellQuote,
|
|
25
|
+
sleep,
|
|
25
26
|
warnCredentialBrokeringUnavailable,
|
|
26
27
|
waitForBridgeReady,
|
|
27
28
|
withBridgeToken,
|
|
@@ -442,7 +443,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
|
|
|
442
443
|
]);
|
|
443
444
|
|
|
444
445
|
// src/version.ts
|
|
445
|
-
var VERSION = true ? "1.0.
|
|
446
|
+
var VERSION = true ? "1.0.122" : "0.0.0-test";
|
|
446
447
|
|
|
447
448
|
// src/claude-code-harness.ts
|
|
448
449
|
var CLAUDE_CODE_CLIENT_APP = `ai-sdk/harness-claude-code/${VERSION}`;
|
|
@@ -1164,10 +1165,13 @@ function createClaudeCode(settings = {}) {
|
|
|
1164
1165
|
sessionId: startOpts.sessionId
|
|
1165
1166
|
});
|
|
1166
1167
|
let supportsUserMessageResponses = false;
|
|
1167
|
-
const buildConnect = (endpoint2) => async (
|
|
1168
|
+
const buildConnect = (endpoint2) => async ({
|
|
1169
|
+
abortSignal
|
|
1170
|
+
}) => {
|
|
1168
1171
|
return openBridgeWebSocket({
|
|
1169
1172
|
endpoint: endpoint2,
|
|
1170
1173
|
timeoutMs,
|
|
1174
|
+
abortSignal,
|
|
1171
1175
|
onHello: (supportsResponses) => {
|
|
1172
1176
|
supportsUserMessageResponses = supportsResponses;
|
|
1173
1177
|
}
|
|
@@ -1189,7 +1193,8 @@ function createClaudeCode(settings = {}) {
|
|
|
1189
1193
|
outboundSchema: outboundMessageSchema,
|
|
1190
1194
|
initialLastSeenEventId: coords.lastSeenEventId,
|
|
1191
1195
|
onDiagnostic,
|
|
1192
|
-
onBridgeError
|
|
1196
|
+
onBridgeError,
|
|
1197
|
+
reconnect: settings.reconnect
|
|
1193
1198
|
});
|
|
1194
1199
|
await attachChannel.open(isContinue ? { resume: true } : void 0);
|
|
1195
1200
|
return createSession({
|
|
@@ -1303,6 +1308,7 @@ function createClaudeCode(settings = {}) {
|
|
|
1303
1308
|
outboundSchema: outboundMessageSchema,
|
|
1304
1309
|
onDiagnostic,
|
|
1305
1310
|
onBridgeError,
|
|
1311
|
+
reconnect: settings.reconnect,
|
|
1306
1312
|
// In replay mode the respawned bridge reloaded the finished turn from
|
|
1307
1313
|
// disk; seed the cursor and resume so it streams the tail (incl.
|
|
1308
1314
|
// `finish`) rather than starting empty.
|
|
@@ -1388,9 +1394,15 @@ function openWebSocketAndWaitForBridgeHello({
|
|
|
1388
1394
|
endpoint,
|
|
1389
1395
|
openTimeoutMs,
|
|
1390
1396
|
getHelloTimeoutMs,
|
|
1397
|
+
abortSignal,
|
|
1391
1398
|
onHello
|
|
1392
1399
|
}) {
|
|
1393
1400
|
return new Promise((resolve2, reject) => {
|
|
1401
|
+
const abortReason = () => abortSignal.reason ?? new Error("WebSocket connection aborted");
|
|
1402
|
+
if (abortSignal.aborted) {
|
|
1403
|
+
reject(abortReason());
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1394
1406
|
const ws = new WebSocket(endpoint.url, {
|
|
1395
1407
|
headers: endpoint.headers == null ? void 0 : { ...endpoint.headers }
|
|
1396
1408
|
});
|
|
@@ -1399,6 +1411,7 @@ function openWebSocketAndWaitForBridgeHello({
|
|
|
1399
1411
|
let settled = false;
|
|
1400
1412
|
let openTimer;
|
|
1401
1413
|
let helloTimer;
|
|
1414
|
+
let onAbort;
|
|
1402
1415
|
const cleanup = ({
|
|
1403
1416
|
keepTerminationListeners = false
|
|
1404
1417
|
} = {}) => {
|
|
@@ -1406,6 +1419,9 @@ function openWebSocketAndWaitForBridgeHello({
|
|
|
1406
1419
|
if (helloTimer) clearTimeout(helloTimer);
|
|
1407
1420
|
ws.off("open", onOpen);
|
|
1408
1421
|
ws.off("message", onMessage);
|
|
1422
|
+
if (onAbort != null) {
|
|
1423
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
1424
|
+
}
|
|
1409
1425
|
if (!keepTerminationListeners) {
|
|
1410
1426
|
ws.off("close", onClose);
|
|
1411
1427
|
ws.off("error", onError);
|
|
@@ -1474,6 +1490,7 @@ function openWebSocketAndWaitForBridgeHello({
|
|
|
1474
1490
|
cleanup();
|
|
1475
1491
|
};
|
|
1476
1492
|
const onError = (err) => settle(err);
|
|
1493
|
+
onAbort = () => settle(abortReason());
|
|
1477
1494
|
openTimer = setTimeout(
|
|
1478
1495
|
() => settle(new Error(`WebSocket open timed out after ${openTimeoutMs}ms`)),
|
|
1479
1496
|
openTimeoutMs
|
|
@@ -1483,17 +1500,19 @@ function openWebSocketAndWaitForBridgeHello({
|
|
|
1483
1500
|
ws.on("message", onMessage);
|
|
1484
1501
|
ws.on("close", onClose);
|
|
1485
1502
|
ws.on("error", onError);
|
|
1503
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1486
1504
|
});
|
|
1487
1505
|
}
|
|
1488
1506
|
async function openBridgeWebSocket({
|
|
1489
1507
|
endpoint,
|
|
1490
1508
|
timeoutMs,
|
|
1509
|
+
abortSignal,
|
|
1491
1510
|
onHello
|
|
1492
1511
|
}) {
|
|
1493
1512
|
const deadline = Date.now() + timeoutMs;
|
|
1494
1513
|
let attempt = 0;
|
|
1495
1514
|
let lastError;
|
|
1496
|
-
while (Date.now() < deadline) {
|
|
1515
|
+
while (!abortSignal.aborted && Date.now() < deadline) {
|
|
1497
1516
|
attempt++;
|
|
1498
1517
|
try {
|
|
1499
1518
|
const remaining = Math.max(1, deadline - Date.now());
|
|
@@ -1501,15 +1520,25 @@ async function openBridgeWebSocket({
|
|
|
1501
1520
|
endpoint,
|
|
1502
1521
|
openTimeoutMs: Math.min(1e4, remaining),
|
|
1503
1522
|
getHelloTimeoutMs: () => Math.min(5e3, Math.max(1, deadline - Date.now())),
|
|
1523
|
+
abortSignal,
|
|
1504
1524
|
onHello
|
|
1505
1525
|
});
|
|
1506
1526
|
} catch (err) {
|
|
1527
|
+
if (abortSignal.aborted) {
|
|
1528
|
+
throw abortSignal.reason ?? new Error("WebSocket connection aborted");
|
|
1529
|
+
}
|
|
1507
1530
|
lastError = err;
|
|
1508
1531
|
const remaining = deadline - Date.now();
|
|
1509
1532
|
if (remaining <= 0) break;
|
|
1510
|
-
await sleep(
|
|
1533
|
+
await sleep({
|
|
1534
|
+
ms: Math.min(250 * attempt, 1e3, remaining),
|
|
1535
|
+
abortSignal
|
|
1536
|
+
});
|
|
1511
1537
|
}
|
|
1512
1538
|
}
|
|
1539
|
+
if (abortSignal.aborted) {
|
|
1540
|
+
throw abortSignal.reason ?? new Error("WebSocket connection aborted");
|
|
1541
|
+
}
|
|
1513
1542
|
throw new Error(
|
|
1514
1543
|
`claude-code bridge did not complete WebSocket handshake within ${timeoutMs}ms after ${attempt} attempt(s). Last error: ${formatUnknownError(lastError)}`
|
|
1515
1544
|
);
|
|
@@ -1526,12 +1555,6 @@ function webSocketMessageToString(raw) {
|
|
|
1526
1555
|
}
|
|
1527
1556
|
return String(raw);
|
|
1528
1557
|
}
|
|
1529
|
-
function sleep(ms) {
|
|
1530
|
-
return new Promise((resolve2) => {
|
|
1531
|
-
const timer = setTimeout(resolve2, ms);
|
|
1532
|
-
timer.unref?.();
|
|
1533
|
-
});
|
|
1534
|
-
}
|
|
1535
1558
|
function formatUnknownError(error) {
|
|
1536
1559
|
if (error instanceof Error) return error.message;
|
|
1537
1560
|
return String(error);
|