claude-threads 1.8.3 → 1.9.1
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 +44 -0
- package/README.md +1 -1
- package/dist/index.js +830 -613
- package/dist/mcp/permission-server.js +3002 -293
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11862,7 +11862,7 @@ import * as fs from "fs/promises";
|
|
|
11862
11862
|
import { homedir as homedir4 } from "os";
|
|
11863
11863
|
async function execGit(args, cwd) {
|
|
11864
11864
|
const cmd = `git ${args.join(" ")}`;
|
|
11865
|
-
|
|
11865
|
+
log7.debug(`Executing: ${cmd}`);
|
|
11866
11866
|
return new Promise((resolve3, reject) => {
|
|
11867
11867
|
const proc = crossSpawn("git", args, { cwd });
|
|
11868
11868
|
let stdout = "";
|
|
@@ -11875,15 +11875,15 @@ async function execGit(args, cwd) {
|
|
|
11875
11875
|
});
|
|
11876
11876
|
proc.on("close", (code) => {
|
|
11877
11877
|
if (code === 0) {
|
|
11878
|
-
|
|
11878
|
+
log7.debug(`${cmd} → success`);
|
|
11879
11879
|
resolve3(stdout.trim());
|
|
11880
11880
|
} else {
|
|
11881
|
-
|
|
11881
|
+
log7.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
|
|
11882
11882
|
reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
|
|
11883
11883
|
}
|
|
11884
11884
|
});
|
|
11885
11885
|
proc.on("error", (err) => {
|
|
11886
|
-
|
|
11886
|
+
log7.warn(`${cmd} → error: ${err}`);
|
|
11887
11887
|
reject(err);
|
|
11888
11888
|
});
|
|
11889
11889
|
});
|
|
@@ -11893,7 +11893,7 @@ async function isGitRepository(dir) {
|
|
|
11893
11893
|
await execGit(["rev-parse", "--git-dir"], dir);
|
|
11894
11894
|
return true;
|
|
11895
11895
|
} catch (err) {
|
|
11896
|
-
|
|
11896
|
+
log7.debug(`Not a git repository: ${dir} (${err})`);
|
|
11897
11897
|
return false;
|
|
11898
11898
|
}
|
|
11899
11899
|
}
|
|
@@ -12028,7 +12028,7 @@ async function detectWorktreeInfo(workingDir) {
|
|
|
12028
12028
|
const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
|
|
12029
12029
|
const branch = branchOutput?.trim();
|
|
12030
12030
|
if (!branch) {
|
|
12031
|
-
|
|
12031
|
+
log7.debug(`Could not detect branch for worktree at ${workingDir}`);
|
|
12032
12032
|
return null;
|
|
12033
12033
|
}
|
|
12034
12034
|
const gitDirOutput = await execGit(["rev-parse", "--git-common-dir"], workingDir);
|
|
@@ -12040,45 +12040,45 @@ async function detectWorktreeInfo(workingDir) {
|
|
|
12040
12040
|
repoRoot = repoRoot.slice(0, -4);
|
|
12041
12041
|
}
|
|
12042
12042
|
}
|
|
12043
|
-
|
|
12043
|
+
log7.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
|
|
12044
12044
|
return {
|
|
12045
12045
|
worktreePath: workingDir,
|
|
12046
12046
|
branch,
|
|
12047
12047
|
repoRoot: repoRoot || workingDir
|
|
12048
12048
|
};
|
|
12049
12049
|
} catch (err) {
|
|
12050
|
-
|
|
12050
|
+
log7.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
|
|
12051
12051
|
return null;
|
|
12052
12052
|
}
|
|
12053
12053
|
}
|
|
12054
12054
|
async function createWorktree(repoRoot, branch, targetDir) {
|
|
12055
|
-
|
|
12055
|
+
log7.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
|
|
12056
12056
|
const parentDir = path.dirname(targetDir);
|
|
12057
|
-
|
|
12057
|
+
log7.debug(`Creating parent directory: ${parentDir}`);
|
|
12058
12058
|
await fs.mkdir(parentDir, { recursive: true });
|
|
12059
12059
|
const exists = await branchExists(repoRoot, branch);
|
|
12060
12060
|
if (exists) {
|
|
12061
|
-
|
|
12061
|
+
log7.debug(`Branch '${branch}' exists, adding worktree`);
|
|
12062
12062
|
await execGit(["worktree", "add", targetDir, branch], repoRoot);
|
|
12063
12063
|
} else {
|
|
12064
|
-
|
|
12064
|
+
log7.debug(`Branch '${branch}' does not exist, creating with worktree`);
|
|
12065
12065
|
await execGit(["worktree", "add", "-b", branch, targetDir], repoRoot);
|
|
12066
12066
|
}
|
|
12067
|
-
|
|
12067
|
+
log7.info(`Worktree created successfully: ${targetDir}`);
|
|
12068
12068
|
return targetDir;
|
|
12069
12069
|
}
|
|
12070
12070
|
async function removeWorktree(repoRoot, worktreePath) {
|
|
12071
|
-
|
|
12071
|
+
log7.info(`Removing worktree: ${worktreePath}`);
|
|
12072
12072
|
try {
|
|
12073
12073
|
await execGit(["worktree", "remove", worktreePath], repoRoot);
|
|
12074
|
-
|
|
12074
|
+
log7.debug("Worktree removed cleanly");
|
|
12075
12075
|
} catch (err) {
|
|
12076
|
-
|
|
12076
|
+
log7.debug(`Clean remove failed (${err}), trying force remove`);
|
|
12077
12077
|
await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
|
|
12078
12078
|
}
|
|
12079
|
-
|
|
12079
|
+
log7.debug("Pruning stale worktree references");
|
|
12080
12080
|
await execGit(["worktree", "prune"], repoRoot);
|
|
12081
|
-
|
|
12081
|
+
log7.info("Worktree removed and pruned successfully");
|
|
12082
12082
|
}
|
|
12083
12083
|
async function findWorktreeByBranch(repoRoot, branch) {
|
|
12084
12084
|
const worktrees = await listWorktrees(repoRoot);
|
|
@@ -12119,14 +12119,14 @@ async function writeMetadataStore(store) {
|
|
|
12119
12119
|
await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
|
|
12120
12120
|
await fs.chmod(METADATA_STORE_PATH, 384);
|
|
12121
12121
|
} catch (err) {
|
|
12122
|
-
|
|
12122
|
+
log7.warn(`Failed to write worktree metadata store: ${err}`);
|
|
12123
12123
|
}
|
|
12124
12124
|
}
|
|
12125
12125
|
async function writeWorktreeMetadata(worktreePath, metadata) {
|
|
12126
12126
|
const store = await readMetadataStore();
|
|
12127
12127
|
store[worktreePath] = metadata;
|
|
12128
12128
|
await writeMetadataStore(store);
|
|
12129
|
-
|
|
12129
|
+
log7.debug(`Wrote worktree metadata for: ${worktreePath}`);
|
|
12130
12130
|
}
|
|
12131
12131
|
async function readWorktreeMetadata(worktreePath) {
|
|
12132
12132
|
const store = await readMetadataStore();
|
|
@@ -12149,14 +12149,14 @@ async function removeWorktreeMetadata(worktreePath) {
|
|
|
12149
12149
|
if (store[worktreePath]) {
|
|
12150
12150
|
delete store[worktreePath];
|
|
12151
12151
|
await writeMetadataStore(store);
|
|
12152
|
-
|
|
12152
|
+
log7.debug(`Removed worktree metadata for: ${worktreePath}`);
|
|
12153
12153
|
}
|
|
12154
12154
|
}
|
|
12155
|
-
var
|
|
12155
|
+
var log7, WORKTREES_DIR, METADATA_STORE_PATH;
|
|
12156
12156
|
var init_worktree = __esm(() => {
|
|
12157
12157
|
init_spawn();
|
|
12158
12158
|
init_logger();
|
|
12159
|
-
|
|
12159
|
+
log7 = createLogger("git-wt");
|
|
12160
12160
|
WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
|
|
12161
12161
|
METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
|
|
12162
12162
|
});
|
|
@@ -19496,7 +19496,7 @@ var require_react_reconciler_development = __commonJS((exports, module) => {
|
|
|
19496
19496
|
return hook.checkDCE ? true : false;
|
|
19497
19497
|
}
|
|
19498
19498
|
function setIsStrictModeForDevtools(newIsStrictMode) {
|
|
19499
|
-
typeof
|
|
19499
|
+
typeof log30 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
|
|
19500
19500
|
if (injectedHook && typeof injectedHook.setStrictMode === "function")
|
|
19501
19501
|
try {
|
|
19502
19502
|
injectedHook.setStrictMode(rendererID, newIsStrictMode);
|
|
@@ -27580,7 +27580,7 @@ Check the render method of %s.`, getComponentNameFromFiber(current) || "Unknown"
|
|
|
27580
27580
|
var fiberStack = [];
|
|
27581
27581
|
var index$jscomp$0 = -1, emptyContextObject = {};
|
|
27582
27582
|
Object.freeze(emptyContextObject);
|
|
27583
|
-
var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority,
|
|
27583
|
+
var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority, log30 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
|
|
27584
27584
|
if (typeof performance === "object" && typeof performance.now === "function") {
|
|
27585
27585
|
var localPerformance = performance;
|
|
27586
27586
|
var getCurrentTime = function() {
|
|
@@ -47101,7 +47101,7 @@ var {
|
|
|
47101
47101
|
Help
|
|
47102
47102
|
} = import__.default;
|
|
47103
47103
|
|
|
47104
|
-
// src/config/
|
|
47104
|
+
// src/config/index.ts
|
|
47105
47105
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
|
|
47106
47106
|
import { resolve, dirname } from "path";
|
|
47107
47107
|
import { homedir } from "os";
|
|
@@ -49799,7 +49799,8 @@ var LIMITS_DEFAULTS = {
|
|
|
49799
49799
|
cleanupIntervalMinutes: 60,
|
|
49800
49800
|
maxWorktreeAgeHours: 24,
|
|
49801
49801
|
cleanupWorktrees: true,
|
|
49802
|
-
permissionTimeoutSeconds: 120
|
|
49802
|
+
permissionTimeoutSeconds: 120,
|
|
49803
|
+
flushDelayMs: 500
|
|
49803
49804
|
};
|
|
49804
49805
|
function resolveLimits(limits) {
|
|
49805
49806
|
const envMaxSessions = process.env.MAX_SESSIONS ? parseInt(process.env.MAX_SESSIONS, 10) : undefined;
|
|
@@ -49811,11 +49812,52 @@ function resolveLimits(limits) {
|
|
|
49811
49812
|
cleanupIntervalMinutes: limits?.cleanupIntervalMinutes ?? LIMITS_DEFAULTS.cleanupIntervalMinutes,
|
|
49812
49813
|
maxWorktreeAgeHours: limits?.maxWorktreeAgeHours ?? LIMITS_DEFAULTS.maxWorktreeAgeHours,
|
|
49813
49814
|
cleanupWorktrees: limits?.cleanupWorktrees ?? LIMITS_DEFAULTS.cleanupWorktrees,
|
|
49814
|
-
permissionTimeoutSeconds: limits?.permissionTimeoutSeconds ?? LIMITS_DEFAULTS.permissionTimeoutSeconds
|
|
49815
|
+
permissionTimeoutSeconds: limits?.permissionTimeoutSeconds ?? LIMITS_DEFAULTS.permissionTimeoutSeconds,
|
|
49816
|
+
flushDelayMs: limits?.flushDelayMs ?? LIMITS_DEFAULTS.flushDelayMs
|
|
49815
49817
|
};
|
|
49816
49818
|
}
|
|
49819
|
+
function resolvePermissionMode(opts) {
|
|
49820
|
+
if (opts.permissionMode)
|
|
49821
|
+
return opts.permissionMode;
|
|
49822
|
+
if (opts.skipPermissions === true)
|
|
49823
|
+
return "bypass";
|
|
49824
|
+
if (opts.skipPermissions === false)
|
|
49825
|
+
return "default";
|
|
49826
|
+
return "default";
|
|
49827
|
+
}
|
|
49828
|
+
var MODE_INFO = {
|
|
49829
|
+
default: {
|
|
49830
|
+
icon: "\uD83D\uDD10",
|
|
49831
|
+
label: "Default",
|
|
49832
|
+
description: "Every tool-use prompts for approval."
|
|
49833
|
+
},
|
|
49834
|
+
auto: {
|
|
49835
|
+
icon: "⚡",
|
|
49836
|
+
label: "Auto",
|
|
49837
|
+
description: "Claude classifier auto-approves low-risk tools; high-risk still prompts."
|
|
49838
|
+
},
|
|
49839
|
+
bypass: {
|
|
49840
|
+
icon: "⚠️",
|
|
49841
|
+
label: "Bypass",
|
|
49842
|
+
description: "No prompts — every tool-use is allowed."
|
|
49843
|
+
}
|
|
49844
|
+
};
|
|
49845
|
+
function permissionModeDisplay(mode) {
|
|
49846
|
+
const info = MODE_INFO[mode];
|
|
49847
|
+
return { icon: info.icon, label: info.label, chip: `${info.icon} ${info.label}` };
|
|
49848
|
+
}
|
|
49849
|
+
function permissionModeDescription(mode) {
|
|
49850
|
+
return MODE_INFO[mode].description;
|
|
49851
|
+
}
|
|
49852
|
+
function effectivePermissionMode(input) {
|
|
49853
|
+
if (input.override)
|
|
49854
|
+
return input.override;
|
|
49855
|
+
if (input.sessionHasInteractiveOverride)
|
|
49856
|
+
return "default";
|
|
49857
|
+
return input.botWideMode;
|
|
49858
|
+
}
|
|
49817
49859
|
|
|
49818
|
-
// src/config/
|
|
49860
|
+
// src/config/index.ts
|
|
49819
49861
|
var CONFIG_PATH = resolve(homedir(), ".config", "claude-threads", "config.yaml");
|
|
49820
49862
|
function loadConfigWithMigration() {
|
|
49821
49863
|
if (existsSync(CONFIG_PATH)) {
|
|
@@ -50034,6 +50076,26 @@ function validateClaudeCli() {
|
|
|
50034
50076
|
}
|
|
50035
50077
|
|
|
50036
50078
|
// src/onboarding.ts
|
|
50079
|
+
var PERMISSION_MODE_CHOICES = [
|
|
50080
|
+
{
|
|
50081
|
+
title: "Auto (recommended)",
|
|
50082
|
+
value: "auto",
|
|
50083
|
+
description: "Classifier auto-approves low-risk; high-risk tools still prompt via reactions"
|
|
50084
|
+
},
|
|
50085
|
+
{
|
|
50086
|
+
title: "Default",
|
|
50087
|
+
value: "default",
|
|
50088
|
+
description: "Every tool-use prompts — strictest mode, can be noisy for everyday work"
|
|
50089
|
+
},
|
|
50090
|
+
{
|
|
50091
|
+
title: "Bypass",
|
|
50092
|
+
value: "bypass",
|
|
50093
|
+
description: "No prompts, all tools allowed — use only in trusted environments"
|
|
50094
|
+
}
|
|
50095
|
+
];
|
|
50096
|
+
function permissionModeChoiceIndex(mode) {
|
|
50097
|
+
return PERMISSION_MODE_CHOICES.findIndex((c) => c.value === mode);
|
|
50098
|
+
}
|
|
50037
50099
|
var __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
50038
50100
|
var SLACK_MANIFEST_PATH = join2(__dirname2, "..", "docs", "slack-app-manifest.yaml");
|
|
50039
50101
|
var onCancel = () => {
|
|
@@ -50600,14 +50662,14 @@ async function showConfigSummary(config) {
|
|
|
50600
50662
|
console.log(dim(` Bot: @${mm.botName}`));
|
|
50601
50663
|
const allowedUsers = mm.allowedUsers.length > 0 ? mm.allowedUsers.join(", ") : "ANYONE (⚠️ no restrictions)";
|
|
50602
50664
|
console.log(dim(` Allowed Users: ${allowedUsers}`));
|
|
50603
|
-
console.log(dim(`
|
|
50665
|
+
console.log(dim(` Permission Mode: ${permissionModeDisplay(resolvePermissionMode({ permissionMode: mm.permissionMode, skipPermissions: mm.skipPermissions })).chip}`));
|
|
50604
50666
|
} else {
|
|
50605
50667
|
const slack = platform;
|
|
50606
50668
|
console.log(dim(` Channel: ${slack.channelId}`));
|
|
50607
50669
|
console.log(dim(` Bot: @${slack.botName}`));
|
|
50608
50670
|
const allowedUsers = slack.allowedUsers.length > 0 ? slack.allowedUsers.join(", ") : "ANYONE (⚠️ no restrictions)";
|
|
50609
50671
|
console.log(dim(` Allowed Users: ${allowedUsers}`));
|
|
50610
|
-
console.log(dim(`
|
|
50672
|
+
console.log(dim(` Permission Mode: ${permissionModeDisplay(resolvePermissionMode({ permissionMode: slack.permissionMode, skipPermissions: slack.skipPermissions })).chip}`));
|
|
50611
50673
|
}
|
|
50612
50674
|
}
|
|
50613
50675
|
const hasAdvancedSettings = config.limits || config.threadLogs || config.keepAlive === false;
|
|
@@ -50799,7 +50861,10 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50799
50861
|
let lastChannelId = existingMattermost?.channelId || "";
|
|
50800
50862
|
let lastBotName = existingMattermost?.botName || "claude-code";
|
|
50801
50863
|
let lastAllowedUsers = existingMattermost?.allowedUsers?.join(",") || "";
|
|
50802
|
-
let
|
|
50864
|
+
let lastPermissionMode = existingMattermost ? resolvePermissionMode({
|
|
50865
|
+
permissionMode: existingMattermost.permissionMode,
|
|
50866
|
+
skipPermissions: existingMattermost.skipPermissions
|
|
50867
|
+
}) : "auto";
|
|
50803
50868
|
while (true) {
|
|
50804
50869
|
console.log("");
|
|
50805
50870
|
console.log(dim(" Now enter your Mattermost credentials:"));
|
|
@@ -50897,12 +50962,12 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50897
50962
|
allowedUsersConfirmed = true;
|
|
50898
50963
|
}
|
|
50899
50964
|
}
|
|
50900
|
-
const {
|
|
50901
|
-
type: "
|
|
50902
|
-
name: "
|
|
50903
|
-
message: "
|
|
50904
|
-
|
|
50905
|
-
|
|
50965
|
+
const { permissionMode } = await import_prompts.default({
|
|
50966
|
+
type: "select",
|
|
50967
|
+
name: "permissionMode",
|
|
50968
|
+
message: "Permission mode for Claude tool-uses?",
|
|
50969
|
+
choices: PERMISSION_MODE_CHOICES,
|
|
50970
|
+
initial: permissionModeChoiceIndex(lastPermissionMode)
|
|
50906
50971
|
}, { onCancel });
|
|
50907
50972
|
lastUrl = basicSettings.url;
|
|
50908
50973
|
lastDisplayName = basicSettings.displayName;
|
|
@@ -50910,7 +50975,7 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50910
50975
|
lastChannelId = basicSettings.channelId;
|
|
50911
50976
|
lastBotName = basicSettings.botName;
|
|
50912
50977
|
lastAllowedUsers = allowedUsers.join(",");
|
|
50913
|
-
|
|
50978
|
+
lastPermissionMode = permissionMode;
|
|
50914
50979
|
console.log("");
|
|
50915
50980
|
console.log(dim(" Validating credentials..."));
|
|
50916
50981
|
const validationResult = await validateMattermostCredentials(basicSettings.url, finalToken, basicSettings.channelId);
|
|
@@ -50969,7 +51034,7 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50969
51034
|
channelId: basicSettings.channelId,
|
|
50970
51035
|
botName: basicSettings.botName,
|
|
50971
51036
|
allowedUsers,
|
|
50972
|
-
|
|
51037
|
+
permissionMode: lastPermissionMode
|
|
50973
51038
|
};
|
|
50974
51039
|
}
|
|
50975
51040
|
}
|
|
@@ -51082,7 +51147,10 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51082
51147
|
let lastChannelId = existingSlack?.channelId || "";
|
|
51083
51148
|
let lastBotName = existingSlack?.botName || "claude";
|
|
51084
51149
|
let lastAllowedUsers = existingSlack?.allowedUsers?.join(",") || "";
|
|
51085
|
-
let
|
|
51150
|
+
let lastPermissionMode = existingSlack ? resolvePermissionMode({
|
|
51151
|
+
permissionMode: existingSlack.permissionMode,
|
|
51152
|
+
skipPermissions: existingSlack.skipPermissions
|
|
51153
|
+
}) : "auto";
|
|
51086
51154
|
while (true) {
|
|
51087
51155
|
console.log("");
|
|
51088
51156
|
console.log(dim(" Now enter your Slack credentials:"));
|
|
@@ -51191,12 +51259,12 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51191
51259
|
allowedUsersConfirmed = true;
|
|
51192
51260
|
}
|
|
51193
51261
|
}
|
|
51194
|
-
const {
|
|
51195
|
-
type: "
|
|
51196
|
-
name: "
|
|
51197
|
-
message: "
|
|
51198
|
-
|
|
51199
|
-
|
|
51262
|
+
const { permissionMode } = await import_prompts.default({
|
|
51263
|
+
type: "select",
|
|
51264
|
+
name: "permissionMode",
|
|
51265
|
+
message: "Permission mode for Claude tool-uses?",
|
|
51266
|
+
choices: PERMISSION_MODE_CHOICES,
|
|
51267
|
+
initial: permissionModeChoiceIndex(lastPermissionMode)
|
|
51200
51268
|
}, { onCancel });
|
|
51201
51269
|
lastDisplayName = basicSettings.displayName;
|
|
51202
51270
|
lastBotToken = finalBotToken;
|
|
@@ -51204,7 +51272,7 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51204
51272
|
lastChannelId = basicSettings.channelId;
|
|
51205
51273
|
lastBotName = basicSettings.botName;
|
|
51206
51274
|
lastAllowedUsers = allowedUsers.join(",");
|
|
51207
|
-
|
|
51275
|
+
lastPermissionMode = permissionMode;
|
|
51208
51276
|
console.log("");
|
|
51209
51277
|
console.log(dim(" Validating credentials..."));
|
|
51210
51278
|
const validationResult = await validateSlackCredentials(finalBotToken, finalAppToken, basicSettings.channelId);
|
|
@@ -51269,7 +51337,7 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51269
51337
|
channelId: basicSettings.channelId,
|
|
51270
51338
|
botName: basicSettings.botName,
|
|
51271
51339
|
allowedUsers,
|
|
51272
|
-
|
|
51340
|
+
permissionMode: lastPermissionMode
|
|
51273
51341
|
};
|
|
51274
51342
|
}
|
|
51275
51343
|
}
|
|
@@ -52290,7 +52358,7 @@ class SlackClient extends BasePlatformClient {
|
|
|
52290
52358
|
this.channelId = platformConfig.channelId;
|
|
52291
52359
|
this.botName = platformConfig.botName;
|
|
52292
52360
|
this.allowedUsers = platformConfig.allowedUsers;
|
|
52293
|
-
this.skipPermissions = platformConfig.skipPermissions;
|
|
52361
|
+
this.skipPermissions = platformConfig.skipPermissions ?? false;
|
|
52294
52362
|
this.apiUrl = platformConfig.apiUrl || "https://slack.com/api";
|
|
52295
52363
|
}
|
|
52296
52364
|
normalizePlatformUser(slackUser) {
|
|
@@ -52914,12 +52982,12 @@ class SlackClient extends BasePlatformClient {
|
|
|
52914
52982
|
return this.normalizePlatformFile(response.file);
|
|
52915
52983
|
}
|
|
52916
52984
|
}
|
|
52917
|
-
// src/mattermost/api.ts
|
|
52985
|
+
// src/platform/mattermost/permission-api.ts
|
|
52918
52986
|
init_logger();
|
|
52919
|
-
var
|
|
52987
|
+
var apiLog = createLogger("mm-api");
|
|
52920
52988
|
async function mattermostApi(config, method, path, body) {
|
|
52921
52989
|
const url = `${config.url}/api/v4${path}`;
|
|
52922
|
-
|
|
52990
|
+
apiLog.debug(`API ${method} ${path}`);
|
|
52923
52991
|
const response = await fetch(url, {
|
|
52924
52992
|
method,
|
|
52925
52993
|
headers: {
|
|
@@ -52930,10 +52998,10 @@ async function mattermostApi(config, method, path, body) {
|
|
|
52930
52998
|
});
|
|
52931
52999
|
if (!response.ok) {
|
|
52932
53000
|
const text = await response.text();
|
|
52933
|
-
|
|
53001
|
+
apiLog.warn(`API ${method} ${path} failed: ${response.status} ${text.substring(0, 100)}`);
|
|
52934
53002
|
throw new Error(`Mattermost API error ${response.status}: ${text}`);
|
|
52935
53003
|
}
|
|
52936
|
-
|
|
53004
|
+
apiLog.debug(`API ${method} ${path} → ${response.status}`);
|
|
52937
53005
|
return response.json();
|
|
52938
53006
|
}
|
|
52939
53007
|
async function getMe(config) {
|
|
@@ -52943,7 +53011,7 @@ async function getUser(config, userId) {
|
|
|
52943
53011
|
try {
|
|
52944
53012
|
return await mattermostApi(config, "GET", `/users/${userId}`);
|
|
52945
53013
|
} catch (err) {
|
|
52946
|
-
|
|
53014
|
+
apiLog.debug(`Failed to get user ${userId}: ${err}`);
|
|
52947
53015
|
return null;
|
|
52948
53016
|
}
|
|
52949
53017
|
}
|
|
@@ -52954,7 +53022,7 @@ async function createPost(config, channelId, message, rootId) {
|
|
|
52954
53022
|
root_id: rootId
|
|
52955
53023
|
});
|
|
52956
53024
|
}
|
|
52957
|
-
async function
|
|
53025
|
+
async function updatePostRaw(config, postId, message) {
|
|
52958
53026
|
return mattermostApi(config, "PUT", `/posts/${postId}`, {
|
|
52959
53027
|
id: postId,
|
|
52960
53028
|
message
|
|
@@ -52967,25 +53035,23 @@ async function addReaction(config, postId, userId, emojiName) {
|
|
|
52967
53035
|
emoji_name: emojiName
|
|
52968
53036
|
});
|
|
52969
53037
|
}
|
|
52970
|
-
function
|
|
53038
|
+
function isUserInAllowList(username, allowList) {
|
|
52971
53039
|
if (allowList.length === 0)
|
|
52972
53040
|
return true;
|
|
52973
53041
|
return allowList.includes(username);
|
|
52974
53042
|
}
|
|
52975
|
-
async function
|
|
53043
|
+
async function createInteractivePostInternal(config, channelId, message, reactions, rootId, botUserId) {
|
|
52976
53044
|
const post = await createPost(config, channelId, message, rootId);
|
|
52977
53045
|
for (const emoji of reactions) {
|
|
52978
53046
|
try {
|
|
52979
53047
|
await addReaction(config, post.id, botUserId, emoji);
|
|
52980
53048
|
} catch (err) {
|
|
52981
|
-
|
|
53049
|
+
apiLog.warn(`Failed to add reaction ${emoji}: ${err}`);
|
|
52982
53050
|
}
|
|
52983
53051
|
}
|
|
52984
53052
|
return post;
|
|
52985
53053
|
}
|
|
52986
53054
|
|
|
52987
|
-
// src/platform/mattermost/permission-api.ts
|
|
52988
|
-
init_logger();
|
|
52989
53055
|
class MattermostPermissionApi {
|
|
52990
53056
|
apiConfig;
|
|
52991
53057
|
config;
|
|
@@ -53026,18 +53092,18 @@ class MattermostPermissionApi {
|
|
|
53026
53092
|
}
|
|
53027
53093
|
}
|
|
53028
53094
|
isUserAllowed(username) {
|
|
53029
|
-
return
|
|
53095
|
+
return isUserInAllowList(username, this.config.allowedUsers);
|
|
53030
53096
|
}
|
|
53031
53097
|
async createInteractivePost(message, reactions, threadId) {
|
|
53032
53098
|
mcpLogger.debug(`Creating interactive post with ${reactions.length} reaction options`);
|
|
53033
53099
|
const botUserId = await this.getBotUserId();
|
|
53034
|
-
const post = await
|
|
53100
|
+
const post = await createInteractivePostInternal(this.apiConfig, this.config.channelId, message, reactions, threadId, botUserId);
|
|
53035
53101
|
mcpLogger.debug(`Created post ${formatShortId(post.id)}`);
|
|
53036
53102
|
return { id: post.id };
|
|
53037
53103
|
}
|
|
53038
53104
|
async updatePost(postId, message) {
|
|
53039
53105
|
mcpLogger.debug(`Updating post ${postId.substring(0, 8)}`);
|
|
53040
|
-
await
|
|
53106
|
+
await updatePostRaw(this.apiConfig, postId, message);
|
|
53041
53107
|
}
|
|
53042
53108
|
async waitForReaction(postId, botUserId, timeoutMs) {
|
|
53043
53109
|
return new Promise((resolve3) => {
|
|
@@ -53331,7 +53397,7 @@ init_logger();
|
|
|
53331
53397
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync, chmodSync as chmodSync2 } from "fs";
|
|
53332
53398
|
import { homedir as homedir2 } from "os";
|
|
53333
53399
|
import { join as join3 } from "path";
|
|
53334
|
-
var
|
|
53400
|
+
var log4 = createLogger("persist");
|
|
53335
53401
|
var STORE_VERSION = 2;
|
|
53336
53402
|
var DEFAULT_CONFIG_DIR = join3(homedir2(), ".config", "claude-threads");
|
|
53337
53403
|
var DEFAULT_SESSIONS_FILE = join3(DEFAULT_CONFIG_DIR, "sessions.json");
|
|
@@ -53356,13 +53422,13 @@ class SessionStore {
|
|
|
53356
53422
|
load() {
|
|
53357
53423
|
const sessions = new Map;
|
|
53358
53424
|
if (!existsSync5(this.sessionsFile)) {
|
|
53359
|
-
|
|
53425
|
+
log4.debug("No sessions file found");
|
|
53360
53426
|
return sessions;
|
|
53361
53427
|
}
|
|
53362
53428
|
try {
|
|
53363
53429
|
const data = this.loadRaw();
|
|
53364
53430
|
if (data.version === 1) {
|
|
53365
|
-
|
|
53431
|
+
log4.info("Migrating sessions from v1 to v2 (adding platformId)");
|
|
53366
53432
|
const newSessions = {};
|
|
53367
53433
|
for (const [_oldKey, session] of Object.entries(data.sessions)) {
|
|
53368
53434
|
const v1Session = session;
|
|
@@ -53376,7 +53442,7 @@ class SessionStore {
|
|
|
53376
53442
|
data.version = 2;
|
|
53377
53443
|
this.writeAtomic(data);
|
|
53378
53444
|
} else if (data.version !== STORE_VERSION) {
|
|
53379
|
-
|
|
53445
|
+
log4.warn(`Sessions file version ${data.version} not supported, starting fresh`);
|
|
53380
53446
|
return sessions;
|
|
53381
53447
|
}
|
|
53382
53448
|
for (const session of Object.values(data.sessions)) {
|
|
@@ -53385,9 +53451,9 @@ class SessionStore {
|
|
|
53385
53451
|
const sessionId = `${session.platformId}:${session.threadId}`;
|
|
53386
53452
|
sessions.set(sessionId, session);
|
|
53387
53453
|
}
|
|
53388
|
-
|
|
53454
|
+
log4.debug(`Loaded ${sessions.size} active session(s)`);
|
|
53389
53455
|
} catch (err) {
|
|
53390
|
-
|
|
53456
|
+
log4.error(`Failed to load sessions: ${err}`);
|
|
53391
53457
|
}
|
|
53392
53458
|
return sessions;
|
|
53393
53459
|
}
|
|
@@ -53396,7 +53462,7 @@ class SessionStore {
|
|
|
53396
53462
|
data.sessions[sessionId] = session;
|
|
53397
53463
|
this.writeAtomic(data);
|
|
53398
53464
|
const shortId = sessionId.substring(0, 20);
|
|
53399
|
-
|
|
53465
|
+
log4.debug(`Saved session ${shortId}...`);
|
|
53400
53466
|
}
|
|
53401
53467
|
remove(sessionId) {
|
|
53402
53468
|
const data = this.loadRaw();
|
|
@@ -53404,7 +53470,7 @@ class SessionStore {
|
|
|
53404
53470
|
delete data.sessions[sessionId];
|
|
53405
53471
|
this.writeAtomic(data);
|
|
53406
53472
|
const shortId = sessionId.substring(0, 20);
|
|
53407
|
-
|
|
53473
|
+
log4.debug(`Removed session ${shortId}...`);
|
|
53408
53474
|
}
|
|
53409
53475
|
}
|
|
53410
53476
|
softDelete(sessionId) {
|
|
@@ -53413,7 +53479,7 @@ class SessionStore {
|
|
|
53413
53479
|
data.sessions[sessionId].cleanedAt = new Date().toISOString();
|
|
53414
53480
|
this.writeAtomic(data);
|
|
53415
53481
|
const shortId = sessionId.substring(0, 20);
|
|
53416
|
-
|
|
53482
|
+
log4.debug(`Soft-deleted session ${shortId}...`);
|
|
53417
53483
|
}
|
|
53418
53484
|
}
|
|
53419
53485
|
cleanStale(maxAgeMs) {
|
|
@@ -53431,7 +53497,7 @@ class SessionStore {
|
|
|
53431
53497
|
}
|
|
53432
53498
|
if (staleIds.length > 0) {
|
|
53433
53499
|
this.writeAtomic(data);
|
|
53434
|
-
|
|
53500
|
+
log4.debug(`Soft-deleted ${staleIds.length} stale session(s)`);
|
|
53435
53501
|
}
|
|
53436
53502
|
return staleIds;
|
|
53437
53503
|
}
|
|
@@ -53450,7 +53516,7 @@ class SessionStore {
|
|
|
53450
53516
|
}
|
|
53451
53517
|
if (removedCount > 0) {
|
|
53452
53518
|
this.writeAtomic(data);
|
|
53453
|
-
|
|
53519
|
+
log4.debug(`Permanently removed ${removedCount} old session(s) from history`);
|
|
53454
53520
|
}
|
|
53455
53521
|
return removedCount;
|
|
53456
53522
|
}
|
|
@@ -53477,7 +53543,7 @@ class SessionStore {
|
|
|
53477
53543
|
clear() {
|
|
53478
53544
|
const data = this.loadRaw();
|
|
53479
53545
|
this.writeAtomic({ version: STORE_VERSION, sessions: {}, stickyPostIds: data.stickyPostIds });
|
|
53480
|
-
|
|
53546
|
+
log4.debug("Cleared all sessions");
|
|
53481
53547
|
}
|
|
53482
53548
|
saveStickyPostId(platformId, postId) {
|
|
53483
53549
|
const data = this.loadRaw();
|
|
@@ -53486,7 +53552,7 @@ class SessionStore {
|
|
|
53486
53552
|
}
|
|
53487
53553
|
data.stickyPostIds[platformId] = postId;
|
|
53488
53554
|
this.writeAtomic(data);
|
|
53489
|
-
|
|
53555
|
+
log4.debug(`Saved sticky post ID for ${platformId}: ${postId.substring(0, 8)}...`);
|
|
53490
53556
|
}
|
|
53491
53557
|
getStickyPostIds() {
|
|
53492
53558
|
const data = this.loadRaw();
|
|
@@ -53497,7 +53563,7 @@ class SessionStore {
|
|
|
53497
53563
|
if (data.stickyPostIds && data.stickyPostIds[platformId]) {
|
|
53498
53564
|
delete data.stickyPostIds[platformId];
|
|
53499
53565
|
this.writeAtomic(data);
|
|
53500
|
-
|
|
53566
|
+
log4.debug(`Removed sticky post ID for ${platformId}`);
|
|
53501
53567
|
}
|
|
53502
53568
|
}
|
|
53503
53569
|
getPlatformEnabledState() {
|
|
@@ -53515,7 +53581,7 @@ class SessionStore {
|
|
|
53515
53581
|
}
|
|
53516
53582
|
data.platformEnabledState[platformId] = enabled;
|
|
53517
53583
|
this.writeAtomic(data);
|
|
53518
|
-
|
|
53584
|
+
log4.debug(`Set platform ${platformId} enabled state to ${enabled}`);
|
|
53519
53585
|
}
|
|
53520
53586
|
findByThread(platformId, threadId) {
|
|
53521
53587
|
const sessionId = `${platformId}:${threadId}`;
|
|
@@ -53566,9 +53632,10 @@ class SessionStore {
|
|
|
53566
53632
|
chmodSync2(this.sessionsFile, 384);
|
|
53567
53633
|
}
|
|
53568
53634
|
}
|
|
53635
|
+
|
|
53569
53636
|
// src/claude/account-pool.ts
|
|
53570
53637
|
init_logger();
|
|
53571
|
-
var
|
|
53638
|
+
var log5 = createLogger("account-pool");
|
|
53572
53639
|
|
|
53573
53640
|
class AccountPool {
|
|
53574
53641
|
accounts;
|
|
@@ -53580,11 +53647,11 @@ class AccountPool {
|
|
|
53580
53647
|
this.accounts = (accounts ?? []).filter((acc) => {
|
|
53581
53648
|
const hasAuth = !!acc.home || !!acc.apiKey;
|
|
53582
53649
|
if (!hasAuth) {
|
|
53583
|
-
|
|
53650
|
+
log5.warn(`Claude account ${acc.id} has neither home nor apiKey — ignoring`);
|
|
53584
53651
|
return false;
|
|
53585
53652
|
}
|
|
53586
53653
|
if (acc.home && acc.apiKey) {
|
|
53587
|
-
|
|
53654
|
+
log5.warn(`Claude account ${acc.id} has both home and apiKey set — must choose one; ignoring`);
|
|
53588
53655
|
return false;
|
|
53589
53656
|
}
|
|
53590
53657
|
return true;
|
|
@@ -53609,7 +53676,7 @@ class AccountPool {
|
|
|
53609
53676
|
this.incrementActive(preferred.id);
|
|
53610
53677
|
return preferred;
|
|
53611
53678
|
}
|
|
53612
|
-
|
|
53679
|
+
log5.warn(`Preferred account "${preferredId}" not in pool — falling back to round-robin`);
|
|
53613
53680
|
}
|
|
53614
53681
|
const now = Date.now();
|
|
53615
53682
|
const n = this.accounts.length;
|
|
@@ -53623,7 +53690,7 @@ class AccountPool {
|
|
|
53623
53690
|
return candidate;
|
|
53624
53691
|
}
|
|
53625
53692
|
}
|
|
53626
|
-
|
|
53693
|
+
log5.warn(`All ${n} accounts are in rate-limit cooldown`);
|
|
53627
53694
|
return null;
|
|
53628
53695
|
}
|
|
53629
53696
|
release(accountId) {
|
|
@@ -53634,14 +53701,14 @@ class AccountPool {
|
|
|
53634
53701
|
}
|
|
53635
53702
|
markCooling(accountId, untilEpochMs) {
|
|
53636
53703
|
if (!this.byId.has(accountId)) {
|
|
53637
|
-
|
|
53704
|
+
log5.warn(`markCooling called for unknown account "${accountId}"`);
|
|
53638
53705
|
return;
|
|
53639
53706
|
}
|
|
53640
53707
|
const existing = this.coolingUntil.get(accountId) ?? 0;
|
|
53641
53708
|
if (untilEpochMs > existing) {
|
|
53642
53709
|
this.coolingUntil.set(accountId, untilEpochMs);
|
|
53643
53710
|
const minutes = Math.ceil((untilEpochMs - Date.now()) / 60000);
|
|
53644
|
-
|
|
53711
|
+
log5.info(`Account "${accountId}" cooling for ~${minutes}min`);
|
|
53645
53712
|
}
|
|
53646
53713
|
}
|
|
53647
53714
|
get(accountId) {
|
|
@@ -53678,7 +53745,7 @@ init_logger();
|
|
|
53678
53745
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync, readFileSync as readFileSync5, chmodSync as chmodSync3 } from "fs";
|
|
53679
53746
|
import { homedir as homedir3 } from "os";
|
|
53680
53747
|
import { join as join4, dirname as dirname4 } from "path";
|
|
53681
|
-
var
|
|
53748
|
+
var log6 = createLogger("thread-log");
|
|
53682
53749
|
var LOGS_BASE_DIR = join4(homedir3(), ".claude-threads", "logs");
|
|
53683
53750
|
|
|
53684
53751
|
class ThreadLoggerImpl {
|
|
@@ -53708,7 +53775,7 @@ class ThreadLoggerImpl {
|
|
|
53708
53775
|
this.flushTimer = setInterval(() => {
|
|
53709
53776
|
this.flushSync();
|
|
53710
53777
|
}, this.flushIntervalMs);
|
|
53711
|
-
|
|
53778
|
+
log6.debug(`Thread logger initialized: ${this.logPath}`);
|
|
53712
53779
|
}
|
|
53713
53780
|
}
|
|
53714
53781
|
isEnabled() {
|
|
@@ -53822,7 +53889,7 @@ class ThreadLoggerImpl {
|
|
|
53822
53889
|
this.flushTimer = null;
|
|
53823
53890
|
}
|
|
53824
53891
|
this.flushSync();
|
|
53825
|
-
|
|
53892
|
+
log6.debug(`Thread logger closed: ${this.logPath}`);
|
|
53826
53893
|
}
|
|
53827
53894
|
addEntry(entry) {
|
|
53828
53895
|
this.buffer.push(entry);
|
|
@@ -53844,7 +53911,7 @@ class ThreadLoggerImpl {
|
|
|
53844
53911
|
}
|
|
53845
53912
|
this.buffer = [];
|
|
53846
53913
|
} catch (err) {
|
|
53847
|
-
|
|
53914
|
+
log6.error(`Failed to flush thread log: ${err}`);
|
|
53848
53915
|
}
|
|
53849
53916
|
}
|
|
53850
53917
|
}
|
|
@@ -53895,25 +53962,25 @@ function cleanupOldLogs(retentionDays = 30) {
|
|
|
53895
53962
|
if (fileStat.mtimeMs < cutoffMs) {
|
|
53896
53963
|
unlinkSync(filePath);
|
|
53897
53964
|
deletedCount++;
|
|
53898
|
-
|
|
53965
|
+
log6.debug(`Deleted old log file: ${filePath}`);
|
|
53899
53966
|
}
|
|
53900
53967
|
} catch (err) {
|
|
53901
|
-
|
|
53968
|
+
log6.warn(`Failed to check/delete log file ${filePath}: ${err}`);
|
|
53902
53969
|
}
|
|
53903
53970
|
}
|
|
53904
53971
|
try {
|
|
53905
53972
|
const remaining = readdirSync(platformDir);
|
|
53906
53973
|
if (remaining.length === 0) {
|
|
53907
53974
|
rmdirSync(platformDir);
|
|
53908
|
-
|
|
53975
|
+
log6.debug(`Removed empty platform log directory: ${platformDir}`);
|
|
53909
53976
|
}
|
|
53910
53977
|
} catch {}
|
|
53911
53978
|
}
|
|
53912
53979
|
if (deletedCount > 0) {
|
|
53913
|
-
|
|
53980
|
+
log6.info(`Cleaned up ${deletedCount} old log file(s)`);
|
|
53914
53981
|
}
|
|
53915
53982
|
} catch (err) {
|
|
53916
|
-
|
|
53983
|
+
log6.error(`Failed to clean up old logs: ${err}`);
|
|
53917
53984
|
}
|
|
53918
53985
|
return deletedCount;
|
|
53919
53986
|
}
|
|
@@ -53922,16 +53989,16 @@ function getLogFilePath(platformId, sessionId) {
|
|
|
53922
53989
|
}
|
|
53923
53990
|
function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
53924
53991
|
const logPath = getLogFilePath(platformId, sessionId);
|
|
53925
|
-
|
|
53992
|
+
log6.debug(`Reading log entries from: ${logPath}`);
|
|
53926
53993
|
if (!existsSync6(logPath)) {
|
|
53927
|
-
|
|
53994
|
+
log6.debug(`Log file does not exist: ${logPath}`);
|
|
53928
53995
|
return [];
|
|
53929
53996
|
}
|
|
53930
53997
|
try {
|
|
53931
53998
|
const content = readFileSync5(logPath, "utf8");
|
|
53932
53999
|
const lines = content.trim().split(`
|
|
53933
54000
|
`);
|
|
53934
|
-
|
|
54001
|
+
log6.debug(`Log file has ${lines.length} lines`);
|
|
53935
54002
|
const recentLines = lines.slice(-maxLines);
|
|
53936
54003
|
const entries = [];
|
|
53937
54004
|
for (const line of recentLines) {
|
|
@@ -53941,17 +54008,17 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
|
53941
54008
|
entries.push(JSON.parse(line));
|
|
53942
54009
|
} catch {}
|
|
53943
54010
|
}
|
|
53944
|
-
|
|
54011
|
+
log6.debug(`Parsed ${entries.length} log entries`);
|
|
53945
54012
|
return entries;
|
|
53946
54013
|
} catch (err) {
|
|
53947
|
-
|
|
54014
|
+
log6.error(`Failed to read log file: ${err}`);
|
|
53948
54015
|
return [];
|
|
53949
54016
|
}
|
|
53950
54017
|
}
|
|
53951
54018
|
|
|
53952
54019
|
// src/cleanup/scheduler.ts
|
|
53953
54020
|
init_worktree();
|
|
53954
|
-
var
|
|
54021
|
+
var log8 = createLogger("cleanup");
|
|
53955
54022
|
var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
|
53956
54023
|
var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
53957
54024
|
|
|
@@ -53974,17 +54041,17 @@ class CleanupScheduler {
|
|
|
53974
54041
|
}
|
|
53975
54042
|
start() {
|
|
53976
54043
|
if (this.isRunning) {
|
|
53977
|
-
|
|
54044
|
+
log8.debug("Cleanup scheduler already running");
|
|
53978
54045
|
return;
|
|
53979
54046
|
}
|
|
53980
54047
|
this.isRunning = true;
|
|
53981
|
-
|
|
54048
|
+
log8.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
|
|
53982
54049
|
this.runCleanup().catch((err) => {
|
|
53983
|
-
|
|
54050
|
+
log8.warn(`Initial cleanup failed: ${err}`);
|
|
53984
54051
|
});
|
|
53985
54052
|
this.timer = setInterval(() => {
|
|
53986
54053
|
this.runCleanup().catch((err) => {
|
|
53987
|
-
|
|
54054
|
+
log8.warn(`Periodic cleanup failed: ${err}`);
|
|
53988
54055
|
});
|
|
53989
54056
|
}, this.intervalMs);
|
|
53990
54057
|
}
|
|
@@ -53994,11 +54061,11 @@ class CleanupScheduler {
|
|
|
53994
54061
|
this.timer = null;
|
|
53995
54062
|
}
|
|
53996
54063
|
this.isRunning = false;
|
|
53997
|
-
|
|
54064
|
+
log8.debug("Cleanup scheduler stopped");
|
|
53998
54065
|
}
|
|
53999
54066
|
async runCleanup() {
|
|
54000
54067
|
const startTime = Date.now();
|
|
54001
|
-
|
|
54068
|
+
log8.debug("Running background cleanup...");
|
|
54002
54069
|
const stats = {
|
|
54003
54070
|
logsDeleted: 0,
|
|
54004
54071
|
worktreesCleaned: 0,
|
|
@@ -54024,9 +54091,9 @@ class CleanupScheduler {
|
|
|
54024
54091
|
const elapsed = Date.now() - startTime;
|
|
54025
54092
|
const totalCleaned = stats.logsDeleted + stats.worktreesCleaned + stats.metadataCleaned;
|
|
54026
54093
|
if (totalCleaned > 0 || stats.errors.length > 0) {
|
|
54027
|
-
|
|
54094
|
+
log8.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
|
|
54028
54095
|
} else {
|
|
54029
|
-
|
|
54096
|
+
log8.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
|
|
54030
54097
|
}
|
|
54031
54098
|
return stats;
|
|
54032
54099
|
}
|
|
@@ -54039,7 +54106,7 @@ class CleanupScheduler {
|
|
|
54039
54106
|
const deleted = cleanupOldLogs(this.logRetentionDays);
|
|
54040
54107
|
resolve3(deleted);
|
|
54041
54108
|
} catch (err) {
|
|
54042
|
-
|
|
54109
|
+
log8.warn(`Log cleanup error: ${err}`);
|
|
54043
54110
|
resolve3(0);
|
|
54044
54111
|
}
|
|
54045
54112
|
});
|
|
@@ -54048,7 +54115,7 @@ class CleanupScheduler {
|
|
|
54048
54115
|
const worktreesDir = getWorktreesDir();
|
|
54049
54116
|
const result = { cleaned: 0, metadata: 0 };
|
|
54050
54117
|
if (!existsSync7(worktreesDir)) {
|
|
54051
|
-
|
|
54118
|
+
log8.debug("No worktrees directory exists, nothing to clean");
|
|
54052
54119
|
return result;
|
|
54053
54120
|
}
|
|
54054
54121
|
const persisted = this.sessionStore.load();
|
|
@@ -54066,7 +54133,7 @@ class CleanupScheduler {
|
|
|
54066
54133
|
continue;
|
|
54067
54134
|
const worktreePath = join6(worktreesDir, entry.name);
|
|
54068
54135
|
if (activeWorktrees.has(worktreePath)) {
|
|
54069
|
-
|
|
54136
|
+
log8.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
|
|
54070
54137
|
continue;
|
|
54071
54138
|
}
|
|
54072
54139
|
const meta = await readWorktreeMetadata(worktreePath);
|
|
@@ -54076,7 +54143,7 @@ class CleanupScheduler {
|
|
|
54076
54143
|
const lastActivity = new Date(meta.lastActivityAt).getTime();
|
|
54077
54144
|
const age = now - lastActivity;
|
|
54078
54145
|
if (meta.sessionId && age < this.maxWorktreeAgeMs) {
|
|
54079
|
-
|
|
54146
|
+
log8.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
54080
54147
|
continue;
|
|
54081
54148
|
}
|
|
54082
54149
|
const merged = age >= this.maxWorktreeAgeMs ? await isBranchMerged(meta.repoRoot, meta.branch).catch(() => false) : false;
|
|
@@ -54087,7 +54154,7 @@ class CleanupScheduler {
|
|
|
54087
54154
|
shouldCleanup = true;
|
|
54088
54155
|
cleanupReason = `inactive for ${Math.round(age / 3600000)}h`;
|
|
54089
54156
|
} else {
|
|
54090
|
-
|
|
54157
|
+
log8.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
54091
54158
|
continue;
|
|
54092
54159
|
}
|
|
54093
54160
|
} else {
|
|
@@ -54096,7 +54163,7 @@ class CleanupScheduler {
|
|
|
54096
54163
|
}
|
|
54097
54164
|
if (!shouldCleanup)
|
|
54098
54165
|
continue;
|
|
54099
|
-
|
|
54166
|
+
log8.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
|
|
54100
54167
|
try {
|
|
54101
54168
|
if (meta?.repoRoot) {
|
|
54102
54169
|
await removeWorktree(meta.repoRoot, worktreePath);
|
|
@@ -54107,19 +54174,19 @@ class CleanupScheduler {
|
|
|
54107
54174
|
await removeWorktreeMetadata(worktreePath);
|
|
54108
54175
|
result.metadata++;
|
|
54109
54176
|
} catch (err) {
|
|
54110
|
-
|
|
54177
|
+
log8.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
|
|
54111
54178
|
try {
|
|
54112
54179
|
await rm(worktreePath, { recursive: true, force: true });
|
|
54113
54180
|
result.cleaned++;
|
|
54114
54181
|
await removeWorktreeMetadata(worktreePath);
|
|
54115
54182
|
result.metadata++;
|
|
54116
54183
|
} catch (rmErr) {
|
|
54117
|
-
|
|
54184
|
+
log8.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
|
|
54118
54185
|
}
|
|
54119
54186
|
}
|
|
54120
54187
|
}
|
|
54121
54188
|
} catch (err) {
|
|
54122
|
-
|
|
54189
|
+
log8.warn(`Failed to scan worktrees directory: ${err}`);
|
|
54123
54190
|
}
|
|
54124
54191
|
return result;
|
|
54125
54192
|
}
|
|
@@ -54193,7 +54260,7 @@ init_logger();
|
|
|
54193
54260
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
54194
54261
|
import { resolve as resolve3, dirname as dirname6 } from "path";
|
|
54195
54262
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
54196
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
|
|
54263
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
54197
54264
|
import { tmpdir } from "os";
|
|
54198
54265
|
import { join as join7 } from "path";
|
|
54199
54266
|
|
|
@@ -54259,7 +54326,7 @@ function extractResetAt(text, now) {
|
|
|
54259
54326
|
}
|
|
54260
54327
|
|
|
54261
54328
|
// src/claude/cli.ts
|
|
54262
|
-
var
|
|
54329
|
+
var log9 = createLogger("claude");
|
|
54263
54330
|
function cleanupBrowserBridgeSockets() {
|
|
54264
54331
|
try {
|
|
54265
54332
|
const tempDir = tmpdir();
|
|
@@ -54271,13 +54338,13 @@ function cleanupBrowserBridgeSockets() {
|
|
|
54271
54338
|
const stats = statSync2(filePath);
|
|
54272
54339
|
if (stats.isSocket()) {
|
|
54273
54340
|
unlinkSync2(filePath);
|
|
54274
|
-
|
|
54341
|
+
log9.debug(`Removed stale browser bridge socket: ${file}`);
|
|
54275
54342
|
}
|
|
54276
54343
|
} catch {}
|
|
54277
54344
|
}
|
|
54278
54345
|
}
|
|
54279
54346
|
} catch (err) {
|
|
54280
|
-
|
|
54347
|
+
log9.debug(`Browser bridge cleanup failed: ${err}`);
|
|
54281
54348
|
}
|
|
54282
54349
|
}
|
|
54283
54350
|
function buildClaudeChildEnv(parentEnv, account) {
|
|
@@ -54307,6 +54374,64 @@ function isErrorResultEvent(event) {
|
|
|
54307
54374
|
return true;
|
|
54308
54375
|
return false;
|
|
54309
54376
|
}
|
|
54377
|
+
function materializeMcpConfig(config, sessionId, opts = {}) {
|
|
54378
|
+
if (opts.inline ?? process.env.CLAUDE_THREADS_MCP_CONFIG_INLINE === "1") {
|
|
54379
|
+
return { mode: "inline", value: JSON.stringify(config) };
|
|
54380
|
+
}
|
|
54381
|
+
const dir = opts.tmpDirOverride ?? tmpdir();
|
|
54382
|
+
const path2 = join7(dir, `claude-threads-mcp-${sessionId ?? process.pid}-${Date.now()}.json`);
|
|
54383
|
+
writeFileSync3(path2, JSON.stringify(config), { mode: 384 });
|
|
54384
|
+
return { mode: "file", path: path2 };
|
|
54385
|
+
}
|
|
54386
|
+
function buildPermissionArgs(opts) {
|
|
54387
|
+
const args = [];
|
|
54388
|
+
if (opts.permissionMode === "bypass") {
|
|
54389
|
+
args.push("--dangerously-skip-permissions");
|
|
54390
|
+
return { args, tempFile: null };
|
|
54391
|
+
}
|
|
54392
|
+
if (!opts.platformConfig) {
|
|
54393
|
+
throw new Error(`platformConfig is required when permissionMode is '${opts.permissionMode}'`);
|
|
54394
|
+
}
|
|
54395
|
+
const mcpEnv = {
|
|
54396
|
+
PLATFORM_TYPE: opts.platformConfig.type,
|
|
54397
|
+
PLATFORM_URL: opts.platformConfig.url,
|
|
54398
|
+
PLATFORM_TOKEN: opts.platformConfig.token,
|
|
54399
|
+
PLATFORM_CHANNEL_ID: opts.platformConfig.channelId,
|
|
54400
|
+
PLATFORM_THREAD_ID: opts.threadId || "",
|
|
54401
|
+
ALLOWED_USERS: opts.platformConfig.allowedUsers.join(","),
|
|
54402
|
+
DEBUG: opts.debug ? "1" : "",
|
|
54403
|
+
PERMISSION_TIMEOUT_MS: String(opts.permissionTimeoutMs)
|
|
54404
|
+
};
|
|
54405
|
+
if (opts.platformConfig.appToken) {
|
|
54406
|
+
mcpEnv.PLATFORM_APP_TOKEN = opts.platformConfig.appToken;
|
|
54407
|
+
}
|
|
54408
|
+
const mcpConfig = {
|
|
54409
|
+
mcpServers: {
|
|
54410
|
+
"claude-threads-permissions": {
|
|
54411
|
+
type: "stdio",
|
|
54412
|
+
command: "node",
|
|
54413
|
+
args: [opts.mcpServerPath],
|
|
54414
|
+
env: mcpEnv
|
|
54415
|
+
}
|
|
54416
|
+
}
|
|
54417
|
+
};
|
|
54418
|
+
const materialized = materializeMcpConfig(mcpConfig, opts.sessionId, { inline: opts.inline });
|
|
54419
|
+
let tempFile = null;
|
|
54420
|
+
if (materialized.mode === "file") {
|
|
54421
|
+
tempFile = materialized.path;
|
|
54422
|
+
args.push("--mcp-config", materialized.path);
|
|
54423
|
+
} else {
|
|
54424
|
+
args.push("--mcp-config", materialized.value);
|
|
54425
|
+
}
|
|
54426
|
+
args.push("--permission-prompt-tool", "mcp__claude-threads-permissions__permission_prompt");
|
|
54427
|
+
if (opts.permissionMode === "auto") {
|
|
54428
|
+
args.push("--permission-mode", "auto");
|
|
54429
|
+
}
|
|
54430
|
+
return { args, tempFile };
|
|
54431
|
+
}
|
|
54432
|
+
var STDERR_PER_INSTANCE_CAP = 10240;
|
|
54433
|
+
var STDERR_AGGREGATE_SOFT_CAP = 10 * 1024 * 1024;
|
|
54434
|
+
var totalStderrBytes = 0;
|
|
54310
54435
|
|
|
54311
54436
|
class ClaudeCli extends EventEmitter2 {
|
|
54312
54437
|
process = null;
|
|
@@ -54316,6 +54441,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54316
54441
|
statusFilePath = null;
|
|
54317
54442
|
lastStatusData = null;
|
|
54318
54443
|
stderrBuffer = "";
|
|
54444
|
+
mcpConfigTempFile = null;
|
|
54319
54445
|
lastEmittedRateLimitDeadline = 0;
|
|
54320
54446
|
log;
|
|
54321
54447
|
constructor(options2) {
|
|
@@ -54367,6 +54493,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54367
54493
|
start() {
|
|
54368
54494
|
if (this.process)
|
|
54369
54495
|
throw new Error("Already running");
|
|
54496
|
+
totalStderrBytes -= this.stderrBuffer.length;
|
|
54370
54497
|
this.stderrBuffer = "";
|
|
54371
54498
|
this.lastEmittedRateLimitDeadline = 0;
|
|
54372
54499
|
cleanupBrowserBridgeSockets();
|
|
@@ -54385,40 +54512,18 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54385
54512
|
args.push("--session-id", this.options.sessionId);
|
|
54386
54513
|
}
|
|
54387
54514
|
}
|
|
54388
|
-
|
|
54389
|
-
|
|
54390
|
-
|
|
54391
|
-
|
|
54392
|
-
|
|
54393
|
-
|
|
54394
|
-
|
|
54395
|
-
|
|
54396
|
-
|
|
54397
|
-
|
|
54398
|
-
|
|
54399
|
-
|
|
54400
|
-
PLATFORM_CHANNEL_ID: platformConfig.channelId,
|
|
54401
|
-
PLATFORM_THREAD_ID: this.options.threadId || "",
|
|
54402
|
-
ALLOWED_USERS: platformConfig.allowedUsers.join(","),
|
|
54403
|
-
DEBUG: this.debug ? "1" : "",
|
|
54404
|
-
PERMISSION_TIMEOUT_MS: String(this.options.permissionTimeoutMs ?? 120000)
|
|
54405
|
-
};
|
|
54406
|
-
if (platformConfig.appToken) {
|
|
54407
|
-
mcpEnv.PLATFORM_APP_TOKEN = platformConfig.appToken;
|
|
54408
|
-
}
|
|
54409
|
-
const mcpConfig = {
|
|
54410
|
-
mcpServers: {
|
|
54411
|
-
"claude-threads-permissions": {
|
|
54412
|
-
type: "stdio",
|
|
54413
|
-
command: "node",
|
|
54414
|
-
args: [mcpServerPath],
|
|
54415
|
-
env: mcpEnv
|
|
54416
|
-
}
|
|
54417
|
-
}
|
|
54418
|
-
};
|
|
54419
|
-
args.push("--mcp-config", JSON.stringify(mcpConfig));
|
|
54420
|
-
args.push("--permission-prompt-tool", "mcp__claude-threads-permissions__permission_prompt");
|
|
54421
|
-
}
|
|
54515
|
+
const permissionMode = this.options.permissionMode ?? "default";
|
|
54516
|
+
const permResult = buildPermissionArgs({
|
|
54517
|
+
permissionMode,
|
|
54518
|
+
mcpServerPath: this.getMcpServerPath(),
|
|
54519
|
+
platformConfig: this.options.platformConfig,
|
|
54520
|
+
threadId: this.options.threadId,
|
|
54521
|
+
sessionId: this.options.sessionId,
|
|
54522
|
+
permissionTimeoutMs: this.options.permissionTimeoutMs ?? 120000,
|
|
54523
|
+
debug: this.debug
|
|
54524
|
+
});
|
|
54525
|
+
args.push(...permResult.args);
|
|
54526
|
+
this.mcpConfigTempFile = permResult.tempFile;
|
|
54422
54527
|
if (this.options.chrome) {
|
|
54423
54528
|
args.push("--chrome");
|
|
54424
54529
|
}
|
|
@@ -54453,10 +54558,13 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54453
54558
|
});
|
|
54454
54559
|
this.process.stderr?.on("data", (chunk) => {
|
|
54455
54560
|
const text = chunk.toString();
|
|
54561
|
+
const before = this.stderrBuffer.length;
|
|
54456
54562
|
this.stderrBuffer += text;
|
|
54457
|
-
|
|
54458
|
-
|
|
54563
|
+
const cap = totalStderrBytes > STDERR_AGGREGATE_SOFT_CAP ? 1024 : STDERR_PER_INSTANCE_CAP;
|
|
54564
|
+
if (this.stderrBuffer.length > cap) {
|
|
54565
|
+
this.stderrBuffer = this.stderrBuffer.slice(-cap);
|
|
54459
54566
|
}
|
|
54567
|
+
totalStderrBytes += this.stderrBuffer.length - before;
|
|
54460
54568
|
this.log.debug(`stderr: ${text.trim()}`);
|
|
54461
54569
|
if (process.env.INTEGRATION_TEST === "1") {
|
|
54462
54570
|
process.stderr.write(text);
|
|
@@ -54471,6 +54579,14 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54471
54579
|
this.log.debug(`Exited ${code}`);
|
|
54472
54580
|
this.process = null;
|
|
54473
54581
|
this.buffer = "";
|
|
54582
|
+
totalStderrBytes -= this.stderrBuffer.length;
|
|
54583
|
+
if (this.mcpConfigTempFile) {
|
|
54584
|
+
const path2 = this.mcpConfigTempFile;
|
|
54585
|
+
this.mcpConfigTempFile = null;
|
|
54586
|
+
try {
|
|
54587
|
+
unlinkSync2(path2);
|
|
54588
|
+
} catch {}
|
|
54589
|
+
}
|
|
54474
54590
|
this.emit("exit", code);
|
|
54475
54591
|
});
|
|
54476
54592
|
}
|
|
@@ -54716,8 +54832,8 @@ var COMMAND_REGISTRY = [
|
|
|
54716
54832
|
},
|
|
54717
54833
|
{
|
|
54718
54834
|
command: "permissions",
|
|
54719
|
-
description: "
|
|
54720
|
-
args: "
|
|
54835
|
+
description: "Set permission mode (default prompts; auto lets Claude classify; bypass skips all)",
|
|
54836
|
+
args: "default / auto / bypass",
|
|
54721
54837
|
category: "settings",
|
|
54722
54838
|
audience: "user",
|
|
54723
54839
|
claudeNotes: "User decisions, not yours",
|
|
@@ -54832,7 +54948,7 @@ var COMMAND_PATTERNS = [
|
|
|
54832
54948
|
["worktree", /^!worktree\s+(\S+(?:\s+.*)?)$/i],
|
|
54833
54949
|
["invite", /^!invite\s+@?([\w.-]+)\s*$/i],
|
|
54834
54950
|
["kick", /^!kick\s+@?([\w.-]+)\s*$/i],
|
|
54835
|
-
["permissions", /^!permissions?\s+(interactive|
|
|
54951
|
+
["permissions", /^!permissions?\s+(default|auto|bypass|interactive|skip)\s*$/i],
|
|
54836
54952
|
["update", /^!update(?:\s+(now|defer))?\s*$/i],
|
|
54837
54953
|
["context", /^!context\s*$/i],
|
|
54838
54954
|
["cost", /^!cost\s*$/i],
|
|
@@ -54897,7 +55013,7 @@ function removeCommandFromText(text, command) {
|
|
|
54897
55013
|
}
|
|
54898
55014
|
var STACKABLE_PATTERNS = [
|
|
54899
55015
|
["cd", /^!cd\s+(\S+)(?:\s+(.*))?$/i, 1, 2],
|
|
54900
|
-
["permissions", /^!permissions?\s+(interactive)(?:\s+(.*))?$/i, 1, 2],
|
|
55016
|
+
["permissions", /^!permissions?\s+(default|auto|bypass|interactive|skip)(?:\s+(.*))?$/i, 1, 2],
|
|
54901
55017
|
["worktree", /^!worktree\s+(.+)$/i, 1, -1]
|
|
54902
55018
|
];
|
|
54903
55019
|
var IMMEDIATE_PATTERNS = [
|
|
@@ -55175,22 +55291,39 @@ var handleCd = async (ctx, args) => {
|
|
|
55175
55291
|
await ctx.sessionManager.changeDirectory(ctx.threadId, args, ctx.username);
|
|
55176
55292
|
return { handled: true };
|
|
55177
55293
|
};
|
|
55294
|
+
function parsePermissionMode(arg) {
|
|
55295
|
+
switch (arg?.toLowerCase()) {
|
|
55296
|
+
case "default":
|
|
55297
|
+
case "interactive":
|
|
55298
|
+
return "default";
|
|
55299
|
+
case "auto":
|
|
55300
|
+
return "auto";
|
|
55301
|
+
case "bypass":
|
|
55302
|
+
case "skip":
|
|
55303
|
+
return "bypass";
|
|
55304
|
+
default:
|
|
55305
|
+
return null;
|
|
55306
|
+
}
|
|
55307
|
+
}
|
|
55178
55308
|
var handlePermissions = async (ctx, args) => {
|
|
55179
|
-
const mode = args
|
|
55309
|
+
const mode = parsePermissionMode(args);
|
|
55180
55310
|
if (ctx.commandContext === "first-message") {
|
|
55181
|
-
if (mode
|
|
55311
|
+
if (mode) {
|
|
55182
55312
|
return {
|
|
55183
|
-
sessionOptions: {
|
|
55313
|
+
sessionOptions: {
|
|
55314
|
+
permissionMode: mode,
|
|
55315
|
+
forceInteractivePermissions: mode === "default"
|
|
55316
|
+
},
|
|
55184
55317
|
continueProcessing: true
|
|
55185
55318
|
};
|
|
55186
55319
|
}
|
|
55187
55320
|
return { handled: false };
|
|
55188
55321
|
}
|
|
55189
|
-
if (mode
|
|
55190
|
-
await ctx.
|
|
55191
|
-
|
|
55192
|
-
await ctx.client.createPost(`⚠️ Cannot upgrade to auto permissions - can only downgrade to interactive`, ctx.threadId);
|
|
55322
|
+
if (!mode) {
|
|
55323
|
+
await ctx.client.createPost(`⚠️ Unknown permission mode. Usage: \`!permissions default|auto|bypass\` (aliases: \`interactive\`, \`skip\`).`, ctx.threadId);
|
|
55324
|
+
return { handled: true };
|
|
55193
55325
|
}
|
|
55326
|
+
await ctx.sessionManager.setSessionPermissionMode(ctx.threadId, ctx.username, mode);
|
|
55194
55327
|
return { handled: true };
|
|
55195
55328
|
};
|
|
55196
55329
|
var handleWorktree = async (ctx, args) => {
|
|
@@ -55447,7 +55580,7 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
55447
55580
|
// src/utils/keep-alive.ts
|
|
55448
55581
|
init_logger();
|
|
55449
55582
|
import { spawn as spawn2 } from "child_process";
|
|
55450
|
-
var
|
|
55583
|
+
var log10 = createLogger("keepalive");
|
|
55451
55584
|
|
|
55452
55585
|
class KeepAliveManager {
|
|
55453
55586
|
activeSessionCount = 0;
|
|
@@ -55462,7 +55595,7 @@ class KeepAliveManager {
|
|
|
55462
55595
|
if (!enabled && this.keepAliveProcess) {
|
|
55463
55596
|
this.stopKeepAlive();
|
|
55464
55597
|
}
|
|
55465
|
-
|
|
55598
|
+
log10.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
|
|
55466
55599
|
}
|
|
55467
55600
|
isEnabled() {
|
|
55468
55601
|
return this.enabled;
|
|
@@ -55472,7 +55605,7 @@ class KeepAliveManager {
|
|
|
55472
55605
|
}
|
|
55473
55606
|
sessionStarted() {
|
|
55474
55607
|
this.activeSessionCount++;
|
|
55475
|
-
|
|
55608
|
+
log10.debug(`Session started (${this.activeSessionCount} active)`);
|
|
55476
55609
|
if (this.activeSessionCount === 1) {
|
|
55477
55610
|
this.startKeepAlive();
|
|
55478
55611
|
}
|
|
@@ -55481,7 +55614,7 @@ class KeepAliveManager {
|
|
|
55481
55614
|
if (this.activeSessionCount > 0) {
|
|
55482
55615
|
this.activeSessionCount--;
|
|
55483
55616
|
}
|
|
55484
|
-
|
|
55617
|
+
log10.debug(`Session ended (${this.activeSessionCount} active)`);
|
|
55485
55618
|
if (this.activeSessionCount === 0) {
|
|
55486
55619
|
this.stopKeepAlive();
|
|
55487
55620
|
}
|
|
@@ -55495,11 +55628,11 @@ class KeepAliveManager {
|
|
|
55495
55628
|
}
|
|
55496
55629
|
startKeepAlive() {
|
|
55497
55630
|
if (!this.enabled) {
|
|
55498
|
-
|
|
55631
|
+
log10.debug("Keep-alive disabled, skipping");
|
|
55499
55632
|
return;
|
|
55500
55633
|
}
|
|
55501
55634
|
if (this.keepAliveProcess) {
|
|
55502
|
-
|
|
55635
|
+
log10.debug("Keep-alive already running");
|
|
55503
55636
|
return;
|
|
55504
55637
|
}
|
|
55505
55638
|
switch (this.platform) {
|
|
@@ -55513,12 +55646,12 @@ class KeepAliveManager {
|
|
|
55513
55646
|
this.startWindowsKeepAlive();
|
|
55514
55647
|
break;
|
|
55515
55648
|
default:
|
|
55516
|
-
|
|
55649
|
+
log10.warn(`Keep-alive not supported on ${this.platform}`);
|
|
55517
55650
|
}
|
|
55518
55651
|
}
|
|
55519
55652
|
stopKeepAlive() {
|
|
55520
55653
|
if (this.keepAliveProcess) {
|
|
55521
|
-
|
|
55654
|
+
log10.debug("Stopping keep-alive");
|
|
55522
55655
|
this.keepAliveProcess.kill();
|
|
55523
55656
|
this.keepAliveProcess = null;
|
|
55524
55657
|
}
|
|
@@ -55530,18 +55663,18 @@ class KeepAliveManager {
|
|
|
55530
55663
|
detached: false
|
|
55531
55664
|
});
|
|
55532
55665
|
this.keepAliveProcess.on("error", (err) => {
|
|
55533
|
-
|
|
55666
|
+
log10.error(`Failed to start caffeinate: ${err.message}`);
|
|
55534
55667
|
this.keepAliveProcess = null;
|
|
55535
55668
|
});
|
|
55536
55669
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55537
55670
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55538
|
-
|
|
55671
|
+
log10.debug(`caffeinate exited with code ${code}`);
|
|
55539
55672
|
}
|
|
55540
55673
|
this.keepAliveProcess = null;
|
|
55541
55674
|
});
|
|
55542
|
-
|
|
55675
|
+
log10.info("Sleep prevention active (caffeinate)");
|
|
55543
55676
|
} catch (err) {
|
|
55544
|
-
|
|
55677
|
+
log10.error(`Failed to start caffeinate: ${err}`);
|
|
55545
55678
|
}
|
|
55546
55679
|
}
|
|
55547
55680
|
startLinuxKeepAlive() {
|
|
@@ -55557,19 +55690,19 @@ class KeepAliveManager {
|
|
|
55557
55690
|
detached: false
|
|
55558
55691
|
});
|
|
55559
55692
|
this.keepAliveProcess.on("error", (err) => {
|
|
55560
|
-
|
|
55693
|
+
log10.debug(`systemd-inhibit not available: ${err.message}`);
|
|
55561
55694
|
this.keepAliveProcess = null;
|
|
55562
55695
|
this.startLinuxKeepAliveFallback();
|
|
55563
55696
|
});
|
|
55564
55697
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55565
55698
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55566
|
-
|
|
55699
|
+
log10.debug(`systemd-inhibit exited with code ${code}`);
|
|
55567
55700
|
}
|
|
55568
55701
|
this.keepAliveProcess = null;
|
|
55569
55702
|
});
|
|
55570
|
-
|
|
55703
|
+
log10.info("Sleep prevention active (systemd-inhibit)");
|
|
55571
55704
|
} catch (err) {
|
|
55572
|
-
|
|
55705
|
+
log10.debug(`Failed to start systemd-inhibit: ${err}`);
|
|
55573
55706
|
this.startLinuxKeepAliveFallback();
|
|
55574
55707
|
}
|
|
55575
55708
|
}
|
|
@@ -55583,15 +55716,15 @@ class KeepAliveManager {
|
|
|
55583
55716
|
detached: false
|
|
55584
55717
|
});
|
|
55585
55718
|
this.keepAliveProcess.on("error", (err) => {
|
|
55586
|
-
|
|
55719
|
+
log10.warn(`Linux keep-alive fallback not available: ${err.message}`);
|
|
55587
55720
|
this.keepAliveProcess = null;
|
|
55588
55721
|
});
|
|
55589
55722
|
this.keepAliveProcess.on("exit", () => {
|
|
55590
55723
|
this.keepAliveProcess = null;
|
|
55591
55724
|
});
|
|
55592
|
-
|
|
55725
|
+
log10.info("Sleep prevention active (xdg-screensaver)");
|
|
55593
55726
|
} catch (err) {
|
|
55594
|
-
|
|
55727
|
+
log10.warn(`Linux keep-alive not available: ${err}`);
|
|
55595
55728
|
}
|
|
55596
55729
|
}
|
|
55597
55730
|
startWindowsKeepAlive() {
|
|
@@ -55616,18 +55749,18 @@ class KeepAliveManager {
|
|
|
55616
55749
|
windowsHide: true
|
|
55617
55750
|
});
|
|
55618
55751
|
this.keepAliveProcess.on("error", (err) => {
|
|
55619
|
-
|
|
55752
|
+
log10.warn(`Windows keep-alive not available: ${err.message}`);
|
|
55620
55753
|
this.keepAliveProcess = null;
|
|
55621
55754
|
});
|
|
55622
55755
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55623
55756
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55624
|
-
|
|
55757
|
+
log10.debug(`PowerShell keep-alive exited with code ${code}`);
|
|
55625
55758
|
}
|
|
55626
55759
|
this.keepAliveProcess = null;
|
|
55627
55760
|
});
|
|
55628
|
-
|
|
55761
|
+
log10.info("Sleep prevention active (SetThreadExecutionState)");
|
|
55629
55762
|
} catch (err) {
|
|
55630
|
-
|
|
55763
|
+
log10.warn(`Windows keep-alive not available: ${err}`);
|
|
55631
55764
|
}
|
|
55632
55765
|
}
|
|
55633
55766
|
}
|
|
@@ -55635,7 +55768,7 @@ var keepAlive = new KeepAliveManager;
|
|
|
55635
55768
|
|
|
55636
55769
|
// src/utils/error-handler/index.ts
|
|
55637
55770
|
init_logger();
|
|
55638
|
-
var
|
|
55771
|
+
var log11 = createLogger("error");
|
|
55639
55772
|
|
|
55640
55773
|
class SessionError extends Error {
|
|
55641
55774
|
sessionId;
|
|
@@ -55661,19 +55794,19 @@ async function handleError(error, context, severity = "recoverable") {
|
|
|
55661
55794
|
const sessionPart = sessionId ? ` (${formatShortId(sessionId)})` : "";
|
|
55662
55795
|
const logMessage = `${context.action}${sessionPart}: ${message}`;
|
|
55663
55796
|
if (severity === "recoverable") {
|
|
55664
|
-
|
|
55797
|
+
log11.warn(logMessage);
|
|
55665
55798
|
} else {
|
|
55666
|
-
|
|
55799
|
+
log11.error(logMessage, error instanceof Error ? error : undefined);
|
|
55667
55800
|
}
|
|
55668
55801
|
if (context.details) {
|
|
55669
|
-
|
|
55802
|
+
log11.debugJson("Error details", context.details);
|
|
55670
55803
|
}
|
|
55671
55804
|
if (context.notifyUser && context.session) {
|
|
55672
55805
|
try {
|
|
55673
55806
|
const fmt = context.session.platform.getFormatter();
|
|
55674
55807
|
await context.session.platform.createPost(`⚠️ ${fmt.formatBold("Error")}: ${context.action} failed - ${message}`, context.session.threadId);
|
|
55675
55808
|
} catch (notifyError) {
|
|
55676
|
-
|
|
55809
|
+
log11.warn(`Could not notify user: ${notifyError}`);
|
|
55677
55810
|
}
|
|
55678
55811
|
}
|
|
55679
55812
|
if (severity === "session-fatal" || severity === "system-fatal") {
|
|
@@ -55700,7 +55833,7 @@ async function logAndNotify(error, context) {
|
|
|
55700
55833
|
}
|
|
55701
55834
|
function logSilentError(context, error) {
|
|
55702
55835
|
const message = error instanceof Error ? error.message : String(error);
|
|
55703
|
-
|
|
55836
|
+
log11.debug(`[${context}] Silently caught: ${message}`);
|
|
55704
55837
|
}
|
|
55705
55838
|
|
|
55706
55839
|
// src/session/lifecycle.ts
|
|
@@ -55720,8 +55853,8 @@ function createSessionLog(baseLog) {
|
|
|
55720
55853
|
init_logger();
|
|
55721
55854
|
init_emoji();
|
|
55722
55855
|
init_worktree();
|
|
55723
|
-
var
|
|
55724
|
-
var sessionLog = createSessionLog(
|
|
55856
|
+
var log12 = createLogger("helpers");
|
|
55857
|
+
var sessionLog = createSessionLog(log12);
|
|
55725
55858
|
var POST_TYPES = {
|
|
55726
55859
|
info: "",
|
|
55727
55860
|
success: "✅",
|
|
@@ -55771,17 +55904,17 @@ async function postInteractiveAndRegister(session, message, reactions, registerP
|
|
|
55771
55904
|
registerPost(post2.id, session.threadId);
|
|
55772
55905
|
return post2;
|
|
55773
55906
|
}
|
|
55774
|
-
async function
|
|
55907
|
+
async function updatePost(session, postId, message) {
|
|
55775
55908
|
await withErrorHandling(() => session.platform.updatePost(postId, message), { action: "Update post", session });
|
|
55776
55909
|
}
|
|
55777
55910
|
async function updatePostSuccess(session, postId, message) {
|
|
55778
|
-
await
|
|
55911
|
+
await updatePost(session, postId, `✅ ${message}`);
|
|
55779
55912
|
}
|
|
55780
55913
|
async function updatePostError(session, postId, message) {
|
|
55781
|
-
await
|
|
55914
|
+
await updatePost(session, postId, `❌ ${message}`);
|
|
55782
55915
|
}
|
|
55783
55916
|
async function updatePostCancelled(session, postId, message) {
|
|
55784
|
-
await
|
|
55917
|
+
await updatePost(session, postId, `\uD83D\uDEAB ${message}`);
|
|
55785
55918
|
}
|
|
55786
55919
|
async function removeReaction(session, postId, emoji) {
|
|
55787
55920
|
await withErrorHandling(() => session.platform.removeReaction(postId, emoji), { action: `Remove ${emoji} reaction`, session });
|
|
@@ -55805,7 +55938,7 @@ function updateLastMessage(session, post2) {
|
|
|
55805
55938
|
// src/claude/quick-query.ts
|
|
55806
55939
|
init_spawn();
|
|
55807
55940
|
init_logger();
|
|
55808
|
-
var
|
|
55941
|
+
var log13 = createLogger("query");
|
|
55809
55942
|
async function quickQuery(options2) {
|
|
55810
55943
|
const {
|
|
55811
55944
|
prompt,
|
|
@@ -55821,7 +55954,7 @@ async function quickQuery(options2) {
|
|
|
55821
55954
|
args.push("--system-prompt", systemPrompt);
|
|
55822
55955
|
}
|
|
55823
55956
|
args.push(prompt);
|
|
55824
|
-
|
|
55957
|
+
log13.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
|
|
55825
55958
|
return new Promise((resolve5) => {
|
|
55826
55959
|
let stdout = "";
|
|
55827
55960
|
let stderr = "";
|
|
@@ -55835,7 +55968,7 @@ async function quickQuery(options2) {
|
|
|
55835
55968
|
if (!resolved) {
|
|
55836
55969
|
resolved = true;
|
|
55837
55970
|
proc.kill("SIGTERM");
|
|
55838
|
-
|
|
55971
|
+
log13.debug(`Quick query timed out after ${timeout}ms`);
|
|
55839
55972
|
resolve5({
|
|
55840
55973
|
success: false,
|
|
55841
55974
|
error: "timeout",
|
|
@@ -55853,7 +55986,7 @@ async function quickQuery(options2) {
|
|
|
55853
55986
|
if (!resolved) {
|
|
55854
55987
|
resolved = true;
|
|
55855
55988
|
clearTimeout(timeoutId);
|
|
55856
|
-
|
|
55989
|
+
log13.debug(`Quick query error: ${err.message}`);
|
|
55857
55990
|
resolve5({
|
|
55858
55991
|
success: false,
|
|
55859
55992
|
error: err.message,
|
|
@@ -55867,14 +56000,14 @@ async function quickQuery(options2) {
|
|
|
55867
56000
|
clearTimeout(timeoutId);
|
|
55868
56001
|
const durationMs = Date.now() - startTime;
|
|
55869
56002
|
if (code === 0 && stdout.trim()) {
|
|
55870
|
-
|
|
56003
|
+
log13.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
|
|
55871
56004
|
resolve5({
|
|
55872
56005
|
success: true,
|
|
55873
56006
|
response: stdout.trim(),
|
|
55874
56007
|
durationMs
|
|
55875
56008
|
});
|
|
55876
56009
|
} else {
|
|
55877
|
-
|
|
56010
|
+
log13.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
|
|
55878
56011
|
resolve5({
|
|
55879
56012
|
success: false,
|
|
55880
56013
|
error: stderr || `exit code ${code}`,
|
|
@@ -55889,7 +56022,7 @@ async function quickQuery(options2) {
|
|
|
55889
56022
|
|
|
55890
56023
|
// src/operations/suggestions/title.ts
|
|
55891
56024
|
init_logger();
|
|
55892
|
-
var
|
|
56025
|
+
var log14 = createLogger("title");
|
|
55893
56026
|
var SUGGESTION_TIMEOUT = 15000;
|
|
55894
56027
|
var MIN_TITLE_LENGTH = 3;
|
|
55895
56028
|
var MAX_TITLE_LENGTH = 50;
|
|
@@ -55953,32 +56086,32 @@ function parseMetadata(response) {
|
|
|
55953
56086
|
const titleMatch = response.match(/TITLE:\s*(.+)/i);
|
|
55954
56087
|
const descMatch = response.match(/DESC:\s*(.+)/i);
|
|
55955
56088
|
if (!titleMatch || !descMatch) {
|
|
55956
|
-
|
|
56089
|
+
log14.debug("Failed to parse title/description from response");
|
|
55957
56090
|
return null;
|
|
55958
56091
|
}
|
|
55959
56092
|
let title = titleMatch[1].trim();
|
|
55960
56093
|
let description = descMatch[1].trim();
|
|
55961
56094
|
if (title.length < MIN_TITLE_LENGTH) {
|
|
55962
|
-
|
|
56095
|
+
log14.debug(`Title too short: ${title.length} chars`);
|
|
55963
56096
|
return null;
|
|
55964
56097
|
}
|
|
55965
56098
|
if (title.length > MAX_TITLE_LENGTH) {
|
|
55966
|
-
|
|
56099
|
+
log14.debug(`Title too long (${title.length} chars), truncating`);
|
|
55967
56100
|
title = truncateAtWord(title, MAX_TITLE_LENGTH);
|
|
55968
56101
|
}
|
|
55969
56102
|
if (description.length < MIN_DESC_LENGTH) {
|
|
55970
|
-
|
|
56103
|
+
log14.debug(`Description too short: ${description.length} chars`);
|
|
55971
56104
|
return null;
|
|
55972
56105
|
}
|
|
55973
56106
|
if (description.length > MAX_DESC_LENGTH) {
|
|
55974
|
-
|
|
56107
|
+
log14.debug(`Description too long (${description.length} chars), truncating`);
|
|
55975
56108
|
description = truncateAtWord(description, MAX_DESC_LENGTH);
|
|
55976
56109
|
}
|
|
55977
56110
|
return { title, description };
|
|
55978
56111
|
}
|
|
55979
56112
|
async function suggestSessionMetadata(context) {
|
|
55980
56113
|
const logContext = typeof context === "string" ? context.substring(0, 50) : context.originalTask.substring(0, 50);
|
|
55981
|
-
|
|
56114
|
+
log14.debug(`Suggesting title for: "${logContext}..."`);
|
|
55982
56115
|
try {
|
|
55983
56116
|
const result = await quickQuery({
|
|
55984
56117
|
prompt: buildTitlePrompt(context),
|
|
@@ -55986,23 +56119,23 @@ async function suggestSessionMetadata(context) {
|
|
|
55986
56119
|
timeout: SUGGESTION_TIMEOUT
|
|
55987
56120
|
});
|
|
55988
56121
|
if (!result.success || !result.response) {
|
|
55989
|
-
|
|
56122
|
+
log14.debug(`Title suggestion failed: ${result.error || "no response"}`);
|
|
55990
56123
|
return null;
|
|
55991
56124
|
}
|
|
55992
56125
|
const metadata = parseMetadata(result.response);
|
|
55993
56126
|
if (metadata) {
|
|
55994
|
-
|
|
56127
|
+
log14.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
|
|
55995
56128
|
}
|
|
55996
56129
|
return metadata;
|
|
55997
56130
|
} catch (err) {
|
|
55998
|
-
|
|
56131
|
+
log14.debug(`Title suggestion error: ${err}`);
|
|
55999
56132
|
return null;
|
|
56000
56133
|
}
|
|
56001
56134
|
}
|
|
56002
56135
|
|
|
56003
56136
|
// src/operations/suggestions/tag.ts
|
|
56004
56137
|
init_logger();
|
|
56005
|
-
var
|
|
56138
|
+
var log15 = createLogger("tags");
|
|
56006
56139
|
var SUGGESTION_TIMEOUT2 = 15000;
|
|
56007
56140
|
var MAX_TAGS = 3;
|
|
56008
56141
|
var VALID_TAGS = [
|
|
@@ -56034,7 +56167,7 @@ function parseTags(response) {
|
|
|
56034
56167
|
return [...new Set(tags)].slice(0, MAX_TAGS);
|
|
56035
56168
|
}
|
|
56036
56169
|
async function suggestSessionTags(userMessage) {
|
|
56037
|
-
|
|
56170
|
+
log15.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
|
|
56038
56171
|
try {
|
|
56039
56172
|
const result = await quickQuery({
|
|
56040
56173
|
prompt: buildTagPrompt(userMessage),
|
|
@@ -56042,14 +56175,14 @@ async function suggestSessionTags(userMessage) {
|
|
|
56042
56175
|
timeout: SUGGESTION_TIMEOUT2
|
|
56043
56176
|
});
|
|
56044
56177
|
if (!result.success || !result.response) {
|
|
56045
|
-
|
|
56178
|
+
log15.debug(`Tag suggestion failed: ${result.error || "no response"}`);
|
|
56046
56179
|
return [];
|
|
56047
56180
|
}
|
|
56048
56181
|
const tags = parseTags(result.response);
|
|
56049
|
-
|
|
56182
|
+
log15.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
|
|
56050
56183
|
return tags;
|
|
56051
56184
|
} catch (err) {
|
|
56052
|
-
|
|
56185
|
+
log15.debug(`Tag suggestion error: ${err}`);
|
|
56053
56186
|
return [];
|
|
56054
56187
|
}
|
|
56055
56188
|
}
|
|
@@ -57968,6 +58101,18 @@ class ContentExecutor extends BaseExecutor {
|
|
|
57968
58101
|
}
|
|
57969
58102
|
this.state = this.getInitialState();
|
|
57970
58103
|
}
|
|
58104
|
+
async tryUpdatePost(ctx, postId, content, logTag, successDetails, failureDetails, onSuccess, onFailure) {
|
|
58105
|
+
try {
|
|
58106
|
+
await ctx.platform.updatePost(postId, content);
|
|
58107
|
+
onSuccess();
|
|
58108
|
+
ctx.threadLogger?.logExecutor("content", "update", postId, successDetails, logTag);
|
|
58109
|
+
} catch (err) {
|
|
58110
|
+
ctx.logger.debug(`Update failed (${logTag}): ${err}`);
|
|
58111
|
+
const resolvedFailureDetails = typeof failureDetails === "function" ? failureDetails(err) : failureDetails;
|
|
58112
|
+
ctx.threadLogger?.logExecutor("content", "error", postId, resolvedFailureDetails, logTag);
|
|
58113
|
+
onFailure();
|
|
58114
|
+
}
|
|
58115
|
+
}
|
|
57971
58116
|
closeCurrentPost(ctx) {
|
|
57972
58117
|
const oldPostId = this.state.currentPostId;
|
|
57973
58118
|
const contentLength = this.state.currentPostContent.length;
|
|
@@ -58066,23 +58211,13 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58066
58211
|
await this.createNewPost(ctx, content, pendingAtFlushStart);
|
|
58067
58212
|
return;
|
|
58068
58213
|
}
|
|
58069
|
-
|
|
58070
|
-
await ctx.platform.updatePost(postId, combinedContent2);
|
|
58214
|
+
await this.tryUpdatePost(ctx, postId, combinedContent2, "flush", { newContentLength: content.length, combinedLength: combinedContent2.length }, (err) => ({ failedOp: "updatePost", error: String(err) }), () => {
|
|
58071
58215
|
this.state.currentPostContent = combinedContent2;
|
|
58072
58216
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58073
|
-
|
|
58074
|
-
newContentLength: content.length,
|
|
58075
|
-
combinedLength: combinedContent2.length
|
|
58076
|
-
}, "flush");
|
|
58077
|
-
} catch (err) {
|
|
58078
|
-
ctx.logger.debug(`Update failed, will create new post on next flush: ${err}`);
|
|
58079
|
-
ctx.threadLogger?.logExecutor("content", "error", postId, {
|
|
58080
|
-
failedOp: "updatePost",
|
|
58081
|
-
error: String(err)
|
|
58082
|
-
}, "flush");
|
|
58217
|
+
}, () => {
|
|
58083
58218
|
this.state.currentPostId = null;
|
|
58084
58219
|
this.state.currentPostContent = "";
|
|
58085
|
-
}
|
|
58220
|
+
});
|
|
58086
58221
|
} else {
|
|
58087
58222
|
const chunks = splitContentForHeight(content, ctx.contentBreaker);
|
|
58088
58223
|
ctx.threadLogger?.logExecutor("content", "create_start", "none", {
|
|
@@ -58143,21 +58278,13 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58143
58278
|
breakPoint = bestBreakPoint;
|
|
58144
58279
|
} else {
|
|
58145
58280
|
if (this.state.currentPostId) {
|
|
58146
|
-
|
|
58147
|
-
|
|
58281
|
+
const postId = this.state.currentPostId;
|
|
58282
|
+
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "soft_break_no_breakpoint", contentLength: content.length }, { reason: "soft_break_no_breakpoint_failed" }, () => {
|
|
58148
58283
|
this.state.currentPostContent = content;
|
|
58149
58284
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58150
|
-
|
|
58151
|
-
reason: "soft_break_no_breakpoint",
|
|
58152
|
-
contentLength: content.length
|
|
58153
|
-
}, "handleSplit");
|
|
58154
|
-
} catch {
|
|
58155
|
-
ctx.logger.debug("Update failed (no breakpoint), will create new post on next flush");
|
|
58156
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58157
|
-
reason: "soft_break_no_breakpoint_failed"
|
|
58158
|
-
}, "handleSplit");
|
|
58285
|
+
}, () => {
|
|
58159
58286
|
this.state.currentPostId = null;
|
|
58160
|
-
}
|
|
58287
|
+
});
|
|
58161
58288
|
}
|
|
58162
58289
|
return;
|
|
58163
58290
|
}
|
|
@@ -58165,22 +58292,14 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58165
58292
|
if (codeBlockOpenPosition !== undefined) {
|
|
58166
58293
|
if (codeBlockOpenPosition === 0) {
|
|
58167
58294
|
if (this.state.currentPostId) {
|
|
58168
|
-
|
|
58169
|
-
|
|
58295
|
+
const postId = this.state.currentPostId;
|
|
58296
|
+
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "code_block_at_start", contentLength: content.length }, { reason: "code_block_at_start_failed" }, () => {
|
|
58170
58297
|
this.state.currentPostContent = content;
|
|
58171
58298
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58172
|
-
|
|
58173
|
-
reason: "code_block_at_start",
|
|
58174
|
-
contentLength: content.length
|
|
58175
|
-
}, "handleSplit");
|
|
58176
|
-
} catch {
|
|
58177
|
-
ctx.logger.debug("Update failed (code block at start)");
|
|
58178
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58179
|
-
reason: "code_block_at_start_failed"
|
|
58180
|
-
}, "handleSplit");
|
|
58299
|
+
}, () => {
|
|
58181
58300
|
this.state.currentPostId = null;
|
|
58182
58301
|
this.state.currentPostContent = "";
|
|
58183
|
-
}
|
|
58302
|
+
});
|
|
58184
58303
|
}
|
|
58185
58304
|
return;
|
|
58186
58305
|
}
|
|
@@ -58190,22 +58309,14 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58190
58309
|
breakPoint = breakBeforeCodeBlock;
|
|
58191
58310
|
} else {
|
|
58192
58311
|
if (this.state.currentPostId) {
|
|
58193
|
-
|
|
58194
|
-
|
|
58312
|
+
const postId = this.state.currentPostId;
|
|
58313
|
+
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "no_break_before_code_block", contentLength: content.length }, { reason: "no_break_before_code_block_failed" }, () => {
|
|
58195
58314
|
this.state.currentPostContent = content;
|
|
58196
58315
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58197
|
-
|
|
58198
|
-
reason: "no_break_before_code_block",
|
|
58199
|
-
contentLength: content.length
|
|
58200
|
-
}, "handleSplit");
|
|
58201
|
-
} catch {
|
|
58202
|
-
ctx.logger.debug("Update failed (no break before code block)");
|
|
58203
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58204
|
-
reason: "no_break_before_code_block_failed"
|
|
58205
|
-
}, "handleSplit");
|
|
58316
|
+
}, () => {
|
|
58206
58317
|
this.state.currentPostId = null;
|
|
58207
58318
|
this.state.currentPostContent = "";
|
|
58208
|
-
}
|
|
58319
|
+
});
|
|
58209
58320
|
}
|
|
58210
58321
|
return;
|
|
58211
58322
|
}
|
|
@@ -58213,19 +58324,8 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58213
58324
|
const firstPart = content.substring(0, breakPoint).trim();
|
|
58214
58325
|
const remainder = content.substring(breakPoint).trim();
|
|
58215
58326
|
if (this.state.currentPostId) {
|
|
58216
|
-
|
|
58217
|
-
|
|
58218
|
-
ctx.threadLogger?.logExecutor("content", "update", this.state.currentPostId, {
|
|
58219
|
-
reason: "split_first_part",
|
|
58220
|
-
firstPartLength: firstPart.length,
|
|
58221
|
-
remainderLength: remainder.length
|
|
58222
|
-
}, "handleSplit");
|
|
58223
|
-
} catch {
|
|
58224
|
-
ctx.logger.debug("Update failed during split, continuing with new post");
|
|
58225
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58226
|
-
reason: "split_first_part_failed"
|
|
58227
|
-
}, "handleSplit");
|
|
58228
|
-
}
|
|
58327
|
+
const postId = this.state.currentPostId;
|
|
58328
|
+
await this.tryUpdatePost(ctx, postId, firstPart, "handleSplit", { reason: "split_first_part", firstPartLength: firstPart.length, remainderLength: remainder.length }, { reason: "split_first_part_failed" }, () => {}, () => {});
|
|
58229
58329
|
}
|
|
58230
58330
|
this.state.currentPostId = null;
|
|
58231
58331
|
this.state.currentPostContent = "";
|
|
@@ -58511,7 +58611,15 @@ class TaskListExecutor extends BaseExecutor {
|
|
|
58511
58611
|
await ctx.platform.updatePost(this.state.tasksPostId, displayContent);
|
|
58512
58612
|
} catch {}
|
|
58513
58613
|
}
|
|
58514
|
-
|
|
58614
|
+
serialize() {
|
|
58615
|
+
return {
|
|
58616
|
+
postId: this.state.tasksPostId,
|
|
58617
|
+
content: this.state.lastTasksContent,
|
|
58618
|
+
isMinimized: this.state.tasksMinimized,
|
|
58619
|
+
isCompleted: this.state.tasksCompleted
|
|
58620
|
+
};
|
|
58621
|
+
}
|
|
58622
|
+
async handleReaction(postId, emoji, _user, action, ctx) {
|
|
58515
58623
|
ctx.logger.debug(`TaskListExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji}, action=${action}, tasksPostId=${this.state.tasksPostId?.substring(0, 8) ?? "none"}`);
|
|
58516
58624
|
if (postId !== this.state.tasksPostId) {
|
|
58517
58625
|
ctx.logger.debug(`TaskListExecutor: postId does not match tasksPostId, ignoring`);
|
|
@@ -58780,7 +58888,7 @@ class SubagentExecutor extends BaseExecutor {
|
|
|
58780
58888
|
}
|
|
58781
58889
|
return false;
|
|
58782
58890
|
}
|
|
58783
|
-
async handleReaction(postId, emoji, action, ctx) {
|
|
58891
|
+
async handleReaction(postId, emoji, _user, action, ctx) {
|
|
58784
58892
|
ctx.logger.debug(`SubagentExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji}, action=${action}`);
|
|
58785
58893
|
if (!isMinimizeToggleEmoji(emoji)) {
|
|
58786
58894
|
ctx.logger.debug(`SubagentExecutor: emoji ${emoji} is not minimize toggle, ignoring`);
|
|
@@ -59320,6 +59428,9 @@ class PromptExecutor extends BaseExecutor {
|
|
|
59320
59428
|
getPendingContextPrompt() {
|
|
59321
59429
|
return this.state.pendingContextPrompt;
|
|
59322
59430
|
}
|
|
59431
|
+
serialize() {
|
|
59432
|
+
return this.state.pendingContextPrompt;
|
|
59433
|
+
}
|
|
59323
59434
|
hasPendingContextPrompt() {
|
|
59324
59435
|
return this.state.pendingContextPrompt !== null;
|
|
59325
59436
|
}
|
|
@@ -59591,7 +59702,7 @@ class BugReportExecutor extends BaseExecutor {
|
|
|
59591
59702
|
// src/operations/executors/worktree-prompt.ts
|
|
59592
59703
|
init_emoji();
|
|
59593
59704
|
init_logger();
|
|
59594
|
-
var
|
|
59705
|
+
var log16 = createLogger("wt-prompt");
|
|
59595
59706
|
// src/operations/message-manager.ts
|
|
59596
59707
|
init_logger();
|
|
59597
59708
|
|
|
@@ -59628,7 +59739,7 @@ var import_yauzl = __toESM(require_yauzl(), 1);
|
|
|
59628
59739
|
import { createGunzip } from "zlib";
|
|
59629
59740
|
import { pipeline } from "stream/promises";
|
|
59630
59741
|
import { Readable, Writable } from "stream";
|
|
59631
|
-
var
|
|
59742
|
+
var log17 = createLogger("streaming");
|
|
59632
59743
|
var MAX_PDF_SIZE = 32 * 1024 * 1024;
|
|
59633
59744
|
var MAX_TEXT_FILE_SIZE = 1 * 1024 * 1024;
|
|
59634
59745
|
var MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024;
|
|
@@ -59754,7 +59865,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59754
59865
|
const buffer = await platform.downloadFile(file.id);
|
|
59755
59866
|
const base64 = buffer.toString("base64");
|
|
59756
59867
|
if (debug) {
|
|
59757
|
-
|
|
59868
|
+
log17.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
|
|
59758
59869
|
}
|
|
59759
59870
|
return {
|
|
59760
59871
|
block: {
|
|
@@ -59767,7 +59878,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59767
59878
|
}
|
|
59768
59879
|
};
|
|
59769
59880
|
} catch (err) {
|
|
59770
|
-
|
|
59881
|
+
log17.error(`Failed to download image ${file.name}: ${err}`);
|
|
59771
59882
|
return {
|
|
59772
59883
|
skipped: {
|
|
59773
59884
|
name: file.name,
|
|
@@ -59798,7 +59909,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59798
59909
|
}
|
|
59799
59910
|
const base64 = buffer.toString("base64");
|
|
59800
59911
|
if (debug) {
|
|
59801
|
-
|
|
59912
|
+
log17.debug(`Attached PDF: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59802
59913
|
}
|
|
59803
59914
|
return {
|
|
59804
59915
|
block: {
|
|
@@ -59812,7 +59923,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59812
59923
|
}
|
|
59813
59924
|
};
|
|
59814
59925
|
} catch (err) {
|
|
59815
|
-
|
|
59926
|
+
log17.error(`Failed to process PDF ${file.name}: ${err}`);
|
|
59816
59927
|
return {
|
|
59817
59928
|
skipped: {
|
|
59818
59929
|
name: file.name,
|
|
@@ -59843,7 +59954,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59843
59954
|
}
|
|
59844
59955
|
const content = buffer.toString("utf-8");
|
|
59845
59956
|
if (debug) {
|
|
59846
|
-
|
|
59957
|
+
log17.debug(`Attached text file: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59847
59958
|
}
|
|
59848
59959
|
const wrappedContent = formatTextFileContent(file.name, content);
|
|
59849
59960
|
return {
|
|
@@ -59853,7 +59964,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59853
59964
|
}
|
|
59854
59965
|
};
|
|
59855
59966
|
} catch (err) {
|
|
59856
|
-
|
|
59967
|
+
log17.error(`Failed to process text file ${file.name}: ${err}`);
|
|
59857
59968
|
return {
|
|
59858
59969
|
skipped: {
|
|
59859
59970
|
name: file.name,
|
|
@@ -59911,7 +60022,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59911
60022
|
compressedBuffer = await platform.downloadFile(file.id);
|
|
59912
60023
|
} catch (err) {
|
|
59913
60024
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59914
|
-
|
|
60025
|
+
log17.error(`Failed to download gzip file ${file.name}: ${errorMessage}`);
|
|
59915
60026
|
return {
|
|
59916
60027
|
skipped: {
|
|
59917
60028
|
name: file.name,
|
|
@@ -59921,7 +60032,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59921
60032
|
};
|
|
59922
60033
|
}
|
|
59923
60034
|
if (file.size && compressedBuffer.length !== file.size) {
|
|
59924
|
-
|
|
60035
|
+
log17.warn(`Downloaded size mismatch for ${file.name}: expected ${file.size}, got ${compressedBuffer.length}`);
|
|
59925
60036
|
}
|
|
59926
60037
|
let decompressedBuffer;
|
|
59927
60038
|
try {
|
|
@@ -59957,7 +60068,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59957
60068
|
const innerFilename = file.name.toLowerCase().endsWith(".gz") ? file.name.slice(0, -3) : file.name;
|
|
59958
60069
|
const contentType = detectDecompressedContentType(decompressedBuffer, innerFilename);
|
|
59959
60070
|
if (debug) {
|
|
59960
|
-
|
|
60071
|
+
log17.debug(`Decompressed ${file.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
|
|
59961
60072
|
}
|
|
59962
60073
|
if (contentType === "pdf") {
|
|
59963
60074
|
const base64 = decompressedBuffer.toString("base64");
|
|
@@ -59992,7 +60103,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59992
60103
|
}
|
|
59993
60104
|
} catch (err) {
|
|
59994
60105
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59995
|
-
|
|
60106
|
+
log17.error(`Failed to process gzip file ${file.name}: ${errorMessage}`);
|
|
59996
60107
|
return {
|
|
59997
60108
|
skipped: {
|
|
59998
60109
|
name: file.name,
|
|
@@ -60044,7 +60155,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
60044
60155
|
}
|
|
60045
60156
|
const zipBuffer = await platform.downloadFile(file.id);
|
|
60046
60157
|
if (debug) {
|
|
60047
|
-
|
|
60158
|
+
log17.debug(`Processing zip file ${file.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
|
|
60048
60159
|
}
|
|
60049
60160
|
const zipfile = await new Promise((resolve5, reject) => {
|
|
60050
60161
|
import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
|
|
@@ -60118,7 +60229,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
60118
60229
|
const buffer = await extractZipEntry(zipfile2, entry);
|
|
60119
60230
|
const contentType = detectDecompressedContentType(buffer, entry.fileName);
|
|
60120
60231
|
if (debug) {
|
|
60121
|
-
|
|
60232
|
+
log17.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
|
|
60122
60233
|
}
|
|
60123
60234
|
if (contentType === "pdf") {
|
|
60124
60235
|
const base64 = buffer.toString("base64");
|
|
@@ -60161,11 +60272,11 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
60161
60272
|
});
|
|
60162
60273
|
zipfile2.close();
|
|
60163
60274
|
if (debug) {
|
|
60164
|
-
|
|
60275
|
+
log17.debug(`Zip ${file.name}: processed ${processedCount} files, skipped ${skipped.length}`);
|
|
60165
60276
|
}
|
|
60166
60277
|
return { blocks, skipped };
|
|
60167
60278
|
} catch (err) {
|
|
60168
|
-
|
|
60279
|
+
log17.error(`Failed to process zip file ${file.name}: ${err}`);
|
|
60169
60280
|
return {
|
|
60170
60281
|
blocks: [],
|
|
60171
60282
|
skipped: [{
|
|
@@ -60262,7 +60373,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60262
60373
|
blocks.push(...zipResult.blocks);
|
|
60263
60374
|
for (const s of zipResult.skipped) {
|
|
60264
60375
|
skipped.push(s);
|
|
60265
|
-
|
|
60376
|
+
log17.warn(`Skipped file ${s.name}: ${s.reason}`);
|
|
60266
60377
|
}
|
|
60267
60378
|
continue;
|
|
60268
60379
|
}
|
|
@@ -60296,7 +60407,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60296
60407
|
}
|
|
60297
60408
|
if (result.skipped) {
|
|
60298
60409
|
skipped.push(result.skipped);
|
|
60299
|
-
|
|
60410
|
+
log17.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
|
|
60300
60411
|
}
|
|
60301
60412
|
}
|
|
60302
60413
|
return { blocks, skipped };
|
|
@@ -60329,7 +60440,7 @@ function stopTyping(session) {
|
|
|
60329
60440
|
}
|
|
60330
60441
|
|
|
60331
60442
|
// src/operations/message-manager.ts
|
|
60332
|
-
var
|
|
60443
|
+
var log18 = createLogger("msg-mgr");
|
|
60333
60444
|
|
|
60334
60445
|
class MessageManager {
|
|
60335
60446
|
platform;
|
|
@@ -60355,7 +60466,8 @@ class MessageManager {
|
|
|
60355
60466
|
emitSessionUpdateCallback;
|
|
60356
60467
|
toolStartTimes = new Map;
|
|
60357
60468
|
flushTimer = null;
|
|
60358
|
-
static
|
|
60469
|
+
static DEFAULT_FLUSH_DELAY_MS = 500;
|
|
60470
|
+
flushDelayMs;
|
|
60359
60471
|
events;
|
|
60360
60472
|
constructor(options2) {
|
|
60361
60473
|
this.session = options2.session;
|
|
@@ -60370,6 +60482,7 @@ class MessageManager {
|
|
|
60370
60482
|
this.buildMessageContentCallback = options2.buildMessageContent;
|
|
60371
60483
|
this.startTypingCallback = options2.startTyping;
|
|
60372
60484
|
this.emitSessionUpdateCallback = options2.emitSessionUpdate;
|
|
60485
|
+
this.flushDelayMs = options2.flushDelayMs ?? MessageManager.DEFAULT_FLUSH_DELAY_MS;
|
|
60373
60486
|
this.events = createMessageManagerEvents();
|
|
60374
60487
|
this.contentBreaker = new DefaultContentBreaker;
|
|
60375
60488
|
this.contentExecutor = new ContentExecutor({
|
|
@@ -60417,7 +60530,7 @@ class MessageManager {
|
|
|
60417
60530
|
});
|
|
60418
60531
|
}
|
|
60419
60532
|
async handleEvent(event) {
|
|
60420
|
-
const logger =
|
|
60533
|
+
const logger = log18.forSession(this.sessionId);
|
|
60421
60534
|
const transformCtx = {
|
|
60422
60535
|
sessionId: this.sessionId,
|
|
60423
60536
|
formatter: this.platform.getFormatter(),
|
|
@@ -60467,7 +60580,7 @@ class MessageManager {
|
|
|
60467
60580
|
}
|
|
60468
60581
|
}
|
|
60469
60582
|
async executeOperation(op) {
|
|
60470
|
-
const logger =
|
|
60583
|
+
const logger = log18.forSession(this.sessionId);
|
|
60471
60584
|
const ctx = this.getExecutorContext();
|
|
60472
60585
|
try {
|
|
60473
60586
|
if (isContentOp(op)) {
|
|
@@ -60516,7 +60629,7 @@ class MessageManager {
|
|
|
60516
60629
|
this.flushTimer = null;
|
|
60517
60630
|
const flushOp = createFlushOp(this.sessionId, "soft_threshold");
|
|
60518
60631
|
await this.contentExecutor.executeFlush(flushOp, ctx);
|
|
60519
|
-
},
|
|
60632
|
+
}, this.flushDelayMs);
|
|
60520
60633
|
}
|
|
60521
60634
|
cancelScheduledFlush() {
|
|
60522
60635
|
if (this.flushTimer) {
|
|
@@ -60535,7 +60648,7 @@ class MessageManager {
|
|
|
60535
60648
|
threadId: this.threadId,
|
|
60536
60649
|
platform: this.platform,
|
|
60537
60650
|
formatter: this.platform.getFormatter(),
|
|
60538
|
-
logger:
|
|
60651
|
+
logger: log18.forSession(this.sessionId),
|
|
60539
60652
|
postTracker: this.postTracker,
|
|
60540
60653
|
contentBreaker: this.contentBreaker,
|
|
60541
60654
|
threadLogger: this.session.threadLogger,
|
|
@@ -60680,12 +60793,12 @@ class MessageManager {
|
|
|
60680
60793
|
await this.taskListExecutor.bumpToBottom(this.getExecutorContext());
|
|
60681
60794
|
}
|
|
60682
60795
|
getTaskListState() {
|
|
60683
|
-
|
|
60796
|
+
return this.taskListExecutor.serialize();
|
|
60797
|
+
}
|
|
60798
|
+
serialize() {
|
|
60684
60799
|
return {
|
|
60685
|
-
|
|
60686
|
-
|
|
60687
|
-
isMinimized: state.tasksMinimized,
|
|
60688
|
-
isCompleted: state.tasksCompleted
|
|
60800
|
+
taskList: this.taskListExecutor.serialize(),
|
|
60801
|
+
contextPrompt: this.promptExecutor.serialize()
|
|
60689
60802
|
};
|
|
60690
60803
|
}
|
|
60691
60804
|
hydrateTaskListState(persisted) {
|
|
@@ -60746,13 +60859,13 @@ class MessageManager {
|
|
|
60746
60859
|
return this.systemExecutor.postSuccess(message, this.getExecutorContext());
|
|
60747
60860
|
}
|
|
60748
60861
|
async prepareForUserMessage() {
|
|
60749
|
-
const logger =
|
|
60862
|
+
const logger = log18.forSession(this.sessionId);
|
|
60750
60863
|
logger.debug("Preparing for new user message");
|
|
60751
60864
|
await this.closeCurrentPost();
|
|
60752
60865
|
await this.bumpTaskList();
|
|
60753
60866
|
}
|
|
60754
60867
|
async handleUserMessage(message, files, username, displayName) {
|
|
60755
|
-
const logger =
|
|
60868
|
+
const logger = log18.forSession(this.sessionId);
|
|
60756
60869
|
if (!this.session.claude.isRunning()) {
|
|
60757
60870
|
logger.debug("Claude not running, ignoring user message");
|
|
60758
60871
|
return false;
|
|
@@ -60778,33 +60891,27 @@ class MessageManager {
|
|
|
60778
60891
|
getSession() {
|
|
60779
60892
|
return this.session;
|
|
60780
60893
|
}
|
|
60894
|
+
reactionDispatchList() {
|
|
60895
|
+
return [
|
|
60896
|
+
{ name: "QuestionApprovalExecutor", executor: this.questionApprovalExecutor },
|
|
60897
|
+
{ name: "MessageApprovalExecutor", executor: this.messageApprovalExecutor },
|
|
60898
|
+
{ name: "PromptExecutor", executor: this.promptExecutor },
|
|
60899
|
+
{ name: "BugReportExecutor", executor: this.bugReportExecutor },
|
|
60900
|
+
{ name: "TaskListExecutor", executor: this.taskListExecutor },
|
|
60901
|
+
{ name: "SubagentExecutor", executor: this.subagentExecutor }
|
|
60902
|
+
];
|
|
60903
|
+
}
|
|
60781
60904
|
async handleReaction(postId, emoji, user, action) {
|
|
60782
|
-
const logger =
|
|
60905
|
+
const logger = log18.forSession(this.sessionId);
|
|
60783
60906
|
const ctx = this.getExecutorContext();
|
|
60784
60907
|
logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
|
|
60785
|
-
|
|
60786
|
-
|
|
60787
|
-
|
|
60788
|
-
|
|
60789
|
-
|
|
60790
|
-
|
|
60791
|
-
|
|
60792
|
-
}
|
|
60793
|
-
if (await this.promptExecutor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
60794
|
-
logger.debug("Reaction handled by PromptExecutor");
|
|
60795
|
-
return true;
|
|
60796
|
-
}
|
|
60797
|
-
if (await this.bugReportExecutor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
60798
|
-
logger.debug("Reaction handled by BugReportExecutor");
|
|
60799
|
-
return true;
|
|
60800
|
-
}
|
|
60801
|
-
if (await this.taskListExecutor.handleReaction(postId, emoji, action, ctx)) {
|
|
60802
|
-
logger.debug("Reaction handled by TaskListExecutor");
|
|
60803
|
-
return true;
|
|
60804
|
-
}
|
|
60805
|
-
if (await this.subagentExecutor.handleReaction(postId, emoji, action, ctx)) {
|
|
60806
|
-
logger.debug("Reaction handled by SubagentExecutor");
|
|
60807
|
-
return true;
|
|
60908
|
+
for (const { name, executor } of this.reactionDispatchList()) {
|
|
60909
|
+
if (!executor.handleReaction)
|
|
60910
|
+
continue;
|
|
60911
|
+
if (await executor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
60912
|
+
logger.debug(`Reaction handled by ${name}`);
|
|
60913
|
+
return true;
|
|
60914
|
+
}
|
|
60808
60915
|
}
|
|
60809
60916
|
logger.debug("Reaction not handled by any executor");
|
|
60810
60917
|
return false;
|
|
@@ -60975,7 +61082,7 @@ function formatPullRequestLink(url, formatter) {
|
|
|
60975
61082
|
}
|
|
60976
61083
|
|
|
60977
61084
|
// src/operations/sticky-message/handler.ts
|
|
60978
|
-
var
|
|
61085
|
+
var log19 = createLogger("sticky");
|
|
60979
61086
|
var botStartedAt = new Date;
|
|
60980
61087
|
function getPendingPrompts(session) {
|
|
60981
61088
|
const prompts2 = [];
|
|
@@ -61050,21 +61157,21 @@ function initialize(store) {
|
|
|
61050
61157
|
stickyPostIds.set(platformId, postId);
|
|
61051
61158
|
}
|
|
61052
61159
|
if (persistedIds.size > 0) {
|
|
61053
|
-
|
|
61160
|
+
log19.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
|
|
61054
61161
|
}
|
|
61055
61162
|
}
|
|
61056
61163
|
function setPlatformPaused(platformId, paused) {
|
|
61057
61164
|
if (paused) {
|
|
61058
61165
|
pausedPlatforms.set(platformId, true);
|
|
61059
|
-
|
|
61166
|
+
log19.debug(`Platform ${platformId} marked as paused`);
|
|
61060
61167
|
} else {
|
|
61061
61168
|
pausedPlatforms.delete(platformId);
|
|
61062
|
-
|
|
61169
|
+
log19.debug(`Platform ${platformId} marked as active`);
|
|
61063
61170
|
}
|
|
61064
61171
|
}
|
|
61065
61172
|
function setShuttingDown(shuttingDown) {
|
|
61066
61173
|
isShuttingDown = shuttingDown;
|
|
61067
|
-
|
|
61174
|
+
log19.debug(`Bot shutdown state: ${shuttingDown}`);
|
|
61068
61175
|
}
|
|
61069
61176
|
function getTaskContent(session) {
|
|
61070
61177
|
const taskState = session.messageManager?.getTaskListState();
|
|
@@ -61168,8 +61275,7 @@ async function buildStatusBar(sessionCount, config, formatter, platformId) {
|
|
|
61168
61275
|
const label = cooling > 0 ? `\uD83D\uDD11 ${available}/${total} accounts (${cooling} cooling)` : `\uD83D\uDD11 ${total} account${total === 1 ? "" : "s"}`;
|
|
61169
61276
|
items.push(formatter.formatCode(label));
|
|
61170
61277
|
}
|
|
61171
|
-
|
|
61172
|
-
items.push(formatter.formatCode(permMode));
|
|
61278
|
+
items.push(formatter.formatCode(permissionModeDisplay(config.permissionMode).chip));
|
|
61173
61279
|
if (config.worktreeMode === "require") {
|
|
61174
61280
|
items.push(formatter.formatCode("\uD83C\uDF3F Worktree: require"));
|
|
61175
61281
|
} else if (config.worktreeMode === "off") {
|
|
@@ -61376,12 +61482,12 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61376
61482
|
try {
|
|
61377
61483
|
const post2 = await platform.getPost(lastMessageId);
|
|
61378
61484
|
if (!post2) {
|
|
61379
|
-
|
|
61485
|
+
log19.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
|
|
61380
61486
|
session.lastMessageId = undefined;
|
|
61381
61487
|
session.lastMessageTs = undefined;
|
|
61382
61488
|
}
|
|
61383
61489
|
} catch (err) {
|
|
61384
|
-
|
|
61490
|
+
log19.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
|
|
61385
61491
|
session.lastMessageId = undefined;
|
|
61386
61492
|
session.lastMessageTs = undefined;
|
|
61387
61493
|
}
|
|
@@ -61390,63 +61496,63 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61390
61496
|
}
|
|
61391
61497
|
async function updateStickyMessageImpl(platform, sessions, config) {
|
|
61392
61498
|
const platformSessions = [...sessions.values()].filter((s) => s.platformId === platform.platformId);
|
|
61393
|
-
|
|
61499
|
+
log19.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
|
|
61394
61500
|
for (const s of platformSessions) {
|
|
61395
|
-
|
|
61501
|
+
log19.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
|
|
61396
61502
|
}
|
|
61397
61503
|
await validateLastMessageIds(platform, platformSessions);
|
|
61398
61504
|
const formatter = platform.getFormatter();
|
|
61399
61505
|
const content = await buildStickyMessage(sessions, platform.platformId, config, formatter, (threadId) => platform.getThreadLink(threadId));
|
|
61400
61506
|
const existingPostId = stickyPostIds.get(platform.platformId);
|
|
61401
61507
|
const shouldBump = needsBump.get(platform.platformId) ?? false;
|
|
61402
|
-
|
|
61508
|
+
log19.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
|
|
61403
61509
|
try {
|
|
61404
61510
|
if (existingPostId && !shouldBump) {
|
|
61405
|
-
|
|
61511
|
+
log19.debug(`Updating existing post in place...`);
|
|
61406
61512
|
try {
|
|
61407
61513
|
await platform.updatePost(existingPostId, content);
|
|
61408
61514
|
try {
|
|
61409
61515
|
await platform.pinPost(existingPostId);
|
|
61410
|
-
|
|
61516
|
+
log19.debug(`Re-pinned post`);
|
|
61411
61517
|
} catch (pinErr) {
|
|
61412
|
-
|
|
61518
|
+
log19.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
|
|
61413
61519
|
}
|
|
61414
|
-
|
|
61520
|
+
log19.debug(`Updated successfully`);
|
|
61415
61521
|
return;
|
|
61416
61522
|
} catch (err) {
|
|
61417
|
-
|
|
61523
|
+
log19.debug(`Update failed, will create new: ${err}`);
|
|
61418
61524
|
}
|
|
61419
61525
|
}
|
|
61420
61526
|
needsBump.set(platform.platformId, false);
|
|
61421
61527
|
if (existingPostId) {
|
|
61422
|
-
|
|
61528
|
+
log19.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
|
|
61423
61529
|
try {
|
|
61424
61530
|
await platform.unpinPost(existingPostId);
|
|
61425
|
-
|
|
61531
|
+
log19.debug(`Unpinned successfully`);
|
|
61426
61532
|
} catch (err) {
|
|
61427
|
-
|
|
61533
|
+
log19.debug(`Unpin failed (probably already unpinned): ${err}`);
|
|
61428
61534
|
}
|
|
61429
61535
|
try {
|
|
61430
61536
|
await platform.deletePost(existingPostId);
|
|
61431
|
-
|
|
61537
|
+
log19.debug(`Deleted successfully`);
|
|
61432
61538
|
} catch (err) {
|
|
61433
|
-
|
|
61539
|
+
log19.debug(`Delete failed (probably already deleted): ${err}`);
|
|
61434
61540
|
}
|
|
61435
61541
|
stickyPostIds.delete(platform.platformId);
|
|
61436
61542
|
}
|
|
61437
|
-
|
|
61543
|
+
log19.debug(`Creating new post...`);
|
|
61438
61544
|
const post2 = await platform.createPost(content);
|
|
61439
61545
|
stickyPostIds.set(platform.platformId, post2.id);
|
|
61440
61546
|
try {
|
|
61441
61547
|
await platform.pinPost(post2.id);
|
|
61442
|
-
|
|
61548
|
+
log19.debug(`Pinned post successfully`);
|
|
61443
61549
|
} catch (err) {
|
|
61444
|
-
|
|
61550
|
+
log19.debug(`Failed to pin post: ${err}`);
|
|
61445
61551
|
}
|
|
61446
61552
|
if (sessionStore) {
|
|
61447
61553
|
sessionStore.saveStickyPostId(platform.platformId, post2.id);
|
|
61448
61554
|
}
|
|
61449
|
-
|
|
61555
|
+
log19.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
|
|
61450
61556
|
const excludePostIds = new Set;
|
|
61451
61557
|
if (sessionStore) {
|
|
61452
61558
|
for (const session of sessionStore.load().values()) {
|
|
@@ -61462,10 +61568,10 @@ async function updateStickyMessageImpl(platform, sessions, config) {
|
|
|
61462
61568
|
}
|
|
61463
61569
|
const botUser = await platform.getBotUser();
|
|
61464
61570
|
cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
|
|
61465
|
-
|
|
61571
|
+
log19.debug(`Background cleanup failed: ${err}`);
|
|
61466
61572
|
});
|
|
61467
61573
|
} catch (err) {
|
|
61468
|
-
|
|
61574
|
+
log19.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
|
|
61469
61575
|
}
|
|
61470
61576
|
}
|
|
61471
61577
|
async function updateAllStickyMessages(platforms, sessions, config) {
|
|
@@ -61490,7 +61596,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61490
61596
|
if (!forceRun) {
|
|
61491
61597
|
const lastRun = lastCleanupTime.get(platformId) || 0;
|
|
61492
61598
|
if (now - lastRun < CLEANUP_THROTTLE_MS) {
|
|
61493
|
-
|
|
61599
|
+
log19.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
|
|
61494
61600
|
return;
|
|
61495
61601
|
}
|
|
61496
61602
|
}
|
|
@@ -61500,36 +61606,36 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61500
61606
|
const pinnedPostIds = await platform.getPinnedPosts();
|
|
61501
61607
|
const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
|
|
61502
61608
|
if (recentPinnedIds.length === 0) {
|
|
61503
|
-
|
|
61609
|
+
log19.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
|
|
61504
61610
|
return;
|
|
61505
61611
|
}
|
|
61506
|
-
|
|
61612
|
+
log19.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
|
|
61507
61613
|
for (const postId of recentPinnedIds) {
|
|
61508
61614
|
try {
|
|
61509
61615
|
const post2 = await platform.getPost(postId);
|
|
61510
61616
|
if (!post2)
|
|
61511
61617
|
continue;
|
|
61512
61618
|
if (post2.userId === botUserId) {
|
|
61513
|
-
|
|
61619
|
+
log19.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
|
|
61514
61620
|
try {
|
|
61515
61621
|
await platform.unpinPost(postId);
|
|
61516
61622
|
await platform.deletePost(postId);
|
|
61517
|
-
|
|
61623
|
+
log19.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
|
|
61518
61624
|
} catch (err) {
|
|
61519
|
-
|
|
61625
|
+
log19.debug(`Failed to cleanup ${postId}: ${err}`);
|
|
61520
61626
|
}
|
|
61521
61627
|
}
|
|
61522
61628
|
} catch (err) {
|
|
61523
|
-
|
|
61629
|
+
log19.debug(`Could not check post ${postId}: ${err}`);
|
|
61524
61630
|
}
|
|
61525
61631
|
}
|
|
61526
61632
|
} catch (err) {
|
|
61527
|
-
|
|
61633
|
+
log19.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
|
|
61528
61634
|
}
|
|
61529
61635
|
}
|
|
61530
61636
|
// src/operations/bug-report/handler.ts
|
|
61531
61637
|
import { execSync as execSync2 } from "child_process";
|
|
61532
|
-
import { writeFileSync as
|
|
61638
|
+
import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
61533
61639
|
import { tmpdir as tmpdir2 } from "os";
|
|
61534
61640
|
import { join as join8 } from "path";
|
|
61535
61641
|
|
|
@@ -62113,7 +62219,7 @@ async function createGitHubIssue(title, body, workingDir) {
|
|
|
62113
62219
|
}
|
|
62114
62220
|
const bodyFile = join8(tmpdir2(), `bug-body-${Date.now()}.md`);
|
|
62115
62221
|
try {
|
|
62116
|
-
|
|
62222
|
+
writeFileSync4(bodyFile, body, "utf-8");
|
|
62117
62223
|
const cmd = `gh issue create --repo "${GITHUB_REPO}" --title "${escapeShell(title)}" --body-file "${bodyFile}"`;
|
|
62118
62224
|
const result = execSync2(cmd, {
|
|
62119
62225
|
cwd: workingDir,
|
|
@@ -62518,9 +62624,9 @@ node_default(Temp.purgeSyncAll);
|
|
|
62518
62624
|
var temp_default = Temp;
|
|
62519
62625
|
|
|
62520
62626
|
// node_modules/atomically/dist/index.js
|
|
62521
|
-
function
|
|
62627
|
+
function writeFileSync5(filePath, data, options2 = DEFAULT_WRITE_OPTIONS) {
|
|
62522
62628
|
if (isString(options2))
|
|
62523
|
-
return
|
|
62629
|
+
return writeFileSync5(filePath, data, { encoding: options2 });
|
|
62524
62630
|
const timeout = options2.timeout ?? DEFAULT_TIMEOUT_SYNC;
|
|
62525
62631
|
const retryOptions = { timeout };
|
|
62526
62632
|
let tempDisposer = null;
|
|
@@ -62848,7 +62954,7 @@ class Configstore {
|
|
|
62848
62954
|
}
|
|
62849
62955
|
if (error.name === "SyntaxError") {
|
|
62850
62956
|
if (this._clearInvalidConfig) {
|
|
62851
|
-
|
|
62957
|
+
writeFileSync5(this._path, "", writeFileOptions);
|
|
62852
62958
|
return {};
|
|
62853
62959
|
}
|
|
62854
62960
|
throw error;
|
|
@@ -62860,7 +62966,7 @@ class Configstore {
|
|
|
62860
62966
|
set all(value) {
|
|
62861
62967
|
try {
|
|
62862
62968
|
import_graceful_fs.default.mkdirSync(path5.dirname(this._path), mkdirOptions);
|
|
62863
|
-
|
|
62969
|
+
writeFileSync5(this._path, JSON.stringify(value, undefined, "\t"), writeFileOptions);
|
|
62864
62970
|
} catch (error) {
|
|
62865
62971
|
handlePermissionError(error);
|
|
62866
62972
|
}
|
|
@@ -65587,8 +65693,8 @@ function getUpdateInfo() {
|
|
|
65587
65693
|
init_emoji();
|
|
65588
65694
|
init_logger();
|
|
65589
65695
|
init_worktree();
|
|
65590
|
-
var
|
|
65591
|
-
var sessionLog2 = createSessionLog(
|
|
65696
|
+
var log20 = createLogger("commands");
|
|
65697
|
+
var sessionLog2 = createSessionLog(log20);
|
|
65592
65698
|
function sessionAccountOption(session, ctx) {
|
|
65593
65699
|
if (!session.claudeAccountId)
|
|
65594
65700
|
return;
|
|
@@ -65751,7 +65857,11 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
65751
65857
|
const cliOptions = {
|
|
65752
65858
|
workingDir: absoluteDir,
|
|
65753
65859
|
threadId: session.threadId,
|
|
65754
|
-
|
|
65860
|
+
permissionMode: effectivePermissionMode({
|
|
65861
|
+
override: session.permissionModeOverride,
|
|
65862
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
65863
|
+
botWideMode: ctx.config.permissionMode
|
|
65864
|
+
}),
|
|
65755
65865
|
sessionId: newSessionId,
|
|
65756
65866
|
resume: false,
|
|
65757
65867
|
chrome: ctx.config.chromeEnabled,
|
|
@@ -65828,45 +65938,37 @@ async function kickUser(session, kickedUser, kickedBy, ctx) {
|
|
|
65828
65938
|
sessionLog2(session).warn(`\uD83D\uDEAB @${kickedUser} was not in session`);
|
|
65829
65939
|
}
|
|
65830
65940
|
}
|
|
65831
|
-
async function
|
|
65941
|
+
async function setSessionPermissionMode(session, username, mode, ctx) {
|
|
65832
65942
|
if (!await requireSessionOwner(session, username, "change permissions")) {
|
|
65833
65943
|
return;
|
|
65834
65944
|
}
|
|
65835
|
-
|
|
65836
|
-
|
|
65837
|
-
|
|
65838
|
-
|
|
65839
|
-
|
|
65840
|
-
if (session.forceInteractivePermissions) {
|
|
65841
|
-
await post(session, "info", `Interactive permissions already enabled for this session`);
|
|
65842
|
-
sessionLog2(session).debug(`\uD83D\uDD10 Permissions already interactive (session override)`);
|
|
65843
|
-
return;
|
|
65844
|
-
}
|
|
65845
|
-
session.forceInteractivePermissions = true;
|
|
65846
|
-
sessionLog2(session).info(`\uD83D\uDD10 Enabling interactive permissions`);
|
|
65847
|
-
session.threadLogger?.logCommand("permissions", "interactive", username);
|
|
65945
|
+
session.permissionModeOverride = mode;
|
|
65946
|
+
session.forceInteractivePermissions = mode === "default";
|
|
65947
|
+
sessionLog2(session).info(`\uD83D\uDD10 Setting permission mode to "${mode}"`);
|
|
65948
|
+
session.threadLogger?.logCommand("permissions", mode, username);
|
|
65949
|
+
const canResume = session.lifecycle.hasClaudeResponded;
|
|
65848
65950
|
const cliOptions = {
|
|
65849
65951
|
workingDir: session.workingDir,
|
|
65850
65952
|
threadId: session.threadId,
|
|
65851
|
-
|
|
65953
|
+
permissionMode: mode,
|
|
65852
65954
|
sessionId: session.claudeSessionId,
|
|
65853
|
-
resume:
|
|
65955
|
+
resume: canResume,
|
|
65854
65956
|
chrome: ctx.config.chromeEnabled,
|
|
65855
65957
|
platformConfig: session.platform.getMcpConfig(),
|
|
65856
65958
|
logSessionId: session.sessionId,
|
|
65857
65959
|
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
65858
65960
|
account: sessionAccountOption(session, ctx)
|
|
65859
65961
|
};
|
|
65860
|
-
const success = await restartClaudeSession(session, cliOptions, ctx,
|
|
65962
|
+
const success = await restartClaudeSession(session, cliOptions, ctx, `Set permission mode to ${mode}`);
|
|
65861
65963
|
if (!success)
|
|
65862
65964
|
return;
|
|
65863
65965
|
await updateSessionHeader(session, ctx);
|
|
65864
65966
|
const formatter = session.platform.getFormatter();
|
|
65865
|
-
|
|
65866
|
-
${formatter.
|
|
65867
|
-
|
|
65868
|
-
|
|
65869
|
-
|
|
65967
|
+
const display = permissionModeDisplay(mode);
|
|
65968
|
+
await post(session, "secure", `${display.icon} ${formatter.formatBold(`Permission mode: ${display.label}`)} set for this session by ${formatter.formatUserMention(username)}
|
|
65969
|
+
` + `${formatter.formatItalic(permissionModeDescription(mode))}
|
|
65970
|
+
` + `${formatter.formatItalic("Claude Code restarted.")}`);
|
|
65971
|
+
sessionLog2(session).info(`\uD83D\uDD10 Permission mode set to "${mode}" by @${username}`);
|
|
65870
65972
|
}
|
|
65871
65973
|
async function requestMessageApproval(session, username, message, ctx) {
|
|
65872
65974
|
if (session.messageManager?.getPendingMessageApproval()) {
|
|
@@ -65892,8 +65994,12 @@ async function updateSessionHeader(session, ctx) {
|
|
|
65892
65994
|
const formatter = session.platform.getFormatter();
|
|
65893
65995
|
const worktreeContext = session.worktreeInfo ? { path: session.worktreeInfo.worktreePath, branch: session.worktreeInfo.branch } : undefined;
|
|
65894
65996
|
const shortDir = shortenPath(session.workingDir, undefined, worktreeContext);
|
|
65895
|
-
const
|
|
65896
|
-
|
|
65997
|
+
const effectiveMode = effectivePermissionMode({
|
|
65998
|
+
override: session.permissionModeOverride,
|
|
65999
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
66000
|
+
botWideMode: ctx.config.permissionMode
|
|
66001
|
+
});
|
|
66002
|
+
const permMode = permissionModeDisplay(effectiveMode).chip;
|
|
65897
66003
|
const otherParticipants = [...session.sessionAllowedUsers].filter((u) => u !== session.startedBy).map((u) => formatter.formatUserMention(u)).join(", ");
|
|
65898
66004
|
const statusItems = [];
|
|
65899
66005
|
const versionStr = formatVersionString();
|
|
@@ -65982,7 +66088,7 @@ async function updateSessionHeader(session, ctx) {
|
|
|
65982
66088
|
].filter((item) => item !== null && item !== undefined).join(`
|
|
65983
66089
|
`);
|
|
65984
66090
|
const postId = session.sessionStartPostId;
|
|
65985
|
-
await
|
|
66091
|
+
await updatePost(session, postId, msg);
|
|
65986
66092
|
}
|
|
65987
66093
|
async function showUpdateStatus(session, updateManager, ctx) {
|
|
65988
66094
|
const formatter = session.platform.getFormatter();
|
|
@@ -66122,7 +66228,7 @@ init_logger();
|
|
|
66122
66228
|
import { exec as exec3 } from "child_process";
|
|
66123
66229
|
import { promisify as promisify3 } from "util";
|
|
66124
66230
|
var execAsync2 = promisify3(exec3);
|
|
66125
|
-
var
|
|
66231
|
+
var log21 = createLogger("branch");
|
|
66126
66232
|
var SUGGESTION_TIMEOUT3 = 15000;
|
|
66127
66233
|
var MAX_SUGGESTIONS = 3;
|
|
66128
66234
|
async function getCurrentBranch3(workingDir) {
|
|
@@ -66171,7 +66277,7 @@ function parseBranchSuggestions(response) {
|
|
|
66171
66277
|
return lines.slice(0, MAX_SUGGESTIONS);
|
|
66172
66278
|
}
|
|
66173
66279
|
async function suggestBranchNames(workingDir, userMessage) {
|
|
66174
|
-
|
|
66280
|
+
log21.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
|
|
66175
66281
|
try {
|
|
66176
66282
|
const [currentBranch, recentCommits] = await Promise.all([
|
|
66177
66283
|
getCurrentBranch3(workingDir),
|
|
@@ -66185,14 +66291,14 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
66185
66291
|
workingDir
|
|
66186
66292
|
});
|
|
66187
66293
|
if (!result.success || !result.response) {
|
|
66188
|
-
|
|
66294
|
+
log21.debug(`Branch suggestion failed: ${result.error || "no response"}`);
|
|
66189
66295
|
return [];
|
|
66190
66296
|
}
|
|
66191
66297
|
const suggestions = parseBranchSuggestions(result.response);
|
|
66192
|
-
|
|
66298
|
+
log21.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
|
|
66193
66299
|
return suggestions;
|
|
66194
66300
|
} catch (err) {
|
|
66195
|
-
|
|
66301
|
+
log21.debug(`Branch suggestion error: ${err}`);
|
|
66196
66302
|
return [];
|
|
66197
66303
|
}
|
|
66198
66304
|
}
|
|
@@ -66201,8 +66307,8 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
66201
66307
|
init_worktree();
|
|
66202
66308
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
66203
66309
|
init_logger();
|
|
66204
|
-
var
|
|
66205
|
-
var sessionLog3 = createSessionLog(
|
|
66310
|
+
var log22 = createLogger("worktree");
|
|
66311
|
+
var sessionLog3 = createSessionLog(log22);
|
|
66206
66312
|
function parseWorktreeError(error) {
|
|
66207
66313
|
const message = error instanceof Error ? error.message : String(error);
|
|
66208
66314
|
const lowerMessage = message.toLowerCase();
|
|
@@ -66441,7 +66547,11 @@ async function createAndSwitchToWorktree(session, branch, username, options2) {
|
|
|
66441
66547
|
const cliOptions = {
|
|
66442
66548
|
workingDir: existing.path,
|
|
66443
66549
|
threadId: session.threadId,
|
|
66444
|
-
|
|
66550
|
+
permissionMode: effectivePermissionMode({
|
|
66551
|
+
override: session.permissionModeOverride,
|
|
66552
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
66553
|
+
botWideMode: options2.permissionMode
|
|
66554
|
+
}),
|
|
66445
66555
|
sessionId: newSessionId,
|
|
66446
66556
|
resume: false,
|
|
66447
66557
|
chrome: options2.chromeEnabled,
|
|
@@ -66530,7 +66640,11 @@ ${fmt.formatItalic("Claude Code restarted in the worktree")}`);
|
|
|
66530
66640
|
const cliOptions = {
|
|
66531
66641
|
workingDir: worktreePath,
|
|
66532
66642
|
threadId: session.threadId,
|
|
66533
|
-
|
|
66643
|
+
permissionMode: effectivePermissionMode({
|
|
66644
|
+
override: session.permissionModeOverride,
|
|
66645
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
66646
|
+
botWideMode: options2.permissionMode
|
|
66647
|
+
}),
|
|
66534
66648
|
sessionId: newSessionId,
|
|
66535
66649
|
resume: false,
|
|
66536
66650
|
chrome: options2.chromeEnabled,
|
|
@@ -66576,7 +66690,7 @@ ${fmt.formatItalic("Claude Code restarted in the new worktree")}`);
|
|
|
66576
66690
|
const { summary, suggestion } = parseWorktreeError(err);
|
|
66577
66691
|
const worktreePromptId = session.worktreePromptPostId;
|
|
66578
66692
|
if (worktreePromptId) {
|
|
66579
|
-
await
|
|
66693
|
+
await updatePost(session, worktreePromptId, `❌ ${fmt.formatBold(summary)}: ${fmt.formatCode(branch)}`);
|
|
66580
66694
|
await removeReaction(session, worktreePromptId, "x");
|
|
66581
66695
|
}
|
|
66582
66696
|
if (options2.worktreeMode === "require") {
|
|
@@ -66753,8 +66867,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
|
|
|
66753
66867
|
}
|
|
66754
66868
|
// src/operations/events/handler.ts
|
|
66755
66869
|
init_logger();
|
|
66756
|
-
var
|
|
66757
|
-
var sessionLog4 = createSessionLog(
|
|
66870
|
+
var log23 = createLogger("events");
|
|
66871
|
+
var sessionLog4 = createSessionLog(log23);
|
|
66758
66872
|
function detectAndExecuteClaudeCommands(text, session, ctx) {
|
|
66759
66873
|
const parsed = parseClaudeCommand(text);
|
|
66760
66874
|
if (parsed && isClaudeAllowedCommand(parsed.command)) {
|
|
@@ -66887,7 +67001,7 @@ async function handleCompactionComplete(session, compactMetadata, _ctx) {
|
|
|
66887
67001
|
const formatter = session.platform.getFormatter();
|
|
66888
67002
|
const completionMessage = `✅ ${formatter.formatBold("Context compacted")} ${formatter.formatItalic(`(${info})`)}`;
|
|
66889
67003
|
if (session.compactionPostId) {
|
|
66890
|
-
await
|
|
67004
|
+
await updatePost(session, session.compactionPostId, completionMessage);
|
|
66891
67005
|
session.compactionPostId = undefined;
|
|
66892
67006
|
} else {
|
|
66893
67007
|
await withErrorHandling(() => post(session, "info", completionMessage), { action: "Post compaction complete", session });
|
|
@@ -67002,8 +67116,8 @@ function createSessionContext(config, state, ops) {
|
|
|
67002
67116
|
// src/operations/context-prompt/handler.ts
|
|
67003
67117
|
init_emoji();
|
|
67004
67118
|
init_logger();
|
|
67005
|
-
var
|
|
67006
|
-
var sessionLog5 = createSessionLog(
|
|
67119
|
+
var log24 = createLogger("context");
|
|
67120
|
+
var sessionLog5 = createSessionLog(log24);
|
|
67007
67121
|
var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
|
|
67008
67122
|
var CONTEXT_OPTIONS = [3, 5, 10];
|
|
67009
67123
|
var contextPromptTimeouts = new Map;
|
|
@@ -67253,8 +67367,8 @@ function formatRelativeTime(date) {
|
|
|
67253
67367
|
}
|
|
67254
67368
|
// src/session/lifecycle.ts
|
|
67255
67369
|
init_worktree();
|
|
67256
|
-
var
|
|
67257
|
-
var sessionLog6 = createSessionLog(
|
|
67370
|
+
var log25 = createLogger("lifecycle");
|
|
67371
|
+
var sessionLog6 = createSessionLog(log25);
|
|
67258
67372
|
function mutableSessions(ctx) {
|
|
67259
67373
|
return ctx.state.sessions;
|
|
67260
67374
|
}
|
|
@@ -67366,7 +67480,8 @@ function createMessageManager(session, ctx) {
|
|
|
67366
67480
|
},
|
|
67367
67481
|
emitSessionUpdate: (updates) => {
|
|
67368
67482
|
ctx.ops.emitSessionUpdate(session.sessionId, updates);
|
|
67369
|
-
}
|
|
67483
|
+
},
|
|
67484
|
+
flushDelayMs: ctx.config.flushDelayMs
|
|
67370
67485
|
});
|
|
67371
67486
|
messageManager.events.on("question:complete", ({ toolUseId: _toolUseId, answers }) => {
|
|
67372
67487
|
const answerJson = JSON.stringify(answers);
|
|
@@ -67618,8 +67733,9 @@ async function startSession(options2, username, displayName, replyToPostId, plat
|
|
|
67618
67733
|
platform.sendTyping(actualThreadId);
|
|
67619
67734
|
const claudeSessionId = randomUUID4();
|
|
67620
67735
|
let workingDir = ctx.config.workingDir;
|
|
67621
|
-
let
|
|
67736
|
+
let permissionMode = ctx.config.permissionMode;
|
|
67622
67737
|
let forceInteractivePermissions = false;
|
|
67738
|
+
let sessionPermissionModeOverride;
|
|
67623
67739
|
const formatter = platform.getFormatter();
|
|
67624
67740
|
if (initialOptions?.workingDir) {
|
|
67625
67741
|
const { resolve: resolve6 } = await import("path");
|
|
@@ -67637,12 +67753,17 @@ async function startSession(options2, username, displayName, replyToPostId, plat
|
|
|
67637
67753
|
return;
|
|
67638
67754
|
}
|
|
67639
67755
|
workingDir = resolvedDir;
|
|
67640
|
-
|
|
67641
|
-
}
|
|
67642
|
-
if (initialOptions?.
|
|
67756
|
+
log25.info(`Starting session in directory: ${workingDir} (from !cd command)`);
|
|
67757
|
+
}
|
|
67758
|
+
if (initialOptions?.permissionMode) {
|
|
67759
|
+
permissionMode = initialOptions.permissionMode;
|
|
67760
|
+
forceInteractivePermissions = permissionMode === "default";
|
|
67761
|
+
sessionPermissionModeOverride = permissionMode;
|
|
67762
|
+
log25.info(`Starting session with permission mode "${permissionMode}" (from !permissions command)`);
|
|
67763
|
+
} else if (initialOptions?.forceInteractivePermissions) {
|
|
67643
67764
|
forceInteractivePermissions = true;
|
|
67644
|
-
|
|
67645
|
-
|
|
67765
|
+
permissionMode = "default";
|
|
67766
|
+
log25.info(`Starting session with interactive permissions (from !permissions command)`);
|
|
67646
67767
|
}
|
|
67647
67768
|
const sessionContext = buildSessionContext(platform, workingDir);
|
|
67648
67769
|
const systemPrompt = `${sessionContext}
|
|
@@ -67651,12 +67772,12 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67651
67772
|
const platformMcpConfig = platform.getMcpConfig();
|
|
67652
67773
|
const claudeAccount = ctx.ops.acquireClaudeAccount();
|
|
67653
67774
|
if (claudeAccount) {
|
|
67654
|
-
|
|
67775
|
+
log25.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
|
|
67655
67776
|
}
|
|
67656
67777
|
const cliOptions = {
|
|
67657
67778
|
workingDir,
|
|
67658
67779
|
threadId: actualThreadId,
|
|
67659
|
-
|
|
67780
|
+
permissionMode,
|
|
67660
67781
|
sessionId: claudeSessionId,
|
|
67661
67782
|
resume: false,
|
|
67662
67783
|
chrome: ctx.config.chromeEnabled,
|
|
@@ -67684,6 +67805,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67684
67805
|
planApproved: false,
|
|
67685
67806
|
sessionAllowedUsers: new Set([username]),
|
|
67686
67807
|
forceInteractivePermissions,
|
|
67808
|
+
permissionModeOverride: sessionPermissionModeOverride,
|
|
67687
67809
|
sessionStartPostId: startPost.id,
|
|
67688
67810
|
timers: createSessionTimers(),
|
|
67689
67811
|
lifecycle: createSessionLifecycle(),
|
|
@@ -67755,28 +67877,28 @@ async function resumeSession(state, ctx) {
|
|
|
67755
67877
|
!state.claudeSessionId && "claudeSessionId",
|
|
67756
67878
|
!state.workingDir && "workingDir"
|
|
67757
67879
|
].filter(Boolean).join(", ");
|
|
67758
|
-
|
|
67880
|
+
log25.warn(`Skipping session with missing required fields: ${missing}`);
|
|
67759
67881
|
return;
|
|
67760
67882
|
}
|
|
67761
67883
|
const shortId = state.threadId.substring(0, 8);
|
|
67762
67884
|
const platforms = ctx.state.platforms;
|
|
67763
67885
|
const platform = platforms.get(state.platformId);
|
|
67764
67886
|
if (!platform) {
|
|
67765
|
-
|
|
67887
|
+
log25.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
|
|
67766
67888
|
return;
|
|
67767
67889
|
}
|
|
67768
67890
|
const threadPost = await platform.getPost(state.threadId);
|
|
67769
67891
|
if (!threadPost) {
|
|
67770
|
-
|
|
67892
|
+
log25.warn(`Thread ${shortId}... deleted, skipping resume`);
|
|
67771
67893
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67772
67894
|
return;
|
|
67773
67895
|
}
|
|
67774
67896
|
if (ctx.state.sessions.size >= ctx.config.maxSessions) {
|
|
67775
|
-
|
|
67897
|
+
log25.warn(`Max sessions reached, skipping resume for ${shortId}...`);
|
|
67776
67898
|
return;
|
|
67777
67899
|
}
|
|
67778
67900
|
if (!existsSync11(state.workingDir)) {
|
|
67779
|
-
|
|
67901
|
+
log25.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
|
|
67780
67902
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67781
67903
|
const resumeFormatter = platform.getFormatter();
|
|
67782
67904
|
const tempSession = {
|
|
@@ -67792,7 +67914,7 @@ Please start a new session.`), { action: "Post resume failure notification" });
|
|
|
67792
67914
|
}
|
|
67793
67915
|
const platformId = state.platformId;
|
|
67794
67916
|
const sessionId = ctx.ops.getSessionId(platformId, state.threadId);
|
|
67795
|
-
const
|
|
67917
|
+
const resumePermissionMode = state.forceInteractivePermissions ? "default" : ctx.config.permissionMode;
|
|
67796
67918
|
const platformMcpConfig = platform.getMcpConfig();
|
|
67797
67919
|
const sessionContext = buildSessionContext(platform, state.workingDir);
|
|
67798
67920
|
const appendSystemPrompt = `${sessionContext}
|
|
@@ -67800,12 +67922,12 @@ Please start a new session.`), { action: "Post resume failure notification" });
|
|
|
67800
67922
|
${CHAT_PLATFORM_PROMPT}`;
|
|
67801
67923
|
const claudeAccount = ctx.ops.acquireClaudeAccount(state.claudeAccountId);
|
|
67802
67924
|
if (state.claudeAccountId && !claudeAccount) {
|
|
67803
|
-
|
|
67925
|
+
log25.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
|
|
67804
67926
|
}
|
|
67805
67927
|
const cliOptions = {
|
|
67806
67928
|
workingDir: state.workingDir,
|
|
67807
67929
|
threadId: state.threadId,
|
|
67808
|
-
|
|
67930
|
+
permissionMode: resumePermissionMode,
|
|
67809
67931
|
sessionId: state.claudeSessionId,
|
|
67810
67932
|
resume: true,
|
|
67811
67933
|
chrome: ctx.config.chromeEnabled,
|
|
@@ -67865,7 +67987,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67865
67987
|
worktreePath: detected.worktreePath,
|
|
67866
67988
|
branch: detected.branch
|
|
67867
67989
|
};
|
|
67868
|
-
|
|
67990
|
+
log25.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
|
|
67869
67991
|
}
|
|
67870
67992
|
}
|
|
67871
67993
|
session.messageManager = createMessageManager(session, ctx);
|
|
@@ -67921,7 +68043,7 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
|
|
|
67921
68043
|
await ctx.ops.updateStickyMessage();
|
|
67922
68044
|
ctx.ops.persistSession(session);
|
|
67923
68045
|
} catch (err) {
|
|
67924
|
-
|
|
68046
|
+
log25.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
|
|
67925
68047
|
ctx.ops.emitSessionRemove(sessionId);
|
|
67926
68048
|
mutableSessions(ctx).delete(sessionId);
|
|
67927
68049
|
ctx.state.sessionStore.remove(sessionId);
|
|
@@ -67961,18 +68083,18 @@ async function resumePausedSession(threadId, message, files, ctx) {
|
|
|
67961
68083
|
const persisted = ctx.state.sessionStore.load();
|
|
67962
68084
|
const state = findPersistedByThreadId(persisted, threadId);
|
|
67963
68085
|
if (!state) {
|
|
67964
|
-
|
|
68086
|
+
log25.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
|
|
67965
68087
|
return;
|
|
67966
68088
|
}
|
|
67967
68089
|
const shortId = threadId.substring(0, 8);
|
|
67968
|
-
|
|
68090
|
+
log25.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
|
|
67969
68091
|
await resumeSession(state, ctx);
|
|
67970
68092
|
const session = ctx.ops.findSessionByThreadId(threadId);
|
|
67971
68093
|
if (session && session.claude.isRunning() && session.messageManager) {
|
|
67972
68094
|
session.messageCount++;
|
|
67973
68095
|
await session.messageManager.handleUserMessage(message, files, state.startedBy);
|
|
67974
68096
|
} else {
|
|
67975
|
-
|
|
68097
|
+
log25.warn(`Failed to resume session ${shortId}..., could not send message`);
|
|
67976
68098
|
}
|
|
67977
68099
|
}
|
|
67978
68100
|
async function handleExit(sessionId, code, ctx) {
|
|
@@ -67980,7 +68102,7 @@ async function handleExit(sessionId, code, ctx) {
|
|
|
67980
68102
|
const shortId = sessionId.substring(0, 8);
|
|
67981
68103
|
sessionLog6(session).debug(`handleExit called code=${code} isShuttingDown=${ctx.state.isShuttingDown}`);
|
|
67982
68104
|
if (!session) {
|
|
67983
|
-
|
|
68105
|
+
log25.debug(`Session ${shortId}... not found (already cleaned up)`);
|
|
67984
68106
|
return;
|
|
67985
68107
|
}
|
|
67986
68108
|
if (isSessionRestarting(session)) {
|
|
@@ -68173,7 +68295,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
|
|
|
68173
68295
|
}
|
|
68174
68296
|
|
|
68175
68297
|
// src/operations/monitor/handler.ts
|
|
68176
|
-
var
|
|
68298
|
+
var log26 = createLogger("monitor");
|
|
68177
68299
|
var DEFAULT_INTERVAL_MS = 60 * 1000;
|
|
68178
68300
|
|
|
68179
68301
|
class SessionMonitor {
|
|
@@ -68195,14 +68317,14 @@ class SessionMonitor {
|
|
|
68195
68317
|
}
|
|
68196
68318
|
start() {
|
|
68197
68319
|
if (this.isRunning) {
|
|
68198
|
-
|
|
68320
|
+
log26.debug("Session monitor already running");
|
|
68199
68321
|
return;
|
|
68200
68322
|
}
|
|
68201
68323
|
this.isRunning = true;
|
|
68202
|
-
|
|
68324
|
+
log26.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
|
|
68203
68325
|
this.timer = setInterval(() => {
|
|
68204
68326
|
this.runCheck().catch((err) => {
|
|
68205
|
-
|
|
68327
|
+
log26.error(`Error during session monitoring: ${err}`);
|
|
68206
68328
|
});
|
|
68207
68329
|
}, this.intervalMs);
|
|
68208
68330
|
}
|
|
@@ -68212,7 +68334,7 @@ class SessionMonitor {
|
|
|
68212
68334
|
this.timer = null;
|
|
68213
68335
|
}
|
|
68214
68336
|
this.isRunning = false;
|
|
68215
|
-
|
|
68337
|
+
log26.debug("Session monitor stopped");
|
|
68216
68338
|
}
|
|
68217
68339
|
async runCheck() {
|
|
68218
68340
|
await cleanupIdleSessions(this.sessionTimeoutMs, this.sessionWarningMs, this.getContext());
|
|
@@ -68224,8 +68346,8 @@ class SessionMonitor {
|
|
|
68224
68346
|
// src/operations/plugin/handler.ts
|
|
68225
68347
|
init_spawn();
|
|
68226
68348
|
init_logger();
|
|
68227
|
-
var
|
|
68228
|
-
var sessionLog7 = createSessionLog(
|
|
68349
|
+
var log27 = createLogger("plugin");
|
|
68350
|
+
var sessionLog7 = createSessionLog(log27);
|
|
68229
68351
|
async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
68230
68352
|
return new Promise((resolve6) => {
|
|
68231
68353
|
const claudePath = process.env.CLAUDE_PATH || "claude";
|
|
@@ -68246,7 +68368,7 @@ async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
|
68246
68368
|
});
|
|
68247
68369
|
proc.on("error", (err) => {
|
|
68248
68370
|
resolve6({ stdout, stderr, exitCode: 1 });
|
|
68249
|
-
|
|
68371
|
+
log27.error(`Plugin command error: ${err.message}`);
|
|
68250
68372
|
});
|
|
68251
68373
|
});
|
|
68252
68374
|
}
|
|
@@ -68282,9 +68404,13 @@ ${formatter.formatCodeBlock(errorMsg, "text")}`);
|
|
|
68282
68404
|
const cliOptions = {
|
|
68283
68405
|
workingDir: session.workingDir,
|
|
68284
68406
|
threadId: session.threadId,
|
|
68285
|
-
|
|
68407
|
+
permissionMode: effectivePermissionMode({
|
|
68408
|
+
override: session.permissionModeOverride,
|
|
68409
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
68410
|
+
botWideMode: ctx.config.permissionMode
|
|
68411
|
+
}),
|
|
68286
68412
|
sessionId: session.claudeSessionId,
|
|
68287
|
-
resume:
|
|
68413
|
+
resume: session.lifecycle.hasClaudeResponded,
|
|
68288
68414
|
chrome: ctx.config.chromeEnabled,
|
|
68289
68415
|
platformConfig: session.platform.getMcpConfig(),
|
|
68290
68416
|
logSessionId: session.sessionId,
|
|
@@ -68315,9 +68441,13 @@ ${formatter.formatCodeBlock(errorMsg, "text")}`);
|
|
|
68315
68441
|
const cliOptions = {
|
|
68316
68442
|
workingDir: session.workingDir,
|
|
68317
68443
|
threadId: session.threadId,
|
|
68318
|
-
|
|
68444
|
+
permissionMode: effectivePermissionMode({
|
|
68445
|
+
override: session.permissionModeOverride,
|
|
68446
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
68447
|
+
botWideMode: ctx.config.permissionMode
|
|
68448
|
+
}),
|
|
68319
68449
|
sessionId: session.claudeSessionId,
|
|
68320
|
-
resume:
|
|
68450
|
+
resume: session.lifecycle.hasClaudeResponded,
|
|
68321
68451
|
chrome: ctx.config.chromeEnabled,
|
|
68322
68452
|
platformConfig: session.platform.getMcpConfig(),
|
|
68323
68453
|
logSessionId: session.sessionId,
|
|
@@ -68439,12 +68569,12 @@ class SessionRegistry {
|
|
|
68439
68569
|
|
|
68440
68570
|
// src/session/manager.ts
|
|
68441
68571
|
init_logger();
|
|
68442
|
-
var
|
|
68572
|
+
var log28 = createLogger("manager");
|
|
68443
68573
|
|
|
68444
68574
|
class SessionManager extends EventEmitter4 {
|
|
68445
68575
|
platforms = new Map;
|
|
68446
68576
|
workingDir;
|
|
68447
|
-
|
|
68577
|
+
permissionMode;
|
|
68448
68578
|
chromeEnabled;
|
|
68449
68579
|
worktreeMode;
|
|
68450
68580
|
threadLogsEnabled;
|
|
@@ -68463,10 +68593,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68463
68593
|
customFooter;
|
|
68464
68594
|
autoUpdateManager = null;
|
|
68465
68595
|
accountPool;
|
|
68466
|
-
constructor(workingDir,
|
|
68596
|
+
constructor(workingDir, permissionModeOrSkipFlag = "default", chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts) {
|
|
68467
68597
|
super();
|
|
68468
68598
|
this.workingDir = workingDir;
|
|
68469
|
-
this.
|
|
68599
|
+
this.permissionMode = typeof permissionModeOrSkipFlag === "boolean" ? permissionModeOrSkipFlag ? "bypass" : "default" : permissionModeOrSkipFlag;
|
|
68470
68600
|
this.chromeEnabled = chromeEnabled;
|
|
68471
68601
|
this.worktreeMode = worktreeMode;
|
|
68472
68602
|
this.threadLogsEnabled = threadLogsEnabled;
|
|
@@ -68508,7 +68638,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68508
68638
|
markNeedsBump(platformId);
|
|
68509
68639
|
this.updateStickyMessage();
|
|
68510
68640
|
});
|
|
68511
|
-
|
|
68641
|
+
log28.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
|
|
68512
68642
|
}
|
|
68513
68643
|
removePlatform(platformId) {
|
|
68514
68644
|
this.platforms.delete(platformId);
|
|
@@ -68524,7 +68654,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68524
68654
|
if (users) {
|
|
68525
68655
|
users.add(sessionId);
|
|
68526
68656
|
}
|
|
68527
|
-
|
|
68657
|
+
log28.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
|
|
68528
68658
|
}
|
|
68529
68659
|
unregisterWorktreeUser(worktreePath, sessionId) {
|
|
68530
68660
|
const users = this.worktreeUsers.get(worktreePath);
|
|
@@ -68544,13 +68674,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68544
68674
|
getContext() {
|
|
68545
68675
|
const config = {
|
|
68546
68676
|
workingDir: this.workingDir,
|
|
68547
|
-
|
|
68677
|
+
permissionMode: this.permissionMode,
|
|
68548
68678
|
chromeEnabled: this.chromeEnabled,
|
|
68549
68679
|
debug: this.debug,
|
|
68550
68680
|
maxSessions: this.limits.maxSessions,
|
|
68551
68681
|
threadLogsEnabled: this.threadLogsEnabled,
|
|
68552
68682
|
threadLogsRetentionDays: this.threadLogsRetentionDays,
|
|
68553
|
-
permissionTimeoutMs: this.limits.permissionTimeoutSeconds * 1000
|
|
68683
|
+
permissionTimeoutMs: this.limits.permissionTimeoutSeconds * 1000,
|
|
68684
|
+
flushDelayMs: this.limits.flushDelayMs
|
|
68554
68685
|
};
|
|
68555
68686
|
const state = {
|
|
68556
68687
|
sessions: this.registry.getSessions(),
|
|
@@ -68649,6 +68780,15 @@ class SessionManager extends EventEmitter4 {
|
|
|
68649
68780
|
if (session.platformId !== platformId)
|
|
68650
68781
|
return;
|
|
68651
68782
|
if (!session.sessionAllowedUsers.has(username) && !session.platform.isUserAllowed(username)) {
|
|
68783
|
+
log28.info(`\uD83D\uDEAB rejected reaction from unauthorized user`, {
|
|
68784
|
+
event: "reaction.rejected",
|
|
68785
|
+
platformId,
|
|
68786
|
+
sessionId: session.sessionId,
|
|
68787
|
+
postId,
|
|
68788
|
+
emoji: normalizedEmoji,
|
|
68789
|
+
action,
|
|
68790
|
+
user: username
|
|
68791
|
+
});
|
|
68652
68792
|
return;
|
|
68653
68793
|
}
|
|
68654
68794
|
await this.handleSessionReaction(session, postId, normalizedEmoji, username, action);
|
|
@@ -68676,7 +68816,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68676
68816
|
return false;
|
|
68677
68817
|
}
|
|
68678
68818
|
const shortId = persistedSession.threadId.substring(0, 8);
|
|
68679
|
-
|
|
68819
|
+
log28.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
|
|
68680
68820
|
await resumeSession(persistedSession, this.getContext());
|
|
68681
68821
|
return true;
|
|
68682
68822
|
}
|
|
@@ -68706,7 +68846,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68706
68846
|
}
|
|
68707
68847
|
if (session.lastError?.postId === postId && isBugReportEmoji(emojiName)) {
|
|
68708
68848
|
if (session.startedBy === username || session.platform.isUserAllowed(username) || session.sessionAllowedUsers.has(username)) {
|
|
68709
|
-
|
|
68849
|
+
log28.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
|
|
68710
68850
|
await reportBug(session, undefined, username, this.getContext(), session.lastError);
|
|
68711
68851
|
return;
|
|
68712
68852
|
}
|
|
@@ -68775,19 +68915,29 @@ class SessionManager extends EventEmitter4 {
|
|
|
68775
68915
|
this.stopTyping(session);
|
|
68776
68916
|
}
|
|
68777
68917
|
persistSession(session) {
|
|
68778
|
-
|
|
68779
|
-
|
|
68780
|
-
|
|
68781
|
-
|
|
68782
|
-
|
|
68783
|
-
|
|
68784
|
-
|
|
68785
|
-
|
|
68786
|
-
|
|
68787
|
-
|
|
68788
|
-
|
|
68918
|
+
const useSerializeV2 = process.env.CLAUDE_THREADS_SERIALIZE_V2 !== "0";
|
|
68919
|
+
let taskListSnapshot;
|
|
68920
|
+
let contextPromptSnapshot;
|
|
68921
|
+
if (useSerializeV2 && session.messageManager) {
|
|
68922
|
+
const serialized = session.messageManager.serialize();
|
|
68923
|
+
taskListSnapshot = serialized.taskList;
|
|
68924
|
+
if (serialized.contextPrompt) {
|
|
68925
|
+
contextPromptSnapshot = serialized.contextPrompt;
|
|
68926
|
+
}
|
|
68927
|
+
} else {
|
|
68928
|
+
const legacyPrompt = session.messageManager?.getPendingContextPrompt();
|
|
68929
|
+
if (legacyPrompt) {
|
|
68930
|
+
contextPromptSnapshot = {
|
|
68931
|
+
postId: legacyPrompt.postId,
|
|
68932
|
+
queuedPrompt: legacyPrompt.queuedPrompt,
|
|
68933
|
+
queuedFiles: legacyPrompt.queuedFiles,
|
|
68934
|
+
threadMessageCount: legacyPrompt.threadMessageCount,
|
|
68935
|
+
createdAt: legacyPrompt.createdAt,
|
|
68936
|
+
availableOptions: legacyPrompt.availableOptions
|
|
68937
|
+
};
|
|
68938
|
+
}
|
|
68939
|
+
taskListSnapshot = session.messageManager?.getTaskListState();
|
|
68789
68940
|
}
|
|
68790
|
-
const taskState = session.messageManager?.getTaskListState();
|
|
68791
68941
|
const state = {
|
|
68792
68942
|
platformId: session.platformId,
|
|
68793
68943
|
threadId: session.threadId,
|
|
@@ -68802,10 +68952,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68802
68952
|
sessionAllowedUsers: [...session.sessionAllowedUsers],
|
|
68803
68953
|
forceInteractivePermissions: session.forceInteractivePermissions,
|
|
68804
68954
|
sessionStartPostId: session.sessionStartPostId,
|
|
68805
|
-
tasksPostId:
|
|
68806
|
-
lastTasksContent:
|
|
68807
|
-
tasksCompleted:
|
|
68808
|
-
tasksMinimized:
|
|
68955
|
+
tasksPostId: taskListSnapshot?.postId ?? null,
|
|
68956
|
+
lastTasksContent: taskListSnapshot?.content ?? null,
|
|
68957
|
+
tasksCompleted: taskListSnapshot?.isCompleted ?? false,
|
|
68958
|
+
tasksMinimized: taskListSnapshot?.isMinimized ?? false,
|
|
68809
68959
|
worktreeInfo: session.worktreeInfo,
|
|
68810
68960
|
isWorktreeOwner: session.isWorktreeOwner,
|
|
68811
68961
|
pendingWorktreePrompt: session.pendingWorktreePrompt,
|
|
@@ -68813,7 +68963,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68813
68963
|
queuedPrompt: session.queuedPrompt,
|
|
68814
68964
|
queuedFiles: session.queuedFiles,
|
|
68815
68965
|
firstPrompt: session.firstPrompt,
|
|
68816
|
-
pendingContextPrompt:
|
|
68966
|
+
pendingContextPrompt: contextPromptSnapshot,
|
|
68817
68967
|
needsContextPromptOnNextMessage: session.needsContextPromptOnNextMessage,
|
|
68818
68968
|
lifecyclePostId: session.lifecyclePostId,
|
|
68819
68969
|
isPaused: session.lifecycle.state === "paused" || session.lifecycle.state === "interrupted",
|
|
@@ -68843,7 +68993,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68843
68993
|
await updateAllStickyMessages(this.platforms, this.registry.getSessions(), {
|
|
68844
68994
|
maxSessions: this.limits.maxSessions,
|
|
68845
68995
|
chromeEnabled: this.chromeEnabled,
|
|
68846
|
-
|
|
68996
|
+
permissionMode: this.permissionMode,
|
|
68847
68997
|
worktreeMode: this.worktreeMode,
|
|
68848
68998
|
workingDir: this.workingDir,
|
|
68849
68999
|
debug: this.debug,
|
|
@@ -68859,8 +69009,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68859
69009
|
this.customDescription = description;
|
|
68860
69010
|
this.customFooter = footer;
|
|
68861
69011
|
}
|
|
69012
|
+
setPermissionMode(mode) {
|
|
69013
|
+
this.permissionMode = mode;
|
|
69014
|
+
}
|
|
68862
69015
|
setSkipPermissions(value) {
|
|
68863
|
-
this.
|
|
69016
|
+
this.permissionMode = value ? "bypass" : "default";
|
|
69017
|
+
}
|
|
69018
|
+
getPermissionMode() {
|
|
69019
|
+
return this.permissionMode;
|
|
68864
69020
|
}
|
|
68865
69021
|
setChromeEnabled(value) {
|
|
68866
69022
|
this.chromeEnabled = value;
|
|
@@ -68874,11 +69030,11 @@ class SessionManager extends EventEmitter4 {
|
|
|
68874
69030
|
}
|
|
68875
69031
|
}
|
|
68876
69032
|
if (sessionsToKill.length === 0) {
|
|
68877
|
-
|
|
69033
|
+
log28.info(`No active sessions to pause for platform ${platformId}`);
|
|
68878
69034
|
await this.updateStickyMessage();
|
|
68879
69035
|
return;
|
|
68880
69036
|
}
|
|
68881
|
-
|
|
69037
|
+
log28.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
|
|
68882
69038
|
for (const session of sessionsToKill) {
|
|
68883
69039
|
try {
|
|
68884
69040
|
const fmt = session.platform.getFormatter();
|
|
@@ -68894,9 +69050,9 @@ class SessionManager extends EventEmitter4 {
|
|
|
68894
69050
|
session.claude.kill();
|
|
68895
69051
|
this.registry.unregister(session.sessionId);
|
|
68896
69052
|
this.emitSessionRemove(session.sessionId);
|
|
68897
|
-
|
|
69053
|
+
log28.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
|
|
68898
69054
|
} catch (err) {
|
|
68899
|
-
|
|
69055
|
+
log28.warn(`Failed to pause session ${session.threadId}: ${err}`);
|
|
68900
69056
|
}
|
|
68901
69057
|
}
|
|
68902
69058
|
for (const session of sessionsToKill) {
|
|
@@ -68917,17 +69073,17 @@ class SessionManager extends EventEmitter4 {
|
|
|
68917
69073
|
sessionsToResume.push(state);
|
|
68918
69074
|
}
|
|
68919
69075
|
if (sessionsToResume.length === 0) {
|
|
68920
|
-
|
|
69076
|
+
log28.info(`No paused sessions to resume for platform ${platformId}`);
|
|
68921
69077
|
await this.updateStickyMessage();
|
|
68922
69078
|
return;
|
|
68923
69079
|
}
|
|
68924
|
-
|
|
69080
|
+
log28.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
|
|
68925
69081
|
for (const state of sessionsToResume) {
|
|
68926
69082
|
try {
|
|
68927
69083
|
await resumeSession(state, this.getContext());
|
|
68928
|
-
|
|
69084
|
+
log28.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
|
|
68929
69085
|
} catch (err) {
|
|
68930
|
-
|
|
69086
|
+
log28.warn(`Failed to resume session ${state.threadId}: ${err}`);
|
|
68931
69087
|
}
|
|
68932
69088
|
}
|
|
68933
69089
|
await this.updateStickyMessage();
|
|
@@ -68939,14 +69095,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68939
69095
|
const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
|
|
68940
69096
|
const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
|
|
68941
69097
|
if (staleIds.length > 0) {
|
|
68942
|
-
|
|
69098
|
+
log28.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
|
|
68943
69099
|
}
|
|
68944
69100
|
const removedCount = this.sessionStore.cleanHistory();
|
|
68945
69101
|
if (removedCount > 0) {
|
|
68946
|
-
|
|
69102
|
+
log28.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
|
|
68947
69103
|
}
|
|
68948
69104
|
const persisted = this.sessionStore.load();
|
|
68949
|
-
|
|
69105
|
+
log28.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
|
|
68950
69106
|
const excludePostIdsByPlatform = new Map;
|
|
68951
69107
|
for (const session of persisted.values()) {
|
|
68952
69108
|
const platformId = session.platformId;
|
|
@@ -68966,10 +69122,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68966
69122
|
const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
|
|
68967
69123
|
platform.getBotUser().then((botUser) => {
|
|
68968
69124
|
cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
|
|
68969
|
-
|
|
69125
|
+
log28.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
|
|
68970
69126
|
});
|
|
68971
69127
|
}).catch((err) => {
|
|
68972
|
-
|
|
69128
|
+
log28.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
|
|
68973
69129
|
});
|
|
68974
69130
|
}
|
|
68975
69131
|
if (persisted.size > 0) {
|
|
@@ -68983,10 +69139,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68983
69139
|
}
|
|
68984
69140
|
}
|
|
68985
69141
|
if (pausedToSkip.length > 0) {
|
|
68986
|
-
|
|
69142
|
+
log28.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
|
|
68987
69143
|
}
|
|
68988
69144
|
if (activeToResume.length > 0) {
|
|
68989
|
-
|
|
69145
|
+
log28.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
|
|
68990
69146
|
for (const state of activeToResume) {
|
|
68991
69147
|
await resumeSession(state, this.getContext());
|
|
68992
69148
|
}
|
|
@@ -69081,11 +69237,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
69081
69237
|
return;
|
|
69082
69238
|
await kickUser(session, kickedUser, kickedBy, this.getContext());
|
|
69083
69239
|
}
|
|
69084
|
-
async
|
|
69240
|
+
async setSessionPermissionMode(threadId, username, mode) {
|
|
69085
69241
|
const session = this.findSessionByThreadId(threadId);
|
|
69086
69242
|
if (!session)
|
|
69087
69243
|
return;
|
|
69088
|
-
await
|
|
69244
|
+
await setSessionPermissionMode(session, username, mode, this.getContext());
|
|
69245
|
+
}
|
|
69246
|
+
async enableInteractivePermissions(threadId, username) {
|
|
69247
|
+
await this.setSessionPermissionMode(threadId, username, "default");
|
|
69089
69248
|
}
|
|
69090
69249
|
async reportBug(threadId, description, username, files) {
|
|
69091
69250
|
const session = this.findSessionByThreadId(threadId);
|
|
@@ -69168,10 +69327,13 @@ class SessionManager extends EventEmitter4 {
|
|
|
69168
69327
|
isSessionInteractive(threadId) {
|
|
69169
69328
|
const session = this.findSessionByThreadId(threadId);
|
|
69170
69329
|
if (!session)
|
|
69171
|
-
return
|
|
69172
|
-
|
|
69173
|
-
|
|
69174
|
-
|
|
69330
|
+
return this.permissionMode !== "bypass";
|
|
69331
|
+
const effective = effectivePermissionMode({
|
|
69332
|
+
override: session.permissionModeOverride,
|
|
69333
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
69334
|
+
botWideMode: this.permissionMode
|
|
69335
|
+
});
|
|
69336
|
+
return effective !== "bypass";
|
|
69175
69337
|
}
|
|
69176
69338
|
async requestMessageApproval(threadId, username, message) {
|
|
69177
69339
|
const session = this.findSessionByThreadId(threadId);
|
|
@@ -69196,7 +69358,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
69196
69358
|
if (!session)
|
|
69197
69359
|
return;
|
|
69198
69360
|
await createAndSwitchToWorktree(session, branch, username, {
|
|
69199
|
-
|
|
69361
|
+
permissionMode: this.permissionMode,
|
|
69200
69362
|
chromeEnabled: this.chromeEnabled,
|
|
69201
69363
|
worktreeMode: this.worktreeMode,
|
|
69202
69364
|
permissionTimeoutMs: this.limits.permissionTimeoutSeconds * 1000,
|
|
@@ -69405,7 +69567,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69405
69567
|
const message = messageBuilder(formatter);
|
|
69406
69568
|
await post(session, "info", message);
|
|
69407
69569
|
} catch (err) {
|
|
69408
|
-
|
|
69570
|
+
log28.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
|
|
69409
69571
|
}
|
|
69410
69572
|
}
|
|
69411
69573
|
}
|
|
@@ -69424,7 +69586,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69424
69586
|
session.messageManager?.setPendingUpdatePrompt({ postId: post2.id });
|
|
69425
69587
|
this.registerPost(post2.id, session.threadId);
|
|
69426
69588
|
} catch (err) {
|
|
69427
|
-
|
|
69589
|
+
log28.warn(`Failed to post ask message to ${threadId}: ${err}`);
|
|
69428
69590
|
}
|
|
69429
69591
|
}
|
|
69430
69592
|
}
|
|
@@ -77020,29 +77182,29 @@ function SessionLog({ logs, maxLines = 20 }) {
|
|
|
77020
77182
|
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
77021
77183
|
flexDirection: "column",
|
|
77022
77184
|
flexShrink: 0,
|
|
77023
|
-
children: displayLogs.map((
|
|
77185
|
+
children: displayLogs.map((log29) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
77024
77186
|
flexShrink: 0,
|
|
77025
77187
|
children: [
|
|
77026
77188
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
77027
|
-
color: getColorForLevel(
|
|
77189
|
+
color: getColorForLevel(log29.level),
|
|
77028
77190
|
dimColor: true,
|
|
77029
77191
|
wrap: "truncate",
|
|
77030
77192
|
children: [
|
|
77031
77193
|
"[",
|
|
77032
|
-
padComponent(
|
|
77194
|
+
padComponent(log29.component),
|
|
77033
77195
|
"]"
|
|
77034
77196
|
]
|
|
77035
77197
|
}, undefined, true, undefined, this),
|
|
77036
77198
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
77037
|
-
color: getColorForLevel(
|
|
77199
|
+
color: getColorForLevel(log29.level),
|
|
77038
77200
|
wrap: "truncate",
|
|
77039
77201
|
children: [
|
|
77040
77202
|
" ",
|
|
77041
|
-
|
|
77203
|
+
log29.message
|
|
77042
77204
|
]
|
|
77043
77205
|
}, undefined, true, undefined, this)
|
|
77044
77206
|
]
|
|
77045
|
-
},
|
|
77207
|
+
}, log29.id, true, undefined, this))
|
|
77046
77208
|
}, undefined, false, undefined, this);
|
|
77047
77209
|
}
|
|
77048
77210
|
// src/ui/components/Footer.tsx
|
|
@@ -77072,6 +77234,34 @@ function ToggleKey({ keyChar, label, enabled, color }) {
|
|
|
77072
77234
|
]
|
|
77073
77235
|
}, undefined, true, undefined, this);
|
|
77074
77236
|
}
|
|
77237
|
+
function PermissionModeKey({ mode }) {
|
|
77238
|
+
const color = mode === "default" ? "green" : mode === "auto" ? "yellow" : "red";
|
|
77239
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
77240
|
+
gap: 0,
|
|
77241
|
+
children: [
|
|
77242
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77243
|
+
dimColor: true,
|
|
77244
|
+
children: "["
|
|
77245
|
+
}, undefined, false, undefined, this),
|
|
77246
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77247
|
+
color,
|
|
77248
|
+
bold: true,
|
|
77249
|
+
children: "p"
|
|
77250
|
+
}, undefined, false, undefined, this),
|
|
77251
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77252
|
+
dimColor: true,
|
|
77253
|
+
children: "]"
|
|
77254
|
+
}, undefined, false, undefined, this),
|
|
77255
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77256
|
+
color,
|
|
77257
|
+
children: [
|
|
77258
|
+
"erms:",
|
|
77259
|
+
permissionModeDisplay(mode).label.toLowerCase()
|
|
77260
|
+
]
|
|
77261
|
+
}, undefined, true, undefined, this)
|
|
77262
|
+
]
|
|
77263
|
+
}, undefined, true, undefined, this);
|
|
77264
|
+
}
|
|
77075
77265
|
function PlatformToggle({ index, platform: platform2 }) {
|
|
77076
77266
|
let color;
|
|
77077
77267
|
if (!platform2.enabled) {
|
|
@@ -77172,10 +77362,8 @@ function Footer({
|
|
|
77172
77362
|
label: "ebug",
|
|
77173
77363
|
enabled: toggles.debugMode
|
|
77174
77364
|
}, undefined, false, undefined, this),
|
|
77175
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(
|
|
77176
|
-
|
|
77177
|
-
label: "erms",
|
|
77178
|
-
enabled: !toggles.skipPermissions
|
|
77365
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(PermissionModeKey, {
|
|
77366
|
+
mode: toggles.permissionMode
|
|
77179
77367
|
}, undefined, false, undefined, this),
|
|
77180
77368
|
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(ToggleKey, {
|
|
77181
77369
|
keyChar: "c",
|
|
@@ -77540,7 +77728,7 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77540
77728
|
const scrollRef = import_react59.default.useRef(null);
|
|
77541
77729
|
const { stdout } = use_stdout_default();
|
|
77542
77730
|
const isDebug = process.env.DEBUG === "1";
|
|
77543
|
-
const displayLogs = logs.filter((
|
|
77731
|
+
const displayLogs = logs.filter((log29) => isDebug || log29.level !== "debug");
|
|
77544
77732
|
const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
|
|
77545
77733
|
import_react59.default.useEffect(() => {
|
|
77546
77734
|
const handleResize = () => scrollRef.current?.remeasure();
|
|
@@ -77580,25 +77768,25 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77580
77768
|
overflow: "hidden",
|
|
77581
77769
|
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ScrollView, {
|
|
77582
77770
|
ref: scrollRef,
|
|
77583
|
-
children: visibleLogs.map((
|
|
77771
|
+
children: visibleLogs.map((log29) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
77584
77772
|
children: [
|
|
77585
77773
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77586
77774
|
dimColor: true,
|
|
77587
77775
|
children: [
|
|
77588
77776
|
"[",
|
|
77589
|
-
padComponent2(
|
|
77777
|
+
padComponent2(log29.component),
|
|
77590
77778
|
"]"
|
|
77591
77779
|
]
|
|
77592
77780
|
}, undefined, true, undefined, this),
|
|
77593
77781
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77594
|
-
color: getLevelColor(
|
|
77782
|
+
color: getLevelColor(log29.level),
|
|
77595
77783
|
children: [
|
|
77596
77784
|
" ",
|
|
77597
|
-
|
|
77785
|
+
log29.message
|
|
77598
77786
|
]
|
|
77599
77787
|
}, undefined, true, undefined, this)
|
|
77600
77788
|
]
|
|
77601
|
-
},
|
|
77789
|
+
}, log29.id, true, undefined, this))
|
|
77602
77790
|
}, undefined, false, undefined, this)
|
|
77603
77791
|
}, undefined, false, undefined, this);
|
|
77604
77792
|
}
|
|
@@ -78115,10 +78303,10 @@ function useAppState(initialConfig) {
|
|
|
78115
78303
|
});
|
|
78116
78304
|
}, []);
|
|
78117
78305
|
const getLogsForSession = import_react60.useCallback((sessionId) => {
|
|
78118
|
-
return state.logs.filter((
|
|
78306
|
+
return state.logs.filter((log29) => log29.sessionId === sessionId);
|
|
78119
78307
|
}, [state.logs]);
|
|
78120
78308
|
const getGlobalLogs = import_react60.useCallback(() => {
|
|
78121
|
-
return state.logs.filter((
|
|
78309
|
+
return state.logs.filter((log29) => !log29.sessionId);
|
|
78122
78310
|
}, [state.logs]);
|
|
78123
78311
|
const togglePlatformEnabled = import_react60.useCallback((platformId) => {
|
|
78124
78312
|
let newEnabled = false;
|
|
@@ -78239,6 +78427,19 @@ function useKeyboard({
|
|
|
78239
78427
|
});
|
|
78240
78428
|
}
|
|
78241
78429
|
|
|
78430
|
+
// src/config/permission-mode-cycle.ts
|
|
78431
|
+
var PERMISSION_MODE_CYCLE = [
|
|
78432
|
+
"default",
|
|
78433
|
+
"auto",
|
|
78434
|
+
"bypass"
|
|
78435
|
+
];
|
|
78436
|
+
function nextPermissionMode(current) {
|
|
78437
|
+
const idx = PERMISSION_MODE_CYCLE.indexOf(current);
|
|
78438
|
+
if (idx === -1)
|
|
78439
|
+
return PERMISSION_MODE_CYCLE[0];
|
|
78440
|
+
return PERMISSION_MODE_CYCLE[(idx + 1) % PERMISSION_MODE_CYCLE.length];
|
|
78441
|
+
}
|
|
78442
|
+
|
|
78242
78443
|
// src/ui/App.tsx
|
|
78243
78444
|
var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
|
|
78244
78445
|
function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks }) {
|
|
@@ -78261,7 +78462,7 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
|
|
|
78261
78462
|
const [resizeCount, setResizeCount] = import_react61.default.useState(0);
|
|
78262
78463
|
const [toggles, setToggles] = import_react61.default.useState({
|
|
78263
78464
|
debugMode: process.env.DEBUG === "1",
|
|
78264
|
-
|
|
78465
|
+
permissionMode: config.permissionMode,
|
|
78265
78466
|
chromeEnabled: config.chromeEnabled,
|
|
78266
78467
|
keepAliveEnabled: config.keepAliveEnabled,
|
|
78267
78468
|
updateModalVisible: false,
|
|
@@ -78282,9 +78483,9 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
|
|
|
78282
78483
|
}, [toggleCallbacks]);
|
|
78283
78484
|
const handlePermissionsToggle = import_react61.default.useCallback(() => {
|
|
78284
78485
|
setToggles((prev) => {
|
|
78285
|
-
const
|
|
78286
|
-
toggleCallbacks?.onPermissionsToggle?.(
|
|
78287
|
-
return { ...prev,
|
|
78486
|
+
const newMode = nextPermissionMode(prev.permissionMode);
|
|
78487
|
+
toggleCallbacks?.onPermissionsToggle?.(newMode);
|
|
78488
|
+
return { ...prev, permissionMode: newMode };
|
|
78288
78489
|
});
|
|
78289
78490
|
}, [toggleCallbacks]);
|
|
78290
78491
|
const handleChromeToggle = import_react61.default.useCallback(() => {
|
|
@@ -78749,7 +78950,7 @@ class HeadlessProvider {
|
|
|
78749
78950
|
this.options = options2;
|
|
78750
78951
|
this.toggles = {
|
|
78751
78952
|
debugMode: process.env.DEBUG === "1",
|
|
78752
|
-
|
|
78953
|
+
permissionMode: options2.config.permissionMode,
|
|
78753
78954
|
chromeEnabled: options2.config.chromeEnabled,
|
|
78754
78955
|
keepAliveEnabled: options2.config.keepAliveEnabled,
|
|
78755
78956
|
updateModalVisible: false,
|
|
@@ -79112,7 +79313,7 @@ import { EventEmitter as EventEmitter9 } from "events";
|
|
|
79112
79313
|
// src/auto-update/checker.ts
|
|
79113
79314
|
init_logger();
|
|
79114
79315
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
79115
|
-
var
|
|
79316
|
+
var log29 = createLogger("checker");
|
|
79116
79317
|
var PACKAGE_NAME = "claude-threads";
|
|
79117
79318
|
function compareVersions(a, b) {
|
|
79118
79319
|
const partsA = a.replace(/^v/, "").split(".").map(Number);
|
|
@@ -79135,13 +79336,13 @@ async function fetchLatestVersion() {
|
|
|
79135
79336
|
}
|
|
79136
79337
|
});
|
|
79137
79338
|
if (!response.ok) {
|
|
79138
|
-
|
|
79339
|
+
log29.warn(`Failed to fetch latest version: HTTP ${response.status}`);
|
|
79139
79340
|
return null;
|
|
79140
79341
|
}
|
|
79141
79342
|
const data = await response.json();
|
|
79142
79343
|
return data.version ?? null;
|
|
79143
79344
|
} catch (err) {
|
|
79144
|
-
|
|
79345
|
+
log29.warn(`Failed to fetch latest version: ${err}`);
|
|
79145
79346
|
return null;
|
|
79146
79347
|
}
|
|
79147
79348
|
}
|
|
@@ -79158,38 +79359,38 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
79158
79359
|
}
|
|
79159
79360
|
start() {
|
|
79160
79361
|
if (!this.config.enabled) {
|
|
79161
|
-
|
|
79362
|
+
log29.debug("Auto-update disabled, not starting checker");
|
|
79162
79363
|
return;
|
|
79163
79364
|
}
|
|
79164
79365
|
setTimeout(() => {
|
|
79165
79366
|
this.check().catch((err) => {
|
|
79166
|
-
|
|
79367
|
+
log29.warn(`Initial update check failed: ${err}`);
|
|
79167
79368
|
});
|
|
79168
79369
|
}, 5000);
|
|
79169
79370
|
const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
|
|
79170
79371
|
this.checkInterval = setInterval(() => {
|
|
79171
79372
|
this.check().catch((err) => {
|
|
79172
|
-
|
|
79373
|
+
log29.warn(`Periodic update check failed: ${err}`);
|
|
79173
79374
|
});
|
|
79174
79375
|
}, intervalMs);
|
|
79175
|
-
|
|
79376
|
+
log29.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
|
|
79176
79377
|
}
|
|
79177
79378
|
stop() {
|
|
79178
79379
|
if (this.checkInterval) {
|
|
79179
79380
|
clearInterval(this.checkInterval);
|
|
79180
79381
|
this.checkInterval = null;
|
|
79181
79382
|
}
|
|
79182
|
-
|
|
79383
|
+
log29.debug("Update checker stopped");
|
|
79183
79384
|
}
|
|
79184
79385
|
async check() {
|
|
79185
79386
|
if (this.isChecking) {
|
|
79186
|
-
|
|
79387
|
+
log29.debug("Check already in progress, skipping");
|
|
79187
79388
|
return this.lastUpdateInfo;
|
|
79188
79389
|
}
|
|
79189
79390
|
this.isChecking = true;
|
|
79190
79391
|
this.emit("check:start");
|
|
79191
79392
|
try {
|
|
79192
|
-
|
|
79393
|
+
log29.debug("Checking for updates...");
|
|
79193
79394
|
const latestVersion2 = await fetchLatestVersion();
|
|
79194
79395
|
if (!latestVersion2) {
|
|
79195
79396
|
this.emit("check:complete", false);
|
|
@@ -79206,18 +79407,18 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
79206
79407
|
detectedAt: new Date
|
|
79207
79408
|
};
|
|
79208
79409
|
if (!this.lastUpdateInfo || this.lastUpdateInfo.latestVersion !== latestVersion2) {
|
|
79209
|
-
|
|
79410
|
+
log29.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
|
|
79210
79411
|
this.lastUpdateInfo = updateInfo;
|
|
79211
79412
|
this.emit("update", updateInfo);
|
|
79212
79413
|
}
|
|
79213
79414
|
this.emit("check:complete", true);
|
|
79214
79415
|
return updateInfo;
|
|
79215
79416
|
}
|
|
79216
|
-
|
|
79417
|
+
log29.debug(`Up to date (v${currentVersion})`);
|
|
79217
79418
|
this.emit("check:complete", false);
|
|
79218
79419
|
return null;
|
|
79219
79420
|
} catch (err) {
|
|
79220
|
-
|
|
79421
|
+
log29.warn(`Update check failed: ${err}`);
|
|
79221
79422
|
this.emit("check:error", err);
|
|
79222
79423
|
return null;
|
|
79223
79424
|
} finally {
|
|
@@ -79288,7 +79489,7 @@ function isInScheduledWindow(window2) {
|
|
|
79288
79489
|
}
|
|
79289
79490
|
|
|
79290
79491
|
// src/auto-update/scheduler.ts
|
|
79291
|
-
var
|
|
79492
|
+
var log30 = createLogger("scheduler");
|
|
79292
79493
|
|
|
79293
79494
|
class UpdateScheduler extends EventEmitter8 {
|
|
79294
79495
|
config;
|
|
@@ -79312,7 +79513,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79312
79513
|
scheduleUpdate(updateInfo) {
|
|
79313
79514
|
this.pendingUpdate = updateInfo;
|
|
79314
79515
|
if (this.config.autoRestartMode === "immediate") {
|
|
79315
|
-
|
|
79516
|
+
log30.info("Immediate mode: triggering update now");
|
|
79316
79517
|
this.emit("ready", updateInfo);
|
|
79317
79518
|
return;
|
|
79318
79519
|
}
|
|
@@ -79325,19 +79526,19 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79325
79526
|
this.scheduledRestartAt = null;
|
|
79326
79527
|
this.askApprovals.clear();
|
|
79327
79528
|
this.askStartTime = null;
|
|
79328
|
-
|
|
79529
|
+
log30.debug("Update schedule cancelled");
|
|
79329
79530
|
}
|
|
79330
79531
|
deferUpdate(minutes) {
|
|
79331
79532
|
const deferUntil = new Date(Date.now() + minutes * 60 * 1000);
|
|
79332
79533
|
this.scheduledRestartAt = null;
|
|
79333
79534
|
this.idleStartTime = null;
|
|
79334
79535
|
this.emit("deferred", deferUntil);
|
|
79335
|
-
|
|
79536
|
+
log30.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
|
|
79336
79537
|
return deferUntil;
|
|
79337
79538
|
}
|
|
79338
79539
|
recordAskResponse(threadId, approved) {
|
|
79339
79540
|
this.askApprovals.set(threadId, approved);
|
|
79340
|
-
|
|
79541
|
+
log30.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
|
|
79341
79542
|
this.checkAskCondition();
|
|
79342
79543
|
}
|
|
79343
79544
|
getScheduledRestartAt() {
|
|
@@ -79358,7 +79559,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79358
79559
|
return;
|
|
79359
79560
|
this.checkCondition();
|
|
79360
79561
|
this.checkTimer = setInterval(() => this.checkCondition(), 1e4);
|
|
79361
|
-
|
|
79562
|
+
log30.debug(`Started checking for ${this.config.autoRestartMode} condition`);
|
|
79362
79563
|
}
|
|
79363
79564
|
stopChecking() {
|
|
79364
79565
|
if (this.checkTimer) {
|
|
@@ -79389,17 +79590,17 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79389
79590
|
if (activity.activeSessionCount === 0) {
|
|
79390
79591
|
if (!this.idleStartTime) {
|
|
79391
79592
|
this.idleStartTime = new Date;
|
|
79392
|
-
|
|
79593
|
+
log30.debug("No active sessions, starting idle timer");
|
|
79393
79594
|
}
|
|
79394
79595
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79395
79596
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79396
79597
|
if (idleMs >= requiredMs) {
|
|
79397
|
-
|
|
79598
|
+
log30.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
|
|
79398
79599
|
this.triggerCountdown();
|
|
79399
79600
|
}
|
|
79400
79601
|
} else {
|
|
79401
79602
|
if (this.idleStartTime) {
|
|
79402
|
-
|
|
79603
|
+
log30.debug("Sessions became active, resetting idle timer");
|
|
79403
79604
|
this.idleStartTime = null;
|
|
79404
79605
|
}
|
|
79405
79606
|
}
|
|
@@ -79410,7 +79611,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79410
79611
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79411
79612
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79412
79613
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79413
|
-
|
|
79614
|
+
log30.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
|
|
79414
79615
|
this.triggerCountdown();
|
|
79415
79616
|
}
|
|
79416
79617
|
} else if (activity.activeSessionCount === 0) {
|
|
@@ -79420,7 +79621,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79420
79621
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79421
79622
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79422
79623
|
if (idleMs >= requiredMs) {
|
|
79423
|
-
|
|
79624
|
+
log30.info("No sessions and quiet timeout reached, triggering update");
|
|
79424
79625
|
this.triggerCountdown();
|
|
79425
79626
|
}
|
|
79426
79627
|
}
|
|
@@ -79431,13 +79632,13 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79431
79632
|
}
|
|
79432
79633
|
const activity = this.getSessionActivity();
|
|
79433
79634
|
if (activity.activeSessionCount === 0) {
|
|
79434
|
-
|
|
79635
|
+
log30.info("Within scheduled window and no active sessions, triggering update");
|
|
79435
79636
|
this.triggerCountdown();
|
|
79436
79637
|
} else if (activity.lastActivityAt) {
|
|
79437
79638
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79438
79639
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79439
79640
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79440
|
-
|
|
79641
|
+
log30.info("Within scheduled window and sessions quiet, triggering update");
|
|
79441
79642
|
this.triggerCountdown();
|
|
79442
79643
|
}
|
|
79443
79644
|
}
|
|
@@ -79445,14 +79646,14 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79445
79646
|
checkAskCondition() {
|
|
79446
79647
|
const threadIds = this.getActiveThreadIds();
|
|
79447
79648
|
if (threadIds.length === 0) {
|
|
79448
|
-
|
|
79649
|
+
log30.info("No active threads, proceeding with update");
|
|
79449
79650
|
this.triggerCountdown();
|
|
79450
79651
|
return;
|
|
79451
79652
|
}
|
|
79452
79653
|
if (!this.askStartTime && this.pendingUpdate) {
|
|
79453
79654
|
this.askStartTime = new Date;
|
|
79454
79655
|
this.postAskMessage(threadIds, this.pendingUpdate.latestVersion).catch((err) => {
|
|
79455
|
-
|
|
79656
|
+
log30.warn(`Failed to post ask message: ${err}`);
|
|
79456
79657
|
});
|
|
79457
79658
|
return;
|
|
79458
79659
|
}
|
|
@@ -79465,12 +79666,12 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79465
79666
|
denials++;
|
|
79466
79667
|
}
|
|
79467
79668
|
if (approvals > threadIds.length / 2) {
|
|
79468
|
-
|
|
79669
|
+
log30.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
|
|
79469
79670
|
this.triggerCountdown();
|
|
79470
79671
|
return;
|
|
79471
79672
|
}
|
|
79472
79673
|
if (denials > threadIds.length / 2) {
|
|
79473
|
-
|
|
79674
|
+
log30.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
|
|
79474
79675
|
this.deferUpdate(60);
|
|
79475
79676
|
return;
|
|
79476
79677
|
}
|
|
@@ -79478,7 +79679,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79478
79679
|
const elapsedMs = Date.now() - this.askStartTime.getTime();
|
|
79479
79680
|
const timeoutMs = this.config.askTimeoutMinutes * 60 * 1000;
|
|
79480
79681
|
if (elapsedMs >= timeoutMs) {
|
|
79481
|
-
|
|
79682
|
+
log30.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
|
|
79482
79683
|
this.triggerCountdown();
|
|
79483
79684
|
}
|
|
79484
79685
|
}
|
|
@@ -79498,7 +79699,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79498
79699
|
this.emit("ready", this.pendingUpdate);
|
|
79499
79700
|
}
|
|
79500
79701
|
}, 1000);
|
|
79501
|
-
|
|
79702
|
+
log30.info("Update countdown started (60 seconds)");
|
|
79502
79703
|
}
|
|
79503
79704
|
stopCountdown() {
|
|
79504
79705
|
if (this.countdownTimer) {
|
|
@@ -79511,27 +79712,27 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79511
79712
|
// src/auto-update/installer.ts
|
|
79512
79713
|
init_logger();
|
|
79513
79714
|
import { spawn as spawn4, spawnSync } from "child_process";
|
|
79514
|
-
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as
|
|
79715
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
79515
79716
|
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
79516
79717
|
import { homedir as homedir5 } from "os";
|
|
79517
|
-
var
|
|
79718
|
+
var log31 = createLogger("installer");
|
|
79518
79719
|
function detectPackageManager() {
|
|
79519
79720
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
79520
79721
|
const originalInstaller = detectOriginalInstaller();
|
|
79521
79722
|
if (originalInstaller) {
|
|
79522
|
-
|
|
79723
|
+
log31.debug(`Detected original installer: ${originalInstaller}`);
|
|
79523
79724
|
if (originalInstaller === "bun") {
|
|
79524
79725
|
const bunCheck2 = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
79525
79726
|
if (bunCheck2.status === 0) {
|
|
79526
79727
|
return { cmd: "bun", isBun: true };
|
|
79527
79728
|
}
|
|
79528
|
-
|
|
79729
|
+
log31.warn("Originally installed with bun, but bun not found. Falling back to npm.");
|
|
79529
79730
|
} else {
|
|
79530
79731
|
const npmCheck2 = spawnSync(npmCmd, ["--version"], { stdio: "ignore" });
|
|
79531
79732
|
if (npmCheck2.status === 0) {
|
|
79532
79733
|
return { cmd: npmCmd, isBun: false };
|
|
79533
79734
|
}
|
|
79534
|
-
|
|
79735
|
+
log31.warn("Originally installed with npm, but npm not found. Falling back to bun.");
|
|
79535
79736
|
}
|
|
79536
79737
|
}
|
|
79537
79738
|
const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
@@ -79582,7 +79783,7 @@ function loadUpdateState() {
|
|
|
79582
79783
|
return JSON.parse(content);
|
|
79583
79784
|
}
|
|
79584
79785
|
} catch (err) {
|
|
79585
|
-
|
|
79786
|
+
log31.warn(`Failed to load update state: ${err}`);
|
|
79586
79787
|
}
|
|
79587
79788
|
return {};
|
|
79588
79789
|
}
|
|
@@ -79592,19 +79793,19 @@ function saveUpdateState(state) {
|
|
|
79592
79793
|
if (!existsSync13(dir)) {
|
|
79593
79794
|
mkdirSync4(dir, { recursive: true });
|
|
79594
79795
|
}
|
|
79595
|
-
|
|
79596
|
-
|
|
79796
|
+
writeFileSync6(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
|
|
79797
|
+
log31.debug("Update state saved");
|
|
79597
79798
|
} catch (err) {
|
|
79598
|
-
|
|
79799
|
+
log31.warn(`Failed to save update state: ${err}`);
|
|
79599
79800
|
}
|
|
79600
79801
|
}
|
|
79601
79802
|
function clearUpdateState() {
|
|
79602
79803
|
try {
|
|
79603
79804
|
if (existsSync13(STATE_PATH)) {
|
|
79604
|
-
|
|
79805
|
+
writeFileSync6(STATE_PATH, "{}", "utf-8");
|
|
79605
79806
|
}
|
|
79606
79807
|
} catch (err) {
|
|
79607
|
-
|
|
79808
|
+
log31.warn(`Failed to clear update state: ${err}`);
|
|
79608
79809
|
}
|
|
79609
79810
|
}
|
|
79610
79811
|
function checkJustUpdated() {
|
|
@@ -79636,11 +79837,11 @@ function clearRuntimeSettings() {
|
|
|
79636
79837
|
}
|
|
79637
79838
|
}
|
|
79638
79839
|
async function installVersion(version) {
|
|
79639
|
-
|
|
79840
|
+
log31.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
|
|
79640
79841
|
const pm = detectPackageManager();
|
|
79641
79842
|
if (!pm) {
|
|
79642
79843
|
const error = "Neither bun nor npm found in PATH. Cannot install update.";
|
|
79643
|
-
|
|
79844
|
+
log31.error(`❌ ${error}`);
|
|
79644
79845
|
return { success: false, error };
|
|
79645
79846
|
}
|
|
79646
79847
|
saveUpdateState({
|
|
@@ -79652,7 +79853,7 @@ async function installVersion(version) {
|
|
|
79652
79853
|
return new Promise((resolve7) => {
|
|
79653
79854
|
const { cmd, isBun: isBun3 } = pm;
|
|
79654
79855
|
const args = ["install", "-g", `${PACKAGE_NAME2}@${version}`];
|
|
79655
|
-
|
|
79856
|
+
log31.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
|
|
79656
79857
|
const child = spawn4(cmd, args, {
|
|
79657
79858
|
stdio: ["ignore", "pipe", "pipe"],
|
|
79658
79859
|
env: {
|
|
@@ -79670,7 +79871,7 @@ async function installVersion(version) {
|
|
|
79670
79871
|
});
|
|
79671
79872
|
child.on("close", (code) => {
|
|
79672
79873
|
if (code === 0) {
|
|
79673
|
-
|
|
79874
|
+
log31.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
|
|
79674
79875
|
saveUpdateState({
|
|
79675
79876
|
previousVersion: VERSION,
|
|
79676
79877
|
targetVersion: version,
|
|
@@ -79680,20 +79881,20 @@ async function installVersion(version) {
|
|
|
79680
79881
|
resolve7({ success: true });
|
|
79681
79882
|
} else {
|
|
79682
79883
|
const errorMsg = stderr || stdout || `Exit code: ${code}`;
|
|
79683
|
-
|
|
79884
|
+
log31.error(`❌ Installation failed: ${errorMsg}`);
|
|
79684
79885
|
clearUpdateState();
|
|
79685
79886
|
resolve7({ success: false, error: errorMsg });
|
|
79686
79887
|
}
|
|
79687
79888
|
});
|
|
79688
79889
|
child.on("error", (err) => {
|
|
79689
|
-
|
|
79890
|
+
log31.error(`❌ Failed to spawn npm: ${err}`);
|
|
79690
79891
|
clearUpdateState();
|
|
79691
79892
|
resolve7({ success: false, error: err.message });
|
|
79692
79893
|
});
|
|
79693
79894
|
setTimeout(() => {
|
|
79694
79895
|
if (child.exitCode === null) {
|
|
79695
79896
|
child.kill();
|
|
79696
|
-
|
|
79897
|
+
log31.error("❌ Installation timed out");
|
|
79697
79898
|
clearUpdateState();
|
|
79698
79899
|
resolve7({ success: false, error: "Installation timed out" });
|
|
79699
79900
|
}
|
|
@@ -79735,7 +79936,7 @@ class UpdateInstaller {
|
|
|
79735
79936
|
}
|
|
79736
79937
|
|
|
79737
79938
|
// src/auto-update/manager.ts
|
|
79738
|
-
var
|
|
79939
|
+
var log32 = createLogger("updater");
|
|
79739
79940
|
|
|
79740
79941
|
class AutoUpdateManager extends EventEmitter9 {
|
|
79741
79942
|
config;
|
|
@@ -79758,23 +79959,23 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79758
79959
|
}
|
|
79759
79960
|
start() {
|
|
79760
79961
|
if (!this.config.enabled) {
|
|
79761
|
-
|
|
79962
|
+
log32.info("Auto-update is disabled");
|
|
79762
79963
|
return;
|
|
79763
79964
|
}
|
|
79764
79965
|
const updateResult = this.installer.checkJustUpdated();
|
|
79765
79966
|
if (updateResult) {
|
|
79766
|
-
|
|
79967
|
+
log32.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
|
|
79767
79968
|
this.callbacks.broadcastUpdate((fmt) => `\uD83C\uDF89 ${fmt.formatBold("Bot updated")} from v${updateResult.previousVersion} to v${updateResult.currentVersion}`).catch((err) => {
|
|
79768
|
-
|
|
79969
|
+
log32.warn(`Failed to broadcast update notification: ${err}`);
|
|
79769
79970
|
});
|
|
79770
79971
|
}
|
|
79771
79972
|
this.checker.start();
|
|
79772
|
-
|
|
79973
|
+
log32.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
|
|
79773
79974
|
}
|
|
79774
79975
|
stop() {
|
|
79775
79976
|
this.checker.stop();
|
|
79776
79977
|
this.scheduler.stop();
|
|
79777
|
-
|
|
79978
|
+
log32.debug("Auto-update manager stopped");
|
|
79778
79979
|
}
|
|
79779
79980
|
getState() {
|
|
79780
79981
|
return { ...this.state };
|
|
@@ -79788,10 +79989,10 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79788
79989
|
async forceUpdate() {
|
|
79789
79990
|
const updateInfo = this.state.updateInfo || await this.checker.check();
|
|
79790
79991
|
if (!updateInfo) {
|
|
79791
|
-
|
|
79992
|
+
log32.info("No update available");
|
|
79792
79993
|
return;
|
|
79793
79994
|
}
|
|
79794
|
-
|
|
79995
|
+
log32.info("Forcing immediate update");
|
|
79795
79996
|
await this.performUpdate(updateInfo);
|
|
79796
79997
|
}
|
|
79797
79998
|
deferUpdate(minutes = 60) {
|
|
@@ -79846,7 +80047,7 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79846
80047
|
await this.callbacks.broadcastUpdate((fmt) => `✅ ${fmt.formatBold("Update installed")} - restarting now. ${fmt.formatItalic("Sessions will resume automatically.")}`).catch(() => {});
|
|
79847
80048
|
await new Promise((resolve7) => setTimeout(resolve7, 1000));
|
|
79848
80049
|
await this.callbacks.prepareForRestart();
|
|
79849
|
-
|
|
80050
|
+
log32.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
|
|
79850
80051
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
79851
80052
|
process.stdout.write("\x1B[?25h");
|
|
79852
80053
|
process.exit(RESTART_EXIT_CODE);
|
|
@@ -79906,7 +80107,7 @@ function wirePlatformEvents(platformId, client, session, ui) {
|
|
|
79906
80107
|
ui.addLog({ level: "error", component: platformId, message: String(e) });
|
|
79907
80108
|
});
|
|
79908
80109
|
}
|
|
79909
|
-
program.name("claude-threads").version(VERSION).description("Share Claude Code sessions in Mattermost").option("--url <url>", "Mattermost server URL").option("--token <token>", "Mattermost bot token").option("--channel <id>", "Mattermost channel ID").option("--bot-name <name>", "Bot mention name (default: claude-code)").option("--allowed-users <users>", "Comma-separated allowed usernames").option("--skip-permissions", "
|
|
80110
|
+
program.name("claude-threads").version(VERSION).description("Share Claude Code sessions in Mattermost").option("--url <url>", "Mattermost server URL").option("--token <token>", "Mattermost bot token").option("--channel <id>", "Mattermost channel ID").option("--bot-name <name>", "Bot mention name (default: claude-code)").option("--allowed-users <users>", "Comma-separated allowed usernames").option("--permission-mode <mode>", "Permission mode: default | auto | bypass (default: from config)").option("--skip-permissions", "[deprecated] Alias for --permission-mode bypass").option("--no-skip-permissions", "[deprecated] Alias for --permission-mode default").option("--chrome", "Enable Claude in Chrome integration").option("--no-chrome", "Disable Claude in Chrome integration").option("--worktree-mode <mode>", "Git worktree mode: off, prompt, require (default: prompt)").option("--keep-alive", "Enable system sleep prevention (default: enabled)").option("--no-keep-alive", "Disable system sleep prevention").option("--setup", "Run interactive setup wizard (reconfigure existing settings)").option("--debug", "Enable debug logging").option("--skip-version-check", "Skip Claude CLI version compatibility check").option("--auto-restart", "Enable auto-restart on updates (default when autoUpdate enabled)").option("--no-auto-restart", "Disable auto-restart on updates").option("--headless", "Run without interactive UI (logs to stdout)").parse();
|
|
79910
80111
|
var opts = program.opts();
|
|
79911
80112
|
var forcedInteractive = !!process.env.CLAUDE_THREADS_INTERACTIVE;
|
|
79912
80113
|
var isHeadless = opts.headless || !forcedInteractive && (!process.stdout.isTTY || !process.stdin.isTTY);
|
|
@@ -79990,6 +80191,10 @@ async function startWithoutDaemon() {
|
|
|
79990
80191
|
if (opts.debug) {
|
|
79991
80192
|
process.env.DEBUG = "1";
|
|
79992
80193
|
}
|
|
80194
|
+
if (opts.permissionMode !== undefined && !["default", "auto", "bypass"].includes(opts.permissionMode)) {
|
|
80195
|
+
console.error(red(` ❌ Invalid --permission-mode: "${opts.permissionMode}". Must be one of: default, auto, bypass.`));
|
|
80196
|
+
process.exit(1);
|
|
80197
|
+
}
|
|
79993
80198
|
const cliArgs = {
|
|
79994
80199
|
url: opts.url,
|
|
79995
80200
|
token: opts.token,
|
|
@@ -79997,6 +80202,7 @@ async function startWithoutDaemon() {
|
|
|
79997
80202
|
botName: opts.botName,
|
|
79998
80203
|
allowedUsers: opts.allowedUsers,
|
|
79999
80204
|
skipPermissions: opts.skipPermissions,
|
|
80205
|
+
permissionMode: opts.permissionMode,
|
|
80000
80206
|
chrome: opts.chrome,
|
|
80001
80207
|
worktreeMode: opts.worktreeMode,
|
|
80002
80208
|
keepAlive: opts.keepAlive
|
|
@@ -80026,7 +80232,10 @@ async function startWithoutDaemon() {
|
|
|
80026
80232
|
}
|
|
80027
80233
|
const config = newConfig;
|
|
80028
80234
|
const firstPlatformConfig = config.platforms[0];
|
|
80029
|
-
const
|
|
80235
|
+
const initialPermissionMode = resolvePermissionMode({
|
|
80236
|
+
permissionMode: cliArgs.permissionMode ?? firstPlatformConfig.permissionMode,
|
|
80237
|
+
skipPermissions: cliArgs.skipPermissions ?? firstPlatformConfig.skipPermissions
|
|
80238
|
+
});
|
|
80030
80239
|
const claudeValidation = validateClaudeCli();
|
|
80031
80240
|
if (!claudeValidation.compatible && !opts.skipVersionCheck) {
|
|
80032
80241
|
console.error(red(` ❌ ${claudeValidation.message}`));
|
|
@@ -80053,8 +80262,10 @@ async function startWithoutDaemon() {
|
|
|
80053
80262
|
process.env.DEBUG = "1";
|
|
80054
80263
|
}
|
|
80055
80264
|
}
|
|
80265
|
+
const restoredPermissionMode = restoredSettings?.permissionMode ?? (restoredSettings?.skipPermissions === true ? "bypass" : restoredSettings?.skipPermissions === false ? "default" : initialPermissionMode);
|
|
80056
80266
|
const runtimeConfig = {
|
|
80057
|
-
|
|
80267
|
+
permissionMode: restoredPermissionMode,
|
|
80268
|
+
skipPermissions: restoredPermissionMode === "bypass",
|
|
80058
80269
|
chromeEnabled: restoredSettings?.chromeEnabled ?? (config.chrome ?? false),
|
|
80059
80270
|
keepAliveEnabled: restoredSettings?.keepAliveEnabled ?? keepAliveEnabled
|
|
80060
80271
|
};
|
|
@@ -80067,7 +80278,7 @@ async function startWithoutDaemon() {
|
|
|
80067
80278
|
workingDir,
|
|
80068
80279
|
claudeVersion: claudeValidation.version || "unknown",
|
|
80069
80280
|
claudeCompatible: claudeValidation.compatible,
|
|
80070
|
-
|
|
80281
|
+
permissionMode: runtimeConfig.permissionMode,
|
|
80071
80282
|
chromeEnabled: runtimeConfig.chromeEnabled,
|
|
80072
80283
|
keepAliveEnabled: runtimeConfig.keepAliveEnabled
|
|
80073
80284
|
},
|
|
@@ -80082,14 +80293,20 @@ async function startWithoutDaemon() {
|
|
|
80082
80293
|
ui.addLog({ level: "info", component: "toggle", message: `Debug mode ${enabled ? "enabled" : "disabled"}` });
|
|
80083
80294
|
sessionManager?.updateAllStickyMessages();
|
|
80084
80295
|
},
|
|
80085
|
-
onPermissionsToggle: (
|
|
80086
|
-
runtimeConfig.
|
|
80087
|
-
saveRuntimeSettings({
|
|
80296
|
+
onPermissionsToggle: (mode) => {
|
|
80297
|
+
runtimeConfig.permissionMode = mode;
|
|
80298
|
+
saveRuntimeSettings({
|
|
80299
|
+
...getRuntimeSettings(),
|
|
80300
|
+
permissionMode: mode,
|
|
80301
|
+
skipPermissions: mode === "bypass"
|
|
80302
|
+
});
|
|
80088
80303
|
for (const platformConfig of config.platforms) {
|
|
80089
|
-
|
|
80304
|
+
const pc = platformConfig;
|
|
80305
|
+
pc.permissionMode = mode;
|
|
80306
|
+
pc.skipPermissions = mode === "bypass";
|
|
80090
80307
|
}
|
|
80091
|
-
sessionManager?.
|
|
80092
|
-
ui.addLog({ level: "info", component: "toggle", message: `
|
|
80308
|
+
sessionManager?.setPermissionMode(mode);
|
|
80309
|
+
ui.addLog({ level: "info", component: "toggle", message: `Permission mode: ${mode}` });
|
|
80093
80310
|
sessionManager?.updateAllStickyMessages();
|
|
80094
80311
|
},
|
|
80095
80312
|
onChromeToggle: (enabled) => {
|
|
@@ -80152,7 +80369,7 @@ async function startWithoutDaemon() {
|
|
|
80152
80369
|
keepAlive.setEnabled(keepAliveEnabled);
|
|
80153
80370
|
const threadLogsEnabled = config.threadLogs?.enabled ?? true;
|
|
80154
80371
|
const threadLogsRetentionDays = config.threadLogs?.retentionDays ?? 30;
|
|
80155
|
-
const session = new SessionManager(workingDir,
|
|
80372
|
+
const session = new SessionManager(workingDir, initialPermissionMode, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits, config.claudeAccounts);
|
|
80156
80373
|
if (config.stickyMessage) {
|
|
80157
80374
|
session.setStickyMessageCustomization(config.stickyMessage.description, config.stickyMessage.footer);
|
|
80158
80375
|
}
|