@ai-sdk/harness-claude-code 1.0.97 → 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/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.97" : "0.0.0-test";
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")) {
@@ -892,7 +911,6 @@ function createClaudeCode(settings = {}) {
892
911
  lifecycleStateSchema: claudeCodeResumeStateSchema,
893
912
  getBootstrap: getClaudeCodeBootstrap,
894
913
  doStart: async (startOpts) => {
895
- const model = startOpts.model ?? settings.model;
896
914
  const sandboxSession = startOpts.sandboxSession;
897
915
  const toolSafeSandboxSession = getRestrictedSandboxSession(sandboxSession);
898
916
  const sandboxId = "id" in sandboxSession ? sandboxSession.id : void 0;
@@ -965,6 +983,7 @@ function createClaudeCode(settings = {}) {
965
983
  defaultWorkingDirectory,
966
984
  CLAUDE_CODE_BOOTSTRAP_DIR
967
985
  );
986
+ const resumeSessionId = resumeData?.claudeSessionId;
968
987
  const workDir = startOpts.sessionWorkDir;
969
988
  const sandboxHomeDir = await resolveSandboxHomeDir({
970
989
  sandbox: toolSafeSandboxSession,
@@ -1016,11 +1035,12 @@ function createClaudeCode(settings = {}) {
1016
1035
  return createSession({
1017
1036
  sessionId: startOpts.sessionId,
1018
1037
  channel: attachChannel,
1038
+ ...resumeSessionId ? { resumeSessionId } : {},
1019
1039
  // The live bridge was spawned by another process; this one owns no
1020
1040
  // process handle. The session lifecycle method decides whether the
1021
1041
  // sandbox is left running, stopped, or destroyed.
1022
1042
  proc: void 0,
1023
- model,
1043
+ model: settings.model,
1024
1044
  maxTurns: settings.maxTurns,
1025
1045
  env: sandboxClaudeEnvironment,
1026
1046
  thinking,
@@ -1136,13 +1156,14 @@ function createClaudeCode(settings = {}) {
1136
1156
  sessionId: startOpts.sessionId,
1137
1157
  channel,
1138
1158
  proc,
1139
- model,
1159
+ model: settings.model,
1140
1160
  maxTurns: settings.maxTurns,
1141
1161
  env: sandboxClaudeEnvironment,
1142
1162
  thinking,
1143
1163
  effort: settings.effort,
1144
1164
  isResume: respawnStrategy !== void 0,
1145
1165
  continueOnFirstPrompt: respawnStrategy !== void 0,
1166
+ ...resumeSessionId ? { resumeSessionId } : {},
1146
1167
  rerunContinue: respawnStrategy === "rerun",
1147
1168
  bridgePort: boundPort,
1148
1169
  bridgeToken: token,
@@ -1392,6 +1413,7 @@ function createSession({
1392
1413
  effort,
1393
1414
  isResume,
1394
1415
  continueOnFirstPrompt,
1416
+ resumeSessionId,
1395
1417
  rerunContinue,
1396
1418
  bridgePort,
1397
1419
  bridgeToken,
@@ -1408,6 +1430,7 @@ function createSession({
1408
1430
  let stopped = false;
1409
1431
  let stopPromise;
1410
1432
  let pendingResumeFlag = continueOnFirstPrompt;
1433
+ let lastClaudeSessionId = resumeSessionId;
1411
1434
  const wireTurn = (turnOpts) => {
1412
1435
  let pendingResolve;
1413
1436
  let pendingReject;
@@ -1450,6 +1473,9 @@ function createSession({
1450
1473
  "reasoning-start",
1451
1474
  "reasoning-delta",
1452
1475
  "reasoning-end",
1476
+ "tool-input-start",
1477
+ "tool-input-delta",
1478
+ "tool-input-end",
1453
1479
  "tool-call",
1454
1480
  "tool-approval-request",
1455
1481
  "tool-result",
@@ -1465,6 +1491,10 @@ function createSession({
1465
1491
  }
1466
1492
  unsubs.push(
1467
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
+ }
1468
1498
  forward(msg);
1469
1499
  settleSuccess();
1470
1500
  })
@@ -1533,7 +1563,6 @@ function createSession({
1533
1563
  return {
1534
1564
  sessionId,
1535
1565
  isResume,
1536
- modelId: model,
1537
1566
  doPromptTurn: async (promptOpts) => {
1538
1567
  if (promptOpts.responseFormat?.type === "json" && promptOpts.responseFormat.schema == null) {
1539
1568
  throw new HarnessCapabilityUnsupportedError({
@@ -1564,7 +1593,7 @@ function createSession({
1564
1593
  })),
1565
1594
  ...promptOpts.responseFormat == null ? {} : { responseFormat: promptOpts.responseFormat },
1566
1595
  ...promptOpts.instructions ? { instructions: promptOpts.instructions } : {},
1567
- model,
1596
+ model: promptOpts.model ?? model,
1568
1597
  maxTurns,
1569
1598
  ...env !== void 0 ? { env } : {},
1570
1599
  thinking,
@@ -1574,7 +1603,7 @@ function createSession({
1574
1603
  ...permissionMode ? { permissionMode } : {},
1575
1604
  ...builtinToolFiltering ? { builtinToolFiltering } : {},
1576
1605
  ...debug ? { debug } : {},
1577
- ...pendingResumeFlag ? { continue: true } : {}
1606
+ ...lastClaudeSessionId ? { resumeSessionId: lastClaudeSessionId } : pendingResumeFlag ? { continue: true } : {}
1578
1607
  };
1579
1608
  pendingResumeFlag = false;
1580
1609
  channel.send(startMessage);
@@ -1616,7 +1645,7 @@ function createSession({
1616
1645
  })),
1617
1646
  ...continueOpts.responseFormat == null ? {} : { responseFormat: continueOpts.responseFormat },
1618
1647
  ...continueOpts.instructions ? { instructions: continueOpts.instructions } : {},
1619
- model,
1648
+ model: continueOpts.model ?? model,
1620
1649
  maxTurns,
1621
1650
  ...env !== void 0 ? { env } : {},
1622
1651
  thinking,
@@ -1626,7 +1655,7 @@ function createSession({
1626
1655
  ...permissionMode ? { permissionMode } : {},
1627
1656
  ...builtinToolFiltering ? { builtinToolFiltering } : {},
1628
1657
  ...debug ? { debug } : {},
1629
- continue: true
1658
+ ...lastClaudeSessionId ? { resumeSessionId: lastClaudeSessionId } : { continue: true }
1630
1659
  });
1631
1660
  }
1632
1661
  return control;
@@ -1654,7 +1683,8 @@ function createSession({
1654
1683
  token: bridgeToken,
1655
1684
  lastSeenEventId,
1656
1685
  ...sandboxId == null ? {} : { sandboxId }
1657
- }
1686
+ },
1687
+ ...lastClaudeSessionId ? { claudeSessionId: lastClaudeSessionId } : {}
1658
1688
  }
1659
1689
  };
1660
1690
  return payload;
@@ -1747,7 +1777,11 @@ function createSession({
1747
1777
  type: "resume-session",
1748
1778
  harnessId: "claude-code",
1749
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.
1750
1783
  data: {
1784
+ ...lastClaudeSessionId ? { claudeSessionId: lastClaudeSessionId } : {},
1751
1785
  ...lifecycleData,
1752
1786
  ...sandboxCredentialEnvironment == null ? {} : { sandboxCredentialEnvironment }
1753
1787
  }
@@ -1773,7 +1807,8 @@ function createSession({
1773
1807
  token: bridgeToken,
1774
1808
  lastSeenEventId,
1775
1809
  ...sandboxId == null ? {} : { sandboxId }
1776
- }
1810
+ },
1811
+ ...lastClaudeSessionId ? { claudeSessionId: lastClaudeSessionId } : {}
1777
1812
  }
1778
1813
  };
1779
1814
  return payload;