@ai-sdk/harness-claude-code 1.0.79 → 1.0.81
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 +18 -0
- package/dist/bridge/index.mjs +252 -38
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +118 -33
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/index.ts +135 -29
- package/src/claude-code-harness.ts +152 -33
package/dist/index.js
CHANGED
|
@@ -8,12 +8,15 @@ import {
|
|
|
8
8
|
} from "@ai-sdk/harness";
|
|
9
9
|
import {
|
|
10
10
|
classifyDiskLog,
|
|
11
|
+
experimental_createBridgeUserMessageSubmitter,
|
|
11
12
|
createBridgeErrorHandler,
|
|
12
13
|
createBridgeStartupError,
|
|
13
14
|
drainBridgeProcessStream,
|
|
14
15
|
forwardBridgeProcessStream,
|
|
16
|
+
getRestrictedSandboxSession,
|
|
15
17
|
markBridgeStarting,
|
|
16
18
|
maskSandboxCredentials,
|
|
19
|
+
resolveSandboxDefaultWorkingDirectory,
|
|
17
20
|
resolveSandboxHomeDir,
|
|
18
21
|
SandboxChannel,
|
|
19
22
|
shellQuote,
|
|
@@ -263,7 +266,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
|
|
|
263
266
|
]);
|
|
264
267
|
|
|
265
268
|
// src/version.ts
|
|
266
|
-
var VERSION = true ? "1.0.
|
|
269
|
+
var VERSION = true ? "1.0.81" : "0.0.0-test";
|
|
267
270
|
|
|
268
271
|
// src/claude-code-harness.ts
|
|
269
272
|
var CLAUDE_CODE_CLIENT_APP = `ai-sdk/harness-claude-code/${VERSION}`;
|
|
@@ -889,13 +892,30 @@ function createClaudeCode(settings = {}) {
|
|
|
889
892
|
getBootstrap: getClaudeCodeBootstrap,
|
|
890
893
|
doStart: async (startOpts) => {
|
|
891
894
|
const sandboxSession = startOpts.sandboxSession;
|
|
895
|
+
const toolSafeSandboxSession = getRestrictedSandboxSession(sandboxSession);
|
|
896
|
+
const sandboxId = "id" in sandboxSession ? sandboxSession.id : void 0;
|
|
897
|
+
validateBasicSandboxSettings({
|
|
898
|
+
sandboxSession,
|
|
899
|
+
port: settings.port,
|
|
900
|
+
portEndpoint: settings.portEndpoint
|
|
901
|
+
});
|
|
902
|
+
if (settings.mintBridgeToken != null && sandboxId == null) {
|
|
903
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
904
|
+
harnessId: "claude-code",
|
|
905
|
+
message: "The Claude Code harness cannot use `mintBridgeToken` with a sandbox session that does not expose an id."
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
const defaultWorkingDirectory = await resolveSandboxDefaultWorkingDirectory({
|
|
909
|
+
sandboxSession,
|
|
910
|
+
abortSignal: startOpts.abortSignal
|
|
911
|
+
});
|
|
892
912
|
const authenticationMode = resolveClaudeCodeAuthenticationMode(
|
|
893
913
|
settings.auth
|
|
894
914
|
);
|
|
895
915
|
const resolvedAuthEnvironment = resolveClaudeCodeEnv(settings.auth);
|
|
896
916
|
let authEnv = resolvedAuthEnvironment;
|
|
897
917
|
let sandboxTurnEnvironment = settings.env;
|
|
898
|
-
if (sandboxSession.addRequestTransformations != null) {
|
|
918
|
+
if ("addRequestTransformations" in sandboxSession && sandboxSession.addRequestTransformations != null) {
|
|
899
919
|
const requestTransformations = createClaudeCodeRequestTransformations(
|
|
900
920
|
{
|
|
901
921
|
...resolvedAuthEnvironment,
|
|
@@ -919,10 +939,8 @@ function createClaudeCode(settings = {}) {
|
|
|
919
939
|
} else {
|
|
920
940
|
warnCredentialBrokeringUnavailable();
|
|
921
941
|
}
|
|
922
|
-
const session = sandboxSession.restricted();
|
|
923
|
-
const sandboxId = sandboxSession.id;
|
|
924
942
|
const bootstrapDir = posix.resolve(
|
|
925
|
-
|
|
943
|
+
defaultWorkingDirectory,
|
|
926
944
|
CLAUDE_CODE_BOOTSTRAP_DIR
|
|
927
945
|
);
|
|
928
946
|
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
@@ -930,7 +948,7 @@ function createClaudeCode(settings = {}) {
|
|
|
930
948
|
const isContinue = startOpts.continueFrom != null;
|
|
931
949
|
const coords = isResume ? lifecycleState?.data?.bridge : void 0;
|
|
932
950
|
const workDir = startOpts.sessionWorkDir;
|
|
933
|
-
const sessionDataDir = `${
|
|
951
|
+
const sessionDataDir = `${defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;
|
|
934
952
|
const bridgeStateDir = `${sessionDataDir}/bridge`;
|
|
935
953
|
const timeoutMs = settings.startupTimeoutMs ?? 12e4;
|
|
936
954
|
const report = startOpts.observability?.report;
|
|
@@ -944,14 +962,22 @@ function createClaudeCode(settings = {}) {
|
|
|
944
962
|
harnessId: "claude-code",
|
|
945
963
|
sessionId: startOpts.sessionId
|
|
946
964
|
});
|
|
965
|
+
let supportsUserMessageResponses = false;
|
|
947
966
|
const buildConnect = (endpoint2) => async () => {
|
|
948
|
-
return openBridgeWebSocket({
|
|
967
|
+
return openBridgeWebSocket({
|
|
968
|
+
endpoint: endpoint2,
|
|
969
|
+
timeoutMs,
|
|
970
|
+
onHello: (supportsResponses) => {
|
|
971
|
+
supportsUserMessageResponses = supportsResponses;
|
|
972
|
+
}
|
|
973
|
+
});
|
|
949
974
|
};
|
|
950
975
|
if (coords) {
|
|
951
976
|
try {
|
|
952
|
-
const endpoint2 = await
|
|
953
|
-
|
|
954
|
-
|
|
977
|
+
const endpoint2 = await resolveBridgeEndpoint({
|
|
978
|
+
sandboxSession,
|
|
979
|
+
override: settings.portEndpoint,
|
|
980
|
+
port: coords.port
|
|
955
981
|
});
|
|
956
982
|
const attachEndpoint = withBridgeToken({
|
|
957
983
|
endpoint: endpoint2,
|
|
@@ -987,7 +1013,8 @@ function createClaudeCode(settings = {}) {
|
|
|
987
1013
|
permissionMode: startOpts.permissionMode,
|
|
988
1014
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
989
1015
|
skills: startOpts.skills ?? [],
|
|
990
|
-
mcpServers: settings.mcpServers
|
|
1016
|
+
mcpServers: settings.mcpServers,
|
|
1017
|
+
supportsUserMessageResponses: () => supportsUserMessageResponses
|
|
991
1018
|
});
|
|
992
1019
|
} catch {
|
|
993
1020
|
}
|
|
@@ -995,7 +1022,7 @@ function createClaudeCode(settings = {}) {
|
|
|
995
1022
|
let respawnStrategy = isResume ? "rerun" : void 0;
|
|
996
1023
|
if (coords && isContinue) {
|
|
997
1024
|
const logRaw = await Promise.resolve(
|
|
998
|
-
|
|
1025
|
+
toolSafeSandboxSession.readTextFile({
|
|
999
1026
|
path: `${bridgeStateDir}/event-log.ndjson`,
|
|
1000
1027
|
abortSignal: startOpts.abortSignal
|
|
1001
1028
|
})
|
|
@@ -1005,10 +1032,13 @@ function createClaudeCode(settings = {}) {
|
|
|
1005
1032
|
}
|
|
1006
1033
|
}
|
|
1007
1034
|
const sandboxHomeDir = startOpts.skills && startOpts.skills.length > 0 ? await resolveSandboxHomeDir({
|
|
1008
|
-
sandbox:
|
|
1035
|
+
sandbox: toolSafeSandboxSession,
|
|
1009
1036
|
abortSignal: startOpts.abortSignal
|
|
1010
1037
|
}) : void 0;
|
|
1011
|
-
const port = resolveBridgePort(
|
|
1038
|
+
const port = resolveBridgePort({
|
|
1039
|
+
sandboxSession,
|
|
1040
|
+
override: settings.port
|
|
1041
|
+
});
|
|
1012
1042
|
const token = settings.mintBridgeToken == null ? randomBytes(32).toString("hex") : settings.mintBridgeToken(sandboxId);
|
|
1013
1043
|
const env = {
|
|
1014
1044
|
...authEnv,
|
|
@@ -1025,7 +1055,7 @@ function createClaudeCode(settings = {}) {
|
|
|
1025
1055
|
...respawnStrategy === "replay" ? { BRIDGE_REPLAY_FROM_DISK: "1" } : {}
|
|
1026
1056
|
};
|
|
1027
1057
|
if (respawnStrategy === void 0) {
|
|
1028
|
-
await
|
|
1058
|
+
await toolSafeSandboxSession.run({
|
|
1029
1059
|
command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
|
|
1030
1060
|
abortSignal: startOpts.abortSignal
|
|
1031
1061
|
});
|
|
@@ -1034,7 +1064,7 @@ function createClaudeCode(settings = {}) {
|
|
|
1034
1064
|
throw new Error("Unable to resolve sandbox HOME directory.");
|
|
1035
1065
|
}
|
|
1036
1066
|
await writeClaudeCodeSkills({
|
|
1037
|
-
sandbox:
|
|
1067
|
+
sandbox: toolSafeSandboxSession,
|
|
1038
1068
|
homeDir: sandboxHomeDir,
|
|
1039
1069
|
skills: startOpts.skills,
|
|
1040
1070
|
abortSignal: startOpts.abortSignal
|
|
@@ -1042,12 +1072,12 @@ function createClaudeCode(settings = {}) {
|
|
|
1042
1072
|
}
|
|
1043
1073
|
}
|
|
1044
1074
|
await markBridgeStarting({
|
|
1045
|
-
sandbox:
|
|
1075
|
+
sandbox: toolSafeSandboxSession,
|
|
1046
1076
|
bridgeStateDir,
|
|
1047
1077
|
bridgeType: "claude-code",
|
|
1048
1078
|
abortSignal: startOpts.abortSignal
|
|
1049
1079
|
});
|
|
1050
|
-
const proc = await
|
|
1080
|
+
const proc = await toolSafeSandboxSession.spawn({
|
|
1051
1081
|
command: `node ${shellQuote(`${bootstrapDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)}`,
|
|
1052
1082
|
env,
|
|
1053
1083
|
abortSignal: startOpts.abortSignal
|
|
@@ -1062,7 +1092,7 @@ function createClaudeCode(settings = {}) {
|
|
|
1062
1092
|
void bridgeStartupStderrDone;
|
|
1063
1093
|
const { port: boundPort } = await waitForBridgeReady({
|
|
1064
1094
|
proc,
|
|
1065
|
-
sandbox:
|
|
1095
|
+
sandbox: toolSafeSandboxSession,
|
|
1066
1096
|
bridgeStateDir,
|
|
1067
1097
|
bridgeType: "claude-code",
|
|
1068
1098
|
timeoutMs,
|
|
@@ -1083,9 +1113,10 @@ function createClaudeCode(settings = {}) {
|
|
|
1083
1113
|
})
|
|
1084
1114
|
});
|
|
1085
1115
|
void drainBridgeProcessStream(proc.stdout);
|
|
1086
|
-
const endpoint = await
|
|
1087
|
-
|
|
1088
|
-
|
|
1116
|
+
const endpoint = await resolveBridgeEndpoint({
|
|
1117
|
+
sandboxSession,
|
|
1118
|
+
override: settings.portEndpoint,
|
|
1119
|
+
port: boundPort
|
|
1089
1120
|
});
|
|
1090
1121
|
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
1091
1122
|
const channel = new SandboxChannel({
|
|
@@ -1120,19 +1151,58 @@ function createClaudeCode(settings = {}) {
|
|
|
1120
1151
|
permissionMode: startOpts.permissionMode,
|
|
1121
1152
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
1122
1153
|
skills: startOpts.skills ?? [],
|
|
1123
|
-
mcpServers: settings.mcpServers
|
|
1154
|
+
mcpServers: settings.mcpServers,
|
|
1155
|
+
supportsUserMessageResponses: () => supportsUserMessageResponses
|
|
1124
1156
|
});
|
|
1125
1157
|
}
|
|
1126
1158
|
};
|
|
1127
1159
|
}
|
|
1128
|
-
function resolveBridgePort(
|
|
1160
|
+
function resolveBridgePort({
|
|
1161
|
+
sandboxSession,
|
|
1162
|
+
override
|
|
1163
|
+
}) {
|
|
1129
1164
|
if (override !== void 0) return override;
|
|
1130
|
-
if (sandboxSession.ports.length > 0)
|
|
1165
|
+
if ("ports" in sandboxSession && sandboxSession.ports.length > 0) {
|
|
1166
|
+
return sandboxSession.ports[0];
|
|
1167
|
+
}
|
|
1131
1168
|
throw new HarnessCapabilityUnsupportedError({
|
|
1132
1169
|
harnessId: "claude-code",
|
|
1133
1170
|
message: "The claude-code harness needs a TCP port exposed by the sandbox. Create the sandbox with `ports: [<port>]` or pass `createClaudeCode({ port })`."
|
|
1134
1171
|
});
|
|
1135
1172
|
}
|
|
1173
|
+
function validateBasicSandboxSettings({
|
|
1174
|
+
sandboxSession,
|
|
1175
|
+
port,
|
|
1176
|
+
portEndpoint
|
|
1177
|
+
}) {
|
|
1178
|
+
if ("getPortEndpoint" in sandboxSession) return;
|
|
1179
|
+
if (port == null) {
|
|
1180
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
1181
|
+
harnessId: "claude-code",
|
|
1182
|
+
message: "The Claude Code harness requires an explicit `port` when using a basic sandbox session."
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
if (portEndpoint == null) {
|
|
1186
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
1187
|
+
harnessId: "claude-code",
|
|
1188
|
+
message: "The Claude Code harness requires an explicit `portEndpoint` when using a basic sandbox session."
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
async function resolveBridgeEndpoint({
|
|
1193
|
+
sandboxSession,
|
|
1194
|
+
override,
|
|
1195
|
+
port
|
|
1196
|
+
}) {
|
|
1197
|
+
if (override != null) return override;
|
|
1198
|
+
if ("getPortEndpoint" in sandboxSession) {
|
|
1199
|
+
return sandboxSession.getPortEndpoint({ port, protocol: "ws" });
|
|
1200
|
+
}
|
|
1201
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
1202
|
+
harnessId: "claude-code",
|
|
1203
|
+
message: "The Claude Code harness requires an explicit `portEndpoint` when using a basic sandbox session."
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1136
1206
|
async function writeClaudeCodeSkills({
|
|
1137
1207
|
sandbox,
|
|
1138
1208
|
homeDir,
|
|
@@ -1152,7 +1222,8 @@ async function writeClaudeCodeSkills({
|
|
|
1152
1222
|
function openWebSocketAndWaitForBridgeHello({
|
|
1153
1223
|
endpoint,
|
|
1154
1224
|
openTimeoutMs,
|
|
1155
|
-
getHelloTimeoutMs
|
|
1225
|
+
getHelloTimeoutMs,
|
|
1226
|
+
onHello
|
|
1156
1227
|
}) {
|
|
1157
1228
|
return new Promise((resolve, reject) => {
|
|
1158
1229
|
const ws = new WebSocket(endpoint.url, {
|
|
@@ -1218,6 +1289,8 @@ function openWebSocketAndWaitForBridgeHello({
|
|
|
1218
1289
|
if (!parsed.success || settled) return;
|
|
1219
1290
|
const value = parsed.value;
|
|
1220
1291
|
if (typeof value === "object" && value !== null && !Array.isArray(value) && value.type === "bridge-hello") {
|
|
1292
|
+
const capabilities = value.capabilities;
|
|
1293
|
+
onHello(capabilities?.experimental_userMessageResponses === true);
|
|
1221
1294
|
sawBridgeHello = true;
|
|
1222
1295
|
tryResolve();
|
|
1223
1296
|
}
|
|
@@ -1242,7 +1315,8 @@ function openWebSocketAndWaitForBridgeHello({
|
|
|
1242
1315
|
}
|
|
1243
1316
|
async function openBridgeWebSocket({
|
|
1244
1317
|
endpoint,
|
|
1245
|
-
timeoutMs
|
|
1318
|
+
timeoutMs,
|
|
1319
|
+
onHello
|
|
1246
1320
|
}) {
|
|
1247
1321
|
const deadline = Date.now() + timeoutMs;
|
|
1248
1322
|
let attempt = 0;
|
|
@@ -1254,7 +1328,8 @@ async function openBridgeWebSocket({
|
|
|
1254
1328
|
return await openWebSocketAndWaitForBridgeHello({
|
|
1255
1329
|
endpoint,
|
|
1256
1330
|
openTimeoutMs: Math.min(1e4, remaining),
|
|
1257
|
-
getHelloTimeoutMs: () => Math.min(5e3, Math.max(1, deadline - Date.now()))
|
|
1331
|
+
getHelloTimeoutMs: () => Math.min(5e3, Math.max(1, deadline - Date.now())),
|
|
1332
|
+
onHello
|
|
1258
1333
|
});
|
|
1259
1334
|
} catch (err) {
|
|
1260
1335
|
lastError = err;
|
|
@@ -1316,7 +1391,8 @@ function createSession({
|
|
|
1316
1391
|
permissionMode,
|
|
1317
1392
|
builtinToolFiltering,
|
|
1318
1393
|
skills,
|
|
1319
|
-
mcpServers
|
|
1394
|
+
mcpServers,
|
|
1395
|
+
supportsUserMessageResponses
|
|
1320
1396
|
}) {
|
|
1321
1397
|
let stopped = false;
|
|
1322
1398
|
let stopPromise;
|
|
@@ -1328,6 +1404,11 @@ function createSession({
|
|
|
1328
1404
|
pendingResolve = resolve;
|
|
1329
1405
|
pendingReject = reject;
|
|
1330
1406
|
});
|
|
1407
|
+
const userMessageSubmitter = supportsUserMessageResponses() ? experimental_createBridgeUserMessageSubmitter({
|
|
1408
|
+
send: (message) => channel.send(message),
|
|
1409
|
+
onResponse: (listener) => channel.on("user-message-response", listener),
|
|
1410
|
+
onReconnect: (listener) => channel.onReconnect(listener)
|
|
1411
|
+
}) : void 0;
|
|
1331
1412
|
const unsubs = [];
|
|
1332
1413
|
const forward = (event) => {
|
|
1333
1414
|
try {
|
|
@@ -1339,12 +1420,14 @@ function createSession({
|
|
|
1339
1420
|
const settleSuccess = () => {
|
|
1340
1421
|
if (isSettled) return;
|
|
1341
1422
|
isSettled = true;
|
|
1423
|
+
userMessageSubmitter?.close();
|
|
1342
1424
|
for (const u of unsubs) u();
|
|
1343
1425
|
pendingResolve();
|
|
1344
1426
|
};
|
|
1345
1427
|
const settleError = (err) => {
|
|
1346
1428
|
if (isSettled) return;
|
|
1347
1429
|
isSettled = true;
|
|
1430
|
+
userMessageSubmitter?.close(err);
|
|
1348
1431
|
for (const u of unsubs) u();
|
|
1349
1432
|
pendingReject(err);
|
|
1350
1433
|
};
|
|
@@ -1428,8 +1511,10 @@ function createSession({
|
|
|
1428
1511
|
reason: input.reason
|
|
1429
1512
|
});
|
|
1430
1513
|
},
|
|
1431
|
-
|
|
1432
|
-
|
|
1514
|
+
...userMessageSubmitter == null ? {} : {
|
|
1515
|
+
submitUserMessage: async (text) => {
|
|
1516
|
+
await userMessageSubmitter.submit(text);
|
|
1517
|
+
}
|
|
1433
1518
|
},
|
|
1434
1519
|
done
|
|
1435
1520
|
};
|
|
@@ -1541,7 +1626,7 @@ function createSession({
|
|
|
1541
1626
|
port: bridgePort,
|
|
1542
1627
|
token: bridgeToken,
|
|
1543
1628
|
lastSeenEventId,
|
|
1544
|
-
sandboxId
|
|
1629
|
+
...sandboxId == null ? {} : { sandboxId }
|
|
1545
1630
|
}
|
|
1546
1631
|
}
|
|
1547
1632
|
};
|
|
@@ -1655,7 +1740,7 @@ function createSession({
|
|
|
1655
1740
|
port: bridgePort,
|
|
1656
1741
|
token: bridgeToken,
|
|
1657
1742
|
lastSeenEventId,
|
|
1658
|
-
sandboxId
|
|
1743
|
+
...sandboxId == null ? {} : { sandboxId }
|
|
1659
1744
|
}
|
|
1660
1745
|
}
|
|
1661
1746
|
};
|