claude-threads 1.21.1 → 1.22.0

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
@@ -14177,7 +14177,7 @@ var require_minimist = __commonJS((exports, module) => {
14177
14177
  // node_modules/rc/index.js
14178
14178
  var require_rc = __commonJS((exports, module) => {
14179
14179
  var cc = require_utils();
14180
- var join10 = __require("path").join;
14180
+ var join11 = __require("path").join;
14181
14181
  var deepExtend = require_deep_extend();
14182
14182
  var etc = "/etc";
14183
14183
  var win = process.platform === "win32";
@@ -14203,15 +14203,15 @@ var require_rc = __commonJS((exports, module) => {
14203
14203
  }
14204
14204
  if (!win)
14205
14205
  [
14206
- join10(etc, name, "config"),
14207
- join10(etc, name + "rc")
14206
+ join11(etc, name, "config"),
14207
+ join11(etc, name + "rc")
14208
14208
  ].forEach(addConfigFile);
14209
14209
  if (home)
14210
14210
  [
14211
- join10(home, ".config", name, "config"),
14212
- join10(home, ".config", name),
14213
- join10(home, "." + name, "config"),
14214
- join10(home, "." + name + "rc")
14211
+ join11(home, ".config", name, "config"),
14212
+ join11(home, ".config", name),
14213
+ join11(home, "." + name, "config"),
14214
+ join11(home, "." + name + "rc")
14215
14215
  ].forEach(addConfigFile);
14216
14216
  addConfigFile(cc.find("." + name + "rc"));
14217
14217
  if (env3.config)
@@ -16965,8 +16965,8 @@ var require_gt3 = __commonJS((exports, module) => {
16965
16965
  // node_modules/package-json/node_modules/semver/functions/lt.js
16966
16966
  var require_lt2 = __commonJS((exports, module) => {
16967
16967
  var compare = require_compare3();
16968
- var lt = (a, b, loose) => compare(a, b, loose) < 0;
16969
- module.exports = lt;
16968
+ var lt2 = (a, b, loose) => compare(a, b, loose) < 0;
16969
+ module.exports = lt2;
16970
16970
  });
16971
16971
 
16972
16972
  // node_modules/package-json/node_modules/semver/functions/eq.js
@@ -17003,7 +17003,7 @@ var require_cmp2 = __commonJS((exports, module) => {
17003
17003
  var neq = require_neq2();
17004
17004
  var gt = require_gt3();
17005
17005
  var gte = require_gte2();
17006
- var lt = require_lt2();
17006
+ var lt2 = require_lt2();
17007
17007
  var lte = require_lte2();
17008
17008
  var cmp = (a, op, b, loose) => {
17009
17009
  switch (op) {
@@ -17034,7 +17034,7 @@ var require_cmp2 = __commonJS((exports, module) => {
17034
17034
  case ">=":
17035
17035
  return gte(a, b, loose);
17036
17036
  case "<":
17037
- return lt(a, b, loose);
17037
+ return lt2(a, b, loose);
17038
17038
  case "<=":
17039
17039
  return lte(a, b, loose);
17040
17040
  default:
@@ -17755,7 +17755,7 @@ var require_outside2 = __commonJS((exports, module) => {
17755
17755
  var Range = require_range2();
17756
17756
  var satisfies2 = require_satisfies2();
17757
17757
  var gt = require_gt3();
17758
- var lt = require_lt2();
17758
+ var lt2 = require_lt2();
17759
17759
  var lte = require_lte2();
17760
17760
  var gte = require_gte2();
17761
17761
  var outside = (version, range, hilo, options) => {
@@ -17766,12 +17766,12 @@ var require_outside2 = __commonJS((exports, module) => {
17766
17766
  case ">":
17767
17767
  gtfn = gt;
17768
17768
  ltefn = lte;
17769
- ltfn = lt;
17769
+ ltfn = lt2;
17770
17770
  comp = ">";
17771
17771
  ecomp = ">=";
17772
17772
  break;
17773
17773
  case "<":
17774
- gtfn = lt;
17774
+ gtfn = lt2;
17775
17775
  ltefn = gte;
17776
17776
  ltfn = gt;
17777
17777
  comp = "<";
@@ -17937,12 +17937,12 @@ var require_subset2 = __commonJS((exports, module) => {
17937
17937
  }
17938
17938
  }
17939
17939
  const eqSet = new Set;
17940
- let gt, lt;
17940
+ let gt, lt2;
17941
17941
  for (const c of sub) {
17942
17942
  if (c.operator === ">" || c.operator === ">=") {
17943
17943
  gt = higherGT(gt, c, options);
17944
17944
  } else if (c.operator === "<" || c.operator === "<=") {
17945
- lt = lowerLT(lt, c, options);
17945
+ lt2 = lowerLT(lt2, c, options);
17946
17946
  } else {
17947
17947
  eqSet.add(c.semver);
17948
17948
  }
@@ -17951,11 +17951,11 @@ var require_subset2 = __commonJS((exports, module) => {
17951
17951
  return null;
17952
17952
  }
17953
17953
  let gtltComp;
17954
- if (gt && lt) {
17955
- gtltComp = compare(gt.semver, lt.semver, options);
17954
+ if (gt && lt2) {
17955
+ gtltComp = compare(gt.semver, lt2.semver, options);
17956
17956
  if (gtltComp > 0) {
17957
17957
  return null;
17958
- } else if (gtltComp === 0 && (gt.operator !== ">=" || lt.operator !== "<=")) {
17958
+ } else if (gtltComp === 0 && (gt.operator !== ">=" || lt2.operator !== "<=")) {
17959
17959
  return null;
17960
17960
  }
17961
17961
  }
@@ -17963,7 +17963,7 @@ var require_subset2 = __commonJS((exports, module) => {
17963
17963
  if (gt && !satisfies2(eq, String(gt), options)) {
17964
17964
  return null;
17965
17965
  }
17966
- if (lt && !satisfies2(eq, String(lt), options)) {
17966
+ if (lt2 && !satisfies2(eq, String(lt2), options)) {
17967
17967
  return null;
17968
17968
  }
17969
17969
  for (const c of dom) {
@@ -17975,9 +17975,9 @@ var require_subset2 = __commonJS((exports, module) => {
17975
17975
  }
17976
17976
  let higher, lower;
17977
17977
  let hasDomLT, hasDomGT;
17978
- let needDomLTPre = lt && !options.includePrerelease && lt.semver.prerelease.length ? lt.semver : false;
17978
+ let needDomLTPre = lt2 && !options.includePrerelease && lt2.semver.prerelease.length ? lt2.semver : false;
17979
17979
  let needDomGTPre = gt && !options.includePrerelease && gt.semver.prerelease.length ? gt.semver : false;
17980
- if (needDomLTPre && needDomLTPre.prerelease.length === 1 && lt.operator === "<" && needDomLTPre.prerelease[0] === 0) {
17980
+ if (needDomLTPre && needDomLTPre.prerelease.length === 1 && lt2.operator === "<" && needDomLTPre.prerelease[0] === 0) {
17981
17981
  needDomLTPre = false;
17982
17982
  }
17983
17983
  for (const c of dom) {
@@ -17998,29 +17998,29 @@ var require_subset2 = __commonJS((exports, module) => {
17998
17998
  return false;
17999
17999
  }
18000
18000
  }
18001
- if (lt) {
18001
+ if (lt2) {
18002
18002
  if (needDomLTPre) {
18003
18003
  if (c.semver.prerelease && c.semver.prerelease.length && c.semver.major === needDomLTPre.major && c.semver.minor === needDomLTPre.minor && c.semver.patch === needDomLTPre.patch) {
18004
18004
  needDomLTPre = false;
18005
18005
  }
18006
18006
  }
18007
18007
  if (c.operator === "<" || c.operator === "<=") {
18008
- lower = lowerLT(lt, c, options);
18009
- if (lower === c && lower !== lt) {
18008
+ lower = lowerLT(lt2, c, options);
18009
+ if (lower === c && lower !== lt2) {
18010
18010
  return false;
18011
18011
  }
18012
- } else if (lt.operator === "<=" && !satisfies2(lt.semver, String(c), options)) {
18012
+ } else if (lt2.operator === "<=" && !satisfies2(lt2.semver, String(c), options)) {
18013
18013
  return false;
18014
18014
  }
18015
18015
  }
18016
- if (!c.operator && (lt || gt) && gtltComp !== 0) {
18016
+ if (!c.operator && (lt2 || gt) && gtltComp !== 0) {
18017
18017
  return false;
18018
18018
  }
18019
18019
  }
18020
- if (gt && hasDomLT && !lt && gtltComp !== 0) {
18020
+ if (gt && hasDomLT && !lt2 && gtltComp !== 0) {
18021
18021
  return false;
18022
18022
  }
18023
- if (lt && hasDomGT && !gt && gtltComp !== 0) {
18023
+ if (lt2 && hasDomGT && !gt && gtltComp !== 0) {
18024
18024
  return false;
18025
18025
  }
18026
18026
  if (needDomGTPre || needDomLTPre) {
@@ -18067,7 +18067,7 @@ var require_semver5 = __commonJS((exports, module) => {
18067
18067
  var sort = require_sort2();
18068
18068
  var rsort = require_rsort2();
18069
18069
  var gt = require_gt3();
18070
- var lt = require_lt2();
18070
+ var lt2 = require_lt2();
18071
18071
  var eq = require_eq2();
18072
18072
  var neq = require_neq2();
18073
18073
  var gte = require_gte2();
@@ -18105,7 +18105,7 @@ var require_semver5 = __commonJS((exports, module) => {
18105
18105
  sort,
18106
18106
  rsort,
18107
18107
  gt,
18108
- lt,
18108
+ lt: lt2,
18109
18109
  eq,
18110
18110
  neq,
18111
18111
  gte,
@@ -51296,6 +51296,7 @@ var dim = (s) => `${colors.dim}${s}${colors.reset}`;
51296
51296
  var bold = (s) => `${colors.bold}${s}${colors.reset}`;
51297
51297
  var green = (s) => `${colors.green}${s}${colors.reset}`;
51298
51298
  var red = (s) => `${colors.red}${s}${colors.reset}`;
51299
+ var yellow = (s) => `${colors.yellow}${s}${colors.reset}`;
51299
51300
 
51300
51301
  // src/claude/version-check.ts
51301
51302
  var import_semver = __toESM(require_semver2(), 1);
@@ -51315,7 +51316,10 @@ var COMMON_CLAUDE_PATHS = process.platform === "win32" ? [
51315
51316
  `${process.env.HOME}/.bun/bin/claude`,
51316
51317
  "/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js"
51317
51318
  ];
51318
- var CLAUDE_CLI_VERSION_RANGE = ">=2.0.74 <2.2.0";
51319
+ var CLAUDE_CLI_MIN_VERSION = "2.0.74";
51320
+ var CLAUDE_CLI_VERIFIED_RANGE = ">=2.0.74 <2.2.0";
51321
+ var CLAUDE_CLI_SUPPORTED_MAJOR = 2;
51322
+ var CLAUDE_CLI_LATEST_VERIFIED = "2.1.223";
51319
51323
  function tryClaudeVersion(claudePath) {
51320
51324
  try {
51321
51325
  const output = execSync(`"${claudePath}" --version`, {
@@ -51387,11 +51391,17 @@ function getClaudeCliVersion() {
51387
51391
  error: `Command 'claude' not found. Searched: ${checkedPaths.slice(0, 3).join(", ")}...`
51388
51392
  };
51389
51393
  }
51390
- function isVersionCompatible(version) {
51394
+ function classifyClaudeVersion(version) {
51391
51395
  const semverVersion = import_semver.coerce(version);
51392
51396
  if (!semverVersion)
51393
- return false;
51394
- return import_semver.satisfies(semverVersion, CLAUDE_CLI_VERSION_RANGE);
51397
+ return "incompatible";
51398
+ if (import_semver.lt(semverVersion, CLAUDE_CLI_MIN_VERSION))
51399
+ return "incompatible";
51400
+ if (semverVersion.major > CLAUDE_CLI_SUPPORTED_MAJOR)
51401
+ return "incompatible";
51402
+ if (import_semver.satisfies(semverVersion, CLAUDE_CLI_VERIFIED_RANGE))
51403
+ return "ok";
51404
+ return "untested";
51395
51405
  }
51396
51406
  function getClaudePath() {
51397
51407
  if (process.env.CLAUDE_PATH) {
@@ -51419,36 +51429,41 @@ function validateClaudeCli() {
51419
51429
  installed: false,
51420
51430
  version: null,
51421
51431
  compatible: false,
51432
+ status: "incompatible",
51422
51433
  message: `Claude CLI not found at '${claudePath}'. Install it with: npm install -g @anthropic-ai/claude-code`,
51423
51434
  error: result.error
51424
51435
  };
51425
51436
  }
51426
- if (!result.version && result.rawOutput) {
51437
+ if (!result.version) {
51427
51438
  return {
51428
51439
  installed: true,
51429
51440
  version: null,
51430
51441
  compatible: true,
51442
+ status: "ok",
51431
51443
  message: `Claude CLI found (version unknown)`,
51432
- rawOutput: result.rawOutput
51444
+ rawOutput: result.rawOutput ?? undefined
51433
51445
  };
51434
51446
  }
51435
- if (!result.version) {
51447
+ const status = classifyClaudeVersion(result.version);
51448
+ if (status === "incompatible") {
51449
+ const semverVersion = import_semver.coerce(result.version);
51450
+ const reason = semverVersion && semverVersion.major > CLAUDE_CLI_SUPPORTED_MAJOR ? `is a new major version (only ${CLAUDE_CLI_SUPPORTED_MAJOR}.x is supported)` : `is too old (minimum: ${CLAUDE_CLI_MIN_VERSION})`;
51436
51451
  return {
51437
51452
  installed: true,
51438
- version: null,
51439
- compatible: true,
51440
- message: "Claude CLI found (version unknown)",
51453
+ version: result.version,
51454
+ compatible: false,
51455
+ status,
51456
+ message: `Claude CLI version ${result.version} ${reason}. ` + `Install a verified version: npm install -g @anthropic-ai/claude-code@${CLAUDE_CLI_LATEST_VERIFIED}`,
51441
51457
  rawOutput: result.rawOutput ?? undefined
51442
51458
  };
51443
51459
  }
51444
- const compatible = isVersionCompatible(result.version);
51445
- if (!compatible) {
51460
+ if (status === "untested") {
51446
51461
  return {
51447
51462
  installed: true,
51448
51463
  version: result.version,
51449
- compatible: false,
51450
- message: `Claude CLI version ${result.version} is not compatible. Required: ${CLAUDE_CLI_VERSION_RANGE}
51451
- ` + `Install a compatible version: npm install -g @anthropic-ai/claude-code@2.1.223`,
51464
+ compatible: true,
51465
+ status,
51466
+ message: `Claude CLI ${result.version} is newer than the latest verified version (${CLAUDE_CLI_LATEST_VERIFIED}) — untested, continuing anyway. ` + `Features may misbehave; pin a verified version with: npm install -g @anthropic-ai/claude-code@${CLAUDE_CLI_LATEST_VERIFIED}`,
51452
51467
  rawOutput: result.rawOutput ?? undefined
51453
51468
  };
51454
51469
  }
@@ -51456,6 +51471,7 @@ function validateClaudeCli() {
51456
51471
  installed: true,
51457
51472
  version: result.version,
51458
51473
  compatible: true,
51474
+ status,
51459
51475
  message: `Claude CLI ${result.version} ✓`,
51460
51476
  rawOutput: result.rawOutput ?? undefined
51461
51477
  };
@@ -51694,34 +51710,30 @@ async function runOnboarding(reconfigure = false) {
51694
51710
  }
51695
51711
  if (!claudeCheck.compatible) {
51696
51712
  console.log("");
51697
- if (claudeCheck.version) {
51698
- console.log(dim(` ⚠️ Claude Code CLI ${claudeCheck.version} is not compatible`));
51699
- console.log("");
51700
- console.log(dim(` Install a compatible version:`));
51701
- console.log(dim(" npm install -g @anthropic-ai/claude-code@2.1.223"));
51702
- } else {
51703
- console.log(dim(" ⚠️ Claude Code CLI found but version could not be determined"));
51704
- if (claudeCheck.rawOutput) {
51705
- console.log(dim(` Output from "claude --version": ${claudeCheck.rawOutput}`));
51706
- }
51707
- console.log("");
51708
- console.log(dim(" This may work fine - Claude was installed in a non-standard way."));
51709
- }
51713
+ console.log(dim(` ⚠️ ${claudeCheck.message}`));
51710
51714
  console.log("");
51711
51715
  const { continueAnyway } = await import_prompts.default({
51712
51716
  type: "confirm",
51713
51717
  name: "continueAnyway",
51714
51718
  message: "Continue anyway? (may not work correctly)",
51715
- initial: !claudeCheck.version
51719
+ initial: false
51716
51720
  }, { onCancel });
51717
51721
  if (!continueAnyway) {
51718
51722
  console.log("");
51719
51723
  console.log(dim(" Setup cancelled."));
51720
51724
  process.exit(0);
51721
51725
  }
51726
+ } else if (claudeCheck.status === "untested") {
51727
+ console.log("");
51728
+ console.log(dim(` ⚠️ ${claudeCheck.message}`));
51729
+ } else if (!claudeCheck.version) {
51730
+ console.log(dim(" ⚠️ Claude Code CLI found but version could not be determined"));
51731
+ if (claudeCheck.rawOutput) {
51732
+ console.log(dim(` Output from "claude --version": ${claudeCheck.rawOutput}`));
51733
+ }
51734
+ console.log(dim(" This may work fine - Claude was installed in a non-standard way."));
51722
51735
  } else {
51723
- const versionInfo = claudeCheck.version ?? "version unknown";
51724
- console.log(dim(` ✓ Claude Code CLI ${versionInfo}`));
51736
+ console.log(dim(` ✓ Claude Code CLI ${claudeCheck.version}`));
51725
51737
  }
51726
51738
  console.log("");
51727
51739
  console.log(dim(" \uD83D\uDCD6 Need help creating a bot?"));
@@ -53034,7 +53046,11 @@ function formatRelativeTimeShort(date) {
53034
53046
  }
53035
53047
  function formatVersionString() {
53036
53048
  const claudeVersion = getClaudeCliVersion().version;
53037
- return claudeVersion ? `CT v${VERSION} · CC v${claudeVersion}` : `CT v${VERSION}`;
53049
+ if (!claudeVersion)
53050
+ return `CT v${VERSION}`;
53051
+ const status = classifyClaudeVersion(claudeVersion);
53052
+ const marker = status === "untested" ? " ⚠️ untested" : status === "incompatible" ? " ⚠️ unsupported" : "";
53053
+ return `CT v${VERSION} · CC v${claudeVersion}${marker}`;
53038
53054
  }
53039
53055
  function truncateAtWord(str2, maxLength) {
53040
53056
  if (str2.length <= maxLength)
@@ -55960,7 +55976,7 @@ function cleanupBrowserBridgeSockets() {
55960
55976
  log8.debug(`Browser bridge cleanup failed: ${err}`);
55961
55977
  }
55962
55978
  }
55963
- function buildClaudeChildEnv(parentEnv, account) {
55979
+ function buildClaudeChildEnv(parentEnv, account, opts) {
55964
55980
  const env = { ...parentEnv };
55965
55981
  if (env.MCP_CONNECTION_NONBLOCKING === undefined) {
55966
55982
  env.MCP_CONNECTION_NONBLOCKING = "true";
@@ -55968,6 +55984,9 @@ function buildClaudeChildEnv(parentEnv, account) {
55968
55984
  if (env.ENABLE_PROMPT_CACHING_1H === undefined) {
55969
55985
  env.ENABLE_PROMPT_CACHING_1H = "true";
55970
55986
  }
55987
+ if (opts?.decisionBridge && env.MCP_TOOL_TIMEOUT === undefined) {
55988
+ env.MCP_TOOL_TIMEOUT = "3600000";
55989
+ }
55971
55990
  if (account?.home) {
55972
55991
  env.HOME = account.home;
55973
55992
  env.USERPROFILE = account.home;
@@ -56016,6 +56035,12 @@ function buildPermissionArgs(opts) {
56016
56035
  PERMISSION_TIMEOUT_MS: String(opts.permissionTimeoutMs),
56017
56036
  SESSION_OWNER_USERNAME: opts.sessionOwnerUsername || ""
56018
56037
  };
56038
+ if (opts.decisionBridgePath) {
56039
+ mcpEnv.DECISION_BRIDGE_PATH = opts.decisionBridgePath;
56040
+ if (process.env.DECISION_BRIDGE_TIMEOUT_MS) {
56041
+ mcpEnv.DECISION_BRIDGE_TIMEOUT_MS = process.env.DECISION_BRIDGE_TIMEOUT_MS;
56042
+ }
56043
+ }
56019
56044
  if (opts.platformConfig.appToken) {
56020
56045
  mcpEnv.PLATFORM_APP_TOKEN = opts.platformConfig.appToken;
56021
56046
  }
@@ -56156,7 +56181,8 @@ class ClaudeCli extends EventEmitter2 {
56156
56181
  workingDir: this.options.workingDir,
56157
56182
  uploadDir: this.options.uploadDir,
56158
56183
  outboundFiles: this.options.outboundFiles,
56159
- sessionOwnerUsername: this.options.sessionOwnerUsername
56184
+ sessionOwnerUsername: this.options.sessionOwnerUsername,
56185
+ decisionBridgePath: this.options.decisionBridgePath
56160
56186
  });
56161
56187
  args.push(...permResult.args);
56162
56188
  this.mcpConfigTempFile = permResult.tempFile;
@@ -56390,7 +56416,9 @@ class ClaudeCli extends EventEmitter2 {
56390
56416
  return true;
56391
56417
  }
56392
56418
  buildChildEnv() {
56393
- return buildClaudeChildEnv(process.env, this.options.account);
56419
+ return buildClaudeChildEnv(process.env, this.options.account, {
56420
+ decisionBridge: this.options.decisionBridgePath !== undefined
56421
+ });
56394
56422
  }
56395
56423
  getMcpServerPath() {
56396
56424
  const __filename2 = fileURLToPath3(import.meta.url);
@@ -57243,6 +57271,109 @@ function isAuthorizedForSession(check) {
57243
57271
  return sessionAllowedUsers?.has(username) ?? false;
57244
57272
  }
57245
57273
 
57274
+ // src/mcp/decision-bridge.ts
57275
+ import { createServer, createConnection } from "node:net";
57276
+ import { tmpdir as tmpdir2 } from "node:os";
57277
+ import { join as join9 } from "node:path";
57278
+ import { randomUUID as randomUUID2 } from "node:crypto";
57279
+ import { mkdtempSync } from "node:fs";
57280
+ import { rm as rm2 } from "node:fs/promises";
57281
+
57282
+ class BridgeUnavailableError extends Error {
57283
+ }
57284
+ function bridgeSocketPath() {
57285
+ if (process.platform === "win32") {
57286
+ return `\\\\.\\pipe\\ctb-${randomUUID2()}`;
57287
+ }
57288
+ const dir = mkdtempSync(join9(tmpdir2(), "ctb-"));
57289
+ return join9(dir, "b.sock");
57290
+ }
57291
+
57292
+ class DecisionBridgeServer {
57293
+ server;
57294
+ path;
57295
+ liveSockets = new Set;
57296
+ constructor(server, path2) {
57297
+ this.server = server;
57298
+ this.path = path2;
57299
+ }
57300
+ static async create(handler) {
57301
+ const path2 = bridgeSocketPath();
57302
+ const liveSockets = new Set;
57303
+ const server = createServer((socket) => {
57304
+ liveSockets.add(socket);
57305
+ socket.once("close", () => liveSockets.delete(socket));
57306
+ let buffer = "";
57307
+ const aborter = new AbortController;
57308
+ let responded = false;
57309
+ socket.on("data", (chunk) => {
57310
+ buffer += chunk.toString("utf8");
57311
+ const newline = buffer.indexOf(`
57312
+ `);
57313
+ if (newline === -1)
57314
+ return;
57315
+ const line = buffer.slice(0, newline);
57316
+ buffer = "";
57317
+ let request;
57318
+ try {
57319
+ request = JSON.parse(line);
57320
+ } catch {
57321
+ responded = true;
57322
+ socket.end(JSON.stringify({ behavior: "deny", message: "Malformed bridge request" }) + `
57323
+ `);
57324
+ return;
57325
+ }
57326
+ handler(request, aborter.signal).then((response) => {
57327
+ responded = true;
57328
+ socket.end(JSON.stringify(response) + `
57329
+ `);
57330
+ }).catch((err) => {
57331
+ responded = true;
57332
+ if (err instanceof BridgeUnavailableError) {
57333
+ socket.destroy();
57334
+ return;
57335
+ }
57336
+ socket.end(JSON.stringify({
57337
+ behavior: "deny",
57338
+ message: `Bridge handler failed: ${err instanceof Error ? err.message : String(err)}`
57339
+ }) + `
57340
+ `);
57341
+ });
57342
+ });
57343
+ socket.on("close", () => {
57344
+ if (!responded)
57345
+ aborter.abort();
57346
+ });
57347
+ socket.on("error", () => {});
57348
+ });
57349
+ try {
57350
+ await new Promise((resolve4, reject) => {
57351
+ server.once("error", reject);
57352
+ server.listen(path2, () => {
57353
+ server.removeListener("error", reject);
57354
+ resolve4();
57355
+ });
57356
+ });
57357
+ } catch (err) {
57358
+ if (process.platform !== "win32") {
57359
+ await rm2(join9(path2, ".."), { recursive: true, force: true }).catch(() => {});
57360
+ }
57361
+ throw err;
57362
+ }
57363
+ const bridge = new DecisionBridgeServer(server, path2);
57364
+ bridge.liveSockets = liveSockets;
57365
+ return bridge;
57366
+ }
57367
+ async close() {
57368
+ for (const socket of this.liveSockets)
57369
+ socket.destroy();
57370
+ await new Promise((resolve4) => this.server.close(() => resolve4()));
57371
+ if (process.platform !== "win32") {
57372
+ await rm2(join9(this.path, ".."), { recursive: true, force: true }).catch(() => {});
57373
+ }
57374
+ }
57375
+ }
57376
+
57246
57377
  // src/commands/registry.ts
57247
57378
  var COMMAND_REGISTRY = [
57248
57379
  {
@@ -58183,7 +58314,7 @@ ${avoidCommands.map((c) => `- \`!${c.command}\` - ${c.reason}`).join(`
58183
58314
  `.trim();
58184
58315
  }
58185
58316
  // src/session/lifecycle.ts
58186
- import { randomUUID as randomUUID4 } from "crypto";
58317
+ import { randomUUID as randomUUID5 } from "crypto";
58187
58318
  import { existsSync as existsSync12 } from "fs";
58188
58319
 
58189
58320
  // src/utils/keep-alive.ts
@@ -58549,23 +58680,23 @@ function updateLastMessage(session, post2) {
58549
58680
 
58550
58681
  // src/operations/streaming/handler.ts
58551
58682
  init_logger();
58552
- import { lstat, mkdir as mkdir2, mkdtemp, rm as rm2, writeFile as writeFile2 } from "fs/promises";
58553
- import { tmpdir as tmpdir2 } from "os";
58554
- import { join as join9 } from "path";
58683
+ import { lstat, mkdir as mkdir2, mkdtemp, rm as rm3, writeFile as writeFile2 } from "fs/promises";
58684
+ import { tmpdir as tmpdir3 } from "os";
58685
+ import { join as join10 } from "path";
58555
58686
  var log19 = createLogger("streaming");
58556
58687
  var UPLOAD_ROOT_DIR = "claude-threads-uploads";
58557
58688
  function safeIdSegment(id) {
58558
58689
  return id.replace(/[^A-Za-z0-9._-]/g, "_");
58559
58690
  }
58560
58691
  function getSessionUploadDir(platformId, threadId) {
58561
- return join9(tmpdir2(), UPLOAD_ROOT_DIR, `${safeIdSegment(platformId)}-${safeIdSegment(threadId)}`);
58692
+ return join10(tmpdir3(), UPLOAD_ROOT_DIR, `${safeIdSegment(platformId)}-${safeIdSegment(threadId)}`);
58562
58693
  }
58563
58694
  async function cleanupSessionUploads(platformId, threadId) {
58564
58695
  if (!platformId || !threadId)
58565
58696
  return;
58566
58697
  const dir = getSessionUploadDir(platformId, threadId);
58567
58698
  try {
58568
- await rm2(dir, { recursive: true, force: true });
58699
+ await rm3(dir, { recursive: true, force: true });
58569
58700
  } catch (err) {
58570
58701
  log19.debug(`Upload cleanup for ${platformId}:${threadId} failed (ignored): ${err}`);
58571
58702
  }
@@ -58591,13 +58722,13 @@ async function saveFilesToUploadDir(platform, uploadDir, files, debug = false) {
58591
58722
  log19.error(`Upload dir is a symlink, refusing all writes: ${uploadDir}`);
58592
58723
  return { saved, skipped };
58593
58724
  }
58594
- const messageDir = await mkdtemp(join9(uploadDir, `${Date.now().toString(36)}-`));
58725
+ const messageDir = await mkdtemp(join10(uploadDir, `${Date.now().toString(36)}-`));
58595
58726
  const usedNames = new Set;
58596
58727
  for (const file of files) {
58597
58728
  try {
58598
58729
  const buffer = await platform.downloadFile(file.id);
58599
58730
  const safeName = dedupeFilename(sanitizeFilename(file.name), usedNames);
58600
- const absolutePath = join9(messageDir, safeName);
58731
+ const absolutePath = join10(messageDir, safeName);
58601
58732
  await writeFile2(absolutePath, buffer, { mode: 384, flag: "wx" });
58602
58733
  saved.push({
58603
58734
  originalName: file.name,
@@ -58680,12 +58811,13 @@ function buildRestartCliOptions(session, ctx) {
58680
58811
  account: ctx.account,
58681
58812
  uploadDir: getSessionUploadDir(session.platformId, session.threadId),
58682
58813
  outboundFiles: platformMcpConfig.outboundFiles,
58683
- sessionOwnerUsername: session.startedBy
58814
+ sessionOwnerUsername: session.startedBy,
58815
+ decisionBridgePath: session.decisionBridge?.path
58684
58816
  };
58685
58817
  }
58686
58818
 
58687
58819
  // src/operations/commands/handler.ts
58688
- import { randomUUID as randomUUID3 } from "crypto";
58820
+ import { randomUUID as randomUUID4 } from "crypto";
58689
58821
  import { resolve as resolve5 } from "path";
58690
58822
  import { existsSync as existsSync11, statSync as statSync3 } from "fs";
58691
58823
 
@@ -62103,8 +62235,8 @@ init_emoji();
62103
62235
  // src/operations/bug-report/handler.ts
62104
62236
  import { execSync as execSync2 } from "child_process";
62105
62237
  import { writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
62106
- import { tmpdir as tmpdir3 } from "os";
62107
- import { join as join10 } from "path";
62238
+ import { tmpdir as tmpdir4 } from "os";
62239
+ import { join as join11 } from "path";
62108
62240
 
62109
62241
  // node_modules/@redactpii/node/lib/index.mjs
62110
62242
  class Redactor {
@@ -62684,7 +62816,7 @@ async function createGitHubIssue(title, body, workingDir) {
62684
62816
  if (!ghStatus.installed || !ghStatus.authenticated) {
62685
62817
  throw new Error(ghStatus.error);
62686
62818
  }
62687
- const bodyFile = join10(tmpdir3(), `bug-body-${Date.now()}.md`);
62819
+ const bodyFile = join11(tmpdir4(), `bug-body-${Date.now()}.md`);
62688
62820
  try {
62689
62821
  writeFileSync6(bodyFile, body, "utf-8");
62690
62822
  const cmd = `gh issue create --repo "${GITHUB_REPO}" --title "${escapeShell(title)}" --body-file "${bodyFile}"`;
@@ -66717,6 +66849,8 @@ class MessageManager {
66717
66849
  emitSessionUpdateCallback;
66718
66850
  toolStartTimes = new Map;
66719
66851
  taskTracker = new TaskTracker;
66852
+ pendingBridgePlan = null;
66853
+ pendingBridgeQuestion = null;
66720
66854
  flushTimer = null;
66721
66855
  static DEFAULT_FLUSH_DELAY_MS = 500;
66722
66856
  flushDelayMs;
@@ -67178,9 +67312,66 @@ class MessageManager {
67178
67312
  logger.debug("Reaction not handled by any executor");
67179
67313
  return false;
67180
67314
  }
67315
+ handleBridgeRequest(request, signal) {
67316
+ if (request.kind === "plan_approval") {
67317
+ this.pendingBridgePlan?.resolve({ behavior: "deny", message: "Superseded by a newer plan" });
67318
+ return new Promise((resolve5) => {
67319
+ const pending = { resolve: resolve5, input: request.input };
67320
+ this.pendingBridgePlan = pending;
67321
+ signal?.addEventListener("abort", () => {
67322
+ if (this.pendingBridgePlan === pending)
67323
+ this.pendingBridgePlan = null;
67324
+ });
67325
+ });
67326
+ }
67327
+ if (request.kind === "question") {
67328
+ this.pendingBridgeQuestion?.resolve({ behavior: "deny", message: "Superseded by newer questions" });
67329
+ return new Promise((resolve5) => {
67330
+ const pending = { resolve: resolve5, input: request.input };
67331
+ this.pendingBridgeQuestion = pending;
67332
+ signal?.addEventListener("abort", () => {
67333
+ if (this.pendingBridgeQuestion === pending)
67334
+ this.pendingBridgeQuestion = null;
67335
+ });
67336
+ });
67337
+ }
67338
+ return Promise.resolve({ behavior: "deny", message: `Unknown bridge request kind` });
67339
+ }
67340
+ resolveBridgePlan(approved) {
67341
+ const pending = this.pendingBridgePlan;
67342
+ if (!pending)
67343
+ return false;
67344
+ this.pendingBridgePlan = null;
67345
+ pending.resolve(approved ? { behavior: "allow", updatedInput: pending.input } : { behavior: "deny", message: "The user rejected the plan." });
67346
+ return true;
67347
+ }
67348
+ resolveBridgeQuestion(answers) {
67349
+ const pending = this.pendingBridgeQuestion;
67350
+ if (!pending)
67351
+ return false;
67352
+ this.pendingBridgeQuestion = null;
67353
+ const questions = Array.isArray(pending.input.questions) ? pending.input.questions : [];
67354
+ const record = {};
67355
+ for (const a of answers) {
67356
+ const match = questions.find((q) => q.header === a.header);
67357
+ record[match?.question ?? a.header] = a.answer;
67358
+ }
67359
+ pending.resolve({
67360
+ behavior: "allow",
67361
+ updatedInput: { ...pending.input, answers: record }
67362
+ });
67363
+ return true;
67364
+ }
67365
+ denyPendingBridgeRequests(reason) {
67366
+ this.pendingBridgePlan?.resolve({ behavior: "deny", message: reason });
67367
+ this.pendingBridgePlan = null;
67368
+ this.pendingBridgeQuestion?.resolve({ behavior: "deny", message: reason });
67369
+ this.pendingBridgeQuestion = null;
67370
+ }
67181
67371
  clearClaudeSessionState() {
67182
67372
  this.toolStartTimes.clear();
67183
67373
  this.taskTracker.clear();
67374
+ this.denyPendingBridgeRequests("Claude was restarted before a decision was made");
67184
67375
  }
67185
67376
  reset() {
67186
67377
  this.cancelScheduledFlush();
@@ -67196,6 +67387,7 @@ class MessageManager {
67196
67387
  this.systemExecutor.reset();
67197
67388
  }
67198
67389
  dispose() {
67390
+ this.denyPendingBridgeRequests("Session ended before a decision was made");
67199
67391
  this.cancelScheduledFlush();
67200
67392
  this.postTracker.clearSession(this.sessionId);
67201
67393
  this.events.removeAllListeners();
@@ -67976,7 +68168,7 @@ async function suggestBranchNames(workingDir, userMessage) {
67976
68168
 
67977
68169
  // src/operations/worktree/handler.ts
67978
68170
  init_worktree();
67979
- import { randomUUID as randomUUID2 } from "crypto";
68171
+ import { randomUUID as randomUUID3 } from "crypto";
67980
68172
  init_logger();
67981
68173
  var log25 = createLogger("worktree");
67982
68174
  var sessionLog2 = createSessionLog(log25);
@@ -68223,7 +68415,7 @@ async function createAndSwitchToWorktree(session, branch, username, options) {
68223
68415
  transitionTo(session, "restarting");
68224
68416
  await session.claude.kill();
68225
68417
  await options.flush(session);
68226
- const newSessionId = randomUUID2();
68418
+ const newSessionId = randomUUID3();
68227
68419
  session.claudeSessionId = newSessionId;
68228
68420
  const needsTitlePrompt = !session.sessionTitle;
68229
68421
  const cliOptions = {
@@ -68320,7 +68512,7 @@ ${fmt.formatItalic("Claude Code restarted in the worktree")}`);
68320
68512
  transitionTo(session, "restarting");
68321
68513
  await session.claude.kill();
68322
68514
  await options.flush(session);
68323
- const newSessionId = randomUUID2();
68515
+ const newSessionId = randomUUID3();
68324
68516
  session.claudeSessionId = newSessionId;
68325
68517
  const needsTitlePrompt = !session.sessionTitle;
68326
68518
  const cliOptions = {
@@ -69244,6 +69436,7 @@ async function restartClaudeSession(session, cliOptions, ctx, actionName) {
69244
69436
  transitionTo(session, "restarting");
69245
69437
  await session.claude.kill();
69246
69438
  await ctx.ops.flush(session);
69439
+ session.messageManager?.denyPendingBridgeRequests("Claude was restarted before a decision was made");
69247
69440
  if (!cliOptions.resume) {
69248
69441
  session.messageManager?.clearClaudeSessionState();
69249
69442
  }
@@ -69325,12 +69518,18 @@ async function approvePendingPlan(session, username, ctx) {
69325
69518
  }
69326
69519
  const { postId } = pendingApproval;
69327
69520
  sessionLog5(session).info(`✅ Plan approved by @${username} via command`);
69521
+ const viaBridge = session.messageManager?.resolveBridgePlan(true) ?? false;
69328
69522
  const formatter = session.platform.getFormatter();
69329
69523
  const statusMessage = `${formatter.formatBold("Plan approved")} by ${formatter.formatUserMention(username)} - starting implementation...`;
69330
69524
  await updatePostSuccess(session, postId, statusMessage);
69331
69525
  session.messageManager?.clearPendingApproval();
69332
69526
  session.messageManager?.clearPendingQuestionSet();
69333
69527
  session.planApproved = true;
69528
+ if (viaBridge) {
69529
+ sessionLog5(session).info("Plan approved via decision bridge (!approve)");
69530
+ ctx.ops.startTyping(session);
69531
+ return;
69532
+ }
69334
69533
  if (session.claude.isRunning()) {
69335
69534
  session.claude.sendMessage("Plan approved! Please proceed with the implementation.");
69336
69535
  ctx.ops.startTyping(session);
@@ -69393,7 +69592,7 @@ async function changeDirectory(session, newDir, username, ctx) {
69393
69592
  sessionLog5(session).debug(`Stored work summary for context preservation`);
69394
69593
  }
69395
69594
  session.workingDir = absoluteDir;
69396
- const newSessionId = randomUUID3();
69595
+ const newSessionId = randomUUID4();
69397
69596
  session.claudeSessionId = newSessionId;
69398
69597
  const appendSystemPrompt = await buildAppendSystemPrompt(session.platform, session.platformId, absoluteDir, session.threadId, session.startedBy, session.sessionAllowedUsers, CHAT_PLATFORM_PROMPT, ctx.state.githubEmailsStore, { userAttribution: session.userAttribution });
69399
69598
  const cliOptions = {
@@ -69910,6 +70109,8 @@ async function cleanupSession(session, ctx, options = {}) {
69910
70109
  await closeThreadLogger(session, action, details);
69911
70110
  }
69912
70111
  session.messageManager?.dispose();
70112
+ session.decisionBridge?.close();
70113
+ session.decisionBridge = undefined;
69913
70114
  ctx.ops.emitSessionRemove(session.sessionId);
69914
70115
  mutableSessions(ctx).delete(session.sessionId);
69915
70116
  if (doCleanupPostIndex) {
@@ -69927,6 +70128,8 @@ function releaseAccountIfHeld(session, ctx) {
69927
70128
  }
69928
70129
  function removeFromRegistry(session, ctx) {
69929
70130
  session.messageManager?.dispose();
70131
+ session.decisionBridge?.close();
70132
+ session.decisionBridge = undefined;
69930
70133
  ctx.ops.emitSessionRemove(session.sessionId);
69931
70134
  mutableSessions(ctx).delete(session.sessionId);
69932
70135
  cleanupPostIndex(ctx, session.threadId);
@@ -69952,6 +70155,20 @@ function findPersistedByThreadId(persisted, threadId) {
69952
70155
  }
69953
70156
  return;
69954
70157
  }
70158
+ async function createSessionDecisionBridge(ref) {
70159
+ try {
70160
+ return await DecisionBridgeServer.create(async (request, signal) => {
70161
+ const messageManager = ref.current?.messageManager;
70162
+ if (!messageManager) {
70163
+ throw new BridgeUnavailableError("Session is not ready for decisions yet");
70164
+ }
70165
+ return messageManager.handleBridgeRequest(request, signal);
70166
+ });
70167
+ } catch (err) {
70168
+ log31.warn(`Decision bridge unavailable — falling back to legacy MCP prompts: ${err}`);
70169
+ return null;
70170
+ }
70171
+ }
69955
70172
  function createMessageManager(session, ctx) {
69956
70173
  const postTracker = new PostTracker;
69957
70174
  const messageManager = new MessageManager({
@@ -69982,10 +70199,20 @@ function createMessageManager(session, ctx) {
69982
70199
  flushDelayMs: ctx.config.flushDelayMs
69983
70200
  });
69984
70201
  messageManager.events.on("question:complete", ({ toolUseId: _toolUseId, answers }) => {
70202
+ if (messageManager.resolveBridgeQuestion(answers)) {
70203
+ sessionLog6(session).info("Question answered via decision bridge");
70204
+ ctx.ops.startTyping(session);
70205
+ return;
70206
+ }
69985
70207
  const answerJson = JSON.stringify(answers);
69986
70208
  session.claude.sendMessage(answerJson);
69987
70209
  });
69988
70210
  messageManager.events.on("approval:complete", ({ toolUseId: _toolUseId, approved }) => {
70211
+ if (messageManager.resolveBridgePlan(approved)) {
70212
+ sessionLog6(session).info(`Plan ${approved ? "approved" : "denied"} via decision bridge`);
70213
+ ctx.ops.startTyping(session);
70214
+ return;
70215
+ }
69989
70216
  const response = approved ? "approved" : "denied";
69990
70217
  session.claude.sendMessage(response);
69991
70218
  });
@@ -70251,7 +70478,7 @@ async function startSession(options, username, displayName, replyToPostId, platf
70251
70478
  const actualThreadId = replyToPostId || (startPost ? startPost.id : "");
70252
70479
  const sessionId = ctx.ops.getSessionId(platformId, actualThreadId);
70253
70480
  platform.sendTyping(actualThreadId);
70254
- const claudeSessionId = randomUUID4();
70481
+ const claudeSessionId = randomUUID5();
70255
70482
  let workingDir = ctx.config.workingDir;
70256
70483
  let permissionMode = ctx.config.permissionMode;
70257
70484
  let forceInteractivePermissions = false;
@@ -70305,6 +70532,8 @@ async function startSession(options, username, displayName, replyToPostId, platf
70305
70532
  if (claudeAccount) {
70306
70533
  log31.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
70307
70534
  }
70535
+ const bridgeSessionRef = {};
70536
+ const decisionBridge = await createSessionDecisionBridge(bridgeSessionRef);
70308
70537
  const cliOptions = {
70309
70538
  workingDir,
70310
70539
  threadId: actualThreadId,
@@ -70319,9 +70548,16 @@ async function startSession(options, username, displayName, replyToPostId, platf
70319
70548
  account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined,
70320
70549
  uploadDir: getSessionUploadDir(platformId, actualThreadId),
70321
70550
  outboundFiles: platformMcpConfig.outboundFiles,
70322
- sessionOwnerUsername: username
70551
+ sessionOwnerUsername: username,
70552
+ decisionBridgePath: decisionBridge?.path
70323
70553
  };
70324
- const claude = new ClaudeCli(cliOptions);
70554
+ let claude;
70555
+ try {
70556
+ claude = new ClaudeCli(cliOptions);
70557
+ } catch (err) {
70558
+ decisionBridge?.close();
70559
+ throw err;
70560
+ }
70325
70561
  const session = {
70326
70562
  platformId,
70327
70563
  threadId: actualThreadId,
@@ -70355,7 +70591,9 @@ async function startSession(options, username, displayName, replyToPostId, platf
70355
70591
  enabled: ctx.config.threadLogsEnabled ?? true
70356
70592
  })
70357
70593
  };
70594
+ session.decisionBridge = decisionBridge ?? undefined;
70358
70595
  session.messageManager = createMessageManager(session, ctx);
70596
+ bridgeSessionRef.current = session;
70359
70597
  session.threadLogger?.logLifecycle("start", {
70360
70598
  username,
70361
70599
  workingDir: ctx.config.workingDir
@@ -70382,6 +70620,8 @@ async function startSession(options, username, displayName, replyToPostId, platf
70382
70620
  await logAndNotify(err, { action: "Start Claude", session });
70383
70621
  ctx.ops.stopTyping(session);
70384
70622
  session.messageManager?.dispose();
70623
+ session.decisionBridge?.close();
70624
+ session.decisionBridge = undefined;
70385
70625
  ctx.ops.emitSessionRemove(session.sessionId);
70386
70626
  mutableSessions(ctx).delete(session.sessionId);
70387
70627
  releaseAccountIfHeld(session, ctx);
@@ -70465,6 +70705,8 @@ Please start a new session.`), { action: "Post resume failure notification" });
70465
70705
  if (state.claudeAccountId && !claudeAccount) {
70466
70706
  log31.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
70467
70707
  }
70708
+ const resumeBridgeRef = {};
70709
+ const resumeBridge = await createSessionDecisionBridge(resumeBridgeRef);
70468
70710
  const cliOptions = {
70469
70711
  workingDir: state.workingDir,
70470
70712
  threadId: state.threadId,
@@ -70479,9 +70721,16 @@ Please start a new session.`), { action: "Post resume failure notification" });
70479
70721
  account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined,
70480
70722
  uploadDir: getSessionUploadDir(platformId, state.threadId),
70481
70723
  outboundFiles: platformMcpConfig.outboundFiles,
70482
- sessionOwnerUsername: state.startedBy
70724
+ sessionOwnerUsername: state.startedBy,
70725
+ decisionBridgePath: resumeBridge?.path
70483
70726
  };
70484
- const claude = new ClaudeCli(cliOptions);
70727
+ let claude;
70728
+ try {
70729
+ claude = new ClaudeCli(cliOptions);
70730
+ } catch (err) {
70731
+ resumeBridge?.close();
70732
+ throw err;
70733
+ }
70485
70734
  const session = {
70486
70735
  platformId,
70487
70736
  threadId: state.threadId,
@@ -70527,6 +70776,7 @@ Please start a new session.`), { action: "Post resume failure notification" });
70527
70776
  enabled: ctx.config.threadLogsEnabled ?? true
70528
70777
  })
70529
70778
  };
70779
+ session.decisionBridge = resumeBridge ?? undefined;
70530
70780
  if (!session.worktreeInfo) {
70531
70781
  const detected = await detectWorktreeInfo(session.workingDir);
70532
70782
  if (detected) {
@@ -70539,6 +70789,7 @@ Please start a new session.`), { action: "Post resume failure notification" });
70539
70789
  }
70540
70790
  }
70541
70791
  session.messageManager = createMessageManager(session, ctx);
70792
+ resumeBridgeRef.current = session;
70542
70793
  await session.messageManager.restoreTaskListFromPersistence({
70543
70794
  tasksPostId: state.tasksPostId,
70544
70795
  lastTasksContent: state.lastTasksContent,
@@ -70594,6 +70845,8 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
70594
70845
  } catch (err) {
70595
70846
  log31.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
70596
70847
  session.messageManager?.dispose();
70848
+ session.decisionBridge?.close();
70849
+ session.decisionBridge = undefined;
70597
70850
  ctx.ops.emitSessionRemove(sessionId);
70598
70851
  mutableSessions(ctx).delete(sessionId);
70599
70852
  ctx.state.sessionStore.remove(sessionId);
@@ -77177,7 +77430,7 @@ var import_react61 = __toESM(require_react(), 1);
77177
77430
 
77178
77431
  // src/ui/components/Header.tsx
77179
77432
  var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
77180
- function Header({ version, workingDir, claudeVersion }) {
77433
+ function Header({ version, workingDir, claudeVersion, claudeStatus }) {
77181
77434
  return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
77182
77435
  borderStyle: "round",
77183
77436
  paddingX: 1,
@@ -77263,7 +77516,15 @@ function Header({ version, workingDir, claudeVersion }) {
77263
77516
  " | Claude ",
77264
77517
  claudeVersion
77265
77518
  ]
77266
- }, undefined, true, undefined, this)
77519
+ }, undefined, true, undefined, this),
77520
+ claudeStatus === "untested" && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
77521
+ color: "yellow",
77522
+ children: " ⚠ untested"
77523
+ }, undefined, false, undefined, this),
77524
+ claudeStatus === "incompatible" && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
77525
+ color: "yellow",
77526
+ children: " ⚠ unsupported"
77527
+ }, undefined, false, undefined, this)
77267
77528
  ]
77268
77529
  }, undefined, true, undefined, this)
77269
77530
  ]
@@ -81231,7 +81492,8 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
81231
81492
  const headerContent = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Header, {
81232
81493
  version: config.version,
81233
81494
  workingDir: config.workingDir,
81234
- claudeVersion: config.claudeVersion
81495
+ claudeVersion: config.claudeVersion,
81496
+ claudeStatus: config.claudeStatus
81235
81497
  }, undefined, false, undefined, this);
81236
81498
  const footerContent = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Footer, {
81237
81499
  ready: state.ready,
@@ -81619,7 +81881,8 @@ class HeadlessProvider {
81619
81881
  const { config } = this.options;
81620
81882
  this.log("HeadlessProvider", `claude-threads v${config.version} starting in headless mode`);
81621
81883
  this.log("HeadlessProvider", `Working directory: ${config.workingDir}`);
81622
- this.log("HeadlessProvider", `Claude CLI: ${config.claudeVersion}`);
81884
+ const cliMarker = config.claudeStatus === "untested" ? " ⚠️ untested (newer than the verified range)" : config.claudeStatus === "incompatible" ? " ⚠️ unsupported (version check bypassed)" : "";
81885
+ this.log("HeadlessProvider", `Claude CLI: ${config.claudeVersion}${cliMarker}`);
81623
81886
  }
81624
81887
  async stop() {
81625
81888
  this.log("HeadlessProvider", "Stopped");
@@ -82588,7 +82851,7 @@ class UpdateInstaller {
82588
82851
  init_logger();
82589
82852
  import { spawn as spawn5 } from "child_process";
82590
82853
  import { existsSync as existsSync15, statSync as statSync4 } from "fs";
82591
- import { delimiter, join as join11 } from "path";
82854
+ import { delimiter, join as join12 } from "path";
82592
82855
  var log39 = createLogger("respawn");
82593
82856
  function decideRespawn(env5 = process.env, isTTY = !!process.stdout.isTTY) {
82594
82857
  if (env5.CLAUDE_THREADS_BIN) {
@@ -82614,16 +82877,16 @@ function resolveClaudeThreadsBin(_env = process.env, _existsSync = existsSync15,
82614
82877
  const path10 = _env.PATH || _env.Path || "";
82615
82878
  const dirs = path10.split(delimiter).filter(Boolean);
82616
82879
  const home = _env.HOME || _env.USERPROFILE;
82617
- const bunRoot = _env.BUN_INSTALL || (home ? join11(home, ".bun") : null);
82880
+ const bunRoot = _env.BUN_INSTALL || (home ? join12(home, ".bun") : null);
82618
82881
  if (bunRoot) {
82619
- const bunBin = join11(bunRoot, "bin");
82882
+ const bunBin = join12(bunRoot, "bin");
82620
82883
  if (!dirs.includes(bunBin)) {
82621
82884
  dirs.push(bunBin);
82622
82885
  }
82623
82886
  }
82624
82887
  for (const dir of dirs) {
82625
82888
  for (const name of names) {
82626
- const candidate = join11(dir, name);
82889
+ const candidate = join12(dir, name);
82627
82890
  if (_existsSync(candidate) && _isFileExecutable(candidate)) {
82628
82891
  return candidate;
82629
82892
  }
@@ -83040,6 +83303,15 @@ async function startWithoutDaemon() {
83040
83303
  console.error("");
83041
83304
  process.exit(1);
83042
83305
  }
83306
+ if (claudeValidation.status === "untested") {
83307
+ console.error(yellow(` ⚠️ ${claudeValidation.message}`));
83308
+ console.error("");
83309
+ }
83310
+ if (!claudeValidation.compatible && opts.skipVersionCheck) {
83311
+ const prefix = claudeValidation.installed ? "running with an unsupported Claude CLI. " : "";
83312
+ console.error(yellow(` ⚠️ --skip-version-check: ${prefix}${claudeValidation.message}`));
83313
+ console.error("");
83314
+ }
83043
83315
  if (process.env.CLAUDE_CODE_SUBPROCESS_ENV_SCRUB === "1") {
83044
83316
  const hasSkipPermissionPlatform = config.platforms.some((p) => p.skipPermissions === true);
83045
83317
  if (hasSkipPermissionPlatform) {
@@ -83074,7 +83346,7 @@ async function startWithoutDaemon() {
83074
83346
  version: VERSION,
83075
83347
  workingDir,
83076
83348
  claudeVersion: claudeValidation.version || "unknown",
83077
- claudeCompatible: claudeValidation.compatible,
83349
+ claudeStatus: claudeValidation.status,
83078
83350
  permissionMode: runtimeConfig.permissionMode,
83079
83351
  chromeEnabled: runtimeConfig.chromeEnabled,
83080
83352
  keepAliveEnabled: runtimeConfig.keepAliveEnabled