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/CHANGELOG.md +12 -0
- package/dist/index.js +369 -97
- package/dist/mcp/mcp-server.js +246 -14
- package/docs/CONFIGURATION.md +4 -2
- package/docs/MCP-TOOLS.md +1 -1
- package/package.json +1 -1
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
|
|
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
|
-
|
|
14207
|
-
|
|
14206
|
+
join11(etc, name, "config"),
|
|
14207
|
+
join11(etc, name + "rc")
|
|
14208
14208
|
].forEach(addConfigFile);
|
|
14209
14209
|
if (home)
|
|
14210
14210
|
[
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
|
|
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
|
|
16969
|
-
module.exports =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
17769
|
+
ltfn = lt2;
|
|
17770
17770
|
comp = ">";
|
|
17771
17771
|
ecomp = ">=";
|
|
17772
17772
|
break;
|
|
17773
17773
|
case "<":
|
|
17774
|
-
gtfn =
|
|
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,
|
|
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
|
-
|
|
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 &&
|
|
17955
|
-
gtltComp = compare(gt.semver,
|
|
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 !== ">=" ||
|
|
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 (
|
|
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 =
|
|
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 &&
|
|
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 (
|
|
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(
|
|
18009
|
-
if (lower === c && lower !==
|
|
18008
|
+
lower = lowerLT(lt2, c, options);
|
|
18009
|
+
if (lower === c && lower !== lt2) {
|
|
18010
18010
|
return false;
|
|
18011
18011
|
}
|
|
18012
|
-
} else if (
|
|
18012
|
+
} else if (lt2.operator === "<=" && !satisfies2(lt2.semver, String(c), options)) {
|
|
18013
18013
|
return false;
|
|
18014
18014
|
}
|
|
18015
18015
|
}
|
|
18016
|
-
if (!c.operator && (
|
|
18016
|
+
if (!c.operator && (lt2 || gt) && gtltComp !== 0) {
|
|
18017
18017
|
return false;
|
|
18018
18018
|
}
|
|
18019
18019
|
}
|
|
18020
|
-
if (gt && hasDomLT && !
|
|
18020
|
+
if (gt && hasDomLT && !lt2 && gtltComp !== 0) {
|
|
18021
18021
|
return false;
|
|
18022
18022
|
}
|
|
18023
|
-
if (
|
|
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
|
|
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
|
|
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
|
|
51394
|
+
function classifyClaudeVersion(version) {
|
|
51391
51395
|
const semverVersion = import_semver.coerce(version);
|
|
51392
51396
|
if (!semverVersion)
|
|
51393
|
-
return
|
|
51394
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
51439
|
-
compatible:
|
|
51440
|
-
|
|
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
|
-
|
|
51445
|
-
if (!compatible) {
|
|
51460
|
+
if (status === "untested") {
|
|
51446
51461
|
return {
|
|
51447
51462
|
installed: true,
|
|
51448
51463
|
version: result.version,
|
|
51449
|
-
compatible:
|
|
51450
|
-
|
|
51451
|
-
` + `
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
58553
|
-
import { tmpdir as
|
|
58554
|
-
import { join as
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
62107
|
-
import { join as
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ?
|
|
82880
|
+
const bunRoot = _env.BUN_INSTALL || (home ? join12(home, ".bun") : null);
|
|
82618
82881
|
if (bunRoot) {
|
|
82619
|
-
const bunBin =
|
|
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 =
|
|
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
|
-
|
|
83349
|
+
claudeStatus: claudeValidation.status,
|
|
83078
83350
|
permissionMode: runtimeConfig.permissionMode,
|
|
83079
83351
|
chromeEnabled: runtimeConfig.chromeEnabled,
|
|
83080
83352
|
keepAliveEnabled: runtimeConfig.keepAliveEnabled
|