@ai-sdk/harness-claude-code 1.0.96 → 1.0.98
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 +28 -0
- package/dist/bridge/index.mjs +137 -27
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/bridge/package.json +3 -3
- package/dist/bridge/pnpm-lock.yaml +128 -126
- package/dist/bridge/pnpm-workspace.yaml +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +74 -32
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/bridge/create-emit-stream-event.ts +74 -13
- package/src/bridge/index.ts +134 -20
- package/src/bridge/package.json +3 -3
- package/src/bridge/pnpm-lock.yaml +128 -126
- package/src/bridge/pnpm-workspace.yaml +1 -1
- package/src/claude-code-bridge-protocol.ts +8 -0
- package/src/claude-code-harness.ts +116 -41
package/dist/index.js
CHANGED
|
@@ -258,7 +258,15 @@ var startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
|
|
|
258
258
|
// Resume signal. When true, the bridge passes `{ continue: true }` to the
|
|
259
259
|
// Claude SDK so the in-workdir thread state is rehydrated. The host sets this
|
|
260
260
|
// on the first prompt after a cross-process resume.
|
|
261
|
-
continue: z.boolean().optional()
|
|
261
|
+
continue: z.boolean().optional(),
|
|
262
|
+
// Exact conversation to resume, as the Claude SDK's `resume` option.
|
|
263
|
+
//
|
|
264
|
+
// Preferred over `continue` whenever the host knows which conversation it
|
|
265
|
+
// means: `continue` resolves to the most recent thread in the workdir, which
|
|
266
|
+
// is the wrong one as soon as anything else has run there since — including
|
|
267
|
+
// a resume that itself started a new thread. Mutually exclusive with
|
|
268
|
+
// `continue` in the SDK, so the bridge sends one or the other.
|
|
269
|
+
resumeSessionId: z.string().optional()
|
|
262
270
|
});
|
|
263
271
|
var inboundMessageSchema = z.discriminatedUnion("type", [
|
|
264
272
|
startMessageSchema,
|
|
@@ -266,7 +274,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
|
|
|
266
274
|
]);
|
|
267
275
|
|
|
268
276
|
// src/version.ts
|
|
269
|
-
var VERSION = true ? "1.0.
|
|
277
|
+
var VERSION = true ? "1.0.98" : "0.0.0-test";
|
|
270
278
|
|
|
271
279
|
// src/claude-code-harness.ts
|
|
272
280
|
var CLAUDE_CODE_CLIENT_APP = `ai-sdk/harness-claude-code/${VERSION}`;
|
|
@@ -871,7 +879,18 @@ var claudeCodeBridgeCoordsSchema = z2.object({
|
|
|
871
879
|
});
|
|
872
880
|
var claudeCodeResumeStateSchema = z2.looseObject({
|
|
873
881
|
bridge: claudeCodeBridgeCoordsSchema.optional(),
|
|
874
|
-
sandboxCredentialEnvironment: z2.record(z2.string(), z2.string()).optional()
|
|
882
|
+
sandboxCredentialEnvironment: z2.record(z2.string(), z2.string()).optional(),
|
|
883
|
+
/**
|
|
884
|
+
* The exact Claude conversation to rehydrate on resume. Written by the
|
|
885
|
+
* adapter on `doStop()`/`doDetach()`/`doSuspendTurn()` from the session id
|
|
886
|
+
* the bridge observed, so a resume names the conversation instead of
|
|
887
|
+
* relying on the SDK's `continue` flag — "most recent thread in this
|
|
888
|
+
* workdir" — which silently picks the wrong one once a second thread
|
|
889
|
+
* exists there. Hosts that captured the id themselves (it is surfaced as
|
|
890
|
+
* `harnessMetadata['claude-code'].sessionId` on `finish` parts) may also
|
|
891
|
+
* set it explicitly.
|
|
892
|
+
*/
|
|
893
|
+
claudeSessionId: z2.string().optional()
|
|
875
894
|
});
|
|
876
895
|
function createClaudeCode(settings = {}) {
|
|
877
896
|
if (settings.mcpServers != null && Object.prototype.hasOwnProperty.call(settings.mcpServers, "harness-tools")) {
|
|
@@ -964,7 +983,12 @@ function createClaudeCode(settings = {}) {
|
|
|
964
983
|
defaultWorkingDirectory,
|
|
965
984
|
CLAUDE_CODE_BOOTSTRAP_DIR
|
|
966
985
|
);
|
|
986
|
+
const resumeSessionId = resumeData?.claudeSessionId;
|
|
967
987
|
const workDir = startOpts.sessionWorkDir;
|
|
988
|
+
const sandboxHomeDir = await resolveSandboxHomeDir({
|
|
989
|
+
sandbox: toolSafeSandboxSession,
|
|
990
|
+
abortSignal: startOpts.abortSignal
|
|
991
|
+
});
|
|
968
992
|
const sessionDataDir = `${defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;
|
|
969
993
|
const bridgeStateDir = `${sessionDataDir}/bridge`;
|
|
970
994
|
const timeoutMs = settings.startupTimeoutMs ?? 12e4;
|
|
@@ -1011,6 +1035,7 @@ function createClaudeCode(settings = {}) {
|
|
|
1011
1035
|
return createSession({
|
|
1012
1036
|
sessionId: startOpts.sessionId,
|
|
1013
1037
|
channel: attachChannel,
|
|
1038
|
+
...resumeSessionId ? { resumeSessionId } : {},
|
|
1014
1039
|
// The live bridge was spawned by another process; this one owns no
|
|
1015
1040
|
// process handle. The session lifecycle method decides whether the
|
|
1016
1041
|
// sandbox is left running, stopped, or destroyed.
|
|
@@ -1030,7 +1055,8 @@ function createClaudeCode(settings = {}) {
|
|
|
1030
1055
|
debug: startOpts.observability?.debug,
|
|
1031
1056
|
permissionMode: startOpts.permissionMode,
|
|
1032
1057
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
1033
|
-
|
|
1058
|
+
sandbox: toolSafeSandboxSession,
|
|
1059
|
+
sandboxHomeDir,
|
|
1034
1060
|
mcpServers: settings.mcpServers,
|
|
1035
1061
|
supportsUserMessageResponses: () => supportsUserMessageResponses
|
|
1036
1062
|
});
|
|
@@ -1049,10 +1075,6 @@ function createClaudeCode(settings = {}) {
|
|
|
1049
1075
|
respawnStrategy = "replay";
|
|
1050
1076
|
}
|
|
1051
1077
|
}
|
|
1052
|
-
const sandboxHomeDir = startOpts.skills && startOpts.skills.length > 0 ? await resolveSandboxHomeDir({
|
|
1053
|
-
sandbox: toolSafeSandboxSession,
|
|
1054
|
-
abortSignal: startOpts.abortSignal
|
|
1055
|
-
}) : void 0;
|
|
1056
1078
|
const port = resolveBridgePort({
|
|
1057
1079
|
sandboxSession,
|
|
1058
1080
|
override: settings.port
|
|
@@ -1061,7 +1083,6 @@ function createClaudeCode(settings = {}) {
|
|
|
1061
1083
|
const env = {
|
|
1062
1084
|
BRIDGE_CHANNEL_TOKEN: token,
|
|
1063
1085
|
BRIDGE_WS_PORT: String(port),
|
|
1064
|
-
...sandboxHomeDir ? { HOME: sandboxHomeDir } : {},
|
|
1065
1086
|
...respawnStrategy === "replay" ? { BRIDGE_REPLAY_FROM_DISK: "1" } : {}
|
|
1066
1087
|
};
|
|
1067
1088
|
if (respawnStrategy === void 0) {
|
|
@@ -1069,17 +1090,6 @@ function createClaudeCode(settings = {}) {
|
|
|
1069
1090
|
command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
|
|
1070
1091
|
abortSignal: startOpts.abortSignal
|
|
1071
1092
|
});
|
|
1072
|
-
if (startOpts.skills && startOpts.skills.length > 0) {
|
|
1073
|
-
if (!sandboxHomeDir) {
|
|
1074
|
-
throw new Error("Unable to resolve sandbox HOME directory.");
|
|
1075
|
-
}
|
|
1076
|
-
await writeClaudeCodeSkills({
|
|
1077
|
-
sandbox: toolSafeSandboxSession,
|
|
1078
|
-
homeDir: sandboxHomeDir,
|
|
1079
|
-
skills: startOpts.skills,
|
|
1080
|
-
abortSignal: startOpts.abortSignal
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
1093
|
}
|
|
1084
1094
|
await markBridgeStarting({
|
|
1085
1095
|
sandbox: toolSafeSandboxSession,
|
|
@@ -1153,6 +1163,7 @@ function createClaudeCode(settings = {}) {
|
|
|
1153
1163
|
effort: settings.effort,
|
|
1154
1164
|
isResume: respawnStrategy !== void 0,
|
|
1155
1165
|
continueOnFirstPrompt: respawnStrategy !== void 0,
|
|
1166
|
+
...resumeSessionId ? { resumeSessionId } : {},
|
|
1156
1167
|
rerunContinue: respawnStrategy === "rerun",
|
|
1157
1168
|
bridgePort: boundPort,
|
|
1158
1169
|
bridgeToken: token,
|
|
@@ -1161,7 +1172,8 @@ function createClaudeCode(settings = {}) {
|
|
|
1161
1172
|
debug: startOpts.observability?.debug,
|
|
1162
1173
|
permissionMode: startOpts.permissionMode,
|
|
1163
1174
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
1164
|
-
|
|
1175
|
+
sandbox: toolSafeSandboxSession,
|
|
1176
|
+
sandboxHomeDir,
|
|
1165
1177
|
mcpServers: settings.mcpServers,
|
|
1166
1178
|
supportsUserMessageResponses: () => supportsUserMessageResponses
|
|
1167
1179
|
});
|
|
@@ -1401,6 +1413,7 @@ function createSession({
|
|
|
1401
1413
|
effort,
|
|
1402
1414
|
isResume,
|
|
1403
1415
|
continueOnFirstPrompt,
|
|
1416
|
+
resumeSessionId,
|
|
1404
1417
|
rerunContinue,
|
|
1405
1418
|
bridgePort,
|
|
1406
1419
|
bridgeToken,
|
|
@@ -1409,13 +1422,15 @@ function createSession({
|
|
|
1409
1422
|
debug,
|
|
1410
1423
|
permissionMode,
|
|
1411
1424
|
builtinToolFiltering,
|
|
1412
|
-
|
|
1425
|
+
sandbox,
|
|
1426
|
+
sandboxHomeDir,
|
|
1413
1427
|
mcpServers,
|
|
1414
1428
|
supportsUserMessageResponses
|
|
1415
1429
|
}) {
|
|
1416
1430
|
let stopped = false;
|
|
1417
1431
|
let stopPromise;
|
|
1418
1432
|
let pendingResumeFlag = continueOnFirstPrompt;
|
|
1433
|
+
let lastClaudeSessionId = resumeSessionId;
|
|
1419
1434
|
const wireTurn = (turnOpts) => {
|
|
1420
1435
|
let pendingResolve;
|
|
1421
1436
|
let pendingReject;
|
|
@@ -1458,6 +1473,9 @@ function createSession({
|
|
|
1458
1473
|
"reasoning-start",
|
|
1459
1474
|
"reasoning-delta",
|
|
1460
1475
|
"reasoning-end",
|
|
1476
|
+
"tool-input-start",
|
|
1477
|
+
"tool-input-delta",
|
|
1478
|
+
"tool-input-end",
|
|
1461
1479
|
"tool-call",
|
|
1462
1480
|
"tool-approval-request",
|
|
1463
1481
|
"tool-result",
|
|
@@ -1473,6 +1491,10 @@ function createSession({
|
|
|
1473
1491
|
}
|
|
1474
1492
|
unsubs.push(
|
|
1475
1493
|
channel.on("finish", (msg) => {
|
|
1494
|
+
const metadata = msg.harnessMetadata?.["claude-code"]?.sessionId;
|
|
1495
|
+
if (typeof metadata === "string" && metadata.length > 0) {
|
|
1496
|
+
lastClaudeSessionId = metadata;
|
|
1497
|
+
}
|
|
1476
1498
|
forward(msg);
|
|
1477
1499
|
settleSuccess();
|
|
1478
1500
|
})
|
|
@@ -1541,7 +1563,6 @@ function createSession({
|
|
|
1541
1563
|
return {
|
|
1542
1564
|
sessionId,
|
|
1543
1565
|
isResume,
|
|
1544
|
-
modelId: model,
|
|
1545
1566
|
doPromptTurn: async (promptOpts) => {
|
|
1546
1567
|
if (promptOpts.responseFormat?.type === "json" && promptOpts.responseFormat.schema == null) {
|
|
1547
1568
|
throw new HarnessCapabilityUnsupportedError({
|
|
@@ -1549,10 +1570,19 @@ function createSession({
|
|
|
1549
1570
|
harnessId: "claude-code"
|
|
1550
1571
|
});
|
|
1551
1572
|
}
|
|
1573
|
+
await writeClaudeCodeSkills({
|
|
1574
|
+
sandbox,
|
|
1575
|
+
homeDir: sandboxHomeDir,
|
|
1576
|
+
skills: promptOpts.skills,
|
|
1577
|
+
abortSignal: promptOpts.abortSignal
|
|
1578
|
+
});
|
|
1552
1579
|
const control = wireTurn({
|
|
1553
1580
|
emit: promptOpts.emit,
|
|
1554
1581
|
abortSignal: promptOpts.abortSignal
|
|
1555
1582
|
});
|
|
1583
|
+
if (promptOpts.abortSignal?.aborted) {
|
|
1584
|
+
return control;
|
|
1585
|
+
}
|
|
1556
1586
|
const startMessage = {
|
|
1557
1587
|
type: "start",
|
|
1558
1588
|
prompt: extractUserText(promptOpts.prompt),
|
|
@@ -1563,17 +1593,17 @@ function createSession({
|
|
|
1563
1593
|
})),
|
|
1564
1594
|
...promptOpts.responseFormat == null ? {} : { responseFormat: promptOpts.responseFormat },
|
|
1565
1595
|
...promptOpts.instructions ? { instructions: promptOpts.instructions } : {},
|
|
1566
|
-
model,
|
|
1596
|
+
model: promptOpts.model ?? model,
|
|
1567
1597
|
maxTurns,
|
|
1568
1598
|
...env !== void 0 ? { env } : {},
|
|
1569
1599
|
thinking,
|
|
1570
1600
|
...effort !== void 0 ? { effort } : {},
|
|
1571
|
-
...skills.length > 0 ? { skills: skills.map((skill) => skill.name) } : {},
|
|
1601
|
+
...promptOpts.skills.length > 0 ? { skills: promptOpts.skills.map((skill) => skill.name) } : {},
|
|
1572
1602
|
...mcpServers == null ? {} : { mcpServers },
|
|
1573
1603
|
...permissionMode ? { permissionMode } : {},
|
|
1574
1604
|
...builtinToolFiltering ? { builtinToolFiltering } : {},
|
|
1575
1605
|
...debug ? { debug } : {},
|
|
1576
|
-
...pendingResumeFlag ? { continue: true } : {}
|
|
1606
|
+
...lastClaudeSessionId ? { resumeSessionId: lastClaudeSessionId } : pendingResumeFlag ? { continue: true } : {}
|
|
1577
1607
|
};
|
|
1578
1608
|
pendingResumeFlag = false;
|
|
1579
1609
|
channel.send(startMessage);
|
|
@@ -1586,11 +1616,17 @@ function createSession({
|
|
|
1586
1616
|
harnessId: "claude-code"
|
|
1587
1617
|
});
|
|
1588
1618
|
}
|
|
1619
|
+
await writeClaudeCodeSkills({
|
|
1620
|
+
sandbox,
|
|
1621
|
+
homeDir: sandboxHomeDir,
|
|
1622
|
+
skills: continueOpts.skills,
|
|
1623
|
+
abortSignal: continueOpts.abortSignal
|
|
1624
|
+
});
|
|
1589
1625
|
const control = wireTurn({
|
|
1590
1626
|
emit: continueOpts.emit,
|
|
1591
1627
|
abortSignal: continueOpts.abortSignal
|
|
1592
1628
|
});
|
|
1593
|
-
if (rerunContinue) {
|
|
1629
|
+
if (rerunContinue && !continueOpts.abortSignal?.aborted) {
|
|
1594
1630
|
pendingResumeFlag = false;
|
|
1595
1631
|
channel.send({
|
|
1596
1632
|
type: "start",
|
|
@@ -1609,17 +1645,17 @@ function createSession({
|
|
|
1609
1645
|
})),
|
|
1610
1646
|
...continueOpts.responseFormat == null ? {} : { responseFormat: continueOpts.responseFormat },
|
|
1611
1647
|
...continueOpts.instructions ? { instructions: continueOpts.instructions } : {},
|
|
1612
|
-
model,
|
|
1648
|
+
model: continueOpts.model ?? model,
|
|
1613
1649
|
maxTurns,
|
|
1614
1650
|
...env !== void 0 ? { env } : {},
|
|
1615
1651
|
thinking,
|
|
1616
1652
|
...effort !== void 0 ? { effort } : {},
|
|
1617
|
-
...skills.length > 0 ? { skills: skills.map((skill) => skill.name) } : {},
|
|
1653
|
+
...continueOpts.skills.length > 0 ? { skills: continueOpts.skills.map((skill) => skill.name) } : {},
|
|
1618
1654
|
...mcpServers == null ? {} : { mcpServers },
|
|
1619
1655
|
...permissionMode ? { permissionMode } : {},
|
|
1620
1656
|
...builtinToolFiltering ? { builtinToolFiltering } : {},
|
|
1621
1657
|
...debug ? { debug } : {},
|
|
1622
|
-
continue: true
|
|
1658
|
+
...lastClaudeSessionId ? { resumeSessionId: lastClaudeSessionId } : { continue: true }
|
|
1623
1659
|
});
|
|
1624
1660
|
}
|
|
1625
1661
|
return control;
|
|
@@ -1647,7 +1683,8 @@ function createSession({
|
|
|
1647
1683
|
token: bridgeToken,
|
|
1648
1684
|
lastSeenEventId,
|
|
1649
1685
|
...sandboxId == null ? {} : { sandboxId }
|
|
1650
|
-
}
|
|
1686
|
+
},
|
|
1687
|
+
...lastClaudeSessionId ? { claudeSessionId: lastClaudeSessionId } : {}
|
|
1651
1688
|
}
|
|
1652
1689
|
};
|
|
1653
1690
|
return payload;
|
|
@@ -1740,7 +1777,11 @@ function createSession({
|
|
|
1740
1777
|
type: "resume-session",
|
|
1741
1778
|
harnessId: "claude-code",
|
|
1742
1779
|
specificationVersion: "harness-v1",
|
|
1780
|
+
// The bridge's stop reply carries the session id it observed; the
|
|
1781
|
+
// adapter's own record backfills it when the reply predates the
|
|
1782
|
+
// field or the channel was already closed.
|
|
1743
1783
|
data: {
|
|
1784
|
+
...lastClaudeSessionId ? { claudeSessionId: lastClaudeSessionId } : {},
|
|
1744
1785
|
...lifecycleData,
|
|
1745
1786
|
...sandboxCredentialEnvironment == null ? {} : { sandboxCredentialEnvironment }
|
|
1746
1787
|
}
|
|
@@ -1766,7 +1807,8 @@ function createSession({
|
|
|
1766
1807
|
token: bridgeToken,
|
|
1767
1808
|
lastSeenEventId,
|
|
1768
1809
|
...sandboxId == null ? {} : { sandboxId }
|
|
1769
|
-
}
|
|
1810
|
+
},
|
|
1811
|
+
...lastClaudeSessionId ? { claudeSessionId: lastClaudeSessionId } : {}
|
|
1770
1812
|
}
|
|
1771
1813
|
};
|
|
1772
1814
|
return payload;
|