claude-threads 1.36.1 → 1.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/bin/claude-threads-daemon +8 -0
- package/dist/index.js +302 -73
- package/dist/mcp/mcp-server.js +93 -86
- package/docs/CONFIGURATION.md +27 -0
- package/docs/systemd/claude-threads.service +3 -0
- package/docs/turn-marker-spec.md +149 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2128,6 +2128,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2128
2128
|
exports.useColor = useColor;
|
|
2129
2129
|
});
|
|
2130
2130
|
|
|
2131
|
+
// src/utils/state-home.ts
|
|
2132
|
+
import { homedir } from "os";
|
|
2133
|
+
import { resolve } from "path";
|
|
2134
|
+
function stateHome() {
|
|
2135
|
+
return resolve(process.env.CLAUDE_THREADS_HOME || homedir());
|
|
2136
|
+
}
|
|
2137
|
+
function hasStateHomeOverride() {
|
|
2138
|
+
return stateHome() !== resolve(homedir());
|
|
2139
|
+
}
|
|
2140
|
+
var init_state_home = () => {};
|
|
2141
|
+
|
|
2131
2142
|
// src/utils/logger.ts
|
|
2132
2143
|
function setLogHandler(handler) {
|
|
2133
2144
|
globalLogHandler = handler;
|
|
@@ -2221,6 +2232,23 @@ function resolveOverheadVisibility(value, fieldPath) {
|
|
|
2221
2232
|
return value;
|
|
2222
2233
|
throw new Error(`Invalid ${fieldPath}: expected one of ${OVERHEAD_VISIBILITY_VALUES.join(", ")}, got ${JSON.stringify(value)}`);
|
|
2223
2234
|
}
|
|
2235
|
+
function resolveTurnMarker(mode, emoji, platformType, fieldPath) {
|
|
2236
|
+
const m = mode === undefined || mode === null ? "off" : mode;
|
|
2237
|
+
if (!TURN_MARKER_VALUES.includes(m)) {
|
|
2238
|
+
throw new Error(`Invalid ${fieldPath}.turnMarker: expected one of ${TURN_MARKER_VALUES.join(", ")}, got ${JSON.stringify(mode)}`);
|
|
2239
|
+
}
|
|
2240
|
+
if (m === "metadata" && platformType !== "slack") {
|
|
2241
|
+
throw new Error(`Invalid ${fieldPath}.turnMarker: metadata is a Slack feature; use reaction on ${platformType}`);
|
|
2242
|
+
}
|
|
2243
|
+
if (emoji !== undefined && emoji !== null) {
|
|
2244
|
+
if (typeof emoji !== "string" || !/^[a-z0-9_+-]+$/.test(emoji)) {
|
|
2245
|
+
throw new Error(`Invalid ${fieldPath}.turnMarkerEmoji: expected an emoji name like checkered_flag, got ${JSON.stringify(emoji)}`);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
if (m === "reaction")
|
|
2249
|
+
return { mode: "reaction", emoji: emoji ?? DEFAULT_TURN_MARKER_EMOJI };
|
|
2250
|
+
return { mode: m };
|
|
2251
|
+
}
|
|
2224
2252
|
function resolveMemoryConfig(value, fieldPath) {
|
|
2225
2253
|
if (value === undefined || value === null || value === true)
|
|
2226
2254
|
return DEFAULT_MEMORY_CONFIG;
|
|
@@ -2395,10 +2423,12 @@ function effectivePermissionMode(input) {
|
|
|
2395
2423
|
return "default";
|
|
2396
2424
|
return input.botWideMode;
|
|
2397
2425
|
}
|
|
2398
|
-
var OVERHEAD_VISIBILITY_VALUES, DEFAULT_OVERHEAD_VISIBILITY = "full", RECONNECT_POLICY_VALUES, DEFAULT_RECONNECT_POLICY = "retry", DEFAULT_MEMORY_CONFIG, MEMORY_DISABLED, BOT_MCP_SERVER_NAME = "claude-threads-mcp", STDIO_KEYS, REMOTE_KEYS, LIMITS_DEFAULTS, MODE_INFO;
|
|
2426
|
+
var OVERHEAD_VISIBILITY_VALUES, DEFAULT_OVERHEAD_VISIBILITY = "full", RECONNECT_POLICY_VALUES, DEFAULT_RECONNECT_POLICY = "retry", TURN_MARKER_VALUES, DEFAULT_TURN_MARKER, DEFAULT_TURN_MARKER_EMOJI = "checkered_flag", TURN_COMPLETE_EVENT_TYPE = "claude_threads_turn_complete", TURN_COMPLETE_PAYLOAD_VERSION = 1, DEFAULT_MEMORY_CONFIG, MEMORY_DISABLED, BOT_MCP_SERVER_NAME = "claude-threads-mcp", STDIO_KEYS, REMOTE_KEYS, LIMITS_DEFAULTS, MODE_INFO;
|
|
2399
2427
|
var init_types = __esm(() => {
|
|
2400
2428
|
OVERHEAD_VISIBILITY_VALUES = ["full", "minimal", "hidden"];
|
|
2401
2429
|
RECONNECT_POLICY_VALUES = ["retry", "exit"];
|
|
2430
|
+
TURN_MARKER_VALUES = ["reaction", "metadata", "off"];
|
|
2431
|
+
DEFAULT_TURN_MARKER = { mode: "off" };
|
|
2402
2432
|
DEFAULT_MEMORY_CONFIG = {
|
|
2403
2433
|
enabled: true,
|
|
2404
2434
|
repoLayer: true,
|
|
@@ -4597,7 +4627,7 @@ var init_rate_limit_detector = __esm(() => {
|
|
|
4597
4627
|
|
|
4598
4628
|
// src/claude/cli.ts
|
|
4599
4629
|
import { EventEmitter } from "events";
|
|
4600
|
-
import { resolve as
|
|
4630
|
+
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
4601
4631
|
import { fileURLToPath } from "url";
|
|
4602
4632
|
import { existsSync as existsSync4, readFileSync as readFileSync2, watchFile, unwatchFile, unlinkSync, statSync, readdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4603
4633
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -5139,15 +5169,15 @@ var init_cli = __esm(() => {
|
|
|
5139
5169
|
getMcpServerPath() {
|
|
5140
5170
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
5141
5171
|
const __dirname2 = dirname2(__filename2);
|
|
5142
|
-
const bundledPath =
|
|
5172
|
+
const bundledPath = resolve3(__dirname2, "mcp", "mcp-server.js");
|
|
5143
5173
|
if (existsSync4(bundledPath)) {
|
|
5144
5174
|
return bundledPath;
|
|
5145
5175
|
}
|
|
5146
|
-
const sourceLayoutPath =
|
|
5176
|
+
const sourceLayoutPath = resolve3(__dirname2, "..", "mcp", "mcp-server.js");
|
|
5147
5177
|
if (existsSync4(sourceLayoutPath)) {
|
|
5148
5178
|
return sourceLayoutPath;
|
|
5149
5179
|
}
|
|
5150
|
-
const tsPath =
|
|
5180
|
+
const tsPath = resolve3(__dirname2, "..", "mcp", "mcp-server.ts");
|
|
5151
5181
|
if (existsSync4(tsPath)) {
|
|
5152
5182
|
return tsPath;
|
|
5153
5183
|
}
|
|
@@ -5156,15 +5186,15 @@ var init_cli = __esm(() => {
|
|
|
5156
5186
|
getStatusLineWriterPath() {
|
|
5157
5187
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
5158
5188
|
const __dirname2 = dirname2(__filename2);
|
|
5159
|
-
const bundledPath =
|
|
5189
|
+
const bundledPath = resolve3(__dirname2, "statusline", "writer.js");
|
|
5160
5190
|
if (existsSync4(bundledPath)) {
|
|
5161
5191
|
return bundledPath;
|
|
5162
5192
|
}
|
|
5163
|
-
const sourceLayoutPath =
|
|
5193
|
+
const sourceLayoutPath = resolve3(__dirname2, "..", "statusline", "writer.js");
|
|
5164
5194
|
if (existsSync4(sourceLayoutPath)) {
|
|
5165
5195
|
return sourceLayoutPath;
|
|
5166
5196
|
}
|
|
5167
|
-
const tsPath =
|
|
5197
|
+
const tsPath = resolve3(__dirname2, "..", "statusline", "writer.ts");
|
|
5168
5198
|
if (existsSync4(tsPath)) {
|
|
5169
5199
|
return tsPath;
|
|
5170
5200
|
}
|
|
@@ -5411,7 +5441,7 @@ var init_render = __esm(() => {
|
|
|
5411
5441
|
});
|
|
5412
5442
|
|
|
5413
5443
|
// src/usage/index.ts
|
|
5414
|
-
import { homedir as
|
|
5444
|
+
import { homedir as homedir2 } from "os";
|
|
5415
5445
|
import path3 from "path";
|
|
5416
5446
|
function toLimits(usage) {
|
|
5417
5447
|
const limits = [
|
|
@@ -5461,7 +5491,7 @@ async function collectUsage(options) {
|
|
|
5461
5491
|
}
|
|
5462
5492
|
return results;
|
|
5463
5493
|
}
|
|
5464
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR ?? path3.join(
|
|
5494
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ?? path3.join(homedir2(), ".claude");
|
|
5465
5495
|
return [await readSeat(profileNameFor(configDir), configDir)];
|
|
5466
5496
|
}
|
|
5467
5497
|
var USAGE_PROBE_TIMEOUT_MS = 1e4;
|
|
@@ -5525,7 +5555,6 @@ var init_emoji = __esm(() => {
|
|
|
5525
5555
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5526
5556
|
import * as path4 from "path";
|
|
5527
5557
|
import * as fs from "fs/promises";
|
|
5528
|
-
import { homedir as homedir6 } from "os";
|
|
5529
5558
|
async function execGit(args, cwd) {
|
|
5530
5559
|
const cmd = `git ${args.join(" ")}`;
|
|
5531
5560
|
log10.debug(`Executing: ${cmd}`);
|
|
@@ -5834,10 +5863,11 @@ async function removeWorktreeMetadata(worktreePath) {
|
|
|
5834
5863
|
var log10, WORKTREES_DIR, METADATA_STORE_PATH;
|
|
5835
5864
|
var init_worktree = __esm(() => {
|
|
5836
5865
|
init_spawn();
|
|
5866
|
+
init_state_home();
|
|
5837
5867
|
init_logger();
|
|
5838
5868
|
log10 = createLogger("git-wt");
|
|
5839
|
-
WORKTREES_DIR = path4.join(
|
|
5840
|
-
METADATA_STORE_PATH = path4.join(
|
|
5869
|
+
WORKTREES_DIR = path4.join(stateHome(), ".claude-threads", "worktrees");
|
|
5870
|
+
METADATA_STORE_PATH = path4.join(stateHome(), ".claude-threads", "worktree-metadata.json");
|
|
5841
5871
|
});
|
|
5842
5872
|
|
|
5843
5873
|
// node_modules/graceful-fs/polyfills.js
|
|
@@ -47494,17 +47524,17 @@ function resolveAckReaction(configured) {
|
|
|
47494
47524
|
}
|
|
47495
47525
|
|
|
47496
47526
|
// src/persistence/audit-log.ts
|
|
47527
|
+
init_state_home();
|
|
47497
47528
|
init_logger();
|
|
47498
47529
|
import { chmodSync, closeSync, constants as fsConstants, fchmodSync, lstatSync, mkdirSync, openSync, writeSync } from "fs";
|
|
47499
47530
|
import { join } from "path";
|
|
47500
|
-
import { homedir } from "os";
|
|
47501
47531
|
var log = createLogger("audit");
|
|
47502
47532
|
var DETAIL_MAX = 500;
|
|
47503
47533
|
var enabledPlatforms = new Set;
|
|
47504
47534
|
var preparedDirs = new Set;
|
|
47505
47535
|
var openFds = new Map;
|
|
47506
47536
|
function auditDir() {
|
|
47507
|
-
return process.env.CLAUDE_THREADS_AUDIT_DIR || join(
|
|
47537
|
+
return process.env.CLAUDE_THREADS_AUDIT_DIR || join(stateHome(), ".claude-threads", "audit");
|
|
47508
47538
|
}
|
|
47509
47539
|
function configureAuditLog(platformId, enabled) {
|
|
47510
47540
|
if (enabled)
|
|
@@ -47612,6 +47642,20 @@ function deriveDmPlatformConfig(parent, channelId, partnerUsernames) {
|
|
|
47612
47642
|
};
|
|
47613
47643
|
}
|
|
47614
47644
|
|
|
47645
|
+
// src/session/lifecycle-visibility.ts
|
|
47646
|
+
function shouldPostLifecycle(visibility, kind) {
|
|
47647
|
+
if (kind === "abnormal-exit")
|
|
47648
|
+
return true;
|
|
47649
|
+
switch (visibility) {
|
|
47650
|
+
case "full":
|
|
47651
|
+
return true;
|
|
47652
|
+
case "minimal":
|
|
47653
|
+
return kind !== "idle-warning";
|
|
47654
|
+
case "hidden":
|
|
47655
|
+
return false;
|
|
47656
|
+
}
|
|
47657
|
+
}
|
|
47658
|
+
|
|
47615
47659
|
// src/session/lifecycle-fsm.ts
|
|
47616
47660
|
init_logger();
|
|
47617
47661
|
var log2 = createLogger("fsm");
|
|
@@ -47718,9 +47762,9 @@ function getSessionStatus(session) {
|
|
|
47718
47762
|
}
|
|
47719
47763
|
|
|
47720
47764
|
// src/config/index.ts
|
|
47765
|
+
init_state_home();
|
|
47721
47766
|
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
|
|
47722
|
-
import { resolve, dirname } from "path";
|
|
47723
|
-
import { homedir as homedir2 } from "os";
|
|
47767
|
+
import { resolve as resolve2, dirname } from "path";
|
|
47724
47768
|
|
|
47725
47769
|
// node_modules/js-yaml/dist/js-yaml.mjs
|
|
47726
47770
|
function getDefaultExportFromCjs(x) {
|
|
@@ -50889,7 +50933,7 @@ function resolvePlatformMcpPosture(platforms, globalMcpServers, managedPresent =
|
|
|
50889
50933
|
}
|
|
50890
50934
|
|
|
50891
50935
|
// src/config/index.ts
|
|
50892
|
-
var CONFIG_PATH =
|
|
50936
|
+
var CONFIG_PATH = resolve2(stateHome(), ".config", "claude-threads", "config.yaml");
|
|
50893
50937
|
function loadConfigWithMigration() {
|
|
50894
50938
|
if (existsSync2(CONFIG_PATH)) {
|
|
50895
50939
|
const content = readFileSync(CONFIG_PATH, "utf-8");
|
|
@@ -51072,7 +51116,6 @@ function writeFileAtomic(file, content) {
|
|
|
51072
51116
|
|
|
51073
51117
|
// src/persistence/session-store.ts
|
|
51074
51118
|
init_logger();
|
|
51075
|
-
import { homedir as homedir3 } from "os";
|
|
51076
51119
|
import { join as join5 } from "path";
|
|
51077
51120
|
|
|
51078
51121
|
// src/sponsor.ts
|
|
@@ -51097,6 +51140,7 @@ function formatMilestoneLine(formatter, milestone) {
|
|
|
51097
51140
|
}
|
|
51098
51141
|
|
|
51099
51142
|
// src/persistence/session-store.ts
|
|
51143
|
+
init_state_home();
|
|
51100
51144
|
var log4 = createLogger("persist");
|
|
51101
51145
|
function resolveEndReason(session) {
|
|
51102
51146
|
if (!session.cleanedAt)
|
|
@@ -51107,7 +51151,7 @@ function isRevivable(session) {
|
|
|
51107
51151
|
return resolveEndReason(session) !== "stopped";
|
|
51108
51152
|
}
|
|
51109
51153
|
var STORE_VERSION = 2;
|
|
51110
|
-
var DEFAULT_CONFIG_DIR = join5(
|
|
51154
|
+
var DEFAULT_CONFIG_DIR = join5(stateHome(), ".config", "claude-threads");
|
|
51111
51155
|
var DEFAULT_SESSIONS_FILE = join5(DEFAULT_CONFIG_DIR, "sessions.json");
|
|
51112
51156
|
|
|
51113
51157
|
class SessionStore {
|
|
@@ -51384,12 +51428,12 @@ class SessionStore {
|
|
|
51384
51428
|
}
|
|
51385
51429
|
|
|
51386
51430
|
// src/persistence/thread-logger.ts
|
|
51431
|
+
init_state_home();
|
|
51387
51432
|
init_logger();
|
|
51388
51433
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4, appendFileSync, readdirSync as readdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, rmdirSync, readFileSync as readFileSync4, chmodSync as chmodSync4 } from "fs";
|
|
51389
|
-
import { homedir as homedir4 } from "os";
|
|
51390
51434
|
import { join as join6, dirname as dirname3 } from "path";
|
|
51391
51435
|
var log5 = createLogger("thread-log");
|
|
51392
|
-
var LOGS_BASE_DIR = join6(
|
|
51436
|
+
var LOGS_BASE_DIR = join6(stateHome(), ".claude-threads", "logs");
|
|
51393
51437
|
|
|
51394
51438
|
class ThreadLoggerImpl {
|
|
51395
51439
|
platformId;
|
|
@@ -51661,14 +51705,14 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
|
51661
51705
|
|
|
51662
51706
|
// src/version.ts
|
|
51663
51707
|
import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
|
|
51664
|
-
import { dirname as dirname4, resolve as
|
|
51708
|
+
import { dirname as dirname4, resolve as resolve4 } from "path";
|
|
51665
51709
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
51666
51710
|
var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
|
|
51667
51711
|
function loadPackageJson() {
|
|
51668
51712
|
const candidates = [
|
|
51669
|
-
|
|
51670
|
-
|
|
51671
|
-
|
|
51713
|
+
resolve4(__dirname2, "..", "package.json"),
|
|
51714
|
+
resolve4(__dirname2, "..", "..", "package.json"),
|
|
51715
|
+
resolve4(process.cwd(), "package.json")
|
|
51672
51716
|
];
|
|
51673
51717
|
for (const candidate of candidates) {
|
|
51674
51718
|
if (existsSync7(candidate)) {
|
|
@@ -52136,13 +52180,13 @@ ${formatter.formatBold("Reactions:")}
|
|
|
52136
52180
|
|
|
52137
52181
|
// src/changelog.ts
|
|
52138
52182
|
import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
|
|
52139
|
-
import { dirname as dirname5, resolve as
|
|
52183
|
+
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
52140
52184
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
52141
52185
|
var __dirname3 = dirname5(fileURLToPath3(import.meta.url));
|
|
52142
52186
|
function getReleaseNotes(version) {
|
|
52143
52187
|
const possiblePaths = [
|
|
52144
|
-
|
|
52145
|
-
|
|
52188
|
+
resolve5(__dirname3, "..", "CHANGELOG.md"),
|
|
52189
|
+
resolve5(__dirname3, "..", "..", "CHANGELOG.md")
|
|
52146
52190
|
];
|
|
52147
52191
|
let changelogPath = null;
|
|
52148
52192
|
for (const p of possiblePaths) {
|
|
@@ -53332,6 +53376,8 @@ function updateLastMessage(session, post) {
|
|
|
53332
53376
|
init_cli();
|
|
53333
53377
|
|
|
53334
53378
|
// src/operations/streaming/handler.ts
|
|
53379
|
+
init_state_home();
|
|
53380
|
+
import { createHash } from "crypto";
|
|
53335
53381
|
import { lstat, mkdir as mkdir2, mkdtemp, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
53336
53382
|
import { tmpdir as tmpdir3 } from "os";
|
|
53337
53383
|
import { join as join8 } from "path";
|
|
@@ -53468,7 +53514,12 @@ function safeIdSegment(id) {
|
|
|
53468
53514
|
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
53469
53515
|
}
|
|
53470
53516
|
function getSessionUploadDir(platformId, threadId) {
|
|
53471
|
-
return join8(tmpdir3(), UPLOAD_ROOT_DIR, `${safeIdSegment(platformId)}-${safeIdSegment(threadId)}`);
|
|
53517
|
+
return join8(tmpdir3(), UPLOAD_ROOT_DIR, `${instancePrefix()}${safeIdSegment(platformId)}-${safeIdSegment(threadId)}`);
|
|
53518
|
+
}
|
|
53519
|
+
function instancePrefix() {
|
|
53520
|
+
if (!hasStateHomeOverride())
|
|
53521
|
+
return "";
|
|
53522
|
+
return createHash("sha1").update(stateHome()).digest("hex").slice(0, 12) + "-";
|
|
53472
53523
|
}
|
|
53473
53524
|
async function cleanupSessionUploads(platformId, threadId) {
|
|
53474
53525
|
if (!platformId || !threadId)
|
|
@@ -53676,7 +53727,7 @@ function buildRestartCliOptions(session, ctx) {
|
|
|
53676
53727
|
|
|
53677
53728
|
// src/operations/commands/handler.ts
|
|
53678
53729
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
53679
|
-
import { resolve as
|
|
53730
|
+
import { resolve as resolve7 } from "path";
|
|
53680
53731
|
import { existsSync as existsSync12, statSync as statSync4 } from "fs";
|
|
53681
53732
|
|
|
53682
53733
|
// node_modules/update-notifier/update-notifier.js
|
|
@@ -57686,8 +57737,8 @@ function checkGitHubCli(exec = execSync2) {
|
|
|
57686
57737
|
}
|
|
57687
57738
|
return { installed: true, authenticated: true };
|
|
57688
57739
|
}
|
|
57689
|
-
async function createGitHubIssue(title, body, workingDir) {
|
|
57690
|
-
const ghStatus = checkGitHubCli();
|
|
57740
|
+
async function createGitHubIssue(title, body, workingDir, exec = execSync2) {
|
|
57741
|
+
const ghStatus = checkGitHubCli(exec);
|
|
57691
57742
|
if (!ghStatus.installed || !ghStatus.authenticated) {
|
|
57692
57743
|
throw new Error(ghStatus.error);
|
|
57693
57744
|
}
|
|
@@ -57695,7 +57746,7 @@ async function createGitHubIssue(title, body, workingDir) {
|
|
|
57695
57746
|
try {
|
|
57696
57747
|
writeFileSync5(bodyFile, body, "utf-8");
|
|
57697
57748
|
const cmd = `gh issue create --repo "${GITHUB_REPO}" --title "${escapeShell(title)}" --body-file "${bodyFile}"`;
|
|
57698
|
-
const result =
|
|
57749
|
+
const result = exec(cmd, {
|
|
57699
57750
|
cwd: workingDir,
|
|
57700
57751
|
encoding: "utf-8",
|
|
57701
57752
|
timeout: 30000,
|
|
@@ -59627,12 +59678,13 @@ function createAppendContentOp(sessionId, content, isToolOutput) {
|
|
|
59627
59678
|
isToolOutput
|
|
59628
59679
|
};
|
|
59629
59680
|
}
|
|
59630
|
-
function createFlushOp(sessionId, reason) {
|
|
59681
|
+
function createFlushOp(sessionId, reason, resultOk) {
|
|
59631
59682
|
return {
|
|
59632
59683
|
type: "flush",
|
|
59633
59684
|
sessionId,
|
|
59634
59685
|
timestamp: Date.now(),
|
|
59635
|
-
reason
|
|
59686
|
+
reason,
|
|
59687
|
+
...resultOk === undefined ? {} : { resultOk }
|
|
59636
59688
|
};
|
|
59637
59689
|
}
|
|
59638
59690
|
function createTaskListOp(sessionId, action, tasks, toolUseId) {
|
|
@@ -59687,6 +59739,7 @@ function createStatusUpdateOp(sessionId, options) {
|
|
|
59687
59739
|
};
|
|
59688
59740
|
}
|
|
59689
59741
|
// src/operations/transformer.ts
|
|
59742
|
+
init_cli();
|
|
59690
59743
|
function transformEvent(event, ctx) {
|
|
59691
59744
|
if (event.parent_tool_use_id) {
|
|
59692
59745
|
return [];
|
|
@@ -59849,7 +59902,7 @@ function transformToolResult(event, ctx) {
|
|
|
59849
59902
|
}
|
|
59850
59903
|
function transformResult(event, ctx) {
|
|
59851
59904
|
const operations = [];
|
|
59852
|
-
operations.push(createFlushOp(ctx.sessionId, "result"));
|
|
59905
|
+
operations.push(createFlushOp(ctx.sessionId, "result", !isErrorResultEvent(event)));
|
|
59853
59906
|
const result = event;
|
|
59854
59907
|
const r = result.result;
|
|
59855
59908
|
operations.push(createStatusUpdateOp(ctx.sessionId, {
|
|
@@ -59932,6 +59985,9 @@ function truncateAtWord2(text, maxLength) {
|
|
|
59932
59985
|
}
|
|
59933
59986
|
return truncated + "...";
|
|
59934
59987
|
}
|
|
59988
|
+
// src/operations/message-manager.ts
|
|
59989
|
+
init_types();
|
|
59990
|
+
|
|
59935
59991
|
// src/operations/task-tracker.ts
|
|
59936
59992
|
var VALID_STATUSES = new Set(["pending", "in_progress", "completed"]);
|
|
59937
59993
|
var CREATED_RESULT_RE = /Task #(\S+) created/;
|
|
@@ -61891,6 +61947,9 @@ class MessageManager {
|
|
|
61891
61947
|
platform;
|
|
61892
61948
|
postTracker;
|
|
61893
61949
|
contentBreaker;
|
|
61950
|
+
turnMarker;
|
|
61951
|
+
flushInFlight = null;
|
|
61952
|
+
turn = 0;
|
|
61894
61953
|
session;
|
|
61895
61954
|
contentExecutor;
|
|
61896
61955
|
taskListExecutor;
|
|
@@ -61933,6 +61992,7 @@ class MessageManager {
|
|
|
61933
61992
|
this.startTypingCallback = options.startTyping;
|
|
61934
61993
|
this.emitSessionUpdateCallback = options.emitSessionUpdate;
|
|
61935
61994
|
this.flushDelayMs = options.flushDelayMs ?? MessageManager.DEFAULT_FLUSH_DELAY_MS;
|
|
61995
|
+
this.turnMarker = options.turnMarker ?? { mode: "off" };
|
|
61936
61996
|
this.events = createMessageManagerEvents();
|
|
61937
61997
|
this.contentBreaker = new DefaultContentBreaker;
|
|
61938
61998
|
this.contentExecutor = new ContentExecutor({
|
|
@@ -62074,15 +62134,58 @@ class MessageManager {
|
|
|
62074
62134
|
}
|
|
62075
62135
|
async handleFlushOp(op, ctx) {
|
|
62076
62136
|
this.cancelScheduledFlush();
|
|
62077
|
-
|
|
62137
|
+
if (this.flushInFlight)
|
|
62138
|
+
await this.flushInFlight.catch(() => {
|
|
62139
|
+
return;
|
|
62140
|
+
});
|
|
62141
|
+
if (op.reason === "result")
|
|
62142
|
+
this.turn++;
|
|
62143
|
+
await this.runTrackedFlush(op, ctx);
|
|
62144
|
+
if (op.reason === "result") {
|
|
62145
|
+
await this.markTurnComplete(ctx, op.resultOk !== false);
|
|
62146
|
+
}
|
|
62147
|
+
}
|
|
62148
|
+
async markTurnComplete(ctx, ok) {
|
|
62149
|
+
if (this.turnMarker.mode === "off")
|
|
62150
|
+
return;
|
|
62151
|
+
const { currentPostId, currentPostContent } = this.contentExecutor.getState();
|
|
62152
|
+
if (!currentPostId)
|
|
62153
|
+
return;
|
|
62154
|
+
try {
|
|
62155
|
+
if (this.turnMarker.mode === "metadata") {
|
|
62156
|
+
await this.platform.updatePost(currentPostId, currentPostContent, {
|
|
62157
|
+
metadata: {
|
|
62158
|
+
event_type: TURN_COMPLETE_EVENT_TYPE,
|
|
62159
|
+
event_payload: { v: TURN_COMPLETE_PAYLOAD_VERSION, session: this.sessionId, turn: this.turn, ok }
|
|
62160
|
+
}
|
|
62161
|
+
});
|
|
62162
|
+
} else {
|
|
62163
|
+
await this.platform.addReaction(currentPostId, this.turnMarker.emoji ?? "checkered_flag");
|
|
62164
|
+
}
|
|
62165
|
+
} catch (err) {
|
|
62166
|
+
const message = err.message ?? String(err);
|
|
62167
|
+
if (this.turnMarker.mode === "reaction" && message.includes("already_reacted"))
|
|
62168
|
+
return;
|
|
62169
|
+
ctx.logger.warn(`turn marker (${this.turnMarker.mode}) failed on ${currentPostId}: ${message}`);
|
|
62170
|
+
}
|
|
62171
|
+
}
|
|
62172
|
+
async runTrackedFlush(op, ctx) {
|
|
62173
|
+
const running = this.contentExecutor.executeFlush(op, ctx).finally(() => {
|
|
62174
|
+
if (this.flushInFlight === running)
|
|
62175
|
+
this.flushInFlight = null;
|
|
62176
|
+
});
|
|
62177
|
+
this.flushInFlight = running;
|
|
62178
|
+
await running;
|
|
62078
62179
|
}
|
|
62079
62180
|
scheduleFlush(ctx) {
|
|
62080
62181
|
if (this.flushTimer)
|
|
62081
62182
|
return;
|
|
62082
|
-
this.flushTimer = setTimeout(
|
|
62183
|
+
this.flushTimer = setTimeout(() => {
|
|
62083
62184
|
this.flushTimer = null;
|
|
62084
62185
|
const flushOp = createFlushOp(this.sessionId, "soft_threshold");
|
|
62085
|
-
|
|
62186
|
+
this.runTrackedFlush(flushOp, ctx).catch(() => {
|
|
62187
|
+
return;
|
|
62188
|
+
});
|
|
62086
62189
|
}, this.flushDelayMs);
|
|
62087
62190
|
}
|
|
62088
62191
|
cancelScheduledFlush() {
|
|
@@ -62093,8 +62196,12 @@ class MessageManager {
|
|
|
62093
62196
|
}
|
|
62094
62197
|
async flush() {
|
|
62095
62198
|
this.cancelScheduledFlush();
|
|
62199
|
+
if (this.flushInFlight)
|
|
62200
|
+
await this.flushInFlight.catch(() => {
|
|
62201
|
+
return;
|
|
62202
|
+
});
|
|
62096
62203
|
const flushOp = createFlushOp(this.sessionId, "explicit");
|
|
62097
|
-
await this.
|
|
62204
|
+
await this.runTrackedFlush(flushOp, this.getExecutorContext());
|
|
62098
62205
|
}
|
|
62099
62206
|
getExecutorContext() {
|
|
62100
62207
|
return {
|
|
@@ -62639,12 +62746,12 @@ function formatHistoryEntry(session, formatter, getThreadLink) {
|
|
|
62639
62746
|
const topic = getHistorySessionTopic(session, formatter);
|
|
62640
62747
|
const threadLink = formatter.formatLink(topic, getThreadLink(session.threadId));
|
|
62641
62748
|
const displayName = session.startedByDisplayName || session.startedBy;
|
|
62642
|
-
const isTimedOut = isRevivable(session)
|
|
62749
|
+
const isTimedOut = isRevivable(session);
|
|
62643
62750
|
const lastActivity = new Date(session.lastActivityAt);
|
|
62644
62751
|
const time = formatRelativeTimeShort(lastActivity);
|
|
62645
62752
|
const prStr = session.pullRequestUrl ? ` · ${formatPullRequestLink(session.pullRequestUrl, formatter)}` : "";
|
|
62646
62753
|
const indicator = isTimedOut ? "⏸️" : "✓";
|
|
62647
|
-
const resumeHint = isTimedOut ? ` · ${formatter.formatItalic("react \uD83D\uDD04 to resume")}` : "";
|
|
62754
|
+
const resumeHint = isTimedOut ? ` · ${formatter.formatItalic(session.lifecyclePostId ? "react \uD83D\uDD04 to resume" : "send a message to resume")}` : "";
|
|
62648
62755
|
const lines = [];
|
|
62649
62756
|
lines.push(` ${indicator} ${threadLink} · ${formatter.formatBold(displayName)}${prStr} · ${time}${resumeHint}`);
|
|
62650
62757
|
if (session.sessionDescription) {
|
|
@@ -63101,19 +63208,19 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
63101
63208
|
}
|
|
63102
63209
|
}
|
|
63103
63210
|
// src/memory/store.ts
|
|
63104
|
-
|
|
63211
|
+
init_state_home();
|
|
63212
|
+
import { createHash as createHash2 } from "crypto";
|
|
63105
63213
|
import {
|
|
63106
63214
|
existsSync as existsSync9,
|
|
63107
63215
|
mkdirSync as mkdirSync5,
|
|
63108
63216
|
readFileSync as readFileSync7,
|
|
63109
63217
|
realpathSync
|
|
63110
63218
|
} from "fs";
|
|
63111
|
-
import { homedir as homedir7 } from "os";
|
|
63112
63219
|
import { basename as basename3, dirname as dirname7, join as join10, sep as sep2 } from "path";
|
|
63113
63220
|
init_logger();
|
|
63114
63221
|
init_worktree();
|
|
63115
63222
|
var log18 = createLogger("memory");
|
|
63116
|
-
var DEFAULT_ROOT = join10(
|
|
63223
|
+
var DEFAULT_ROOT = join10(stateHome(), ".config", "claude-threads", "memory");
|
|
63117
63224
|
var CHANNEL_BLOCK_MAX_LINES = 200;
|
|
63118
63225
|
var CHANNEL_BLOCK_MAX_BYTES = 25 * 1024;
|
|
63119
63226
|
var CHANNEL_FILE_MAX_ENTRIES = 400;
|
|
@@ -63124,7 +63231,7 @@ function safeIdSegment2(id) {
|
|
|
63124
63231
|
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
63125
63232
|
}
|
|
63126
63233
|
function shortHash(value, length) {
|
|
63127
|
-
return
|
|
63234
|
+
return createHash2("sha256").update(value).digest("hex").slice(0, length);
|
|
63128
63235
|
}
|
|
63129
63236
|
function platformSegment(platformId) {
|
|
63130
63237
|
return `${safeIdSegment2(platformId) || "platform"}-${shortHash(platformId, 6)}`;
|
|
@@ -63504,10 +63611,10 @@ import { join as join12 } from "path";
|
|
|
63504
63611
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
63505
63612
|
|
|
63506
63613
|
// src/persistence/platform-list-store.ts
|
|
63614
|
+
init_state_home();
|
|
63507
63615
|
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
63508
|
-
import { homedir as homedir8 } from "os";
|
|
63509
63616
|
import { join as join11 } from "path";
|
|
63510
|
-
var STORES_CONFIG_DIR = join11(
|
|
63617
|
+
var STORES_CONFIG_DIR = join11(stateHome(), ".config", "claude-threads");
|
|
63511
63618
|
var STORE_VERSION2 = 1;
|
|
63512
63619
|
|
|
63513
63620
|
class PlatformListStore {
|
|
@@ -65695,12 +65802,12 @@ async function suggestSessionMetadata(context) {
|
|
|
65695
65802
|
init_quick_query();
|
|
65696
65803
|
|
|
65697
65804
|
// src/persistence/github-emails-store.ts
|
|
65805
|
+
init_state_home();
|
|
65698
65806
|
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync9 } from "fs";
|
|
65699
|
-
import { homedir as homedir9 } from "os";
|
|
65700
65807
|
import { join as join14 } from "path";
|
|
65701
65808
|
init_logger();
|
|
65702
65809
|
var log33 = createLogger("gh-emails");
|
|
65703
|
-
var DEFAULT_CONFIG_DIR2 = join14(
|
|
65810
|
+
var DEFAULT_CONFIG_DIR2 = join14(stateHome(), ".config", "claude-threads");
|
|
65704
65811
|
var DEFAULT_FILE3 = join14(DEFAULT_CONFIG_DIR2, "github-emails.yaml");
|
|
65705
65812
|
var NOREPLY_REGEX = /^\d+\+[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})@users\.noreply\.github\.com$/;
|
|
65706
65813
|
var STORE_VERSION3 = 1;
|
|
@@ -65960,7 +66067,7 @@ async function changeDirectory(session, newDir, username, ctx) {
|
|
|
65960
66067
|
return;
|
|
65961
66068
|
}
|
|
65962
66069
|
const expandedDir = newDir.startsWith("~") ? newDir.replace("~", process.env.HOME || "") : newDir;
|
|
65963
|
-
const absoluteDir =
|
|
66070
|
+
const absoluteDir = resolve7(expandedDir);
|
|
65964
66071
|
const formatter = session.platform.getFormatter();
|
|
65965
66072
|
if (!existsSync12(absoluteDir)) {
|
|
65966
66073
|
await postError(session, `Directory does not exist: ${formatter.formatCode(newDir)}`);
|
|
@@ -66451,7 +66558,7 @@ async function reportBug(session, description, username, ctx, errorContext, atta
|
|
|
66451
66558
|
});
|
|
66452
66559
|
sessionLog8(session).info(`\uD83D\uDC1B Bug report preview created by @${username}: ${title}`);
|
|
66453
66560
|
}
|
|
66454
|
-
async function handleBugReportApproval(session, isApproved, username, ctx) {
|
|
66561
|
+
async function handleBugReportApproval(session, isApproved, username, ctx, exec) {
|
|
66455
66562
|
const pending = session.messageManager?.getPendingBugReport();
|
|
66456
66563
|
if (!pending)
|
|
66457
66564
|
return;
|
|
@@ -66461,7 +66568,7 @@ async function handleBugReportApproval(session, isApproved, username, ctx) {
|
|
|
66461
66568
|
const formatter = session.platform.getFormatter();
|
|
66462
66569
|
if (isApproved) {
|
|
66463
66570
|
try {
|
|
66464
|
-
const issueUrl = await createGitHubIssue(pending.title, pending.body, session.workingDir);
|
|
66571
|
+
const issueUrl = await createGitHubIssue(pending.title, pending.body, session.workingDir, exec);
|
|
66465
66572
|
await updatePostSuccess(session, pending.postId, `${formatter.formatBold("Bug report submitted")}: ${issueUrl}`);
|
|
66466
66573
|
sessionLog8(session).info(`\uD83D\uDC1B Bug report created by @${username}: ${issueUrl}`);
|
|
66467
66574
|
} catch (err) {
|
|
@@ -67335,6 +67442,7 @@ function createMessageManager(session, ctx) {
|
|
|
67335
67442
|
sessionId: session.sessionId,
|
|
67336
67443
|
worktreePath: session.worktreeInfo?.worktreePath,
|
|
67337
67444
|
worktreeBranch: session.worktreeInfo?.branch,
|
|
67445
|
+
turnMarker: ctx.ops.getPlatformOverhead(session.platformId).turnMarker,
|
|
67338
67446
|
registerPost: (postId, options) => {
|
|
67339
67447
|
ctx.ops.registerPost(postId, session.threadId);
|
|
67340
67448
|
postTracker.register(postId, session.threadId, session.sessionId, options);
|
|
@@ -67977,7 +68085,7 @@ ${sessionFormatter.formatItalic(outcome)}`;
|
|
|
67977
68085
|
await withErrorHandling(() => session.platform.updatePost(postId, resumeMsg), { action: "Update timeout/shutdown post for resume", session });
|
|
67978
68086
|
session.lifecyclePostId = undefined;
|
|
67979
68087
|
transitionTo(session, "active");
|
|
67980
|
-
} else {
|
|
68088
|
+
} else if (shouldPostLifecycle(ctx.ops.getPlatformOverhead(session.platformId).lifecycle, "resumed")) {
|
|
67981
68089
|
const suffix = trigger === "platform-enabled" ? " (platform re-enabled)" : ` after bot restart (v${VERSION})`;
|
|
67982
68090
|
const restartMsg = `${sessionFormatter.formatBold("Session resumed")}${suffix}
|
|
67983
68091
|
${sessionFormatter.formatItalic(outcome)}`;
|
|
@@ -68114,7 +68222,7 @@ async function handleExit(sessionId, code, ctx, source) {
|
|
|
68114
68222
|
cleanupSessionTimers(session);
|
|
68115
68223
|
await closeThreadLogger(session, "interrupt", { exitCode: code }, "pause");
|
|
68116
68224
|
const message = session.lifecycle.hasClaudeResponded ? `ℹ️ Session paused. Send a new message to continue.` : `ℹ️ Session ended before Claude could respond. Send a new message to start fresh.`;
|
|
68117
|
-
const pausePost = await withErrorHandling(() => post(session, "info", message), { action: "Post session pause notification", session });
|
|
68225
|
+
const pausePost = shouldPostLifecycle(ctx.ops.getPlatformOverhead(session.platformId).lifecycle, "paused") ? await withErrorHandling(() => post(session, "info", message), { action: "Post session pause notification", session }) : null;
|
|
68118
68226
|
if (session.lifecycle.hasClaudeResponded) {
|
|
68119
68227
|
transitionTo(session, "paused");
|
|
68120
68228
|
if (pausePost) {
|
|
@@ -68193,7 +68301,7 @@ Please start a new session.`), { action: "Post session permanent failure", sessi
|
|
|
68193
68301
|
await session.platform.unpinPost(exitTaskState.postId).catch(() => {});
|
|
68194
68302
|
}
|
|
68195
68303
|
await ctx.ops.flush(session);
|
|
68196
|
-
if (code !== 0 && code !== null) {
|
|
68304
|
+
if (code !== 0 && code !== null && shouldPostLifecycle(ctx.ops.getPlatformOverhead(session.platformId).lifecycle, "abnormal-exit")) {
|
|
68197
68305
|
const exitFormatter = session.platform.getFormatter();
|
|
68198
68306
|
await post(session, "info", exitFormatter.formatBold(`[Exited: ${code}]`));
|
|
68199
68307
|
}
|
|
@@ -68267,7 +68375,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
|
|
|
68267
68375
|
const postId = session.lifecyclePostId;
|
|
68268
68376
|
await withErrorHandling(() => session.platform.updatePost(postId, `⏱️ ${timeoutMessage}`), { action: "Update timeout post", session });
|
|
68269
68377
|
} else {
|
|
68270
|
-
const timeoutPost = await withErrorHandling(() => post(session, "timeout", timeoutMessage), { action: "Post session timeout", session });
|
|
68378
|
+
const timeoutPost = shouldPostLifecycle(ctx.ops.getPlatformOverhead(session.platformId).lifecycle, "timed-out") ? await withErrorHandling(() => post(session, "timeout", timeoutMessage), { action: "Post session timeout", session }) : null;
|
|
68271
68379
|
if (timeoutPost) {
|
|
68272
68380
|
session.lifecyclePostId = timeoutPost.id;
|
|
68273
68381
|
ctx.ops.registerPost(timeoutPost.id, session.threadId);
|
|
@@ -68280,7 +68388,8 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
|
|
|
68280
68388
|
continue;
|
|
68281
68389
|
}
|
|
68282
68390
|
const warningThresholdMs = timeoutMs - warningMs;
|
|
68283
|
-
|
|
68391
|
+
const idleWarningWanted = shouldPostLifecycle(ctx.ops.getPlatformOverhead(session.platformId).lifecycle, "idle-warning");
|
|
68392
|
+
if (idleWarningWanted && idleMs > warningThresholdMs && !session.timeoutWarningPosted) {
|
|
68284
68393
|
const remainingMins = Math.max(0, Math.round((timeoutMs - idleMs) / 60000));
|
|
68285
68394
|
const warningFormatter = session.platform.getFormatter();
|
|
68286
68395
|
const warnWaiting = session.isProcessing && (session.messageManager?.hasPendingApproval() === true || session.messageManager?.hasPendingQuestions() === true);
|
|
@@ -70416,7 +70525,7 @@ class MattermostClient extends BasePlatformClient {
|
|
|
70416
70525
|
const post = await this.api("POST", "/posts", request);
|
|
70417
70526
|
return this.normalizePlatformPost(post);
|
|
70418
70527
|
}
|
|
70419
|
-
async updatePost(postId, message) {
|
|
70528
|
+
async updatePost(postId, message, _options) {
|
|
70420
70529
|
const request = {
|
|
70421
70530
|
id: postId,
|
|
70422
70531
|
message
|
|
@@ -71464,6 +71573,9 @@ class SlackClient extends BasePlatformClient {
|
|
|
71464
71573
|
if (resolvedThreadId) {
|
|
71465
71574
|
body.thread_ts = resolvedThreadId;
|
|
71466
71575
|
}
|
|
71576
|
+
if (options?.metadata) {
|
|
71577
|
+
body.metadata = options.metadata;
|
|
71578
|
+
}
|
|
71467
71579
|
const response = await this.api("POST", "chat.postMessage", body);
|
|
71468
71580
|
return {
|
|
71469
71581
|
id: response.ts,
|
|
@@ -71475,13 +71587,16 @@ class SlackClient extends BasePlatformClient {
|
|
|
71475
71587
|
createAt: Math.floor(parseFloat(response.ts) * 1000)
|
|
71476
71588
|
};
|
|
71477
71589
|
}
|
|
71478
|
-
async updatePost(postId, message) {
|
|
71590
|
+
async updatePost(postId, message, options) {
|
|
71479
71591
|
const truncatedMessage = this.truncateMessageIfNeeded(message);
|
|
71480
|
-
const
|
|
71592
|
+
const body = {
|
|
71481
71593
|
channel: this.channelId,
|
|
71482
71594
|
ts: postId,
|
|
71483
71595
|
text: truncatedMessage
|
|
71484
|
-
}
|
|
71596
|
+
};
|
|
71597
|
+
if (options?.metadata)
|
|
71598
|
+
body.metadata = options.metadata;
|
|
71599
|
+
const response = await this.api("POST", "chat.update", body);
|
|
71485
71600
|
return {
|
|
71486
71601
|
id: response.ts,
|
|
71487
71602
|
platformId: this.platformId,
|
|
@@ -72973,7 +73088,9 @@ class SessionManager extends EventEmitter4 {
|
|
|
72973
73088
|
this.platforms.set(platformId, client);
|
|
72974
73089
|
this.platformOverhead.set(platformId, {
|
|
72975
73090
|
sessionHeader: options?.overhead?.sessionHeader ?? DEFAULT_OVERHEAD_VISIBILITY,
|
|
72976
|
-
stickyMessage: options?.overhead?.stickyMessage ?? DEFAULT_OVERHEAD_VISIBILITY
|
|
73091
|
+
stickyMessage: options?.overhead?.stickyMessage ?? DEFAULT_OVERHEAD_VISIBILITY,
|
|
73092
|
+
lifecycle: options?.overhead?.lifecycle ?? DEFAULT_OVERHEAD_VISIBILITY,
|
|
73093
|
+
turnMarker: options?.overhead?.turnMarker ?? DEFAULT_TURN_MARKER
|
|
72977
73094
|
});
|
|
72978
73095
|
this.platformMemory.set(platformId, options?.memory ?? DEFAULT_MEMORY_CONFIG);
|
|
72979
73096
|
this.platformRoutines.set(platformId, options?.routinesEnabled ?? true);
|
|
@@ -73111,7 +73228,9 @@ class SessionManager extends EventEmitter4 {
|
|
|
73111
73228
|
getClaudeAccountPoolStatus: () => this.accountPool.status(),
|
|
73112
73229
|
getPlatformOverhead: (pid) => this.platformOverhead.get(pid) ?? {
|
|
73113
73230
|
sessionHeader: DEFAULT_OVERHEAD_VISIBILITY,
|
|
73114
|
-
stickyMessage: DEFAULT_OVERHEAD_VISIBILITY
|
|
73231
|
+
stickyMessage: DEFAULT_OVERHEAD_VISIBILITY,
|
|
73232
|
+
lifecycle: DEFAULT_OVERHEAD_VISIBILITY,
|
|
73233
|
+
turnMarker: DEFAULT_TURN_MARKER
|
|
73115
73234
|
},
|
|
73116
73235
|
getPlatformMemoryConfig: (pid) => this.platformMemory.get(pid) ?? DEFAULT_MEMORY_CONFIG,
|
|
73117
73236
|
isRoutinesEnabled: (pid) => this.platformRoutines.get(pid) ?? true,
|
|
@@ -73404,7 +73523,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
73404
73523
|
const pauseMessage = `⏸️ ${fmt.formatBold("Platform disabled")} - session paused. Re-enable platform to resume.`;
|
|
73405
73524
|
if (session.lifecyclePostId) {
|
|
73406
73525
|
await session.platform.updatePost(session.lifecyclePostId, pauseMessage);
|
|
73407
|
-
} else {
|
|
73526
|
+
} else if (shouldPostLifecycle(this.platformOverhead.get(session.platformId)?.lifecycle ?? DEFAULT_OVERHEAD_VISIBILITY, "paused")) {
|
|
73408
73527
|
const post = await session.platform.createPost(pauseMessage, session.threadId);
|
|
73409
73528
|
session.lifecyclePostId = post.id;
|
|
73410
73529
|
}
|
|
@@ -73896,7 +74015,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
73896
74015
|
const shutdownMessage = `⏸️ ${fmt.formatBold("Bot shutting down")} - session will resume on restart`;
|
|
73897
74016
|
if (session.lifecyclePostId) {
|
|
73898
74017
|
await session.platform.updatePost(session.lifecyclePostId, shutdownMessage);
|
|
73899
|
-
} else {
|
|
74018
|
+
} else if (shouldPostLifecycle(this.platformOverhead.get(session.platformId)?.lifecycle ?? DEFAULT_OVERHEAD_VISIBILITY, "shutdown")) {
|
|
73900
74019
|
const post = await session.platform.createPost(shutdownMessage, session.threadId);
|
|
73901
74020
|
session.lifecyclePostId = post.id;
|
|
73902
74021
|
}
|
|
@@ -86492,10 +86611,11 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
86492
86611
|
}
|
|
86493
86612
|
|
|
86494
86613
|
// src/auto-update/installer.ts
|
|
86614
|
+
init_state_home();
|
|
86495
86615
|
import { spawn as spawn4, spawnSync } from "child_process";
|
|
86496
86616
|
import { existsSync as existsSync17, readFileSync as readFileSync12, mkdirSync as mkdirSync8 } from "fs";
|
|
86497
|
-
import { dirname as dirname9, resolve as
|
|
86498
|
-
import { homedir as
|
|
86617
|
+
import { dirname as dirname9, resolve as resolve8 } from "path";
|
|
86618
|
+
import { homedir as homedir3 } from "os";
|
|
86499
86619
|
init_logger();
|
|
86500
86620
|
var log56 = createLogger("installer");
|
|
86501
86621
|
function detectPackageManager() {
|
|
@@ -86536,7 +86656,7 @@ function normalizePath(p) {
|
|
|
86536
86656
|
function detectOriginalInstaller() {
|
|
86537
86657
|
try {
|
|
86538
86658
|
const scriptPath = normalizePath(process.argv[1] || "");
|
|
86539
|
-
const bunGlobalDir = normalizePath(process.env.BUN_INSTALL ||
|
|
86659
|
+
const bunGlobalDir = normalizePath(process.env.BUN_INSTALL || resolve8(homedir3(), ".bun"));
|
|
86540
86660
|
if (scriptPath.startsWith(bunGlobalDir)) {
|
|
86541
86661
|
return "bun";
|
|
86542
86662
|
}
|
|
@@ -86556,7 +86676,7 @@ function detectOriginalInstaller() {
|
|
|
86556
86676
|
return null;
|
|
86557
86677
|
}
|
|
86558
86678
|
}
|
|
86559
|
-
var STATE_PATH =
|
|
86679
|
+
var STATE_PATH = resolve8(stateHome(), ".config", "claude-threads", UPDATE_STATE_FILENAME);
|
|
86560
86680
|
var PACKAGE_NAME2 = "claude-threads";
|
|
86561
86681
|
function loadUpdateState() {
|
|
86562
86682
|
try {
|
|
@@ -86978,6 +87098,102 @@ ${getRollbackInstructions(VERSION)}`).catch(() => {});
|
|
|
86978
87098
|
this.callbacks.refreshUI().catch(() => {});
|
|
86979
87099
|
}
|
|
86980
87100
|
}
|
|
87101
|
+
// src/utils/instance-lock.ts
|
|
87102
|
+
init_state_home();
|
|
87103
|
+
init_logger();
|
|
87104
|
+
import { closeSync as closeSync2, fstatSync, linkSync, mkdirSync as mkdirSync9, openSync as openSync2, readFileSync as readFileSync13, renameSync as renameSync2, statSync as statSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
87105
|
+
import { dirname as dirname10, join as join19 } from "path";
|
|
87106
|
+
var log59 = createLogger("lock");
|
|
87107
|
+
var LOCKED_EXIT_CODE = 3;
|
|
87108
|
+
function acquireInstanceLock() {
|
|
87109
|
+
const path = join19(stateHome(), ".config", "claude-threads", "instance.lock");
|
|
87110
|
+
mkdirSync9(dirname10(path), { recursive: true, mode: 448 });
|
|
87111
|
+
const tmp = `${path}.${process.pid}`;
|
|
87112
|
+
writeFileSync6(tmp, String(process.pid), { mode: 384 });
|
|
87113
|
+
try {
|
|
87114
|
+
for (let attempt = 0;attempt < 5; attempt++) {
|
|
87115
|
+
let linked;
|
|
87116
|
+
try {
|
|
87117
|
+
linked = tryLink(tmp, path);
|
|
87118
|
+
} catch (err) {
|
|
87119
|
+
log59.warn(`instance lock unavailable on this filesystem (${err.code ?? String(err)}); running without it`);
|
|
87120
|
+
return () => {};
|
|
87121
|
+
}
|
|
87122
|
+
if (linked)
|
|
87123
|
+
return () => release(path);
|
|
87124
|
+
const seen = inspect(path);
|
|
87125
|
+
if (!seen)
|
|
87126
|
+
continue;
|
|
87127
|
+
if (seen.pid === process.pid)
|
|
87128
|
+
return () => release(path);
|
|
87129
|
+
if (seen.pid && isAlive(seen.pid)) {
|
|
87130
|
+
throw new Error(`another claude-threads instance (pid ${seen.pid}) already uses ${dirname10(path)} — ` + `set CLAUDE_THREADS_HOME to run a second bot; if pid ${seen.pid} is not a claude-threads process, delete ${path}`);
|
|
87131
|
+
}
|
|
87132
|
+
const aside = `${path}.stale.${process.pid}`;
|
|
87133
|
+
if (!tryRename(path, aside))
|
|
87134
|
+
continue;
|
|
87135
|
+
if (statSync6(aside).ino !== seen.ino) {
|
|
87136
|
+
tryLink(aside, path);
|
|
87137
|
+
unlinkSync4(aside);
|
|
87138
|
+
continue;
|
|
87139
|
+
}
|
|
87140
|
+
unlinkSync4(aside);
|
|
87141
|
+
}
|
|
87142
|
+
throw new Error(`could not acquire ${path}: lost the race five times`);
|
|
87143
|
+
} finally {
|
|
87144
|
+
try {
|
|
87145
|
+
unlinkSync4(tmp);
|
|
87146
|
+
} catch {}
|
|
87147
|
+
}
|
|
87148
|
+
}
|
|
87149
|
+
function inspect(path) {
|
|
87150
|
+
let fd;
|
|
87151
|
+
try {
|
|
87152
|
+
fd = openSync2(path, "r");
|
|
87153
|
+
} catch {
|
|
87154
|
+
return;
|
|
87155
|
+
}
|
|
87156
|
+
try {
|
|
87157
|
+
return { pid: Number(readFileSync13(fd, "utf8").trim()) || undefined, ino: fstatSync(fd).ino };
|
|
87158
|
+
} finally {
|
|
87159
|
+
closeSync2(fd);
|
|
87160
|
+
}
|
|
87161
|
+
}
|
|
87162
|
+
function tryLink(src, path) {
|
|
87163
|
+
try {
|
|
87164
|
+
linkSync(src, path);
|
|
87165
|
+
return true;
|
|
87166
|
+
} catch (err) {
|
|
87167
|
+
if (err.code === "EEXIST")
|
|
87168
|
+
return false;
|
|
87169
|
+
throw err;
|
|
87170
|
+
}
|
|
87171
|
+
}
|
|
87172
|
+
function tryRename(from, to) {
|
|
87173
|
+
try {
|
|
87174
|
+
renameSync2(from, to);
|
|
87175
|
+
return true;
|
|
87176
|
+
} catch (err) {
|
|
87177
|
+
if (err.code === "ENOENT")
|
|
87178
|
+
return false;
|
|
87179
|
+
throw err;
|
|
87180
|
+
}
|
|
87181
|
+
}
|
|
87182
|
+
function release(path) {
|
|
87183
|
+
try {
|
|
87184
|
+
if (inspect(path)?.pid === process.pid)
|
|
87185
|
+
unlinkSync4(path);
|
|
87186
|
+
} catch {}
|
|
87187
|
+
}
|
|
87188
|
+
function isAlive(pid) {
|
|
87189
|
+
try {
|
|
87190
|
+
process.kill(pid, 0);
|
|
87191
|
+
return true;
|
|
87192
|
+
} catch (err) {
|
|
87193
|
+
return err.code === "EPERM";
|
|
87194
|
+
}
|
|
87195
|
+
}
|
|
87196
|
+
|
|
86981
87197
|
// src/index.ts
|
|
86982
87198
|
function createPlatformClient(config) {
|
|
86983
87199
|
switch (config.type) {
|
|
@@ -87207,6 +87423,15 @@ async function startWithoutDaemon() {
|
|
|
87207
87423
|
console.error(yellow(` ⚠️ --skip-version-check: ${prefix}${claudeValidation.message}`));
|
|
87208
87424
|
console.error("");
|
|
87209
87425
|
}
|
|
87426
|
+
let releaseInstanceLock;
|
|
87427
|
+
try {
|
|
87428
|
+
releaseInstanceLock = acquireInstanceLock();
|
|
87429
|
+
} catch (err) {
|
|
87430
|
+
console.error(red(` ❌ ${err instanceof Error ? err.message : String(err)}`));
|
|
87431
|
+
console.error("");
|
|
87432
|
+
process.exit(LOCKED_EXIT_CODE);
|
|
87433
|
+
}
|
|
87434
|
+
process.on("exit", () => releaseInstanceLock());
|
|
87210
87435
|
if (process.env.CLAUDE_CODE_SUBPROCESS_ENV_SCRUB === "1") {
|
|
87211
87436
|
const hasSkipPermissionPlatform = config.platforms.some((p) => p.skipPermissions === true);
|
|
87212
87437
|
if (hasSkipPermissionPlatform) {
|
|
@@ -87385,7 +87610,9 @@ async function startWithoutDaemon() {
|
|
|
87385
87610
|
session.addPlatform(platformConfig.id, client, {
|
|
87386
87611
|
overhead: {
|
|
87387
87612
|
sessionHeader: resolveOverheadVisibility(platformConfig.sessionHeader, `platforms[${platformConfig.id}].sessionHeader`),
|
|
87388
|
-
stickyMessage: resolveOverheadVisibility(platformConfig.stickyMessage, `platforms[${platformConfig.id}].stickyMessage`)
|
|
87613
|
+
stickyMessage: resolveOverheadVisibility(platformConfig.stickyMessage, `platforms[${platformConfig.id}].stickyMessage`),
|
|
87614
|
+
lifecycle: resolveOverheadVisibility(platformConfig.lifecycle, `platforms[${platformConfig.id}].lifecycle`),
|
|
87615
|
+
turnMarker: resolveTurnMarker(platformConfig.turnMarker, platformConfig.turnMarkerEmoji, platformConfig.type, `platforms[${platformConfig.id}]`)
|
|
87389
87616
|
},
|
|
87390
87617
|
memory: resolveMemoryConfig(platformConfig.memory, `platforms[${platformConfig.id}].memory`),
|
|
87391
87618
|
routinesEnabled: resolveRoutinesEnabled(platformConfig.routines, `platforms[${platformConfig.id}].routines`),
|
|
@@ -87412,7 +87639,9 @@ async function startWithoutDaemon() {
|
|
|
87412
87639
|
session.addPlatform(dmConfig.id, dmClient, {
|
|
87413
87640
|
overhead: {
|
|
87414
87641
|
sessionHeader: resolveOverheadVisibility(dmConfig.sessionHeader, `dm[${dmConfig.id}].sessionHeader`),
|
|
87415
|
-
stickyMessage: "hidden"
|
|
87642
|
+
stickyMessage: "hidden",
|
|
87643
|
+
lifecycle: resolveOverheadVisibility(dmConfig.lifecycle, `dm[${dmConfig.id}].lifecycle`),
|
|
87644
|
+
turnMarker: resolveTurnMarker(dmConfig.turnMarker, dmConfig.turnMarkerEmoji, dmConfig.type, `dm[${dmConfig.id}]`)
|
|
87416
87645
|
},
|
|
87417
87646
|
memory: resolveMemoryConfig(dmConfig.memory, `dm[${dmConfig.id}].memory`),
|
|
87418
87647
|
routinesEnabled: resolveRoutinesEnabled(dmConfig.routines, `dm[${dmConfig.id}].routines`),
|