claude-threads 1.33.0 → 1.33.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/dist/index.js CHANGED
@@ -4336,10 +4336,10 @@ __export(exports_worktree, {
4336
4336
  import { randomUUID as randomUUID2 } from "crypto";
4337
4337
  import * as path from "path";
4338
4338
  import * as fs from "fs/promises";
4339
- import { homedir as homedir4 } from "os";
4339
+ import { homedir as homedir5 } from "os";
4340
4340
  async function execGit(args, cwd) {
4341
4341
  const cmd = `git ${args.join(" ")}`;
4342
- log8.debug(`Executing: ${cmd}`);
4342
+ log9.debug(`Executing: ${cmd}`);
4343
4343
  return new Promise((resolve6, reject) => {
4344
4344
  const proc = crossSpawn("git", args, { cwd });
4345
4345
  let stdout = "";
@@ -4352,15 +4352,15 @@ async function execGit(args, cwd) {
4352
4352
  });
4353
4353
  proc.on("close", (code) => {
4354
4354
  if (code === 0) {
4355
- log8.debug(`${cmd} → success`);
4355
+ log9.debug(`${cmd} → success`);
4356
4356
  resolve6(stdout.trim());
4357
4357
  } else {
4358
- log8.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
4358
+ log9.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
4359
4359
  reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
4360
4360
  }
4361
4361
  });
4362
4362
  proc.on("error", (err) => {
4363
- log8.warn(`${cmd} → error: ${err}`);
4363
+ log9.warn(`${cmd} → error: ${err}`);
4364
4364
  reject(err);
4365
4365
  });
4366
4366
  });
@@ -4370,7 +4370,7 @@ async function isGitRepository(dir) {
4370
4370
  await execGit(["rev-parse", "--git-dir"], dir);
4371
4371
  return true;
4372
4372
  } catch (err) {
4373
- log8.debug(`Not a git repository: ${dir} (${err})`);
4373
+ log9.debug(`Not a git repository: ${dir} (${err})`);
4374
4374
  return false;
4375
4375
  }
4376
4376
  }
@@ -4520,7 +4520,7 @@ async function detectWorktreeInfo(workingDir) {
4520
4520
  const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
4521
4521
  const branch = branchOutput?.trim();
4522
4522
  if (!branch || branch === "HEAD") {
4523
- log8.debug(`Could not detect branch for worktree at ${workingDir}`);
4523
+ log9.debug(`Could not detect branch for worktree at ${workingDir}`);
4524
4524
  return null;
4525
4525
  }
4526
4526
  const toplevel = (await execGit(["rev-parse", "--show-toplevel"], workingDir))?.trim();
@@ -4528,45 +4528,45 @@ async function detectWorktreeInfo(workingDir) {
4528
4528
  return null;
4529
4529
  }
4530
4530
  const repoRoot = await getMainRepositoryRoot(workingDir);
4531
- log8.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
4531
+ log9.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
4532
4532
  return {
4533
4533
  worktreePath: toplevel,
4534
4534
  branch,
4535
4535
  repoRoot: repoRoot || toplevel
4536
4536
  };
4537
4537
  } catch (err) {
4538
- log8.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
4538
+ log9.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
4539
4539
  return null;
4540
4540
  }
4541
4541
  }
4542
4542
  async function createWorktree(repoRoot, branch, targetDir) {
4543
- log8.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
4543
+ log9.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
4544
4544
  const parentDir = path.dirname(targetDir);
4545
- log8.debug(`Creating parent directory: ${parentDir}`);
4545
+ log9.debug(`Creating parent directory: ${parentDir}`);
4546
4546
  await fs.mkdir(parentDir, { recursive: true });
4547
4547
  const exists = await branchExists(repoRoot, branch);
4548
4548
  if (exists) {
4549
- log8.debug(`Branch '${branch}' exists, adding worktree`);
4549
+ log9.debug(`Branch '${branch}' exists, adding worktree`);
4550
4550
  await execGit(["worktree", "add", "--", targetDir, branch], repoRoot);
4551
4551
  } else {
4552
- log8.debug(`Branch '${branch}' does not exist, creating with worktree`);
4552
+ log9.debug(`Branch '${branch}' does not exist, creating with worktree`);
4553
4553
  await execGit(["worktree", "add", "-b", branch, "--", targetDir], repoRoot);
4554
4554
  }
4555
- log8.info(`Worktree created successfully: ${targetDir}`);
4555
+ log9.info(`Worktree created successfully: ${targetDir}`);
4556
4556
  return targetDir;
4557
4557
  }
4558
4558
  async function removeWorktree(repoRoot, worktreePath) {
4559
- log8.info(`Removing worktree: ${worktreePath}`);
4559
+ log9.info(`Removing worktree: ${worktreePath}`);
4560
4560
  try {
4561
4561
  await execGit(["worktree", "remove", worktreePath], repoRoot);
4562
- log8.debug("Worktree removed cleanly");
4562
+ log9.debug("Worktree removed cleanly");
4563
4563
  } catch (err) {
4564
- log8.debug(`Clean remove failed (${err}), trying force remove`);
4564
+ log9.debug(`Clean remove failed (${err}), trying force remove`);
4565
4565
  await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
4566
4566
  }
4567
- log8.debug("Pruning stale worktree references");
4567
+ log9.debug("Pruning stale worktree references");
4568
4568
  await execGit(["worktree", "prune"], repoRoot);
4569
- log8.info("Worktree removed and pruned successfully");
4569
+ log9.info("Worktree removed and pruned successfully");
4570
4570
  }
4571
4571
  async function findWorktreeByBranch(repoRoot, branch) {
4572
4572
  const worktrees = await listWorktrees(repoRoot);
@@ -4609,14 +4609,14 @@ async function writeMetadataStore(store) {
4609
4609
  await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
4610
4610
  await fs.chmod(METADATA_STORE_PATH, 384);
4611
4611
  } catch (err) {
4612
- log8.warn(`Failed to write worktree metadata store: ${err}`);
4612
+ log9.warn(`Failed to write worktree metadata store: ${err}`);
4613
4613
  }
4614
4614
  }
4615
4615
  async function writeWorktreeMetadata(worktreePath, metadata) {
4616
4616
  const store = await readMetadataStore();
4617
4617
  store[worktreePath] = metadata;
4618
4618
  await writeMetadataStore(store);
4619
- log8.debug(`Wrote worktree metadata for: ${worktreePath}`);
4619
+ log9.debug(`Wrote worktree metadata for: ${worktreePath}`);
4620
4620
  }
4621
4621
  async function readWorktreeMetadata(worktreePath) {
4622
4622
  const store = await readMetadataStore();
@@ -4639,16 +4639,16 @@ async function removeWorktreeMetadata(worktreePath) {
4639
4639
  if (store[worktreePath]) {
4640
4640
  delete store[worktreePath];
4641
4641
  await writeMetadataStore(store);
4642
- log8.debug(`Removed worktree metadata for: ${worktreePath}`);
4642
+ log9.debug(`Removed worktree metadata for: ${worktreePath}`);
4643
4643
  }
4644
4644
  }
4645
- var log8, WORKTREES_DIR, METADATA_STORE_PATH;
4645
+ var log9, WORKTREES_DIR, METADATA_STORE_PATH;
4646
4646
  var init_worktree = __esm(() => {
4647
4647
  init_spawn();
4648
4648
  init_logger();
4649
- log8 = createLogger("git-wt");
4650
- WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
4651
- METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
4649
+ log9 = createLogger("git-wt");
4650
+ WORKTREES_DIR = path.join(homedir5(), ".claude-threads", "worktrees");
4651
+ METADATA_STORE_PATH = path.join(homedir5(), ".claude-threads", "worktree-metadata.json");
4652
4652
  });
4653
4653
 
4654
4654
  // node_modules/graceful-fs/polyfills.js
@@ -6555,7 +6555,7 @@ var require_minimist = __commonJS((exports, module) => {
6555
6555
  // node_modules/rc/index.js
6556
6556
  var require_rc = __commonJS((exports, module) => {
6557
6557
  var cc = require_utils();
6558
- var join8 = __require("path").join;
6558
+ var join9 = __require("path").join;
6559
6559
  var deepExtend = require_deep_extend();
6560
6560
  var etc = "/etc";
6561
6561
  var win = process.platform === "win32";
@@ -6581,15 +6581,15 @@ var require_rc = __commonJS((exports, module) => {
6581
6581
  }
6582
6582
  if (!win)
6583
6583
  [
6584
- join8(etc, name, "config"),
6585
- join8(etc, name + "rc")
6584
+ join9(etc, name, "config"),
6585
+ join9(etc, name + "rc")
6586
6586
  ].forEach(addConfigFile);
6587
6587
  if (home)
6588
6588
  [
6589
- join8(home, ".config", name, "config"),
6590
- join8(home, ".config", name),
6591
- join8(home, "." + name, "config"),
6592
- join8(home, "." + name + "rc")
6589
+ join9(home, ".config", name, "config"),
6590
+ join9(home, ".config", name),
6591
+ join9(home, "." + name, "config"),
6592
+ join9(home, "." + name + "rc")
6593
6593
  ].forEach(addConfigFile);
6594
6594
  addConfigFile(cc.find("." + name + "rc"));
6595
6595
  if (env3.config)
@@ -10982,7 +10982,7 @@ async function quickQuery(options) {
10982
10982
  if (systemPrompt) {
10983
10983
  args.push("--system-prompt", systemPrompt);
10984
10984
  }
10985
- log18.debug(`Quick query: model=${model}, timeout=${timeout2}ms, prompt="${prompt.substring(0, 50)}..."`);
10985
+ log19.debug(`Quick query: model=${model}, timeout=${timeout2}ms, prompt="${prompt.substring(0, 50)}..."`);
10986
10986
  return new Promise((resolve6) => {
10987
10987
  let stdout = "";
10988
10988
  let stderr = "";
@@ -10996,7 +10996,7 @@ async function quickQuery(options) {
10996
10996
  if (!resolved) {
10997
10997
  resolved = true;
10998
10998
  proc.kill("SIGTERM");
10999
- log18.debug(`Quick query timed out after ${timeout2}ms`);
10999
+ log19.debug(`Quick query timed out after ${timeout2}ms`);
11000
11000
  resolve6({
11001
11001
  success: false,
11002
11002
  error: "timeout",
@@ -11014,7 +11014,7 @@ async function quickQuery(options) {
11014
11014
  if (!resolved) {
11015
11015
  resolved = true;
11016
11016
  clearTimeout(timeoutId);
11017
- log18.debug(`Quick query error: ${err.message}`);
11017
+ log19.debug(`Quick query error: ${err.message}`);
11018
11018
  resolve6({
11019
11019
  success: false,
11020
11020
  error: err.message,
@@ -11028,14 +11028,14 @@ async function quickQuery(options) {
11028
11028
  clearTimeout(timeoutId);
11029
11029
  const durationMs = Date.now() - startTime;
11030
11030
  if (code === 0 && stdout.trim()) {
11031
- log18.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
11031
+ log19.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
11032
11032
  resolve6({
11033
11033
  success: true,
11034
11034
  response: stdout.trim(),
11035
11035
  durationMs
11036
11036
  });
11037
11037
  } else {
11038
- log18.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
11038
+ log19.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
11039
11039
  resolve6({
11040
11040
  success: false,
11041
11041
  error: stderr || `exit code ${code}`,
@@ -11045,17 +11045,17 @@ async function quickQuery(options) {
11045
11045
  }
11046
11046
  });
11047
11047
  proc.stdin?.on("error", (err) => {
11048
- log18.debug(`quickQuery: stdin write failed (${err.code ?? err.message})`);
11048
+ log19.debug(`quickQuery: stdin write failed (${err.code ?? err.message})`);
11049
11049
  });
11050
11050
  proc.stdin?.end(prompt);
11051
11051
  });
11052
11052
  }
11053
- var log18;
11053
+ var log19;
11054
11054
  var init_quick_query = __esm(() => {
11055
11055
  init_spawn();
11056
11056
  init_version_check();
11057
11057
  init_logger();
11058
- log18 = createLogger("query");
11058
+ log19 = createLogger("query");
11059
11059
  });
11060
11060
 
11061
11061
  // node_modules/kleur/index.js
@@ -52332,9 +52332,13 @@ function buildClaudeChildEnv(parentEnv, account, opts) {
52332
52332
  env.USERPROFILE = account.home;
52333
52333
  delete env.ANTHROPIC_API_KEY;
52334
52334
  delete env.CLAUDE_CODE_OAUTH_TOKEN;
52335
+ delete env.ANTHROPIC_AUTH_TOKEN;
52336
+ delete env.CLAUDE_CONFIG_DIR;
52337
+ delete env.CLAUDE_SECURESTORAGE_CONFIG_DIR;
52335
52338
  } else if (account?.apiKey) {
52336
52339
  env.ANTHROPIC_API_KEY = account.apiKey;
52337
52340
  delete env.CLAUDE_CODE_OAUTH_TOKEN;
52341
+ delete env.ANTHROPIC_AUTH_TOKEN;
52338
52342
  }
52339
52343
  return env;
52340
52344
  }
@@ -52835,13 +52839,349 @@ class ClaudeCli extends EventEmitter {
52835
52839
  }
52836
52840
  }
52837
52841
 
52838
- // src/persistence/thread-logger.ts
52842
+ // src/persistence/session-store.ts
52843
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "fs";
52844
+
52845
+ // src/persistence/atomic-file.ts
52846
+ import { chmodSync as chmodSync3, renameSync, writeFileSync as writeFileSync3 } from "fs";
52847
+
52848
+ class SerialQueue {
52849
+ tail = Promise.resolve();
52850
+ run(fn) {
52851
+ const next = this.tail.then(fn, fn);
52852
+ this.tail = next.catch(() => {
52853
+ return;
52854
+ });
52855
+ return next;
52856
+ }
52857
+ }
52858
+ function writeFileAtomic(file, content) {
52859
+ const tempFile = `${file}.tmp`;
52860
+ writeFileSync3(tempFile, content, { encoding: "utf-8", mode: 384 });
52861
+ renameSync(tempFile, file);
52862
+ chmodSync3(file, 384);
52863
+ }
52864
+
52865
+ // src/persistence/session-store.ts
52839
52866
  init_logger();
52840
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, appendFileSync, readdirSync as readdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, rmdirSync, readFileSync as readFileSync3, chmodSync as chmodSync3 } from "fs";
52841
52867
  import { homedir as homedir3 } from "os";
52842
- import { join as join5, dirname as dirname3 } from "path";
52843
- var log4 = createLogger("thread-log");
52844
- var LOGS_BASE_DIR = join5(homedir3(), ".claude-threads", "logs");
52868
+ import { join as join5 } from "path";
52869
+
52870
+ // src/sponsor.ts
52871
+ var SPONSOR_URL = "https://github.com/sponsors/axolotl-systems";
52872
+ var SESSION_MILESTONES = [100, 250, 500, 1000, 2500, 5000, 1e4];
52873
+ var MILESTONE_VISIBLE_MS = 24 * 60 * 60 * 1000;
52874
+ function milestoneReached(totalSessions) {
52875
+ return SESSION_MILESTONES.includes(totalSessions) ? totalSessions : null;
52876
+ }
52877
+ function milestoneStillFresh(reachedAtIso, nowMs) {
52878
+ const reachedAt = Date.parse(reachedAtIso);
52879
+ if (Number.isNaN(reachedAt))
52880
+ return false;
52881
+ const age = nowMs - reachedAt;
52882
+ return age >= 0 && age < MILESTONE_VISIBLE_MS;
52883
+ }
52884
+ function formatSponsorFooter(formatter) {
52885
+ return formatter.formatItalic(`♥ Support claude-threads: ${formatter.formatLink("github.com/sponsors/axolotl-systems", SPONSOR_URL)}`);
52886
+ }
52887
+ function formatMilestoneLine(formatter, milestone) {
52888
+ return `\uD83C\uDF89 ${formatter.formatBold(`Session #${milestone}`)} on this instance — claude-threads is free & open source ${formatter.formatLink("♥ sponsor", SPONSOR_URL)}`;
52889
+ }
52890
+
52891
+ // src/persistence/session-store.ts
52892
+ var log4 = createLogger("persist");
52893
+ function resolveEndReason(session) {
52894
+ if (!session.cleanedAt)
52895
+ return;
52896
+ return session.endReason ?? "stopped";
52897
+ }
52898
+ function isRevivable(session) {
52899
+ return resolveEndReason(session) !== "stopped";
52900
+ }
52901
+ var STORE_VERSION = 2;
52902
+ var DEFAULT_CONFIG_DIR = join5(homedir3(), ".config", "claude-threads");
52903
+ var DEFAULT_SESSIONS_FILE = join5(DEFAULT_CONFIG_DIR, "sessions.json");
52904
+
52905
+ class SessionStore {
52906
+ sessionsFile;
52907
+ configDir;
52908
+ constructor(sessionsPath) {
52909
+ const envPath = process.env.CLAUDE_THREADS_SESSIONS_PATH;
52910
+ const effectivePath = sessionsPath ?? envPath;
52911
+ if (effectivePath) {
52912
+ this.sessionsFile = effectivePath;
52913
+ this.configDir = join5(effectivePath, "..");
52914
+ } else {
52915
+ this.sessionsFile = DEFAULT_SESSIONS_FILE;
52916
+ this.configDir = DEFAULT_CONFIG_DIR;
52917
+ }
52918
+ if (!existsSync4(this.configDir)) {
52919
+ mkdirSync3(this.configDir, { recursive: true });
52920
+ }
52921
+ }
52922
+ load() {
52923
+ const sessions = new Map;
52924
+ if (!existsSync4(this.sessionsFile)) {
52925
+ log4.debug("No sessions file found");
52926
+ return sessions;
52927
+ }
52928
+ try {
52929
+ const data = this.loadRaw();
52930
+ if (data.version === 1) {
52931
+ log4.info("Migrating sessions from v1 to v2 (adding platformId)");
52932
+ const newSessions = {};
52933
+ for (const [_oldKey, session] of Object.entries(data.sessions)) {
52934
+ const v1Session = session;
52935
+ if (!v1Session.platformId) {
52936
+ v1Session.platformId = "default";
52937
+ }
52938
+ const newKey = `${v1Session.platformId}:${v1Session.threadId}`;
52939
+ newSessions[newKey] = v1Session;
52940
+ }
52941
+ data.sessions = newSessions;
52942
+ data.version = 2;
52943
+ this.writeAtomic(data);
52944
+ } else if (data.version !== STORE_VERSION) {
52945
+ log4.warn(`Sessions file version ${data.version} not supported, starting fresh`);
52946
+ return sessions;
52947
+ }
52948
+ for (const session of Object.values(data.sessions)) {
52949
+ if (session.cleanedAt)
52950
+ continue;
52951
+ const sessionId = `${session.platformId}:${session.threadId}`;
52952
+ sessions.set(sessionId, session);
52953
+ }
52954
+ log4.debug(`Loaded ${sessions.size} active session(s)`);
52955
+ } catch (err) {
52956
+ log4.error(`Failed to load sessions: ${err}`);
52957
+ }
52958
+ return sessions;
52959
+ }
52960
+ save(sessionId, session) {
52961
+ const data = this.loadRaw();
52962
+ data.sessions[sessionId] = session;
52963
+ this.writeAtomic(data);
52964
+ const shortId = sessionId.substring(0, 20);
52965
+ log4.debug(`Saved session ${shortId}...`);
52966
+ }
52967
+ remove(sessionId) {
52968
+ const data = this.loadRaw();
52969
+ if (data.sessions[sessionId]) {
52970
+ delete data.sessions[sessionId];
52971
+ this.writeAtomic(data);
52972
+ const shortId = sessionId.substring(0, 20);
52973
+ log4.debug(`Removed session ${shortId}...`);
52974
+ }
52975
+ }
52976
+ softDelete(sessionId, reason) {
52977
+ const data = this.loadRaw();
52978
+ if (data.sessions[sessionId]) {
52979
+ data.sessions[sessionId].cleanedAt = new Date().toISOString();
52980
+ data.sessions[sessionId].endReason = reason;
52981
+ this.writeAtomic(data);
52982
+ const shortId = sessionId.substring(0, 20);
52983
+ log4.debug(`Soft-deleted session ${shortId}...`);
52984
+ }
52985
+ }
52986
+ cleanStale(maxAgeMs) {
52987
+ const data = this.loadRaw();
52988
+ const now = Date.now();
52989
+ const staleIds = [];
52990
+ for (const [sessionId, session] of Object.entries(data.sessions)) {
52991
+ if (session.cleanedAt)
52992
+ continue;
52993
+ if (session.threadId.startsWith("dcm:"))
52994
+ continue;
52995
+ const lastActivity = new Date(session.lastActivityAt).getTime();
52996
+ if (now - lastActivity > maxAgeMs) {
52997
+ staleIds.push(sessionId);
52998
+ session.cleanedAt = new Date().toISOString();
52999
+ session.endReason = "stale";
53000
+ }
53001
+ }
53002
+ if (staleIds.length > 0) {
53003
+ this.writeAtomic(data);
53004
+ log4.debug(`Soft-deleted ${staleIds.length} stale session(s)`);
53005
+ }
53006
+ return staleIds;
53007
+ }
53008
+ cleanHistory(historyRetentionMs = 3 * 24 * 60 * 60 * 1000) {
53009
+ const data = this.loadRaw();
53010
+ const now = Date.now();
53011
+ let removedCount = 0;
53012
+ for (const [sessionId, session] of Object.entries(data.sessions)) {
53013
+ if (!session.cleanedAt)
53014
+ continue;
53015
+ const cleanedTime = new Date(session.cleanedAt).getTime();
53016
+ if (now - cleanedTime > historyRetentionMs) {
53017
+ delete data.sessions[sessionId];
53018
+ removedCount++;
53019
+ }
53020
+ }
53021
+ if (removedCount > 0) {
53022
+ this.writeAtomic(data);
53023
+ log4.debug(`Permanently removed ${removedCount} old session(s) from history`);
53024
+ }
53025
+ return removedCount;
53026
+ }
53027
+ getHistory(platformId, activeSessions) {
53028
+ const data = this.loadRaw();
53029
+ const historySessions = [];
53030
+ for (const [sessionId, session] of Object.entries(data.sessions)) {
53031
+ if (session.platformId !== platformId)
53032
+ continue;
53033
+ if (session.cleanedAt) {
53034
+ historySessions.push(session);
53035
+ continue;
53036
+ }
53037
+ if (session.lifecyclePostId && activeSessions && !activeSessions.has(sessionId)) {
53038
+ historySessions.push(session);
53039
+ }
53040
+ }
53041
+ return historySessions.sort((a, b) => {
53042
+ const aTime = new Date(a.cleanedAt || a.lastActivityAt).getTime();
53043
+ const bTime = new Date(b.cleanedAt || b.lastActivityAt).getTime();
53044
+ return bTime - aTime;
53045
+ });
53046
+ }
53047
+ clear() {
53048
+ const data = this.loadRaw();
53049
+ this.writeAtomic({ version: STORE_VERSION, sessions: {}, stickyPostIds: data.stickyPostIds });
53050
+ log4.debug("Cleared all sessions");
53051
+ }
53052
+ saveStickyPostId(platformId, postId) {
53053
+ const data = this.loadRaw();
53054
+ if (!data.stickyPostIds) {
53055
+ data.stickyPostIds = {};
53056
+ }
53057
+ data.stickyPostIds[platformId] = postId;
53058
+ this.writeAtomic(data);
53059
+ log4.debug(`Saved sticky post ID for ${platformId}: ${postId.substring(0, 8)}...`);
53060
+ }
53061
+ getStickyPostIds() {
53062
+ const data = this.loadRaw();
53063
+ return new Map(Object.entries(data.stickyPostIds || {}));
53064
+ }
53065
+ removeStickyPostId(platformId) {
53066
+ const data = this.loadRaw();
53067
+ if (data.stickyPostIds && data.stickyPostIds[platformId]) {
53068
+ delete data.stickyPostIds[platformId];
53069
+ this.writeAtomic(data);
53070
+ log4.debug(`Removed sticky post ID for ${platformId}`);
53071
+ }
53072
+ }
53073
+ getPlatformEnabledState() {
53074
+ const data = this.loadRaw();
53075
+ return new Map(Object.entries(data.platformEnabledState || {}));
53076
+ }
53077
+ isPlatformEnabled(platformId) {
53078
+ const data = this.loadRaw();
53079
+ return data.platformEnabledState?.[platformId] ?? true;
53080
+ }
53081
+ setPlatformEnabled(platformId, enabled) {
53082
+ const data = this.loadRaw();
53083
+ if (!data.platformEnabledState) {
53084
+ data.platformEnabledState = {};
53085
+ }
53086
+ data.platformEnabledState[platformId] = enabled;
53087
+ this.writeAtomic(data);
53088
+ log4.debug(`Set platform ${platformId} enabled state to ${enabled}`);
53089
+ }
53090
+ findByThread(platformId, threadId) {
53091
+ const sessionId = `${platformId}:${threadId}`;
53092
+ const data = this.loadRaw();
53093
+ return data.sessions[sessionId];
53094
+ }
53095
+ findByThreadIdAnyState(threadId, platformId) {
53096
+ const data = this.loadRaw();
53097
+ for (const session of Object.values(data.sessions)) {
53098
+ if (session.threadId !== threadId)
53099
+ continue;
53100
+ if (platformId !== undefined && session.platformId !== platformId)
53101
+ continue;
53102
+ return session;
53103
+ }
53104
+ return;
53105
+ }
53106
+ findByPostId(platformId, postId) {
53107
+ const data = this.loadRaw();
53108
+ for (const session of Object.values(data.sessions)) {
53109
+ if (session.platformId !== platformId)
53110
+ continue;
53111
+ if (session.lifecyclePostId === postId || session.sessionStartPostId === postId) {
53112
+ return session;
53113
+ }
53114
+ }
53115
+ return;
53116
+ }
53117
+ getStats() {
53118
+ const data = this.loadRaw();
53119
+ return data.stats ?? { totalSessionsStarted: 0 };
53120
+ }
53121
+ recordSessionStarted() {
53122
+ const data = this.loadRaw();
53123
+ const stats = data.stats ?? { totalSessionsStarted: 0 };
53124
+ stats.totalSessionsStarted = (stats.totalSessionsStarted ?? 0) + 1;
53125
+ const milestone = milestoneReached(stats.totalSessionsStarted);
53126
+ if (milestone) {
53127
+ stats.milestone = { n: milestone, reachedAt: new Date().toISOString() };
53128
+ }
53129
+ data.stats = stats;
53130
+ this.writeAtomic(data);
53131
+ return stats.totalSessionsStarted;
53132
+ }
53133
+ lastReadDegraded = false;
53134
+ loadRaw() {
53135
+ if (!existsSync4(this.sessionsFile)) {
53136
+ this.lastReadDegraded = false;
53137
+ return { version: STORE_VERSION, sessions: {} };
53138
+ }
53139
+ try {
53140
+ const raw = readFileSync3(this.sessionsFile, "utf-8");
53141
+ if (raw.trim() === "") {
53142
+ this.lastReadDegraded = false;
53143
+ return { version: STORE_VERSION, sessions: {} };
53144
+ }
53145
+ const data = JSON.parse(raw);
53146
+ if (!data || typeof data !== "object") {
53147
+ this.lastReadDegraded = true;
53148
+ return { version: STORE_VERSION, sessions: {} };
53149
+ }
53150
+ if (data.sessions === undefined || data.sessions === null) {
53151
+ this.lastReadDegraded = false;
53152
+ data.sessions = {};
53153
+ } else if (typeof data.sessions !== "object") {
53154
+ this.lastReadDegraded = true;
53155
+ data.sessions = {};
53156
+ } else {
53157
+ this.lastReadDegraded = false;
53158
+ }
53159
+ if (!data.version) {
53160
+ data.version = STORE_VERSION;
53161
+ }
53162
+ return data;
53163
+ } catch (err) {
53164
+ log4.warn(`Failed to read ${this.sessionsFile}: ${err.message} — reads degrade to empty`);
53165
+ this.lastReadDegraded = true;
53166
+ return { version: STORE_VERSION, sessions: {} };
53167
+ }
53168
+ }
53169
+ writeAtomic(data) {
53170
+ if (this.lastReadDegraded) {
53171
+ log4.error(`Refusing to write ${this.sessionsFile}: the last read of the existing file was degraded — writing would destroy persisted sessions`);
53172
+ return;
53173
+ }
53174
+ writeFileAtomic(this.sessionsFile, JSON.stringify(data, null, 2));
53175
+ }
53176
+ }
53177
+
53178
+ // src/persistence/thread-logger.ts
53179
+ init_logger();
53180
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, appendFileSync, readdirSync as readdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, rmdirSync, readFileSync as readFileSync4, chmodSync as chmodSync4 } from "fs";
53181
+ import { homedir as homedir4 } from "os";
53182
+ import { join as join6, dirname as dirname3 } from "path";
53183
+ var log5 = createLogger("thread-log");
53184
+ var LOGS_BASE_DIR = join6(homedir4(), ".claude-threads", "logs");
52845
53185
 
52846
53186
  class ThreadLoggerImpl {
52847
53187
  platformId;
@@ -52861,16 +53201,16 @@ class ThreadLoggerImpl {
52861
53201
  this.enabled = options?.enabled ?? true;
52862
53202
  this.bufferSize = options?.bufferSize ?? 10;
52863
53203
  this.flushIntervalMs = options?.flushIntervalMs ?? 1000;
52864
- this.logPath = join5(LOGS_BASE_DIR, platformId, `${claudeSessionId}.jsonl`);
53204
+ this.logPath = join6(LOGS_BASE_DIR, platformId, `${claudeSessionId}.jsonl`);
52865
53205
  if (this.enabled) {
52866
53206
  const dir = dirname3(this.logPath);
52867
- if (!existsSync4(dir)) {
52868
- mkdirSync3(dir, { recursive: true });
53207
+ if (!existsSync5(dir)) {
53208
+ mkdirSync4(dir, { recursive: true });
52869
53209
  }
52870
53210
  this.flushTimer = setInterval(() => {
52871
53211
  this.flushSync();
52872
53212
  }, this.flushIntervalMs);
52873
- log4.debug(`Thread logger initialized: ${this.logPath}`);
53213
+ log5.debug(`Thread logger initialized: ${this.logPath}`);
52874
53214
  }
52875
53215
  }
52876
53216
  isEnabled() {
@@ -52984,7 +53324,7 @@ class ThreadLoggerImpl {
52984
53324
  this.flushTimer = null;
52985
53325
  }
52986
53326
  this.flushSync();
52987
- log4.debug(`Thread logger closed: ${this.logPath}`);
53327
+ log5.debug(`Thread logger closed: ${this.logPath}`);
52988
53328
  }
52989
53329
  addEntry(entry) {
52990
53330
  this.buffer.push(entry);
@@ -52999,14 +53339,14 @@ class ThreadLoggerImpl {
52999
53339
  const lines = this.buffer.map((entry) => JSON.stringify(entry)).join(`
53000
53340
  `) + `
53001
53341
  `;
53002
- const isNewFile = !existsSync4(this.logPath);
53342
+ const isNewFile = !existsSync5(this.logPath);
53003
53343
  appendFileSync(this.logPath, lines, { encoding: "utf8", mode: 384 });
53004
53344
  if (isNewFile) {
53005
- chmodSync3(this.logPath, 384);
53345
+ chmodSync4(this.logPath, 384);
53006
53346
  }
53007
53347
  this.buffer = [];
53008
53348
  } catch (err) {
53009
- log4.error(`Failed to flush thread log: ${err}`);
53349
+ log5.error(`Failed to flush thread log: ${err}`);
53010
53350
  }
53011
53351
  }
53012
53352
  }
@@ -53037,13 +53377,13 @@ function createThreadLogger(platformId, threadId, claudeSessionId, options) {
53037
53377
  function cleanupOldLogs(retentionDays = 30) {
53038
53378
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
53039
53379
  let deletedCount = 0;
53040
- if (!existsSync4(LOGS_BASE_DIR)) {
53380
+ if (!existsSync5(LOGS_BASE_DIR)) {
53041
53381
  return 0;
53042
53382
  }
53043
53383
  try {
53044
53384
  const platformDirs = readdirSync2(LOGS_BASE_DIR);
53045
53385
  for (const platformId of platformDirs) {
53046
- const platformDir = join5(LOGS_BASE_DIR, platformId);
53386
+ const platformDir = join6(LOGS_BASE_DIR, platformId);
53047
53387
  const stat = statSync2(platformDir);
53048
53388
  if (!stat.isDirectory())
53049
53389
  continue;
@@ -53051,49 +53391,49 @@ function cleanupOldLogs(retentionDays = 30) {
53051
53391
  for (const file of logFiles) {
53052
53392
  if (!file.endsWith(".jsonl"))
53053
53393
  continue;
53054
- const filePath = join5(platformDir, file);
53394
+ const filePath = join6(platformDir, file);
53055
53395
  try {
53056
53396
  const fileStat = statSync2(filePath);
53057
53397
  if (fileStat.mtimeMs < cutoffMs) {
53058
53398
  unlinkSync2(filePath);
53059
53399
  deletedCount++;
53060
- log4.debug(`Deleted old log file: ${filePath}`);
53400
+ log5.debug(`Deleted old log file: ${filePath}`);
53061
53401
  }
53062
53402
  } catch (err) {
53063
- log4.warn(`Failed to check/delete log file ${filePath}: ${err}`);
53403
+ log5.warn(`Failed to check/delete log file ${filePath}: ${err}`);
53064
53404
  }
53065
53405
  }
53066
53406
  try {
53067
53407
  const remaining = readdirSync2(platformDir);
53068
53408
  if (remaining.length === 0) {
53069
53409
  rmdirSync(platformDir);
53070
- log4.debug(`Removed empty platform log directory: ${platformDir}`);
53410
+ log5.debug(`Removed empty platform log directory: ${platformDir}`);
53071
53411
  }
53072
53412
  } catch {}
53073
53413
  }
53074
53414
  if (deletedCount > 0) {
53075
- log4.info(`Cleaned up ${deletedCount} old log file(s)`);
53415
+ log5.info(`Cleaned up ${deletedCount} old log file(s)`);
53076
53416
  }
53077
53417
  } catch (err) {
53078
- log4.error(`Failed to clean up old logs: ${err}`);
53418
+ log5.error(`Failed to clean up old logs: ${err}`);
53079
53419
  }
53080
53420
  return deletedCount;
53081
53421
  }
53082
53422
  function getLogFilePath(platformId, sessionId) {
53083
- return join5(LOGS_BASE_DIR, platformId, `${sessionId}.jsonl`);
53423
+ return join6(LOGS_BASE_DIR, platformId, `${sessionId}.jsonl`);
53084
53424
  }
53085
53425
  function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
53086
53426
  const logPath = getLogFilePath(platformId, sessionId);
53087
- log4.debug(`Reading log entries from: ${logPath}`);
53088
- if (!existsSync4(logPath)) {
53089
- log4.debug(`Log file does not exist: ${logPath}`);
53427
+ log5.debug(`Reading log entries from: ${logPath}`);
53428
+ if (!existsSync5(logPath)) {
53429
+ log5.debug(`Log file does not exist: ${logPath}`);
53090
53430
  return [];
53091
53431
  }
53092
53432
  try {
53093
- const content = readFileSync3(logPath, "utf8");
53433
+ const content = readFileSync4(logPath, "utf8");
53094
53434
  const lines = content.trim().split(`
53095
53435
  `);
53096
- log4.debug(`Log file has ${lines.length} lines`);
53436
+ log5.debug(`Log file has ${lines.length} lines`);
53097
53437
  const recentLines = lines.slice(-maxLines);
53098
53438
  const entries = [];
53099
53439
  for (const line of recentLines) {
@@ -53103,16 +53443,16 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
53103
53443
  entries.push(JSON.parse(line));
53104
53444
  } catch {}
53105
53445
  }
53106
- log4.debug(`Parsed ${entries.length} log entries`);
53446
+ log5.debug(`Parsed ${entries.length} log entries`);
53107
53447
  return entries;
53108
53448
  } catch (err) {
53109
- log4.error(`Failed to read log file: ${err}`);
53449
+ log5.error(`Failed to read log file: ${err}`);
53110
53450
  return [];
53111
53451
  }
53112
53452
  }
53113
53453
 
53114
53454
  // src/version.ts
53115
- import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
53455
+ import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
53116
53456
  import { dirname as dirname4, resolve as resolve3 } from "path";
53117
53457
  import { fileURLToPath as fileURLToPath2 } from "url";
53118
53458
  var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
@@ -53123,9 +53463,9 @@ function loadPackageJson() {
53123
53463
  resolve3(process.cwd(), "package.json")
53124
53464
  ];
53125
53465
  for (const candidate of candidates) {
53126
- if (existsSync5(candidate)) {
53466
+ if (existsSync6(candidate)) {
53127
53467
  try {
53128
- const pkg = JSON.parse(readFileSync4(candidate, "utf-8"));
53468
+ const pkg = JSON.parse(readFileSync5(candidate, "utf-8"));
53129
53469
  if (pkg.name === "claude-threads") {
53130
53470
  return { version: pkg.version, name: pkg.name };
53131
53471
  }
@@ -53536,27 +53876,6 @@ function parseCommandWithRemainder(text) {
53536
53876
  }
53537
53877
  return null;
53538
53878
  }
53539
- // src/sponsor.ts
53540
- var SPONSOR_URL = "https://github.com/sponsors/axolotl-systems";
53541
- var SESSION_MILESTONES = [100, 250, 500, 1000, 2500, 5000, 1e4];
53542
- var MILESTONE_VISIBLE_MS = 24 * 60 * 60 * 1000;
53543
- function milestoneReached(totalSessions) {
53544
- return SESSION_MILESTONES.includes(totalSessions) ? totalSessions : null;
53545
- }
53546
- function milestoneStillFresh(reachedAtIso, nowMs) {
53547
- const reachedAt = Date.parse(reachedAtIso);
53548
- if (Number.isNaN(reachedAt))
53549
- return false;
53550
- const age = nowMs - reachedAt;
53551
- return age >= 0 && age < MILESTONE_VISIBLE_MS;
53552
- }
53553
- function formatSponsorFooter(formatter) {
53554
- return formatter.formatItalic(`♥ Support claude-threads: ${formatter.formatLink("github.com/sponsors/axolotl-systems", SPONSOR_URL)}`);
53555
- }
53556
- function formatMilestoneLine(formatter, milestone) {
53557
- return `\uD83C\uDF89 ${formatter.formatBold(`Session #${milestone}`)} on this instance — claude-threads is free & open source ${formatter.formatLink("♥ sponsor", SPONSOR_URL)}`;
53558
- }
53559
-
53560
53879
  // src/commands/help-generator.ts
53561
53880
  function formatCommandRows(cmd, code) {
53562
53881
  const rows = [];
@@ -53594,7 +53913,7 @@ ${formatter.formatBold("Reactions:")}
53594
53913
  }
53595
53914
 
53596
53915
  // src/changelog.ts
53597
- import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
53916
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
53598
53917
  import { dirname as dirname5, resolve as resolve4 } from "path";
53599
53918
  import { fileURLToPath as fileURLToPath3 } from "url";
53600
53919
  var __dirname3 = dirname5(fileURLToPath3(import.meta.url));
@@ -53605,7 +53924,7 @@ function getReleaseNotes(version) {
53605
53924
  ];
53606
53925
  let changelogPath = null;
53607
53926
  for (const p of possiblePaths) {
53608
- if (existsSync6(p)) {
53927
+ if (existsSync7(p)) {
53609
53928
  changelogPath = p;
53610
53929
  break;
53611
53930
  }
@@ -53614,7 +53933,7 @@ function getReleaseNotes(version) {
53614
53933
  return null;
53615
53934
  }
53616
53935
  try {
53617
- const content = readFileSync5(changelogPath, "utf-8");
53936
+ const content = readFileSync6(changelogPath, "utf-8");
53618
53937
  return parseChangelog(content, version);
53619
53938
  } catch {
53620
53939
  return null;
@@ -54122,7 +54441,7 @@ async function handleDynamicSlashCommand(command, args, ctx) {
54122
54441
  }
54123
54442
  // src/commands/system-prompt-generator.ts
54124
54443
  init_logger();
54125
- var log5 = createLogger("system-prompt");
54444
+ var log6 = createLogger("system-prompt");
54126
54445
  function formatUserCommand(cmd) {
54127
54446
  const cmdStr = cmd.args ? `\`!${cmd.command} ${cmd.args}\`` : `\`!${cmd.command}\``;
54128
54447
  const description = cmd.description;
@@ -54161,7 +54480,7 @@ async function resolveCollaborators(platform, platformId, ownerUsername, allowed
54161
54480
  continue;
54162
54481
  const email = githubEmailsStore.get(platformId, username);
54163
54482
  if (!email) {
54164
- log5.debug(`Collaborator @${username} has no registered GitHub noreply email — skipping`);
54483
+ log6.debug(`Collaborator @${username} has no registered GitHub noreply email — skipping`);
54165
54484
  continue;
54166
54485
  }
54167
54486
  let name = username;
@@ -54170,7 +54489,7 @@ async function resolveCollaborators(platform, platformId, ownerUsername, allowed
54170
54489
  if (user)
54171
54490
  name = user.displayName || user.username;
54172
54491
  } catch (err) {
54173
- log5.debug(`Display name lookup failed for @${username}: ${err.message}`);
54492
+ log6.debug(`Display name lookup failed for @${username}: ${err.message}`);
54174
54493
  }
54175
54494
  resolved.push({ username, name, email });
54176
54495
  }
@@ -54300,12 +54619,12 @@ ${avoidCommands.map((c) => `- \`!${c.command}\` - ${c.reason}`).join(`
54300
54619
  }
54301
54620
  // src/session/lifecycle.ts
54302
54621
  import { randomUUID as randomUUID7 } from "crypto";
54303
- import { existsSync as existsSync11 } from "fs";
54622
+ import { existsSync as existsSync12 } from "fs";
54304
54623
 
54305
54624
  // src/utils/keep-alive.ts
54306
54625
  init_logger();
54307
54626
  import { spawn } from "child_process";
54308
- var log6 = createLogger("keepalive");
54627
+ var log7 = createLogger("keepalive");
54309
54628
  function keepAliveSpawnSpec(platform, parentPid) {
54310
54629
  switch (platform) {
54311
54630
  case "darwin":
@@ -54361,7 +54680,7 @@ class KeepAliveManager {
54361
54680
  if (!enabled && this.keepAliveProcess) {
54362
54681
  this.stopKeepAlive();
54363
54682
  }
54364
- log6.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
54683
+ log7.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
54365
54684
  }
54366
54685
  isEnabled() {
54367
54686
  return this.enabled;
@@ -54371,7 +54690,7 @@ class KeepAliveManager {
54371
54690
  }
54372
54691
  sessionStarted() {
54373
54692
  this.activeSessionCount++;
54374
- log6.debug(`Session started (${this.activeSessionCount} active)`);
54693
+ log7.debug(`Session started (${this.activeSessionCount} active)`);
54375
54694
  if (this.activeSessionCount === 1) {
54376
54695
  this.startKeepAlive();
54377
54696
  }
@@ -54380,7 +54699,7 @@ class KeepAliveManager {
54380
54699
  if (this.activeSessionCount > 0) {
54381
54700
  this.activeSessionCount--;
54382
54701
  }
54383
- log6.debug(`Session ended (${this.activeSessionCount} active)`);
54702
+ log7.debug(`Session ended (${this.activeSessionCount} active)`);
54384
54703
  if (this.activeSessionCount === 0) {
54385
54704
  this.stopKeepAlive();
54386
54705
  }
@@ -54394,11 +54713,11 @@ class KeepAliveManager {
54394
54713
  }
54395
54714
  startKeepAlive() {
54396
54715
  if (!this.enabled) {
54397
- log6.debug("Keep-alive disabled, skipping");
54716
+ log7.debug("Keep-alive disabled, skipping");
54398
54717
  return;
54399
54718
  }
54400
54719
  if (this.keepAliveProcess) {
54401
- log6.debug("Keep-alive already running");
54720
+ log7.debug("Keep-alive already running");
54402
54721
  return;
54403
54722
  }
54404
54723
  switch (this.platform) {
@@ -54412,12 +54731,12 @@ class KeepAliveManager {
54412
54731
  this.startWindowsKeepAlive();
54413
54732
  break;
54414
54733
  default:
54415
- log6.warn(`Keep-alive not supported on ${this.platform}`);
54734
+ log7.warn(`Keep-alive not supported on ${this.platform}`);
54416
54735
  }
54417
54736
  }
54418
54737
  stopKeepAlive() {
54419
54738
  if (this.keepAliveProcess) {
54420
- log6.debug("Stopping keep-alive");
54739
+ log7.debug("Stopping keep-alive");
54421
54740
  this.keepAliveProcess.kill();
54422
54741
  this.keepAliveProcess = null;
54423
54742
  }
@@ -54432,18 +54751,18 @@ class KeepAliveManager {
54432
54751
  detached: false
54433
54752
  });
54434
54753
  this.keepAliveProcess.on("error", (err) => {
54435
- log6.error(`Failed to start caffeinate: ${err.message}`);
54754
+ log7.error(`Failed to start caffeinate: ${err.message}`);
54436
54755
  this.keepAliveProcess = null;
54437
54756
  });
54438
54757
  this.keepAliveProcess.on("exit", (code) => {
54439
54758
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
54440
- log6.debug(`caffeinate exited with code ${code}`);
54759
+ log7.debug(`caffeinate exited with code ${code}`);
54441
54760
  }
54442
54761
  this.keepAliveProcess = null;
54443
54762
  });
54444
- log6.info("Sleep prevention active (caffeinate)");
54763
+ log7.info("Sleep prevention active (caffeinate)");
54445
54764
  } catch (err) {
54446
- log6.error(`Failed to start caffeinate: ${err}`);
54765
+ log7.error(`Failed to start caffeinate: ${err}`);
54447
54766
  }
54448
54767
  }
54449
54768
  startLinuxKeepAlive() {
@@ -54456,19 +54775,19 @@ class KeepAliveManager {
54456
54775
  detached: false
54457
54776
  });
54458
54777
  this.keepAliveProcess.on("error", (err) => {
54459
- log6.debug(`systemd-inhibit not available: ${err.message}`);
54778
+ log7.debug(`systemd-inhibit not available: ${err.message}`);
54460
54779
  this.keepAliveProcess = null;
54461
54780
  this.startLinuxKeepAliveFallback();
54462
54781
  });
54463
54782
  this.keepAliveProcess.on("exit", (code) => {
54464
54783
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
54465
- log6.debug(`systemd-inhibit exited with code ${code}`);
54784
+ log7.debug(`systemd-inhibit exited with code ${code}`);
54466
54785
  }
54467
54786
  this.keepAliveProcess = null;
54468
54787
  });
54469
- log6.info("Sleep prevention active (systemd-inhibit)");
54788
+ log7.info("Sleep prevention active (systemd-inhibit)");
54470
54789
  } catch (err) {
54471
- log6.debug(`Failed to start systemd-inhibit: ${err}`);
54790
+ log7.debug(`Failed to start systemd-inhibit: ${err}`);
54472
54791
  this.startLinuxKeepAliveFallback();
54473
54792
  }
54474
54793
  }
@@ -54479,15 +54798,15 @@ class KeepAliveManager {
54479
54798
  detached: false
54480
54799
  });
54481
54800
  this.keepAliveProcess.on("error", (err) => {
54482
- log6.warn(`Linux keep-alive fallback not available: ${err.message}`);
54801
+ log7.warn(`Linux keep-alive fallback not available: ${err.message}`);
54483
54802
  this.keepAliveProcess = null;
54484
54803
  });
54485
54804
  this.keepAliveProcess.on("exit", () => {
54486
54805
  this.keepAliveProcess = null;
54487
54806
  });
54488
- log6.info("Sleep prevention active (xdg-screensaver)");
54807
+ log7.info("Sleep prevention active (xdg-screensaver)");
54489
54808
  } catch (err) {
54490
- log6.warn(`Linux keep-alive not available: ${err}`);
54809
+ log7.warn(`Linux keep-alive not available: ${err}`);
54491
54810
  }
54492
54811
  }
54493
54812
  startWindowsKeepAlive() {
@@ -54499,18 +54818,18 @@ class KeepAliveManager {
54499
54818
  windowsHide: true
54500
54819
  });
54501
54820
  this.keepAliveProcess.on("error", (err) => {
54502
- log6.warn(`Windows keep-alive not available: ${err.message}`);
54821
+ log7.warn(`Windows keep-alive not available: ${err.message}`);
54503
54822
  this.keepAliveProcess = null;
54504
54823
  });
54505
54824
  this.keepAliveProcess.on("exit", (code) => {
54506
54825
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
54507
- log6.debug(`PowerShell keep-alive exited with code ${code}`);
54826
+ log7.debug(`PowerShell keep-alive exited with code ${code}`);
54508
54827
  }
54509
54828
  this.keepAliveProcess = null;
54510
54829
  });
54511
- log6.info("Sleep prevention active (SetThreadExecutionState)");
54830
+ log7.info("Sleep prevention active (SetThreadExecutionState)");
54512
54831
  } catch (err) {
54513
- log6.warn(`Windows keep-alive not available: ${err}`);
54832
+ log7.warn(`Windows keep-alive not available: ${err}`);
54514
54833
  }
54515
54834
  }
54516
54835
  }
@@ -54585,7 +54904,7 @@ function singleLine(text) {
54585
54904
  }
54586
54905
 
54587
54906
  // src/utils/error-handler/index.ts
54588
- var log7 = createLogger("error");
54907
+ var log8 = createLogger("error");
54589
54908
 
54590
54909
  class SessionError extends Error {
54591
54910
  sessionId;
@@ -54611,19 +54930,19 @@ async function handleError(error, context, severity = "recoverable") {
54611
54930
  const sessionPart = sessionId ? ` (${formatShortId(sessionId)})` : "";
54612
54931
  const logMessage = `${context.action}${sessionPart}: ${message}`;
54613
54932
  if (severity === "recoverable") {
54614
- log7.warn(logMessage);
54933
+ log8.warn(logMessage);
54615
54934
  } else {
54616
- log7.error(logMessage, error instanceof Error ? error : undefined);
54935
+ log8.error(logMessage, error instanceof Error ? error : undefined);
54617
54936
  }
54618
54937
  if (context.details) {
54619
- log7.debugJson("Error details", context.details);
54938
+ log8.debugJson("Error details", context.details);
54620
54939
  }
54621
54940
  if (context.notifyUser && context.session) {
54622
54941
  try {
54623
54942
  const fmt = context.session.platform.getFormatter();
54624
54943
  await context.session.platform.createPost(`⚠️ ${fmt.formatBold("Error")}: ${context.action} failed - ${message}`, context.session.threadId);
54625
54944
  } catch (notifyError) {
54626
- log7.warn(`Could not notify user: ${notifyError}`);
54945
+ log8.warn(`Could not notify user: ${notifyError}`);
54627
54946
  }
54628
54947
  }
54629
54948
  if (severity === "session-fatal" || severity === "system-fatal") {
@@ -54650,7 +54969,7 @@ async function logAndNotify(error, context) {
54650
54969
  }
54651
54970
  function logSilentError(context, error) {
54652
54971
  const message = error instanceof Error ? error.message : String(error);
54653
- log7.debug(`[${context}] Silently caught: ${message}`);
54972
+ log8.debug(`[${context}] Silently caught: ${message}`);
54654
54973
  }
54655
54974
 
54656
54975
  // src/session/lifecycle.ts
@@ -54670,8 +54989,8 @@ function createSessionLog(baseLog) {
54670
54989
  init_logger();
54671
54990
  init_emoji();
54672
54991
  init_worktree();
54673
- var log9 = createLogger("helpers");
54674
- var sessionLog = createSessionLog(log9);
54992
+ var log10 = createLogger("helpers");
54993
+ var sessionLog = createSessionLog(log10);
54675
54994
  var POST_TYPES = {
54676
54995
  info: "",
54677
54996
  success: "✅",
@@ -54759,7 +55078,7 @@ function updateLastMessage(session, post2) {
54759
55078
  init_logger();
54760
55079
  import { lstat, mkdir as mkdir2, mkdtemp, rm as rm2, writeFile as writeFile2 } from "fs/promises";
54761
55080
  import { tmpdir as tmpdir3 } from "os";
54762
- import { join as join7 } from "path";
55081
+ import { join as join8 } from "path";
54763
55082
 
54764
55083
  // src/utils/safe-filename.ts
54765
55084
  import { basename as basename2 } from "path";
@@ -54800,13 +55119,13 @@ function formatBytes(bytes) {
54800
55119
  }
54801
55120
 
54802
55121
  // src/operations/streaming/handler.ts
54803
- var log10 = createLogger("streaming");
55122
+ var log11 = createLogger("streaming");
54804
55123
  var UPLOAD_ROOT_DIR = "claude-threads-uploads";
54805
55124
  function safeIdSegment(id) {
54806
55125
  return id.replace(/[^A-Za-z0-9._-]/g, "_");
54807
55126
  }
54808
55127
  function getSessionUploadDir(platformId, threadId) {
54809
- return join7(tmpdir3(), UPLOAD_ROOT_DIR, `${safeIdSegment(platformId)}-${safeIdSegment(threadId)}`);
55128
+ return join8(tmpdir3(), UPLOAD_ROOT_DIR, `${safeIdSegment(platformId)}-${safeIdSegment(threadId)}`);
54810
55129
  }
54811
55130
  async function cleanupSessionUploads(platformId, threadId) {
54812
55131
  if (!platformId || !threadId)
@@ -54815,7 +55134,7 @@ async function cleanupSessionUploads(platformId, threadId) {
54815
55134
  try {
54816
55135
  await rm2(dir, { recursive: true, force: true });
54817
55136
  } catch (err) {
54818
- log10.debug(`Upload cleanup for ${platformId}:${threadId} failed (ignored): ${err}`);
55137
+ log11.debug(`Upload cleanup for ${platformId}:${threadId} failed (ignored): ${err}`);
54819
55138
  }
54820
55139
  }
54821
55140
  function sanitizeForPrompt(value) {
@@ -54836,16 +55155,16 @@ async function saveFilesToUploadDir(platform, uploadDir, files, debug = false) {
54836
55155
  for (const file of files) {
54837
55156
  skipped.push({ name: file.name, reason: "Refusing to write under symlinked upload directory" });
54838
55157
  }
54839
- log10.error(`Upload dir is a symlink, refusing all writes: ${uploadDir}`);
55158
+ log11.error(`Upload dir is a symlink, refusing all writes: ${uploadDir}`);
54840
55159
  return { saved, skipped };
54841
55160
  }
54842
- const messageDir = await mkdtemp(join7(uploadDir, `${Date.now().toString(36)}-`));
55161
+ const messageDir = await mkdtemp(join8(uploadDir, `${Date.now().toString(36)}-`));
54843
55162
  const usedNames = new Set;
54844
55163
  for (const file of files) {
54845
55164
  try {
54846
55165
  const buffer = await platform.downloadFile(file.id);
54847
55166
  const safeName = dedupeFilename(sanitizeFilename(file.name), usedNames);
54848
- const absolutePath = join7(messageDir, safeName);
55167
+ const absolutePath = join8(messageDir, safeName);
54849
55168
  await writeFile2(absolutePath, buffer, { mode: 384, flag: "wx" });
54850
55169
  saved.push({
54851
55170
  originalName: file.name,
@@ -54854,11 +55173,11 @@ async function saveFilesToUploadDir(platform, uploadDir, files, debug = false) {
54854
55173
  size: buffer.length
54855
55174
  });
54856
55175
  if (debug) {
54857
- log10.debug(`Saved ${file.name} → ${absolutePath} (${formatBytes(buffer.length)})`);
55176
+ log11.debug(`Saved ${file.name} → ${absolutePath} (${formatBytes(buffer.length)})`);
54858
55177
  }
54859
55178
  } catch (err) {
54860
55179
  const message = err instanceof Error ? err.message : String(err);
54861
- log10.error(`Failed to save uploaded file ${file.name}: ${message}`);
55180
+ log11.error(`Failed to save uploaded file ${file.name}: ${message}`);
54862
55181
  skipped.push({
54863
55182
  name: file.name,
54864
55183
  reason: `Download failed: ${message}`
@@ -54954,7 +55273,7 @@ function buildRestartCliOptions(session, ctx) {
54954
55273
  // src/operations/commands/handler.ts
54955
55274
  import { randomUUID as randomUUID6 } from "crypto";
54956
55275
  import { resolve as resolve6 } from "path";
54957
- import { existsSync as existsSync10, statSync as statSync4 } from "fs";
55276
+ import { existsSync as existsSync11, statSync as statSync4 } from "fs";
54958
55277
 
54959
55278
  // node_modules/update-notifier/update-notifier.js
54960
55279
  import process10 from "node:process";
@@ -55299,9 +55618,9 @@ node_default(Temp.purgeSyncAll);
55299
55618
  var temp_default = Temp;
55300
55619
 
55301
55620
  // node_modules/atomically/dist/index.js
55302
- function writeFileSync3(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
55621
+ function writeFileSync4(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
55303
55622
  if (isString(options))
55304
- return writeFileSync3(filePath, data, { encoding: options });
55623
+ return writeFileSync4(filePath, data, { encoding: options });
55305
55624
  const timeout = options.timeout ?? DEFAULT_TIMEOUT_SYNC;
55306
55625
  const retryOptions = { timeout };
55307
55626
  let tempDisposer = null;
@@ -55629,7 +55948,7 @@ class Configstore {
55629
55948
  }
55630
55949
  if (error.name === "SyntaxError") {
55631
55950
  if (this._clearInvalidConfig) {
55632
- writeFileSync3(this._path, "", writeFileOptions);
55951
+ writeFileSync4(this._path, "", writeFileOptions);
55633
55952
  return {};
55634
55953
  }
55635
55954
  throw error;
@@ -55641,7 +55960,7 @@ class Configstore {
55641
55960
  set all(value) {
55642
55961
  try {
55643
55962
  import_graceful_fs.default.mkdirSync(path5.dirname(this._path), mkdirOptions);
55644
- writeFileSync3(this._path, JSON.stringify(value, undefined, "\t"), writeFileOptions);
55963
+ writeFileSync4(this._path, JSON.stringify(value, undefined, "\t"), writeFileOptions);
55645
55964
  } catch (error) {
55646
55965
  handlePermissionError(error);
55647
55966
  }
@@ -58369,9 +58688,9 @@ init_emoji();
58369
58688
 
58370
58689
  // src/operations/bug-report/handler.ts
58371
58690
  import { execSync as execSync2 } from "child_process";
58372
- import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
58691
+ import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync3 } from "fs";
58373
58692
  import { tmpdir as tmpdir4 } from "os";
58374
- import { join as join8 } from "path";
58693
+ import { join as join9 } from "path";
58375
58694
 
58376
58695
  // node_modules/@redactpii/node/lib/index.mjs
58377
58696
  class Redactor {
@@ -58952,9 +59271,9 @@ async function createGitHubIssue(title, body, workingDir) {
58952
59271
  if (!ghStatus.installed || !ghStatus.authenticated) {
58953
59272
  throw new Error(ghStatus.error);
58954
59273
  }
58955
- const bodyFile = join8(tmpdir4(), `bug-body-${Date.now()}.md`);
59274
+ const bodyFile = join9(tmpdir4(), `bug-body-${Date.now()}.md`);
58956
59275
  try {
58957
- writeFileSync4(bodyFile, body, "utf-8");
59276
+ writeFileSync5(bodyFile, body, "utf-8");
58958
59277
  const cmd = `gh issue create --repo "${GITHUB_REPO}" --title "${escapeShell(title)}" --body-file "${bodyFile}"`;
58959
59278
  const result = execSync2(cmd, {
58960
59279
  cwd: workingDir,
@@ -59099,8 +59418,8 @@ function formatUptime(startedAt) {
59099
59418
 
59100
59419
  // src/operations/commands/guards.ts
59101
59420
  init_logger();
59102
- var log11 = createLogger("commands");
59103
- var sessionLog2 = createSessionLog(log11);
59421
+ var log12 = createLogger("commands");
59422
+ var sessionLog2 = createSessionLog(log12);
59104
59423
  function auditCommand(session, command, detail, username) {
59105
59424
  auditLog(session.platformId, {
59106
59425
  threadId: session.threadId,
@@ -63026,7 +63345,7 @@ class BugReportExecutor extends BaseExecutor {
63026
63345
  // src/operations/executors/worktree-prompt.ts
63027
63346
  init_emoji();
63028
63347
  init_logger();
63029
- var log12 = createLogger("wt-prompt");
63348
+ var log13 = createLogger("wt-prompt");
63030
63349
  // src/operations/message-manager.ts
63031
63350
  init_logger();
63032
63351
 
@@ -63106,7 +63425,7 @@ function formatRelativeTime(date) {
63106
63425
  return `${diffMin} min ago`;
63107
63426
  }
63108
63427
  // src/operations/message-manager.ts
63109
- var log13 = createLogger("msg-mgr");
63428
+ var log14 = createLogger("msg-mgr");
63110
63429
 
63111
63430
  class MessageManager {
63112
63431
  platform;
@@ -63199,7 +63518,7 @@ class MessageManager {
63199
63518
  });
63200
63519
  }
63201
63520
  async handleEvent(event) {
63202
- const logger = log13.forSession(this.sessionId);
63521
+ const logger = log14.forSession(this.sessionId);
63203
63522
  const transformCtx = {
63204
63523
  sessionId: this.sessionId,
63205
63524
  formatter: this.platform.getFormatter(),
@@ -63253,7 +63572,7 @@ class MessageManager {
63253
63572
  }
63254
63573
  }
63255
63574
  async executeOperation(op) {
63256
- const logger = log13.forSession(this.sessionId);
63575
+ const logger = log14.forSession(this.sessionId);
63257
63576
  const ctx = this.getExecutorContext();
63258
63577
  try {
63259
63578
  if (isContentOp(op)) {
@@ -63321,7 +63640,7 @@ class MessageManager {
63321
63640
  threadId: this.threadId,
63322
63641
  platform: this.platform,
63323
63642
  formatter: this.platform.getFormatter(),
63324
- logger: log13.forSession(this.sessionId),
63643
+ logger: log14.forSession(this.sessionId),
63325
63644
  postTracker: this.postTracker,
63326
63645
  contentBreaker: this.contentBreaker,
63327
63646
  threadLogger: this.session.threadLogger,
@@ -63550,13 +63869,13 @@ class MessageManager {
63550
63869
  return this.systemExecutor.postSuccess(message, this.getExecutorContext());
63551
63870
  }
63552
63871
  async prepareForUserMessage() {
63553
- const logger = log13.forSession(this.sessionId);
63872
+ const logger = log14.forSession(this.sessionId);
63554
63873
  logger.debug("Preparing for new user message");
63555
63874
  await this.closeCurrentPost();
63556
63875
  await this.bumpTaskList();
63557
63876
  }
63558
63877
  async handleUserMessage(message, files, username, displayName) {
63559
- const logger = log13.forSession(this.sessionId);
63878
+ const logger = log14.forSession(this.sessionId);
63560
63879
  if (!this.session.claude.isRunning()) {
63561
63880
  logger.debug("Claude not running, ignoring user message");
63562
63881
  return false;
@@ -63599,7 +63918,7 @@ class MessageManager {
63599
63918
  ];
63600
63919
  }
63601
63920
  async handleReaction(postId, emoji, user, action) {
63602
- const logger = log13.forSession(this.sessionId);
63921
+ const logger = log14.forSession(this.sessionId);
63603
63922
  const ctx = this.getExecutorContext();
63604
63923
  logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
63605
63924
  for (const { name, executor } of this.reactionDispatchList()) {
@@ -63697,7 +64016,7 @@ class MessageManager {
63697
64016
  }
63698
64017
  // src/operations/sticky-message/handler.ts
63699
64018
  init_logger();
63700
- var log14 = createLogger("sticky");
64019
+ var log15 = createLogger("sticky");
63701
64020
  var botStartedAt = new Date;
63702
64021
  function getPendingPrompts(session) {
63703
64022
  const prompts = [];
@@ -63772,21 +64091,21 @@ function initialize(store) {
63772
64091
  stickyPostIds.set(platformId, postId);
63773
64092
  }
63774
64093
  if (persistedIds.size > 0) {
63775
- log14.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
64094
+ log15.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
63776
64095
  }
63777
64096
  }
63778
64097
  function setPlatformPaused(platformId, paused) {
63779
64098
  if (paused) {
63780
64099
  pausedPlatforms.set(platformId, true);
63781
- log14.debug(`Platform ${platformId} marked as paused`);
64100
+ log15.debug(`Platform ${platformId} marked as paused`);
63782
64101
  } else {
63783
64102
  pausedPlatforms.delete(platformId);
63784
- log14.debug(`Platform ${platformId} marked as active`);
64103
+ log15.debug(`Platform ${platformId} marked as active`);
63785
64104
  }
63786
64105
  }
63787
64106
  function setShuttingDown(shuttingDown) {
63788
64107
  isShuttingDown = shuttingDown;
63789
- log14.debug(`Bot shutdown state: ${shuttingDown}`);
64108
+ log15.debug(`Bot shutdown state: ${shuttingDown}`);
63790
64109
  }
63791
64110
  function getTaskContent(session) {
63792
64111
  const taskState = session.messageManager?.getTaskListState();
@@ -63844,7 +64163,7 @@ function formatHistoryEntry(session, formatter, getThreadLink) {
63844
64163
  const topic = getHistorySessionTopic(session, formatter);
63845
64164
  const threadLink = formatter.formatLink(topic, getThreadLink(session.threadId));
63846
64165
  const displayName = session.startedByDisplayName || session.startedBy;
63847
- const isTimedOut = !session.cleanedAt && session.lifecyclePostId;
64166
+ const isTimedOut = isRevivable(session) && session.lifecyclePostId;
63848
64167
  const lastActivity = new Date(session.lastActivityAt);
63849
64168
  const time = formatRelativeTimeShort(lastActivity);
63850
64169
  const prStr = session.pullRequestUrl ? ` · ${formatPullRequestLink(session.pullRequestUrl, formatter)}` : "";
@@ -64119,12 +64438,12 @@ async function validateLastMessageIds(platform, sessions) {
64119
64438
  try {
64120
64439
  const post2 = await platform.getPost(lastMessageId);
64121
64440
  if (!post2) {
64122
- log14.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
64441
+ log15.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
64123
64442
  session.lastMessageId = undefined;
64124
64443
  session.lastMessageTs = undefined;
64125
64444
  }
64126
64445
  } catch (err) {
64127
- log14.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
64446
+ log15.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
64128
64447
  session.lastMessageId = undefined;
64129
64448
  session.lastMessageTs = undefined;
64130
64449
  }
@@ -64141,7 +64460,7 @@ async function updateStickyMessageImpl(platform, sessions, config) {
64141
64460
  hiddenCleanupDone.add(platform.platformId);
64142
64461
  const existing = stickyPostIds.get(platform.platformId);
64143
64462
  if (existing) {
64144
- log14.info(`sticky[${platform.platformId}] hidden mode: removing leftover ${formatShortId(existing)}`);
64463
+ log15.info(`sticky[${platform.platformId}] hidden mode: removing leftover ${formatShortId(existing)}`);
64145
64464
  try {
64146
64465
  await platform.unpinPost(existing);
64147
64466
  } catch {}
@@ -64161,63 +64480,63 @@ async function updateStickyMessageImpl(platform, sessions, config) {
64161
64480
  return;
64162
64481
  }
64163
64482
  const platformSessions = [...sessions.values()].filter((s) => s.platformId === platform.platformId);
64164
- log14.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
64483
+ log15.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
64165
64484
  for (const s of platformSessions) {
64166
- log14.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
64485
+ log15.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
64167
64486
  }
64168
64487
  await validateLastMessageIds(platform, platformSessions);
64169
64488
  const formatter = platform.getFormatter();
64170
64489
  const content = await buildStickyMessage(sessions, platform.platformId, config, formatter, (threadId) => platform.getThreadLink(threadId));
64171
64490
  const existingPostId = stickyPostIds.get(platform.platformId);
64172
64491
  const shouldBump = needsBump.get(platform.platformId) ?? false;
64173
- log14.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
64492
+ log15.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
64174
64493
  try {
64175
64494
  if (existingPostId && !shouldBump) {
64176
- log14.debug(`Updating existing post in place...`);
64495
+ log15.debug(`Updating existing post in place...`);
64177
64496
  try {
64178
64497
  await platform.updatePost(existingPostId, content);
64179
64498
  try {
64180
64499
  await platform.pinPost(existingPostId);
64181
- log14.debug(`Re-pinned post`);
64500
+ log15.debug(`Re-pinned post`);
64182
64501
  } catch (pinErr) {
64183
- log14.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
64502
+ log15.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
64184
64503
  }
64185
- log14.debug(`Updated successfully`);
64504
+ log15.debug(`Updated successfully`);
64186
64505
  return;
64187
64506
  } catch (err) {
64188
- log14.debug(`Update failed, will create new: ${err}`);
64507
+ log15.debug(`Update failed, will create new: ${err}`);
64189
64508
  }
64190
64509
  }
64191
64510
  needsBump.set(platform.platformId, false);
64192
64511
  if (existingPostId) {
64193
- log14.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
64512
+ log15.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
64194
64513
  try {
64195
64514
  await platform.unpinPost(existingPostId);
64196
- log14.debug(`Unpinned successfully`);
64515
+ log15.debug(`Unpinned successfully`);
64197
64516
  } catch (err) {
64198
- log14.debug(`Unpin failed (probably already unpinned): ${err}`);
64517
+ log15.debug(`Unpin failed (probably already unpinned): ${err}`);
64199
64518
  }
64200
64519
  try {
64201
64520
  await platform.deletePost(existingPostId);
64202
- log14.debug(`Deleted successfully`);
64521
+ log15.debug(`Deleted successfully`);
64203
64522
  } catch (err) {
64204
- log14.debug(`Delete failed (probably already deleted): ${err}`);
64523
+ log15.debug(`Delete failed (probably already deleted): ${err}`);
64205
64524
  }
64206
64525
  stickyPostIds.delete(platform.platformId);
64207
64526
  }
64208
- log14.debug(`Creating new post...`);
64527
+ log15.debug(`Creating new post...`);
64209
64528
  const post2 = await platform.createPost(content);
64210
64529
  stickyPostIds.set(platform.platformId, post2.id);
64211
64530
  try {
64212
64531
  await platform.pinPost(post2.id);
64213
- log14.debug(`Pinned post successfully`);
64532
+ log15.debug(`Pinned post successfully`);
64214
64533
  } catch (err) {
64215
- log14.debug(`Failed to pin post: ${err}`);
64534
+ log15.debug(`Failed to pin post: ${err}`);
64216
64535
  }
64217
64536
  if (sessionStore) {
64218
64537
  sessionStore.saveStickyPostId(platform.platformId, post2.id);
64219
64538
  }
64220
- log14.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
64539
+ log15.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
64221
64540
  const excludePostIds = new Set;
64222
64541
  if (sessionStore) {
64223
64542
  for (const session of sessionStore.load().values()) {
@@ -64233,10 +64552,10 @@ async function updateStickyMessageImpl(platform, sessions, config) {
64233
64552
  }
64234
64553
  const botUser = await platform.getBotUser();
64235
64554
  cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
64236
- log14.debug(`Background cleanup failed: ${err}`);
64555
+ log15.debug(`Background cleanup failed: ${err}`);
64237
64556
  });
64238
64557
  } catch (err) {
64239
- log14.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
64558
+ log15.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
64240
64559
  }
64241
64560
  }
64242
64561
  async function updateAllStickyMessages(platforms, sessions, config, overheadByPlatform) {
@@ -64264,7 +64583,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
64264
64583
  if (!forceRun) {
64265
64584
  const lastRun = lastCleanupTime.get(platformId) || 0;
64266
64585
  if (now - lastRun < CLEANUP_THROTTLE_MS) {
64267
- log14.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
64586
+ log15.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
64268
64587
  return;
64269
64588
  }
64270
64589
  }
@@ -64274,69 +64593,47 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
64274
64593
  const pinnedPostIds = await platform.getPinnedPosts();
64275
64594
  const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
64276
64595
  if (recentPinnedIds.length === 0) {
64277
- log14.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
64596
+ log15.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
64278
64597
  return;
64279
64598
  }
64280
- log14.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
64599
+ log15.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
64281
64600
  for (const postId of recentPinnedIds) {
64282
64601
  try {
64283
64602
  const post2 = await platform.getPost(postId);
64284
64603
  if (!post2)
64285
64604
  continue;
64286
64605
  if (post2.userId === botUserId) {
64287
- log14.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
64606
+ log15.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
64288
64607
  try {
64289
64608
  await platform.unpinPost(postId);
64290
64609
  await platform.deletePost(postId);
64291
- log14.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
64610
+ log15.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
64292
64611
  } catch (err) {
64293
- log14.debug(`Failed to cleanup ${postId}: ${err}`);
64612
+ log15.debug(`Failed to cleanup ${postId}: ${err}`);
64294
64613
  }
64295
64614
  }
64296
64615
  } catch (err) {
64297
- log14.debug(`Could not check post ${postId}: ${err}`);
64616
+ log15.debug(`Could not check post ${postId}: ${err}`);
64298
64617
  }
64299
64618
  }
64300
64619
  } catch (err) {
64301
- log14.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
64620
+ log15.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
64302
64621
  }
64303
64622
  }
64304
64623
  // src/memory/store.ts
64305
64624
  import { createHash } from "crypto";
64306
64625
  import {
64307
- existsSync as existsSync7,
64308
- mkdirSync as mkdirSync4,
64309
- readFileSync as readFileSync6,
64626
+ existsSync as existsSync8,
64627
+ mkdirSync as mkdirSync5,
64628
+ readFileSync as readFileSync7,
64310
64629
  realpathSync
64311
64630
  } from "fs";
64312
- import { homedir as homedir5 } from "os";
64313
- import { basename as basename3, dirname as dirname7, join as join9, sep as sep2 } from "path";
64314
-
64315
- // src/persistence/atomic-file.ts
64316
- import { chmodSync as chmodSync4, renameSync, writeFileSync as writeFileSync5 } from "fs";
64317
-
64318
- class SerialQueue {
64319
- tail = Promise.resolve();
64320
- run(fn) {
64321
- const next = this.tail.then(fn, fn);
64322
- this.tail = next.catch(() => {
64323
- return;
64324
- });
64325
- return next;
64326
- }
64327
- }
64328
- function writeFileAtomic(file, content) {
64329
- const tempFile = `${file}.tmp`;
64330
- writeFileSync5(tempFile, content, { encoding: "utf-8", mode: 384 });
64331
- renameSync(tempFile, file);
64332
- chmodSync4(file, 384);
64333
- }
64334
-
64335
- // src/memory/store.ts
64631
+ import { homedir as homedir6 } from "os";
64632
+ import { basename as basename3, dirname as dirname7, join as join10, sep as sep2 } from "path";
64336
64633
  init_logger();
64337
64634
  init_worktree();
64338
- var log15 = createLogger("memory");
64339
- var DEFAULT_ROOT = join9(homedir5(), ".config", "claude-threads", "memory");
64635
+ var log16 = createLogger("memory");
64636
+ var DEFAULT_ROOT = join10(homedir6(), ".config", "claude-threads", "memory");
64340
64637
  var CHANNEL_BLOCK_MAX_LINES = 200;
64341
64638
  var CHANNEL_BLOCK_MAX_BYTES = 25 * 1024;
64342
64639
  var CHANNEL_FILE_MAX_ENTRIES = 400;
@@ -64401,10 +64698,10 @@ class MemoryStore {
64401
64698
  return this.root;
64402
64699
  }
64403
64700
  channelMemoryPath(platformId) {
64404
- return join9(this.root, platformSegment(platformId), "channel", "MEMORY.md");
64701
+ return join10(this.root, platformSegment(platformId), "channel", "MEMORY.md");
64405
64702
  }
64406
64703
  repoMemoryDir(platformId, repoKey) {
64407
- const dir = join9(this.root, platformSegment(platformId), "repos", repoKey);
64704
+ const dir = join10(this.root, platformSegment(platformId), "repos", repoKey);
64408
64705
  this.ensureDir(dir);
64409
64706
  return dir;
64410
64707
  }
@@ -64451,7 +64748,7 @@ class MemoryStore {
64451
64748
  if (result.added.length > 0) {
64452
64749
  this.enforceFileCap(lines);
64453
64750
  this.writeLines(platformId, lines);
64454
- log15.debug(`Channel memory for ${platformId}: +${result.added.length} entries` + (result.duplicates.length ? ` (${result.duplicates.length} duplicates skipped)` : ""));
64751
+ log16.debug(`Channel memory for ${platformId}: +${result.added.length} entries` + (result.duplicates.length ? ` (${result.duplicates.length} duplicates skipped)` : ""));
64455
64752
  }
64456
64753
  return result;
64457
64754
  });
@@ -64490,14 +64787,14 @@ class MemoryStore {
64490
64787
  }
64491
64788
  lines.splice(target.lineIndex, 1);
64492
64789
  this.writeLines(platformId, lines);
64493
- log15.debug(`Channel memory for ${platformId}: removed one entry`);
64790
+ log16.debug(`Channel memory for ${platformId}: removed one entry`);
64494
64791
  return { ok: true, removed: target.entry };
64495
64792
  });
64496
64793
  }
64497
64794
  clearChannel(platformId) {
64498
64795
  return this.runExclusive(platformId, () => {
64499
64796
  this.writeLines(platformId, []);
64500
- log15.debug(`Channel memory for ${platformId}: cleared`);
64797
+ log16.debug(`Channel memory for ${platformId}: cleared`);
64501
64798
  });
64502
64799
  }
64503
64800
  buildChannelMemoryBlock(platformId) {
@@ -64505,7 +64802,7 @@ class MemoryStore {
64505
64802
  try {
64506
64803
  lines = this.loadLines(platformId);
64507
64804
  } catch (err) {
64508
- log15.warn(`Failed to read channel memory for ${platformId}: ${err.message}`);
64805
+ log16.warn(`Failed to read channel memory for ${platformId}: ${err.message}`);
64509
64806
  return null;
64510
64807
  }
64511
64808
  if (lines.length === 0)
@@ -64538,9 +64835,9 @@ _(older entries omitted — \`!memory\` shows all)_` : rendered;
64538
64835
  }
64539
64836
  loadLines(platformId) {
64540
64837
  const file = this.channelMemoryPath(platformId);
64541
- if (!existsSync7(file))
64838
+ if (!existsSync8(file))
64542
64839
  return [];
64543
- const raw = readFileSync6(file, "utf-8");
64840
+ const raw = readFileSync7(file, "utf-8");
64544
64841
  const lines = [];
64545
64842
  for (const line of raw.split(`
64546
64843
  `)) {
@@ -64580,8 +64877,8 @@ _(older entries omitted — \`!memory\` shows all)_` : rendered;
64580
64877
  writeFileAtomic(file, content);
64581
64878
  }
64582
64879
  ensureDir(dir) {
64583
- if (!existsSync7(dir)) {
64584
- mkdirSync4(dir, { recursive: true, mode: 448 });
64880
+ if (!existsSync8(dir)) {
64881
+ mkdirSync5(dir, { recursive: true, mode: 448 });
64585
64882
  }
64586
64883
  }
64587
64884
  }
@@ -64592,15 +64889,15 @@ async function resolveSessionMemory(memoryStore, memoryConfig, platformId, worki
64592
64889
  const repoKey = await resolveRepoKey(workingDir, worktreeRepoRoot);
64593
64890
  return { autoMemoryDir: memoryStore.repoMemoryDir(platformId, repoKey) };
64594
64891
  } catch (err) {
64595
- log15.warn(`Failed to resolve repo memory dir for ${platformId}: ${err.message}`);
64892
+ log16.warn(`Failed to resolve repo memory dir for ${platformId}: ${err.message}`);
64596
64893
  return null;
64597
64894
  }
64598
64895
  }
64599
64896
 
64600
64897
  // src/operations/commands/memory.ts
64601
64898
  init_logger();
64602
- var log16 = createLogger("commands");
64603
- var sessionLog3 = createSessionLog(log16);
64899
+ var log17 = createLogger("commands");
64900
+ var sessionLog3 = createSessionLog(log17);
64604
64901
  async function requireChannelMemory(session, ctx) {
64605
64902
  const memoryConfig = ctx.ops.getPlatformMemoryConfig(session.platformId);
64606
64903
  if (memoryConfig.enabled && memoryConfig.channelLayer)
@@ -64723,15 +65020,15 @@ init_emoji();
64723
65020
 
64724
65021
  // src/persistence/routines-store.ts
64725
65022
  init_logger();
64726
- import { join as join11 } from "path";
65023
+ import { join as join12 } from "path";
64727
65024
  import { randomUUID as randomUUID3 } from "crypto";
64728
65025
 
64729
65026
  // src/persistence/platform-list-store.ts
64730
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
64731
- import { homedir as homedir6 } from "os";
64732
- import { join as join10 } from "path";
64733
- var STORES_CONFIG_DIR = join10(homedir6(), ".config", "claude-threads");
64734
- var STORE_VERSION = 1;
65027
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
65028
+ import { homedir as homedir7 } from "os";
65029
+ import { join as join11 } from "path";
65030
+ var STORES_CONFIG_DIR = join11(homedir7(), ".config", "claude-threads");
65031
+ var STORE_VERSION2 = 1;
64735
65032
 
64736
65033
  class PlatformListStore {
64737
65034
  file;
@@ -64743,12 +65040,12 @@ class PlatformListStore {
64743
65040
  this.collectionKey = collectionKey;
64744
65041
  if (filePath) {
64745
65042
  this.file = filePath;
64746
- this.configDir = join10(filePath, "..");
65043
+ this.configDir = join11(filePath, "..");
64747
65044
  } else {
64748
65045
  this.file = defaultFile;
64749
65046
  this.configDir = STORES_CONFIG_DIR;
64750
65047
  }
64751
- mkdirSync5(this.configDir, { recursive: true, mode: 448 });
65048
+ mkdirSync6(this.configDir, { recursive: true, mode: 448 });
64752
65049
  }
64753
65050
  list(platformId) {
64754
65051
  return structuredClone(this.loadRaw().items[platformId] ?? []);
@@ -64804,18 +65101,18 @@ class PlatformListStore {
64804
65101
  return this.queue.run(fn);
64805
65102
  }
64806
65103
  loadRaw(forWrite = false) {
64807
- if (!existsSync8(this.file)) {
65104
+ if (!existsSync9(this.file)) {
64808
65105
  this.cache = null;
64809
- return { version: STORE_VERSION, items: {} };
65106
+ return { version: STORE_VERSION2, items: {} };
64810
65107
  }
64811
65108
  try {
64812
65109
  const stat = statSync3(this.file);
64813
65110
  if (this.cache && this.cache.mtimeMs === stat.mtimeMs && this.cache.size === stat.size) {
64814
65111
  return this.cache.data;
64815
65112
  }
64816
- const raw = readFileSync7(this.file, "utf-8");
65113
+ const raw = readFileSync8(this.file, "utf-8");
64817
65114
  if (raw.trim() === "") {
64818
- const data2 = { version: STORE_VERSION, items: {} };
65115
+ const data2 = { version: STORE_VERSION2, items: {} };
64819
65116
  this.cache = { mtimeMs: stat.mtimeMs, size: stat.size, data: data2 };
64820
65117
  return data2;
64821
65118
  }
@@ -64828,14 +65125,14 @@ class PlatformListStore {
64828
65125
  throw new Error(`refusing to write over unreadable ${this.file}: ${problem}`);
64829
65126
  }
64830
65127
  this.warn(`Failed to read ${this.file}: ${problem} — starting empty`);
64831
- return { version: STORE_VERSION, items: {} };
65128
+ return { version: STORE_VERSION2, items: {} };
64832
65129
  }
64833
65130
  const items = rawItems ?? {};
64834
65131
  for (const list of Object.values(items)) {
64835
65132
  for (const item of list)
64836
65133
  this.applyItemDefaults(item);
64837
65134
  }
64838
- const data = { version: parsed?.version ?? STORE_VERSION, items };
65135
+ const data = { version: parsed?.version ?? STORE_VERSION2, items };
64839
65136
  this.cache = { mtimeMs: stat.mtimeMs, size: stat.size, data };
64840
65137
  return data;
64841
65138
  } catch (err) {
@@ -64844,7 +65141,7 @@ class PlatformListStore {
64844
65141
  throw new Error(`refusing to write over unreadable ${this.file}: ${err.message}`, { cause: err });
64845
65142
  }
64846
65143
  this.warn(`Failed to read ${this.file}: ${err.message} — starting empty`);
64847
- return { version: STORE_VERSION, items: {} };
65144
+ return { version: STORE_VERSION2, items: {} };
64848
65145
  }
64849
65146
  }
64850
65147
  writeAtomic(data) {
@@ -64867,8 +65164,8 @@ class PlatformListStore {
64867
65164
  }
64868
65165
 
64869
65166
  // src/persistence/routines-store.ts
64870
- var log17 = createLogger("routines");
64871
- var DEFAULT_FILE = join11(STORES_CONFIG_DIR, "routines.yaml");
65167
+ var log18 = createLogger("routines");
65168
+ var DEFAULT_FILE = join12(STORES_CONFIG_DIR, "routines.yaml");
64872
65169
  var MAX_CONSECUTIVE_FAILURES = 3;
64873
65170
  var DEFAULT_MAX_ROUTINES = 10;
64874
65171
  var SCHEDULE_PRESETS = ["hourly", "daily", "weekdays", "weekly"];
@@ -64928,10 +65225,10 @@ class RoutinesStore extends PlatformListStore {
64928
65225
  r.requireApproval = r.requireApproval ?? true;
64929
65226
  }
64930
65227
  warn(message) {
64931
- log17.warn(message);
65228
+ log18.warn(message);
64932
65229
  }
64933
65230
  onRemoved(platformId, routine) {
64934
- log17.info(`Routine "${routine.name}" removed from ${platformId}`);
65231
+ log18.info(`Routine "${routine.name}" removed from ${platformId}`);
64935
65232
  }
64936
65233
  async add(platformId, routine, maxRoutines = DEFAULT_MAX_ROUTINES) {
64937
65234
  const result = await this.addItem(platformId, maxRoutines, "routine", () => {
@@ -64955,7 +65252,7 @@ class RoutinesStore extends PlatformListStore {
64955
65252
  });
64956
65253
  if (!result.ok)
64957
65254
  return result;
64958
- log17.info(`Routine "${result.item.name}" created on ${platformId} by @${result.item.createdBy}`);
65255
+ log18.info(`Routine "${result.item.name}" created on ${platformId} by @${result.item.createdBy}`);
64959
65256
  return { ok: true, routine: result.item };
64960
65257
  }
64961
65258
  update(platformId, id, patch) {
@@ -64995,7 +65292,7 @@ async function parseJsonViaHaiku(opts) {
64995
65292
  }
64996
65293
 
64997
65294
  // src/routines/parser.ts
64998
- var log19 = createLogger("routines");
65295
+ var log20 = createLogger("routines");
64999
65296
  var PARSE_TIMEOUT_MS = 15000;
65000
65297
  function hostTimezone() {
65001
65298
  return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
@@ -65049,7 +65346,7 @@ function parseRoutineRequest(request, defaultTimezone = hostTimezone()) {
65049
65346
  return parseJsonViaHaiku({
65050
65347
  prompt: buildParsePrompt(request, defaultTimezone),
65051
65348
  timeoutMs: PARSE_TIMEOUT_MS,
65052
- logDebug: (m) => log19.debug(`Routine parse: ${m}`),
65349
+ logDebug: (m) => log20.debug(`Routine parse: ${m}`),
65053
65350
  unusableMessage: 'could not understand the schedule — try e.g. "every weekday at 9:00, <task>"',
65054
65351
  validate: (raw) => validateParsedRoutine(raw, defaultTimezone)
65055
65352
  });
@@ -65057,10 +65354,10 @@ function parseRoutineRequest(request, defaultTimezone = hostTimezone()) {
65057
65354
 
65058
65355
  // src/persistence/watches-store.ts
65059
65356
  init_logger();
65060
- import { join as join12 } from "path";
65357
+ import { join as join13 } from "path";
65061
65358
  import { randomUUID as randomUUID4 } from "crypto";
65062
- var log20 = createLogger("watches");
65063
- var DEFAULT_FILE2 = join12(STORES_CONFIG_DIR, "watches.yaml");
65359
+ var log21 = createLogger("watches");
65360
+ var DEFAULT_FILE2 = join13(STORES_CONFIG_DIR, "watches.yaml");
65064
65361
  var MAX_CONSECUTIVE_WATCH_FAILURES = 3;
65065
65362
  var DEFAULT_MAX_WATCHES = 10;
65066
65363
  var MIN_KEYWORDS = 1;
@@ -65086,10 +65383,10 @@ class WatchesStore extends PlatformListStore {
65086
65383
  w.keywords = Array.isArray(w.keywords) ? w.keywords.filter((k) => typeof k === "string").map((k) => singleLine(k).toLowerCase()).filter((k) => k.length > 0) : [];
65087
65384
  }
65088
65385
  warn(message) {
65089
- log20.warn(message);
65386
+ log21.warn(message);
65090
65387
  }
65091
65388
  onRemoved(platformId, watch) {
65092
- log20.info(`Watch "${watch.name}" removed from ${platformId}`);
65389
+ log21.info(`Watch "${watch.name}" removed from ${platformId}`);
65093
65390
  }
65094
65391
  async add(platformId, watch, maxWatches = DEFAULT_MAX_WATCHES) {
65095
65392
  const result = await this.addItem(platformId, maxWatches, "watch", () => {
@@ -65116,7 +65413,7 @@ class WatchesStore extends PlatformListStore {
65116
65413
  });
65117
65414
  if (!result.ok)
65118
65415
  return result;
65119
- log20.info(`Watch "${result.item.name}" created on ${platformId} by @${result.item.createdBy}`);
65416
+ log21.info(`Watch "${result.item.name}" created on ${platformId} by @${result.item.createdBy}`);
65120
65417
  return { ok: true, watch: result.item };
65121
65418
  }
65122
65419
  update(platformId, id, patch) {
@@ -65126,7 +65423,7 @@ class WatchesStore extends PlatformListStore {
65126
65423
 
65127
65424
  // src/watches/parser.ts
65128
65425
  init_logger();
65129
- var log21 = createLogger("watches");
65426
+ var log22 = createLogger("watches");
65130
65427
  var PARSE_TIMEOUT_MS2 = 30000;
65131
65428
  function buildWatchParsePrompt(request) {
65132
65429
  return `Parse this event-trigger ("watch") request from a chat user into JSON.
@@ -65161,7 +65458,7 @@ function parseWatchRequest(request) {
65161
65458
  return parseJsonViaHaiku({
65162
65459
  prompt: buildWatchParsePrompt(request),
65163
65460
  timeoutMs: PARSE_TIMEOUT_MS2,
65164
- logDebug: (m) => log21.debug(`Watch parse: ${m}`),
65461
+ logDebug: (m) => log22.debug(`Watch parse: ${m}`),
65165
65462
  unusableMessage: "the parsing model returned an unusable answer — try rephrasing",
65166
65463
  validate: validateParsedWatch
65167
65464
  });
@@ -65169,8 +65466,8 @@ function parseWatchRequest(request) {
65169
65466
 
65170
65467
  // src/operations/commands/automation.ts
65171
65468
  init_logger();
65172
- var log22 = createLogger("commands");
65173
- var sessionLog4 = createSessionLog(log22);
65469
+ var log23 = createLogger("commands");
65470
+ var sessionLog4 = createSessionLog(log23);
65174
65471
  async function refuseInDirectChannelMode(session, message) {
65175
65472
  if (!session.platform.directChannelMode?.enabled)
65176
65473
  return false;
@@ -65424,7 +65721,7 @@ init_logger();
65424
65721
  import { exec as exec3 } from "child_process";
65425
65722
  import { promisify as promisify3 } from "util";
65426
65723
  var execAsync2 = promisify3(exec3);
65427
- var log23 = createLogger("branch");
65724
+ var log24 = createLogger("branch");
65428
65725
  var SUGGESTION_TIMEOUT = 15000;
65429
65726
  var MAX_SUGGESTIONS = 3;
65430
65727
  async function getCurrentBranch3(workingDir) {
@@ -65473,7 +65770,7 @@ function parseBranchSuggestions(response) {
65473
65770
  return lines.slice(0, MAX_SUGGESTIONS);
65474
65771
  }
65475
65772
  async function suggestBranchNames(workingDir, userMessage) {
65476
- log23.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
65773
+ log24.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
65477
65774
  try {
65478
65775
  const [currentBranch, recentCommits] = await Promise.all([
65479
65776
  getCurrentBranch3(workingDir),
@@ -65487,14 +65784,14 @@ async function suggestBranchNames(workingDir, userMessage) {
65487
65784
  workingDir
65488
65785
  });
65489
65786
  if (!result.success || !result.response) {
65490
- log23.debug(`Branch suggestion failed: ${result.error || "no response"}`);
65787
+ log24.debug(`Branch suggestion failed: ${result.error || "no response"}`);
65491
65788
  return [];
65492
65789
  }
65493
65790
  const suggestions = parseBranchSuggestions(result.response);
65494
- log23.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
65791
+ log24.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
65495
65792
  return suggestions;
65496
65793
  } catch (err) {
65497
- log23.debug(`Branch suggestion error: ${err}`);
65794
+ log24.debug(`Branch suggestion error: ${err}`);
65498
65795
  return [];
65499
65796
  }
65500
65797
  }
@@ -65503,8 +65800,8 @@ async function suggestBranchNames(workingDir, userMessage) {
65503
65800
  init_worktree();
65504
65801
  import { randomUUID as randomUUID5 } from "crypto";
65505
65802
  init_logger();
65506
- var log24 = createLogger("worktree");
65507
- var sessionLog5 = createSessionLog(log24);
65803
+ var log25 = createLogger("worktree");
65804
+ var sessionLog5 = createSessionLog(log25);
65508
65805
  function displayBranchName(name) {
65509
65806
  return name.replace(/[`\r\n]/g, "").slice(0, 100);
65510
65807
  }
@@ -66099,8 +66396,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
66099
66396
  }
66100
66397
  // src/operations/events/handler.ts
66101
66398
  init_logger();
66102
- var log25 = createLogger("events");
66103
- var sessionLog6 = createSessionLog(log25);
66399
+ var log26 = createLogger("events");
66400
+ var sessionLog6 = createSessionLog(log26);
66104
66401
  function detectAndExecuteClaudeCommands(text, session, ctx) {
66105
66402
  const parsed = parseClaudeCommand(text);
66106
66403
  if (parsed && isClaudeAllowedCommand(parsed.command)) {
@@ -66443,7 +66740,7 @@ function updateUsageFromStatusLine(session) {
66443
66740
  }
66444
66741
  // src/operations/monitor/handler.ts
66445
66742
  init_logger();
66446
- var log26 = createLogger("monitor");
66743
+ var log27 = createLogger("monitor");
66447
66744
  var DEFAULT_INTERVAL_MS = 60 * 1000;
66448
66745
 
66449
66746
  class SessionMonitor {
@@ -66465,14 +66762,14 @@ class SessionMonitor {
66465
66762
  }
66466
66763
  start() {
66467
66764
  if (this.isRunning) {
66468
- log26.debug("Session monitor already running");
66765
+ log27.debug("Session monitor already running");
66469
66766
  return;
66470
66767
  }
66471
66768
  this.isRunning = true;
66472
- log26.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
66769
+ log27.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
66473
66770
  this.timer = setInterval(() => {
66474
66771
  this.runCheck().catch((err) => {
66475
- log26.error(`Error during session monitoring: ${err}`);
66772
+ log27.error(`Error during session monitoring: ${err}`);
66476
66773
  });
66477
66774
  }, this.intervalMs);
66478
66775
  }
@@ -66482,7 +66779,7 @@ class SessionMonitor {
66482
66779
  this.timer = null;
66483
66780
  }
66484
66781
  this.isRunning = false;
66485
- log26.debug("Session monitor stopped");
66782
+ log27.debug("Session monitor stopped");
66486
66783
  }
66487
66784
  async runCheck() {
66488
66785
  await cleanupIdleSessions(this.sessionTimeoutMs, this.sessionWarningMs, this.getContext());
@@ -66502,8 +66799,8 @@ function createSessionContext(config, state, ops) {
66502
66799
  // src/operations/context-prompt/handler.ts
66503
66800
  init_emoji();
66504
66801
  init_logger();
66505
- var log27 = createLogger("context");
66506
- var sessionLog7 = createSessionLog(log27);
66802
+ var log28 = createLogger("context");
66803
+ var sessionLog7 = createSessionLog(log28);
66507
66804
  var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
66508
66805
  var CONTEXT_OPTIONS = [3, 5, 10];
66509
66806
  var AUTO_INCLUDE_LIMIT = 25;
@@ -66725,7 +67022,7 @@ async function offerContextPrompt(session, queuedPrompt, queuedFiles, ctx, exclu
66725
67022
  // src/operations/suggestions/tag.ts
66726
67023
  init_quick_query();
66727
67024
  init_logger();
66728
- var log28 = createLogger("tags");
67025
+ var log29 = createLogger("tags");
66729
67026
  var SUGGESTION_TIMEOUT2 = 15000;
66730
67027
  var MAX_TAGS = 3;
66731
67028
  var VALID_TAGS = [
@@ -66757,7 +67054,7 @@ function parseTags(response) {
66757
67054
  return [...new Set(tags)].slice(0, MAX_TAGS);
66758
67055
  }
66759
67056
  async function suggestSessionTags(userMessage) {
66760
- log28.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
67057
+ log29.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
66761
67058
  try {
66762
67059
  const result = await quickQuery({
66763
67060
  prompt: buildTagPrompt(userMessage),
@@ -66765,21 +67062,21 @@ async function suggestSessionTags(userMessage) {
66765
67062
  timeout: SUGGESTION_TIMEOUT2
66766
67063
  });
66767
67064
  if (!result.success || !result.response) {
66768
- log28.debug(`Tag suggestion failed: ${result.error || "no response"}`);
67065
+ log29.debug(`Tag suggestion failed: ${result.error || "no response"}`);
66769
67066
  return [];
66770
67067
  }
66771
67068
  const tags = parseTags(result.response);
66772
- log28.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
67069
+ log29.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
66773
67070
  return tags;
66774
67071
  } catch (err) {
66775
- log28.debug(`Tag suggestion error: ${err}`);
67072
+ log29.debug(`Tag suggestion error: ${err}`);
66776
67073
  return [];
66777
67074
  }
66778
67075
  }
66779
67076
  // src/operations/suggestions/title.ts
66780
67077
  init_quick_query();
66781
67078
  init_logger();
66782
- var log29 = createLogger("title");
67079
+ var log30 = createLogger("title");
66783
67080
  var SUGGESTION_TIMEOUT3 = 15000;
66784
67081
  var MIN_TITLE_LENGTH = 3;
66785
67082
  var MAX_TITLE_LENGTH = 50;
@@ -66843,32 +67140,32 @@ function parseMetadata(response) {
66843
67140
  const titleMatch = response.match(/TITLE:\s*(.+)/i);
66844
67141
  const descMatch = response.match(/DESC:\s*(.+)/i);
66845
67142
  if (!titleMatch || !descMatch) {
66846
- log29.debug("Failed to parse title/description from response");
67143
+ log30.debug("Failed to parse title/description from response");
66847
67144
  return null;
66848
67145
  }
66849
67146
  let title = titleMatch[1].trim();
66850
67147
  let description = descMatch[1].trim();
66851
67148
  if (title.length < MIN_TITLE_LENGTH) {
66852
- log29.debug(`Title too short: ${title.length} chars`);
67149
+ log30.debug(`Title too short: ${title.length} chars`);
66853
67150
  return null;
66854
67151
  }
66855
67152
  if (title.length > MAX_TITLE_LENGTH) {
66856
- log29.debug(`Title too long (${title.length} chars), truncating`);
67153
+ log30.debug(`Title too long (${title.length} chars), truncating`);
66857
67154
  title = truncateAtWord(title, MAX_TITLE_LENGTH);
66858
67155
  }
66859
67156
  if (description.length < MIN_DESC_LENGTH) {
66860
- log29.debug(`Description too short: ${description.length} chars`);
67157
+ log30.debug(`Description too short: ${description.length} chars`);
66861
67158
  return null;
66862
67159
  }
66863
67160
  if (description.length > MAX_DESC_LENGTH) {
66864
- log29.debug(`Description too long (${description.length} chars), truncating`);
67161
+ log30.debug(`Description too long (${description.length} chars), truncating`);
66865
67162
  description = truncateAtWord(description, MAX_DESC_LENGTH);
66866
67163
  }
66867
67164
  return { title, description };
66868
67165
  }
66869
67166
  async function suggestSessionMetadata(context) {
66870
67167
  const logContext = typeof context === "string" ? context.substring(0, 50) : context.originalTask.substring(0, 50);
66871
- log29.debug(`Suggesting title for: "${logContext}..."`);
67168
+ log30.debug(`Suggesting title for: "${logContext}..."`);
66872
67169
  try {
66873
67170
  const result = await quickQuery({
66874
67171
  prompt: buildTitlePrompt(context),
@@ -66876,16 +67173,16 @@ async function suggestSessionMetadata(context) {
66876
67173
  timeout: SUGGESTION_TIMEOUT3
66877
67174
  });
66878
67175
  if (!result.success || !result.response) {
66879
- log29.debug(`Title suggestion failed: ${result.error || "no response"}`);
67176
+ log30.debug(`Title suggestion failed: ${result.error || "no response"}`);
66880
67177
  return null;
66881
67178
  }
66882
67179
  const metadata = parseMetadata(result.response);
66883
67180
  if (metadata) {
66884
- log29.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
67181
+ log30.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
66885
67182
  }
66886
67183
  return metadata;
66887
67184
  } catch (err) {
66888
- log29.debug(`Title suggestion error: ${err}`);
67185
+ log30.debug(`Title suggestion error: ${err}`);
66889
67186
  return null;
66890
67187
  }
66891
67188
  }
@@ -66893,15 +67190,15 @@ async function suggestSessionMetadata(context) {
66893
67190
  init_quick_query();
66894
67191
 
66895
67192
  // src/persistence/github-emails-store.ts
66896
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync8 } from "fs";
66897
- import { homedir as homedir7 } from "os";
66898
- import { join as join13 } from "path";
67193
+ import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync9 } from "fs";
67194
+ import { homedir as homedir8 } from "os";
67195
+ import { join as join14 } from "path";
66899
67196
  init_logger();
66900
- var log30 = createLogger("gh-emails");
66901
- var DEFAULT_CONFIG_DIR = join13(homedir7(), ".config", "claude-threads");
66902
- var DEFAULT_FILE3 = join13(DEFAULT_CONFIG_DIR, "github-emails.yaml");
67197
+ var log31 = createLogger("gh-emails");
67198
+ var DEFAULT_CONFIG_DIR2 = join14(homedir8(), ".config", "claude-threads");
67199
+ var DEFAULT_FILE3 = join14(DEFAULT_CONFIG_DIR2, "github-emails.yaml");
66903
67200
  var NOREPLY_REGEX = /^\d+\+[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})@users\.noreply\.github\.com$/;
66904
- var STORE_VERSION2 = 1;
67201
+ var STORE_VERSION3 = 1;
66905
67202
  function isValidGitHubNoreplyEmail(s) {
66906
67203
  return NOREPLY_REGEX.test(s);
66907
67204
  }
@@ -66914,13 +67211,13 @@ class GitHubEmailsStore {
66914
67211
  const effective = filePath ?? envPath;
66915
67212
  if (effective) {
66916
67213
  this.file = effective;
66917
- this.configDir = join13(effective, "..");
67214
+ this.configDir = join14(effective, "..");
66918
67215
  } else {
66919
67216
  this.file = DEFAULT_FILE3;
66920
- this.configDir = DEFAULT_CONFIG_DIR;
67217
+ this.configDir = DEFAULT_CONFIG_DIR2;
66921
67218
  }
66922
- if (!existsSync9(this.configDir)) {
66923
- mkdirSync6(this.configDir, { recursive: true });
67219
+ if (!existsSync10(this.configDir)) {
67220
+ mkdirSync7(this.configDir, { recursive: true });
66924
67221
  }
66925
67222
  }
66926
67223
  get(platformId, username) {
@@ -66937,7 +67234,7 @@ class GitHubEmailsStore {
66937
67234
  }
66938
67235
  data.emails[platformId][username] = email;
66939
67236
  this.writeAtomic(data);
66940
- log30.debug(`Stored GitHub email for ${platformId}/${username}`);
67237
+ log31.debug(`Stored GitHub email for ${platformId}/${username}`);
66941
67238
  }
66942
67239
  delete(platformId, username) {
66943
67240
  const data = this.loadRaw();
@@ -66949,40 +67246,40 @@ class GitHubEmailsStore {
66949
67246
  delete data.emails[platformId];
66950
67247
  }
66951
67248
  this.writeAtomic(data);
66952
- log30.debug(`Removed GitHub email for ${platformId}/${username}`);
67249
+ log31.debug(`Removed GitHub email for ${platformId}/${username}`);
66953
67250
  return true;
66954
67251
  }
66955
67252
  lastReadDegraded = false;
66956
67253
  loadRaw() {
66957
- if (!existsSync9(this.file)) {
67254
+ if (!existsSync10(this.file)) {
66958
67255
  this.lastReadDegraded = false;
66959
- return { version: STORE_VERSION2, emails: {} };
67256
+ return { version: STORE_VERSION3, emails: {} };
66960
67257
  }
66961
67258
  try {
66962
- const raw = readFileSync8(this.file, "utf-8");
67259
+ const raw = readFileSync9(this.file, "utf-8");
66963
67260
  if (raw.trim() === "") {
66964
67261
  this.lastReadDegraded = false;
66965
- return { version: STORE_VERSION2, emails: {} };
67262
+ return { version: STORE_VERSION3, emails: {} };
66966
67263
  }
66967
67264
  const parsed = yaml.load(raw);
66968
67265
  if (!parsed || typeof parsed !== "object") {
66969
67266
  this.lastReadDegraded = true;
66970
- return { version: STORE_VERSION2, emails: {} };
67267
+ return { version: STORE_VERSION3, emails: {} };
66971
67268
  }
66972
67269
  const missing = parsed.emails === undefined || parsed.emails === null;
66973
67270
  const valid = !missing && typeof parsed.emails === "object";
66974
67271
  this.lastReadDegraded = !missing && !valid;
66975
67272
  const emails = valid ? parsed.emails : {};
66976
- return { version: parsed.version ?? STORE_VERSION2, emails };
67273
+ return { version: parsed.version ?? STORE_VERSION3, emails };
66977
67274
  } catch (err) {
66978
- log30.warn(`Failed to read ${this.file}: ${err.message} — reads degrade to empty`);
67275
+ log31.warn(`Failed to read ${this.file}: ${err.message} — reads degrade to empty`);
66979
67276
  this.lastReadDegraded = true;
66980
- return { version: STORE_VERSION2, emails: {} };
67277
+ return { version: STORE_VERSION3, emails: {} };
66981
67278
  }
66982
67279
  }
66983
67280
  writeAtomic(data) {
66984
67281
  if (this.lastReadDegraded) {
66985
- log30.error(`Refusing to write ${this.file}: the last read of the existing file was degraded — writing would destroy stored emails`);
67282
+ log31.error(`Refusing to write ${this.file}: the last read of the existing file was degraded — writing would destroy stored emails`);
66986
67283
  return;
66987
67284
  }
66988
67285
  writeFileAtomic(this.file, yaml.dump(data, { sortKeys: true, lineWidth: -1 }));
@@ -66990,8 +67287,8 @@ class GitHubEmailsStore {
66990
67287
  }
66991
67288
 
66992
67289
  // src/operations/commands/handler.ts
66993
- var log31 = createLogger("commands");
66994
- var sessionLog8 = createSessionLog(log31);
67290
+ var log32 = createLogger("commands");
67291
+ var sessionLog8 = createSessionLog(log32);
66995
67292
  function sessionAccountOption(session, ctx) {
66996
67293
  if (!session.claudeAccountId)
66997
67294
  return;
@@ -67160,7 +67457,7 @@ async function changeDirectory(session, newDir, username, ctx) {
67160
67457
  const expandedDir = newDir.startsWith("~") ? newDir.replace("~", process.env.HOME || "") : newDir;
67161
67458
  const absoluteDir = resolve6(expandedDir);
67162
67459
  const formatter = session.platform.getFormatter();
67163
- if (!existsSync10(absoluteDir)) {
67460
+ if (!existsSync11(absoluteDir)) {
67164
67461
  await postError(session, `Directory does not exist: ${formatter.formatCode(newDir)}`);
67165
67462
  sessionLog8(session).warn(`\uD83D\uDCC2 Directory does not exist: ${newDir}`);
67166
67463
  return;
@@ -67658,8 +67955,8 @@ async function handleBugReportApproval(session, isApproved, username) {
67658
67955
 
67659
67956
  // src/session/metadata-suggestions.ts
67660
67957
  init_logger();
67661
- var log32 = createLogger("session");
67662
- var sessionLog9 = createSessionLog(log32);
67958
+ var log33 = createLogger("session");
67959
+ var sessionLog9 = createSessionLog(log33);
67663
67960
  var METADATA_RETRY_DELAY_MS = 2000;
67664
67961
  var METADATA_MAX_RETRIES = 2;
67665
67962
  async function attemptMetadataFetch(session, prompt, ctx, attempt = 1, options = {}) {
@@ -67799,7 +68096,7 @@ init_worktree();
67799
68096
  // src/memory/distiller.ts
67800
68097
  init_quick_query();
67801
68098
  init_logger();
67802
- var log33 = createLogger("memory");
68099
+ var log34 = createLogger("memory");
67803
68100
  var MIN_THREAD_MESSAGES = 4;
67804
68101
  var DISTILL_MESSAGE_LIMIT = 30;
67805
68102
  var MESSAGE_CHAR_CAP = 500;
@@ -67843,21 +68140,21 @@ function scheduleDistillation(session, ctx, reason) {
67843
68140
  return;
67844
68141
  }
67845
68142
  if (session.unattended) {
67846
- log33.debug(`Skipping distillation for unattended session ${session.platformId}:${session.threadId}`);
68143
+ log34.debug(`Skipping distillation for unattended session ${session.platformId}:${session.threadId}`);
67847
68144
  return;
67848
68145
  }
67849
68146
  if (isDcmThreadId(session.threadId)) {
67850
- log33.debug(`Skipping distillation for DCM session ${session.platformId}:${session.threadId}`);
68147
+ log34.debug(`Skipping distillation for DCM session ${session.platformId}:${session.threadId}`);
67851
68148
  return;
67852
68149
  }
67853
68150
  const { platformId, threadId, platform } = session;
67854
68151
  const store = ctx.state.memoryStore;
67855
68152
  distillThread(store, platformId, threadId, platform).then((added) => {
67856
68153
  if (added > 0) {
67857
- log33.debug(`Distilled ${added} memory entries from ${platformId}:${threadId} (${reason})`);
68154
+ log34.debug(`Distilled ${added} memory entries from ${platformId}:${threadId} (${reason})`);
67858
68155
  }
67859
68156
  }).catch((err) => {
67860
- log33.debug(`Distillation failed for ${platformId}:${threadId}: ${err.message}`);
68157
+ log34.debug(`Distillation failed for ${platformId}:${threadId}: ${err.message}`);
67861
68158
  });
67862
68159
  }
67863
68160
  async function distillThread(store, platformId, threadId, platform) {
@@ -67974,7 +68271,8 @@ class SessionRegistry {
67974
68271
  return this.sessionStore.findByThread(platformId, threadId);
67975
68272
  }
67976
68273
  getPersistedByThreadId(threadId, platformId) {
67977
- return this.sessionStore.findByThreadIdAnyState(threadId, platformId);
68274
+ const persisted = this.sessionStore.findByThreadIdAnyState(threadId, platformId);
68275
+ return persisted && isRevivable(persisted) ? persisted : undefined;
67978
68276
  }
67979
68277
  getSessionStore() {
67980
68278
  return this.sessionStore;
@@ -67999,8 +68297,8 @@ class SessionRegistry {
67999
68297
 
68000
68298
  // src/operations/agent-actions/handler.ts
68001
68299
  init_logger();
68002
- var log34 = createLogger("agent-actions");
68003
- var sessionLog10 = createSessionLog(log34);
68300
+ var log35 = createLogger("agent-actions");
68301
+ var sessionLog10 = createSessionLog(log35);
68004
68302
  var AGENT_MEMORY_WRITES_PER_SESSION = 5;
68005
68303
  var LIST_LIMIT = 100;
68006
68304
  async function handleAgentAction(session, ctx, request, signal) {
@@ -68264,8 +68562,8 @@ function listWatches(session, ctx) {
68264
68562
  }
68265
68563
 
68266
68564
  // src/session/lifecycle.ts
68267
- var log35 = createLogger("lifecycle");
68268
- var sessionLog11 = createSessionLog(log35);
68565
+ var log36 = createLogger("lifecycle");
68566
+ var sessionLog11 = createSessionLog(log36);
68269
68567
  function mutableSessions(ctx) {
68270
68568
  return ctx.state.sessions;
68271
68569
  }
@@ -68429,14 +68727,6 @@ function handleRateLimit(session, hit, ctx) {
68429
68727
  sessionLog11(session).warn(`Rate limit on account "${session.claudeAccountId}" — cooling for ~${minutes}min`);
68430
68728
  post(session, "warning", `⚠️ Claude account \`${session.claudeAccountId}\` hit a rate limit. ` + `New sessions will use another account until it resets (~${minutes}min).`);
68431
68729
  }
68432
- function findPersistedByThreadId(persisted, threadId, platformId) {
68433
- for (const session of persisted.values()) {
68434
- if (session.threadId === threadId && session.platformId === platformId) {
68435
- return session;
68436
- }
68437
- }
68438
- return;
68439
- }
68440
68730
  async function createSessionDecisionBridge(ref, ctx) {
68441
68731
  try {
68442
68732
  return await DecisionBridgeServer.create(async (request, signal) => {
@@ -68454,7 +68744,7 @@ async function createSessionDecisionBridge(ref, ctx) {
68454
68744
  return messageManager.handleBridgeRequest(request, signal);
68455
68745
  });
68456
68746
  } catch (err) {
68457
- log35.warn(`Decision bridge unavailable — falling back to legacy MCP prompts: ${err}`);
68747
+ log36.warn(`Decision bridge unavailable — falling back to legacy MCP prompts: ${err}`);
68458
68748
  return null;
68459
68749
  }
68460
68750
  }
@@ -68611,7 +68901,7 @@ function resumeSessionHeaderMode(persisted, platformConfigured) {
68611
68901
  function resolveSessionHeaderMode(configured, replyToPostId, platformId) {
68612
68902
  const mode = configured ?? DEFAULT_OVERHEAD_VISIBILITY;
68613
68903
  if (mode === "hidden" && !replyToPostId) {
68614
- log35.error(`sessionHeader: hidden requires a replyToPostId for ${platformId}; ` + `downgrading this session to 'minimal' so the header post is still short.`);
68904
+ log36.error(`sessionHeader: hidden requires a replyToPostId for ${platformId}; ` + `downgrading this session to 'minimal' so the header post is still short.`);
68615
68905
  return "minimal";
68616
68906
  }
68617
68907
  return mode;
@@ -68651,7 +68941,7 @@ async function startSessionImpl(options, username, displayName, replyToPostId, p
68651
68941
  throw new Error(`Platform '${platformId}' not found. Call addPlatform() first.`);
68652
68942
  }
68653
68943
  if (!isAuthorizedForSession({ username, platform, sessionAllowedUsers: undefined })) {
68654
- log35.warn(`auth.denied.startSession: @${username || "unknown"} not authorized to start session in ${threadId.substring(0, 8)}...`);
68944
+ log36.warn(`auth.denied.startSession: @${username || "unknown"} not authorized to start session in ${threadId.substring(0, 8)}...`);
68655
68945
  return;
68656
68946
  }
68657
68947
  const activeOrPending = ctx.state.sessions.size + pendingStartsCount;
@@ -68690,7 +68980,7 @@ async function startSessionImpl(options, username, displayName, replyToPostId, p
68690
68980
  const { resolve: resolve7 } = await import("path");
68691
68981
  const requestedDir = initialOptions.workingDir.startsWith("~") ? initialOptions.workingDir.replace("~", process.env.HOME || "") : initialOptions.workingDir;
68692
68982
  const resolvedDir = resolve7(requestedDir);
68693
- if (!existsSync11(resolvedDir)) {
68983
+ if (!existsSync12(resolvedDir)) {
68694
68984
  const msg = `❌ Directory does not exist: ${formatter.formatCode(initialOptions.workingDir)}`;
68695
68985
  if (startPost) {
68696
68986
  await platform.updatePost(startPost.id, msg);
@@ -68712,17 +69002,17 @@ async function startSessionImpl(options, username, displayName, replyToPostId, p
68712
69002
  return;
68713
69003
  }
68714
69004
  workingDir = resolvedDir;
68715
- log35.info(`Starting session in directory: ${workingDir} (from !cd command)`);
69005
+ log36.info(`Starting session in directory: ${workingDir} (from !cd command)`);
68716
69006
  }
68717
69007
  if (initialOptions?.permissionMode) {
68718
69008
  permissionMode = initialOptions.permissionMode;
68719
69009
  forceInteractivePermissions = permissionMode === "default";
68720
69010
  sessionPermissionModeOverride = permissionMode;
68721
- log35.info(`Starting session with permission mode "${permissionMode}" (from !permissions command)`);
69011
+ log36.info(`Starting session with permission mode "${permissionMode}" (from !permissions command)`);
68722
69012
  } else if (initialOptions?.forceInteractivePermissions) {
68723
69013
  forceInteractivePermissions = true;
68724
69014
  permissionMode = "default";
68725
- log35.info(`Starting session with interactive permissions (from !permissions command)`);
69015
+ log36.info(`Starting session with interactive permissions (from !permissions command)`);
68726
69016
  }
68727
69017
  const userAttribution = ctx.config.userAttribution ?? true;
68728
69018
  const memoryConfig = ctx.ops.getPlatformMemoryConfig(platformId);
@@ -68736,7 +69026,7 @@ async function startSessionImpl(options, username, displayName, replyToPostId, p
68736
69026
  balanceByUsage: true
68737
69027
  });
68738
69028
  if (claudeAccount) {
68739
- log35.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
69029
+ log36.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
68740
69030
  }
68741
69031
  const bridgeSessionRef = {};
68742
69032
  const decisionBridge = await createSessionDecisionBridge(bridgeSessionRef, ctx);
@@ -68877,7 +69167,7 @@ async function resumeSession(state, ctx, resumedBy) {
68877
69167
  const sessionKey = compositeSessionId(state.platformId, state.threadId);
68878
69168
  const sessions = ctx.state?.sessions;
68879
69169
  if (sessions?.has(sessionKey)) {
68880
- log35.debug(`Session ${state.threadId.substring(0, 8)}... already active, skipping resume`);
69170
+ log36.debug(`Session ${state.threadId.substring(0, 8)}... already active, skipping resume`);
68881
69171
  return;
68882
69172
  }
68883
69173
  const inFlight = _inFlightSessionStarts.get(sessionKey);
@@ -68904,35 +69194,35 @@ async function resumeSessionImpl(state, ctx, resumedBy) {
68904
69194
  !state.claudeSessionId && "claudeSessionId",
68905
69195
  !state.workingDir && "workingDir"
68906
69196
  ].filter(Boolean).join(", ");
68907
- log35.warn(`Skipping session with missing required fields: ${missing}`);
69197
+ log36.warn(`Skipping session with missing required fields: ${missing}`);
68908
69198
  return;
68909
69199
  }
68910
69200
  const shortId = state.threadId.substring(0, 8);
68911
69201
  const platforms = ctx.state.platforms;
68912
69202
  const platform = platforms.get(state.platformId);
68913
69203
  if (!platform) {
68914
- log35.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
69204
+ log36.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
68915
69205
  return;
68916
69206
  }
68917
69207
  if (isDcmThreadId(state.threadId) && !platform.directChannelMode?.enabled) {
68918
- log35.warn(`Direct channel mode disabled for ${state.platformId}, dropping persisted DCM session`);
69208
+ log36.warn(`Direct channel mode disabled for ${state.platformId}, dropping persisted DCM session`);
68919
69209
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
68920
69210
  return;
68921
69211
  }
68922
69212
  if (!isDcmThreadId(state.threadId)) {
68923
69213
  const threadPost = await platform.getPost(state.threadId);
68924
69214
  if (!threadPost) {
68925
- log35.warn(`Thread ${shortId}... deleted, skipping resume`);
69215
+ log36.warn(`Thread ${shortId}... deleted, skipping resume`);
68926
69216
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
68927
69217
  return;
68928
69218
  }
68929
69219
  }
68930
69220
  if (ctx.state.sessions.size >= ctx.config.maxSessions) {
68931
- log35.warn(`Max sessions reached, skipping resume for ${shortId}...`);
69221
+ log36.warn(`Max sessions reached, skipping resume for ${shortId}...`);
68932
69222
  return;
68933
69223
  }
68934
- if (!existsSync11(state.workingDir)) {
68935
- log35.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
69224
+ if (!existsSync12(state.workingDir)) {
69225
+ log36.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
68936
69226
  ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
68937
69227
  const resumeFormatter = platform.getFormatter();
68938
69228
  const tempSession = {
@@ -68958,7 +69248,7 @@ Please start a new session.`), { action: "Post resume failure notification" });
68958
69248
  const appendSystemPrompt = await buildAppendSystemPrompt(platform, state.platformId, state.workingDir, state.threadId, state.startedBy, [...sessionAllowedUserSet(state)], CHAT_PLATFORM_PROMPT, ctx.state.githubEmailsStore, memoryConfig.enabled && memoryConfig.channelLayer ? ctx.state.memoryStore : null, { userAttribution });
68959
69249
  const claudeAccount = ctx.ops.acquireClaudeAccount(state.claudeAccountId, state.threadId);
68960
69250
  if (state.claudeAccountId && !claudeAccount) {
68961
- log35.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
69251
+ log36.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
68962
69252
  }
68963
69253
  const resumeBridgeRef = {};
68964
69254
  const resumeBridge = await createSessionDecisionBridge(resumeBridgeRef, ctx);
@@ -69043,7 +69333,7 @@ Please start a new session.`), { action: "Post resume failure notification" });
69043
69333
  worktreePath: detected.worktreePath,
69044
69334
  branch: detected.branch
69045
69335
  };
69046
- log35.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
69336
+ log36.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
69047
69337
  }
69048
69338
  }
69049
69339
  session.messageManager = createMessageManager(session, ctx);
@@ -69110,7 +69400,7 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
69110
69400
  await postResumeCoAuthorOnboarding(session, ctx);
69111
69401
  ctx.ops.persistSession(session);
69112
69402
  } catch (err) {
69113
- log35.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
69403
+ log36.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
69114
69404
  auditSessionEnd(session, "resume-failed");
69115
69405
  session.messageManager?.dispose();
69116
69406
  session.decisionBridge?.close();
@@ -69163,31 +69453,40 @@ async function sendFollowUp(session, message, files, ctx, username, displayName,
69163
69453
  await session.messageManager.handleUserMessage(message, files, username, displayName);
69164
69454
  }
69165
69455
  async function resumePausedSession(threadId, message, files, ctx, username, platformId) {
69166
- const persisted = ctx.state.sessionStore.load();
69167
- const state = findPersistedByThreadId(persisted, threadId, platformId);
69456
+ const state = ctx.state.sessionStore.findByThreadIdAnyState(threadId, platformId);
69168
69457
  if (!state) {
69169
- log35.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
69458
+ log36.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
69459
+ return;
69460
+ }
69461
+ if (!isRevivable(state)) {
69462
+ log36.debug(`Not resuming stopped session ${threadId.substring(0, 8)}... — it ended`);
69170
69463
  return;
69171
69464
  }
69172
69465
  const shortId = threadId.substring(0, 8);
69173
69466
  const platform = ctx.state.platforms.get(state.platformId);
69174
69467
  if (!platform) {
69175
- log35.warn(`auth.denied.resume: platform '${state.platformId}' not found for ${shortId}...`);
69468
+ log36.warn(`auth.denied.resume: platform '${state.platformId}' not found for ${shortId}...`);
69176
69469
  return;
69177
69470
  }
69178
69471
  const sessionAllowedUsers = sessionAllowedUserSet(state);
69179
69472
  if (!isAuthorizedForSession({ username, platform, sessionAllowedUsers })) {
69180
- log35.warn(`auth.denied.resume: @${username || "unknown"} not authorized to resume ${shortId}...`);
69473
+ log36.warn(`auth.denied.resume: @${username || "unknown"} not authorized to resume ${shortId}...`);
69181
69474
  return;
69182
69475
  }
69183
- log35.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
69476
+ if (state.cleanedAt) {
69477
+ log36.info(`\uD83E\uDEA6 Reviving soft-deleted session ${shortId}... (resumed by @${username})`);
69478
+ delete state.cleanedAt;
69479
+ delete state.endReason;
69480
+ ctx.state.sessionStore.save(compositeSessionId(state.platformId, state.threadId), state);
69481
+ }
69482
+ log36.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
69184
69483
  await resumeSession(state, ctx, username);
69185
- const session = ctx.ops.findSessionByThreadId(threadId);
69484
+ const session = ctx.state.sessions.get(compositeSessionId(state.platformId, state.threadId));
69186
69485
  if (session && session.claude.isRunning() && session.messageManager) {
69187
69486
  session.messageCount++;
69188
69487
  await session.messageManager.handleUserMessage(message, files, username);
69189
69488
  } else {
69190
- log35.warn(`Failed to resume session ${shortId}..., could not send message`);
69489
+ log36.warn(`Failed to resume session ${shortId}..., could not send message`);
69191
69490
  }
69192
69491
  }
69193
69492
  async function handleExit(sessionId, code, ctx, source) {
@@ -69195,7 +69494,7 @@ async function handleExit(sessionId, code, ctx, source) {
69195
69494
  const shortId = sessionId.substring(0, 8);
69196
69495
  sessionLog11(session).debug(`handleExit called code=${code} isShuttingDown=${ctx.state.isShuttingDown}`);
69197
69496
  if (!session) {
69198
- log35.debug(`Session ${shortId}... not found (already cleaned up)`);
69497
+ log36.debug(`Session ${shortId}... not found (already cleaned up)`);
69199
69498
  return;
69200
69499
  }
69201
69500
  if (source && session.claude !== source) {
@@ -69402,7 +69701,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
69402
69701
 
69403
69702
  // src/platform/dm-discovery-runtime.ts
69404
69703
  function createDmDiscoveryRuntime(deps) {
69405
- const { platforms, session, log: log36 } = deps;
69704
+ const { platforms, session, log: log37 } = deps;
69406
69705
  const graceMs = deps.graceMs ?? 30000;
69407
69706
  const orphanTtlMs = deps.orphanTtlMs ?? 10 * 60000;
69408
69707
  const instanceByChannel = new Map;
@@ -69448,7 +69747,7 @@ function createDmDiscoveryRuntime(deps) {
69448
69747
  configureAuditLog(dmId, false);
69449
69748
  if (deps.isEnabled?.(dmId) !== false)
69450
69749
  deps.removeUiRow?.(dmId);
69451
- log36("info", `\uD83E\uDDF9 DM instance ${dmId} torn down (${reason})`);
69750
+ log37("info", `\uD83E\uDDF9 DM instance ${dmId} torn down (${reason})`);
69452
69751
  };
69453
69752
  const register = (parentCfg, channelId, partnerUsernames) => {
69454
69753
  const dmConfig = deriveDmPlatformConfig(parentCfg, channelId, partnerUsernames);
@@ -69477,10 +69776,10 @@ function createDmDiscoveryRuntime(deps) {
69477
69776
  }
69478
69777
  const dmId = dmPlatformId(parentConfig.id, post2.channelId);
69479
69778
  if (deps.isEnabled && !deps.isEnabled(dmId)) {
69480
- log36("info", `Ignoring DM for disabled instance ${dmId}`);
69779
+ log37("info", `Ignoring DM for disabled instance ${dmId}`);
69481
69780
  return;
69482
69781
  }
69483
- log36("info", `\uD83D\uDCE9 New DM conversation with @${username} — spawning ${dmId}`);
69782
+ log37("info", `\uD83D\uDCE9 New DM conversation with @${username} — spawning ${dmId}`);
69484
69783
  const dmClient = register(parentConfig, post2.channelId, [username]);
69485
69784
  connecting.add(dmId);
69486
69785
  dmClient.connect().then(() => {
@@ -69490,7 +69789,7 @@ function createDmDiscoveryRuntime(deps) {
69490
69789
  }).catch((err) => {
69491
69790
  if (platforms.get(dmId) !== dmClient)
69492
69791
  return;
69493
- log36("error", `Failed to connect DM instance ${dmId}, discarding: ${err}`);
69792
+ log37("error", `Failed to connect DM instance ${dmId}, discarding: ${err}`);
69494
69793
  (async () => {
69495
69794
  const threadId = dcmThreadId(dmId);
69496
69795
  const inFlightDeadline = Date.now() + 30000;
@@ -69499,7 +69798,7 @@ function createDmDiscoveryRuntime(deps) {
69499
69798
  if (!inFlight)
69500
69799
  break;
69501
69800
  if (Date.now() > inFlightDeadline) {
69502
- log36("warn", `In-flight session start for ${dmId} did not settle within 30s — proceeding with teardown`);
69801
+ log37("warn", `In-flight session start for ${dmId} did not settle within 30s — proceeding with teardown`);
69503
69802
  break;
69504
69803
  }
69505
69804
  await Promise.race([
@@ -69514,7 +69813,7 @@ function createDmDiscoveryRuntime(deps) {
69514
69813
  try {
69515
69814
  await session.cancelSession(threadId, dmClient.getBotName());
69516
69815
  } catch (cancelErr) {
69517
- log36("warn", `Failed to cancel stranded DM session ${threadId} (will be reaped by idle cleanup): ${cancelErr}`);
69816
+ log37("warn", `Failed to cancel stranded DM session ${threadId} (will be reaped by idle cleanup): ${cancelErr}`);
69518
69817
  }
69519
69818
  }
69520
69819
  }
@@ -69524,7 +69823,7 @@ function createDmDiscoveryRuntime(deps) {
69524
69823
  return;
69525
69824
  if (!session.registry.findByThreadId(threadId))
69526
69825
  return;
69527
- log36("warn", `Sweeping session stranded on removed DM platform ${dmId}`);
69826
+ log37("warn", `Sweeping session stranded on removed DM platform ${dmId}`);
69528
69827
  session.cancelSession(threadId, dmClient.getBotName()).catch(() => {});
69529
69828
  }, 2000);
69530
69829
  })();
@@ -69555,21 +69854,21 @@ function createDmDiscoveryRuntime(deps) {
69555
69854
  continue;
69556
69855
  const parentCfg = platformConfigs.filter((p) => p.type === "mattermost" && !!p.directMessages && pid.startsWith(`${p.id}${DM_PLATFORM_SEP}`)).sort((a, b) => b.id.length - a.id.length)[0];
69557
69856
  if (!parentCfg) {
69558
- log36("warn", `Skipping persisted DM session for ${pid} (parent missing, renamed, or directMessages off)`);
69857
+ log37("warn", `Skipping persisted DM session for ${pid} (parent missing, renamed, or directMessages off)`);
69559
69858
  continue;
69560
69859
  }
69561
69860
  const channelId = pid.slice(parentCfg.id.length + DM_PLATFORM_SEP.length);
69562
69861
  if (instanceByChannel.has(channelId)) {
69563
- log36("warn", `Skipping persisted DM session for ${pid} (channel already owned by ${instanceByChannel.get(channelId)})`);
69862
+ log37("warn", `Skipping persisted DM session for ${pid} (channel already owned by ${instanceByChannel.get(channelId)})`);
69564
69863
  continue;
69565
69864
  }
69566
69865
  if (!isEnabled(pid)) {
69567
- log36("info", `Skipping disabled DM instance ${pid}`);
69866
+ log37("info", `Skipping disabled DM instance ${pid}`);
69568
69867
  skippedDisabled.push({ platformId: pid, channelId });
69569
69868
  continue;
69570
69869
  }
69571
69870
  const partners = persisted.sessionAllowedUsers && persisted.sessionAllowedUsers.length > 0 ? persisted.sessionAllowedUsers : [persisted.startedBy].filter((u) => !!u);
69572
- log36("info", `♻️ Reconstructing DM instance ${pid}`);
69871
+ log37("info", `♻️ Reconstructing DM instance ${pid}`);
69573
69872
  register(parentCfg, channelId, partners);
69574
69873
  connecting.add(pid);
69575
69874
  reconstructed.set(pid, channelId);
@@ -69610,8 +69909,8 @@ function createDmDiscoveryRuntime(deps) {
69610
69909
 
69611
69910
  // src/onboarding.ts
69612
69911
  var import_prompts = __toESM(require_prompts3(), 1);
69613
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
69614
- import { join as join14, dirname as dirname8 } from "path";
69912
+ import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
69913
+ import { join as join15, dirname as dirname8 } from "path";
69615
69914
  import { spawn as spawn3 } from "child_process";
69616
69915
  import { fileURLToPath as fileURLToPath6 } from "url";
69617
69916
 
@@ -69676,7 +69975,7 @@ function overheadVisibilityChoiceIndex(mode) {
69676
69975
  return OVERHEAD_VISIBILITY_CHOICES.findIndex((c) => c.value === mode);
69677
69976
  }
69678
69977
  var __dirname6 = dirname8(fileURLToPath6(import.meta.url));
69679
- var SLACK_MANIFEST_PATH = join14(__dirname6, "..", "docs", "slack-app-manifest.yaml");
69978
+ var SLACK_MANIFEST_PATH = join15(__dirname6, "..", "docs", "slack-app-manifest.yaml");
69680
69979
  var onCancel = () => {
69681
69980
  console.log("");
69682
69981
  console.log(dim(" Setup cancelled."));
@@ -69731,7 +70030,7 @@ async function showPlatformInstructions(platformType) {
69731
70030
  } else {
69732
70031
  let manifest;
69733
70032
  try {
69734
- manifest = readFileSync9(SLACK_MANIFEST_PATH, "utf-8");
70033
+ manifest = readFileSync10(SLACK_MANIFEST_PATH, "utf-8");
69735
70034
  } catch {
69736
70035
  console.log("");
69737
70036
  console.log(dim(" ⚠️ Could not find Slack manifest file."));
@@ -69835,9 +70134,9 @@ async function runOnboarding(reconfigure = false) {
69835
70134
  console.log(dim(" ─────────────────────────────────"));
69836
70135
  console.log("");
69837
70136
  let existingConfig = null;
69838
- if (reconfigure && existsSync12(CONFIG_PATH)) {
70137
+ if (reconfigure && existsSync13(CONFIG_PATH)) {
69839
70138
  try {
69840
- const content = readFileSync9(CONFIG_PATH, "utf-8");
70139
+ const content = readFileSync10(CONFIG_PATH, "utf-8");
69841
70140
  existingConfig = yaml.load(content);
69842
70141
  console.log(dim(" Reconfiguring existing setup."));
69843
70142
  } catch {
@@ -70980,7 +71279,7 @@ async function setupSlackPlatform(id, existing) {
70980
71279
  // src/platform/base-client.ts
70981
71280
  init_logger();
70982
71281
  import { EventEmitter as EventEmitter3 } from "events";
70983
- var log36 = createLogger("base-client");
71282
+ var log37 = createLogger("base-client");
70984
71283
 
70985
71284
  class BasePlatformClient extends EventEmitter3 {
70986
71285
  closeSocket(ws) {
@@ -71042,7 +71341,7 @@ class BasePlatformClient extends EventEmitter3 {
71042
71341
  try {
71043
71342
  await this.addReaction(post2.id, emoji);
71044
71343
  } catch (err) {
71045
- log36.warn(`Failed to add reaction ${emoji}: ${err}`);
71344
+ log37.warn(`Failed to add reaction ${emoji}: ${err}`);
71046
71345
  }
71047
71346
  }
71048
71347
  return post2;
@@ -71069,7 +71368,7 @@ class BasePlatformClient extends EventEmitter3 {
71069
71368
  this.heartbeatInterval = setInterval(() => {
71070
71369
  const silentFor = Date.now() - this.lastMessageAt;
71071
71370
  if (silentFor > this.HEARTBEAT_TIMEOUT_MS) {
71072
- log36.warn(`Connection dead (no activity for ${Math.round(silentFor / 1000)}s), reconnecting...`);
71371
+ log37.warn(`Connection dead (no activity for ${Math.round(silentFor / 1000)}s), reconnecting...`);
71073
71372
  this.stopHeartbeat();
71074
71373
  this.scheduleReconnect();
71075
71374
  return;
@@ -71089,7 +71388,7 @@ class BasePlatformClient extends EventEmitter3 {
71089
71388
  this.reconnectTimeout = null;
71090
71389
  }
71091
71390
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
71092
- log36.error("Max reconnection attempts reached");
71391
+ log37.error("Max reconnection attempts reached");
71093
71392
  return;
71094
71393
  }
71095
71394
  this.forceCloseConnection();
@@ -71116,7 +71415,7 @@ class BasePlatformClient extends EventEmitter3 {
71116
71415
  this.emit("connected");
71117
71416
  if (this.isReconnecting) {
71118
71417
  this.recoverMissedMessages().catch((err) => {
71119
- log36.warn(`Failed to recover missed messages: ${err}`);
71418
+ log37.warn(`Failed to recover missed messages: ${err}`);
71120
71419
  });
71121
71420
  }
71122
71421
  this.isReconnecting = false;
@@ -71150,7 +71449,7 @@ init_logger();
71150
71449
  // src/platform/mattermost/upload.ts
71151
71450
  init_logger();
71152
71451
  import { readFile as readFile3 } from "fs/promises";
71153
- var log37 = createLogger("mm-upload");
71452
+ var log38 = createLogger("mm-upload");
71154
71453
  async function uploadFileMattermost(args) {
71155
71454
  const { url, token, channelId, threadId, filePath, filename, caption } = args;
71156
71455
  const buffer = await readFile3(filePath);
@@ -71158,7 +71457,7 @@ async function uploadFileMattermost(args) {
71158
71457
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
71159
71458
  const formData = new FormData;
71160
71459
  formData.append("files", new Blob([arrayBuffer]), filename);
71161
- log37.debug(`POST /files (${buffer.length} bytes, ${filename})`);
71460
+ log38.debug(`POST /files (${buffer.length} bytes, ${filename})`);
71162
71461
  const uploadResponse = await fetch(uploadUrl, {
71163
71462
  method: "POST",
71164
71463
  headers: {
@@ -71182,7 +71481,7 @@ async function uploadFileMattermost(args) {
71182
71481
  root_id: resolvePostThreadId(threadId),
71183
71482
  file_ids: [fileInfo.id]
71184
71483
  };
71185
- log37.debug(`POST /posts (file_ids=[${fileInfo.id}])`);
71484
+ log38.debug(`POST /posts (file_ids=[${fileInfo.id}])`);
71186
71485
  const postResponse = await fetch(postUrl, {
71187
71486
  method: "POST",
71188
71487
  headers: {
@@ -71269,7 +71568,7 @@ ${code}
71269
71568
  }
71270
71569
 
71271
71570
  // src/platform/mattermost/client.ts
71272
- var log38 = createLogger("mattermost");
71571
+ var log39 = createLogger("mattermost");
71273
71572
 
71274
71573
  class MattermostClient extends BasePlatformClient {
71275
71574
  platformId;
@@ -71359,7 +71658,7 @@ class MattermostClient extends BasePlatformClient {
71359
71658
  const hasFileIds = fileIds && fileIds.length > 0;
71360
71659
  const hasFileMetadata = post2.metadata?.files && post2.metadata.files.length > 0;
71361
71660
  if (hasFileIds && !hasFileMetadata) {
71362
- log38.debug(`Post ${formatShortId(post2.id)} has ${fileIds.length} file(s), fetching metadata`);
71661
+ log39.debug(`Post ${formatShortId(post2.id)} has ${fileIds.length} file(s), fetching metadata`);
71363
71662
  try {
71364
71663
  const files = [];
71365
71664
  for (const fileId of fileIds) {
@@ -71367,7 +71666,7 @@ class MattermostClient extends BasePlatformClient {
71367
71666
  const file = await this.api("GET", `/files/${fileId}/info`);
71368
71667
  files.push(file);
71369
71668
  } catch (err) {
71370
- log38.warn(`Failed to fetch file info for ${fileId}: ${err}`);
71669
+ log39.warn(`Failed to fetch file info for ${fileId}: ${err}`);
71371
71670
  }
71372
71671
  }
71373
71672
  if (files.length > 0) {
@@ -71375,10 +71674,10 @@ class MattermostClient extends BasePlatformClient {
71375
71674
  ...post2.metadata,
71376
71675
  files
71377
71676
  };
71378
- log38.debug(`Enriched post ${formatShortId(post2.id)} with ${files.length} file(s)`);
71677
+ log39.debug(`Enriched post ${formatShortId(post2.id)} with ${files.length} file(s)`);
71379
71678
  }
71380
71679
  } catch (err) {
71381
- log38.warn(`Failed to fetch file metadata for post ${formatShortId(post2.id)}: ${err}`);
71680
+ log39.warn(`Failed to fetch file metadata for post ${formatShortId(post2.id)}: ${err}`);
71382
71681
  }
71383
71682
  }
71384
71683
  }
@@ -71388,7 +71687,7 @@ class MattermostClient extends BasePlatformClient {
71388
71687
  const user = await this.getUser(post2.user_id);
71389
71688
  this.emit("direct_message", this.normalizePlatformPost(post2), user);
71390
71689
  } catch (err) {
71391
- log38.warn(`Failed to emit direct message: ${err}`);
71690
+ log39.warn(`Failed to emit direct message: ${err}`);
71392
71691
  }
71393
71692
  }
71394
71693
  MAX_RETRIES = 6;
@@ -71396,7 +71695,7 @@ class MattermostClient extends BasePlatformClient {
71396
71695
  RETRY_DELAY_CAP_MS = 2000;
71397
71696
  async api(method, path10, body, retryCount = 0, options) {
71398
71697
  const url = `${this.url}/api/v4${path10}`;
71399
- log38.debug(`API ${method} ${path10}`);
71698
+ log39.debug(`API ${method} ${path10}`);
71400
71699
  const response = await fetch(url, {
71401
71700
  method,
71402
71701
  headers: {
@@ -71409,19 +71708,19 @@ class MattermostClient extends BasePlatformClient {
71409
71708
  const text = await response.text();
71410
71709
  if (response.status === 500 && retryCount < this.MAX_RETRIES) {
71411
71710
  const delay2 = this.retryDelayMs(retryCount);
71412
- log38.warn(`API ${method} ${path10} failed with 500, retrying in ${delay2}ms (attempt ${retryCount + 1}/${this.MAX_RETRIES})`);
71711
+ log39.warn(`API ${method} ${path10} failed with 500, retrying in ${delay2}ms (attempt ${retryCount + 1}/${this.MAX_RETRIES})`);
71413
71712
  await new Promise((resolve7) => setTimeout(resolve7, delay2));
71414
71713
  return this.api(method, path10, body, retryCount + 1, options);
71415
71714
  }
71416
71715
  const isSilent = options?.silent?.includes(response.status);
71417
71716
  if (isSilent) {
71418
- log38.debug(`API ${method} ${path10} failed: ${response.status} (expected)`);
71717
+ log39.debug(`API ${method} ${path10} failed: ${response.status} (expected)`);
71419
71718
  } else {
71420
- log38.warn(`API ${method} ${path10} failed: ${response.status} ${text.substring(0, 100)}`);
71719
+ log39.warn(`API ${method} ${path10} failed: ${response.status} ${text.substring(0, 100)}`);
71421
71720
  }
71422
71721
  throw new Error(`Mattermost API error ${response.status}: ${text}`);
71423
71722
  }
71424
- log38.debug(`API ${method} ${path10} → ${response.status}`);
71723
+ log39.debug(`API ${method} ${path10} → ${response.status}`);
71425
71724
  return response.json();
71426
71725
  }
71427
71726
  retryDelayMs(retryCount) {
@@ -71437,28 +71736,28 @@ class MattermostClient extends BasePlatformClient {
71437
71736
  async getUser(userId) {
71438
71737
  const cached = this.userCache.get(userId);
71439
71738
  if (cached) {
71440
- log38.debug(`User ${userId} found in cache: @${cached.username}`);
71739
+ log39.debug(`User ${userId} found in cache: @${cached.username}`);
71441
71740
  return this.normalizePlatformUser(cached);
71442
71741
  }
71443
71742
  try {
71444
71743
  const user = await this.api("GET", `/users/${userId}`);
71445
71744
  this.userCache.set(userId, user);
71446
- log38.debug(`User ${userId} fetched: @${user.username}`);
71745
+ log39.debug(`User ${userId} fetched: @${user.username}`);
71447
71746
  return this.normalizePlatformUser(user);
71448
71747
  } catch (err) {
71449
- log38.warn(`Failed to get user ${userId}: ${err}`);
71748
+ log39.warn(`Failed to get user ${userId}: ${err}`);
71450
71749
  return null;
71451
71750
  }
71452
71751
  }
71453
71752
  async getUserByUsername(username) {
71454
71753
  try {
71455
- log38.debug(`Looking up user by username: @${username}`);
71754
+ log39.debug(`Looking up user by username: @${username}`);
71456
71755
  const user = await this.api("GET", `/users/username/${username}`);
71457
71756
  this.userCache.set(user.id, user);
71458
- log38.debug(`User @${username} found: ${user.id}`);
71757
+ log39.debug(`User @${username} found: ${user.id}`);
71459
71758
  return this.normalizePlatformUser(user);
71460
71759
  } catch (err) {
71461
- log38.warn(`User @${username} not found: ${err}`);
71760
+ log39.warn(`User @${username} not found: ${err}`);
71462
71761
  return null;
71463
71762
  }
71464
71763
  }
@@ -71480,7 +71779,7 @@ class MattermostClient extends BasePlatformClient {
71480
71779
  return this.normalizePlatformPost(post2);
71481
71780
  }
71482
71781
  async addReaction(postId, emojiName) {
71483
- log38.debug(`Adding reaction :${emojiName}: to post ${postId.substring(0, 8)}`);
71782
+ log39.debug(`Adding reaction :${emojiName}: to post ${postId.substring(0, 8)}`);
71484
71783
  await this.api("POST", "/reactions", {
71485
71784
  user_id: this.botUserId,
71486
71785
  post_id: postId,
@@ -71488,11 +71787,11 @@ class MattermostClient extends BasePlatformClient {
71488
71787
  });
71489
71788
  }
71490
71789
  async removeReaction(postId, emojiName) {
71491
- log38.debug(`Removing reaction :${emojiName}: from post ${postId.substring(0, 8)}`);
71790
+ log39.debug(`Removing reaction :${emojiName}: from post ${postId.substring(0, 8)}`);
71492
71791
  await this.api("DELETE", `/users/${this.botUserId}/posts/${postId}/reactions/${emojiName}`);
71493
71792
  }
71494
71793
  async downloadFile(fileId) {
71495
- log38.debug(`Downloading file ${fileId}`);
71794
+ log39.debug(`Downloading file ${fileId}`);
71496
71795
  const url = `${this.url}/api/v4/files/${fileId}`;
71497
71796
  const response = await fetch(url, {
71498
71797
  headers: {
@@ -71500,11 +71799,11 @@ class MattermostClient extends BasePlatformClient {
71500
71799
  }
71501
71800
  });
71502
71801
  if (!response.ok) {
71503
- log38.warn(`Failed to download file ${fileId}: ${response.status}`);
71802
+ log39.warn(`Failed to download file ${fileId}: ${response.status}`);
71504
71803
  throw new Error(`Failed to download file ${fileId}: ${response.status}`);
71505
71804
  }
71506
71805
  const arrayBuffer = await response.arrayBuffer();
71507
- log38.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
71806
+ log39.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
71508
71807
  return Buffer.from(arrayBuffer);
71509
71808
  }
71510
71809
  async getFileInfo(fileId) {
@@ -71526,24 +71825,24 @@ class MattermostClient extends BasePlatformClient {
71526
71825
  }
71527
71826
  async getPost(postId) {
71528
71827
  try {
71529
- log38.debug(`Fetching post ${postId.substring(0, 8)}`);
71828
+ log39.debug(`Fetching post ${postId.substring(0, 8)}`);
71530
71829
  const post2 = await this.api("GET", `/posts/${postId}`);
71531
71830
  return this.normalizePlatformPost(post2);
71532
71831
  } catch (err) {
71533
- log38.debug(`Post ${postId.substring(0, 8)} not found: ${err}`);
71832
+ log39.debug(`Post ${postId.substring(0, 8)} not found: ${err}`);
71534
71833
  return null;
71535
71834
  }
71536
71835
  }
71537
71836
  async deletePost(postId) {
71538
- log38.debug(`Deleting post ${postId.substring(0, 8)}`);
71837
+ log39.debug(`Deleting post ${postId.substring(0, 8)}`);
71539
71838
  await this.api("DELETE", `/posts/${postId}`);
71540
71839
  }
71541
71840
  async pinPost(postId) {
71542
- log38.debug(`Pinning post ${postId.substring(0, 8)}`);
71841
+ log39.debug(`Pinning post ${postId.substring(0, 8)}`);
71543
71842
  await this.api("POST", `/posts/${postId}/pin`);
71544
71843
  }
71545
71844
  async unpinPost(postId) {
71546
- log38.debug(`Unpinning post ${postId.substring(0, 8)}`);
71845
+ log39.debug(`Unpinning post ${postId.substring(0, 8)}`);
71547
71846
  try {
71548
71847
  await this.api("POST", `/posts/${postId}/unpin`, undefined, 0, { silent: [403, 404] });
71549
71848
  } catch (err) {
@@ -71578,7 +71877,7 @@ class MattermostClient extends BasePlatformClient {
71578
71877
  }
71579
71878
  return messages;
71580
71879
  } catch (err) {
71581
- log38.warn(`Failed to get thread history for ${threadId}: ${err}`);
71880
+ log39.warn(`Failed to get thread history for ${threadId}: ${err}`);
71582
71881
  return [];
71583
71882
  }
71584
71883
  }
@@ -71597,7 +71896,7 @@ class MattermostClient extends BasePlatformClient {
71597
71896
  posts.sort((a, b) => (a.createAt ?? 0) - (b.createAt ?? 0));
71598
71897
  return posts;
71599
71898
  } catch (err) {
71600
- log38.warn(`Failed to get channel posts after ${afterPostId}: ${err}`);
71899
+ log39.warn(`Failed to get channel posts after ${afterPostId}: ${err}`);
71601
71900
  return [];
71602
71901
  }
71603
71902
  }
@@ -71710,13 +72009,13 @@ class MattermostClient extends BasePlatformClient {
71710
72009
  if (!this.lastProcessedPostId) {
71711
72010
  return;
71712
72011
  }
71713
- log38.info(`Recovering missed messages after post ${this.lastProcessedPostId}...`);
72012
+ log39.info(`Recovering missed messages after post ${this.lastProcessedPostId}...`);
71714
72013
  const missedPosts = await this.getChannelPostsAfter(this.lastProcessedPostId);
71715
72014
  if (missedPosts.length === 0) {
71716
- log38.info("No missed messages to recover");
72015
+ log39.info("No missed messages to recover");
71717
72016
  return;
71718
72017
  }
71719
- log38.info(`Recovered ${missedPosts.length} missed message(s)`);
72018
+ log39.info(`Recovered ${missedPosts.length} missed message(s)`);
71720
72019
  for (const post2 of missedPosts) {
71721
72020
  this.lastProcessedPostId = post2.id;
71722
72021
  const user = await this.getUser(post2.userId);
@@ -71776,7 +72075,7 @@ init_logger();
71776
72075
  // src/platform/slack/upload.ts
71777
72076
  init_logger();
71778
72077
  import { readFile as readFile4 } from "fs/promises";
71779
- var log39 = createLogger("slack-upload");
72078
+ var log40 = createLogger("slack-upload");
71780
72079
  var DEFAULT_API_URL = "https://slack.com/api";
71781
72080
  async function uploadFileSlack(args) {
71782
72081
  const { botToken, channelId, threadTs, filePath, filename, caption } = args;
@@ -71784,7 +72083,7 @@ async function uploadFileSlack(args) {
71784
72083
  const buffer = await readFile4(filePath);
71785
72084
  const params = new URLSearchParams({ filename, length: String(buffer.length) });
71786
72085
  const step1Url = `${apiUrl}/files.getUploadURLExternal?${params.toString()}`;
71787
- log39.debug(`GET files.getUploadURLExternal (${buffer.length} bytes, ${filename})`);
72086
+ log40.debug(`GET files.getUploadURLExternal (${buffer.length} bytes, ${filename})`);
71788
72087
  const step1Response = await fetch(step1Url, {
71789
72088
  method: "GET",
71790
72089
  headers: {
@@ -71802,7 +72101,7 @@ async function uploadFileSlack(args) {
71802
72101
  const uploadUrl = step1Data.upload_url;
71803
72102
  const fileId = step1Data.file_id;
71804
72103
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
71805
- log39.debug(`POST <upload_url>`);
72104
+ log40.debug(`POST <upload_url>`);
71806
72105
  const step2Response = await fetch(uploadUrl, {
71807
72106
  method: "POST",
71808
72107
  headers: {
@@ -71822,7 +72121,7 @@ async function uploadFileSlack(args) {
71822
72121
  if (caption !== undefined) {
71823
72122
  step3Body.initial_comment = caption;
71824
72123
  }
71825
- log39.debug(`POST files.completeUploadExternal (file_id=${fileId}, thread_ts=${threadTs})`);
72124
+ log40.debug(`POST files.completeUploadExternal (file_id=${fileId}, thread_ts=${threadTs})`);
71826
72125
  const step3Response = await fetch(`${apiUrl}/files.completeUploadExternal`, {
71827
72126
  method: "POST",
71828
72127
  headers: {
@@ -71840,7 +72139,7 @@ async function uploadFileSlack(args) {
71840
72139
  throw new Error(`Slack completeUploadExternal error: ${step3Data.error || "unknown"}`);
71841
72140
  }
71842
72141
  if (!step3Data.ts) {
71843
- log39.warn(`Slack completeUploadExternal returned no ts; using fileId ${fileId} as postId. ` + `Do not use this id for updatePost/addReaction.`);
72142
+ log40.warn(`Slack completeUploadExternal returned no ts; using fileId ${fileId} as postId. ` + `Do not use this id for updatePost/addReaction.`);
71844
72143
  }
71845
72144
  return { fileId, postId: step3Data.ts ?? fileId };
71846
72145
  }
@@ -71915,7 +72214,7 @@ ${code}
71915
72214
  }
71916
72215
 
71917
72216
  // src/platform/slack/client.ts
71918
- var log40 = createLogger("slack");
72217
+ var log41 = createLogger("slack");
71919
72218
 
71920
72219
  class SlackClient extends BasePlatformClient {
71921
72220
  platformId;
@@ -71984,7 +72283,7 @@ class SlackClient extends BasePlatformClient {
71984
72283
  return;
71985
72284
  for (const secondary of this.channelClients.values()) {
71986
72285
  secondary.recoverMissedMessages().catch((err) => {
71987
- log40.warn(`Failed to recover missed messages for ${secondary.platformId}: ${err}`);
72286
+ log41.warn(`Failed to recover missed messages for ${secondary.platformId}: ${err}`);
71988
72287
  });
71989
72288
  }
71990
72289
  }
@@ -72052,13 +72351,13 @@ class SlackClient extends BasePlatformClient {
72052
72351
  const now = Date.now();
72053
72352
  if (now < this.rateLimitRetryAfter) {
72054
72353
  const waitTime = this.rateLimitRetryAfter - now;
72055
- log40.debug(`Rate limited, waiting ${waitTime}ms`);
72354
+ log41.debug(`Rate limited, waiting ${waitTime}ms`);
72056
72355
  await new Promise((resolve7) => setTimeout(resolve7, waitTime));
72057
72356
  }
72058
72357
  this.rateLimitDelay = 0;
72059
72358
  }
72060
72359
  const url = `${this.apiUrl}/${endpoint}`;
72061
- log40.debug(`API ${method} ${endpoint}`);
72360
+ log41.debug(`API ${method} ${endpoint}`);
72062
72361
  const headers = {
72063
72362
  Authorization: `Bearer ${this.botToken}`,
72064
72363
  "Content-Type": "application/json; charset=utf-8"
@@ -72070,25 +72369,25 @@ class SlackClient extends BasePlatformClient {
72070
72369
  });
72071
72370
  if (response.status === 429) {
72072
72371
  if (retryCount >= this.MAX_RATE_LIMIT_RETRIES) {
72073
- log40.error(`Rate limit max retries (${this.MAX_RATE_LIMIT_RETRIES}) exceeded for ${endpoint}`);
72372
+ log41.error(`Rate limit max retries (${this.MAX_RATE_LIMIT_RETRIES}) exceeded for ${endpoint}`);
72074
72373
  throw new Error(`Slack API rate limit exceeded after ${this.MAX_RATE_LIMIT_RETRIES} retries`);
72075
72374
  }
72076
72375
  const retryAfter = parseInt(response.headers.get("Retry-After") || "5", 10);
72077
72376
  this.rateLimitDelay = retryAfter * 1000;
72078
72377
  this.rateLimitRetryAfter = Date.now() + this.rateLimitDelay;
72079
- log40.warn(`Rate limited by Slack, retrying after ${retryAfter}s (attempt ${retryCount + 1}/${this.MAX_RATE_LIMIT_RETRIES})`);
72378
+ log41.warn(`Rate limited by Slack, retrying after ${retryAfter}s (attempt ${retryCount + 1}/${this.MAX_RATE_LIMIT_RETRIES})`);
72080
72379
  await new Promise((resolve7) => setTimeout(resolve7, this.rateLimitDelay));
72081
72380
  return this.api(method, endpoint, body, retryCount + 1);
72082
72381
  }
72083
72382
  if (!response.ok) {
72084
72383
  const text = await response.text();
72085
- log40.warn(`API ${method} ${endpoint} failed: ${response.status} ${text.substring(0, 100)}`);
72384
+ log41.warn(`API ${method} ${endpoint} failed: ${response.status} ${text.substring(0, 100)}`);
72086
72385
  throw new Error(`Slack API error ${response.status}: ${text}`);
72087
72386
  }
72088
72387
  const data = await response.json();
72089
72388
  if (!data.ok) {
72090
72389
  if (!expectedErrors.includes(data.error || "")) {
72091
- log40.warn(`API ${method} ${endpoint} error: ${data.error}`);
72390
+ log41.warn(`API ${method} ${endpoint} error: ${data.error}`);
72092
72391
  }
72093
72392
  throw new Error(`Slack API error: ${data.error}`);
72094
72393
  }
@@ -72096,7 +72395,7 @@ class SlackClient extends BasePlatformClient {
72096
72395
  }
72097
72396
  async appApi(method, endpoint, body) {
72098
72397
  const url = `${this.apiUrl}/${endpoint}`;
72099
- log40.debug(`App API ${method} ${endpoint}`);
72398
+ log41.debug(`App API ${method} ${endpoint}`);
72100
72399
  const headers = {
72101
72400
  Authorization: `Bearer ${this.appToken}`,
72102
72401
  "Content-Type": "application/json; charset=utf-8"
@@ -72287,7 +72586,7 @@ class SlackClient extends BasePlatformClient {
72287
72586
  this.emit("channel_post", post2, user);
72288
72587
  }
72289
72588
  }).catch((err) => {
72290
- log40.warn(`Failed to get user for message event: ${err}`);
72589
+ log41.warn(`Failed to get user for message event: ${err}`);
72291
72590
  this.emit("message", post2, null);
72292
72591
  });
72293
72592
  }
@@ -72307,7 +72606,7 @@ class SlackClient extends BasePlatformClient {
72307
72606
  this.getUser(event.user || "").then((user) => {
72308
72607
  this.emit("reaction", reaction, user);
72309
72608
  }).catch((err) => {
72310
- log40.warn(`Failed to get user for reaction event: ${err}`);
72609
+ log41.warn(`Failed to get user for reaction event: ${err}`);
72311
72610
  this.emit("reaction", reaction, null);
72312
72611
  });
72313
72612
  }
@@ -72327,7 +72626,7 @@ class SlackClient extends BasePlatformClient {
72327
72626
  this.getUser(event.user || "").then((user) => {
72328
72627
  this.emit("reaction_removed", reaction, user);
72329
72628
  }).catch((err) => {
72330
- log40.warn(`Failed to get user for reaction_removed event: ${err}`);
72629
+ log41.warn(`Failed to get user for reaction_removed event: ${err}`);
72331
72630
  this.emit("reaction_removed", reaction, null);
72332
72631
  });
72333
72632
  }
@@ -72341,15 +72640,15 @@ class SlackClient extends BasePlatformClient {
72341
72640
  if (!this.lastProcessedTs) {
72342
72641
  return;
72343
72642
  }
72344
- log40.info(`Recovering missed messages after ts ${this.lastProcessedTs}...`);
72643
+ log41.info(`Recovering missed messages after ts ${this.lastProcessedTs}...`);
72345
72644
  try {
72346
72645
  const response = await this.api("GET", `conversations.history?channel=${this.channelId}&oldest=${this.lastProcessedTs}&inclusive=false&limit=100`);
72347
72646
  const messages = response.messages || [];
72348
72647
  if (messages.length === 0) {
72349
- log40.info("No missed messages to recover");
72648
+ log41.info("No missed messages to recover");
72350
72649
  return;
72351
72650
  }
72352
- log40.info(`Recovered ${messages.length} missed message(s)`);
72651
+ log41.info(`Recovered ${messages.length} missed message(s)`);
72353
72652
  const sortedMessages = messages.sort((a, b) => parseFloat(a.ts) - parseFloat(b.ts));
72354
72653
  for (const message of sortedMessages) {
72355
72654
  if (this.isBotAuthored(message)) {
@@ -72364,7 +72663,7 @@ class SlackClient extends BasePlatformClient {
72364
72663
  }
72365
72664
  }
72366
72665
  } catch (err) {
72367
- log40.warn(`Failed to recover missed messages: ${err}`);
72666
+ log41.warn(`Failed to recover missed messages: ${err}`);
72368
72667
  }
72369
72668
  }
72370
72669
  async fetchBotUser() {
@@ -72389,17 +72688,17 @@ class SlackClient extends BasePlatformClient {
72389
72688
  }
72390
72689
  const cached = this.userCache.get(userId);
72391
72690
  if (cached) {
72392
- log40.debug(`User ${userId} found in cache: @${cached.name}`);
72691
+ log41.debug(`User ${userId} found in cache: @${cached.name}`);
72393
72692
  return this.normalizePlatformUser(cached);
72394
72693
  }
72395
72694
  try {
72396
72695
  const response = await this.api("GET", `users.info?user=${userId}`);
72397
72696
  this.userCache.set(userId, response.user);
72398
72697
  this.usernameToIdCache.set(response.user.name, userId);
72399
- log40.debug(`User ${userId} fetched: @${response.user.name}`);
72698
+ log41.debug(`User ${userId} fetched: @${response.user.name}`);
72400
72699
  return this.normalizePlatformUser(response.user);
72401
72700
  } catch (err) {
72402
- log40.warn(`Failed to get user ${userId}: ${err}`);
72701
+ log41.warn(`Failed to get user ${userId}: ${err}`);
72403
72702
  return null;
72404
72703
  }
72405
72704
  }
@@ -72409,7 +72708,7 @@ class SlackClient extends BasePlatformClient {
72409
72708
  return this.getUser(cachedId);
72410
72709
  }
72411
72710
  try {
72412
- log40.debug(`Looking up user by username: @${username}`);
72711
+ log41.debug(`Looking up user by username: @${username}`);
72413
72712
  let cursor;
72414
72713
  do {
72415
72714
  const params = cursor ? `cursor=${cursor}&limit=200` : "limit=200";
@@ -72418,16 +72717,16 @@ class SlackClient extends BasePlatformClient {
72418
72717
  this.userCache.set(user.id, user);
72419
72718
  this.usernameToIdCache.set(user.name, user.id);
72420
72719
  if (user.name === username) {
72421
- log40.debug(`User @${username} found: ${user.id}`);
72720
+ log41.debug(`User @${username} found: ${user.id}`);
72422
72721
  return this.normalizePlatformUser(user);
72423
72722
  }
72424
72723
  }
72425
72724
  cursor = response.response_metadata?.next_cursor;
72426
72725
  } while (cursor);
72427
- log40.warn(`User @${username} not found`);
72726
+ log41.warn(`User @${username} not found`);
72428
72727
  return null;
72429
72728
  } catch (err) {
72430
- log40.warn(`Failed to lookup user @${username}: ${err}`);
72729
+ log41.warn(`Failed to lookup user @${username}: ${err}`);
72431
72730
  return null;
72432
72731
  }
72433
72732
  }
@@ -72515,19 +72814,19 @@ class SlackClient extends BasePlatformClient {
72515
72814
  }
72516
72815
  return null;
72517
72816
  } catch (err) {
72518
- log40.debug(`Post ${postId.substring(0, 12)} not found: ${err}`);
72817
+ log41.debug(`Post ${postId.substring(0, 12)} not found: ${err}`);
72519
72818
  return null;
72520
72819
  }
72521
72820
  }
72522
72821
  async deletePost(postId) {
72523
- log40.debug(`Deleting post ${postId.substring(0, 12)}`);
72822
+ log41.debug(`Deleting post ${postId.substring(0, 12)}`);
72524
72823
  await this.api("POST", "chat.delete", {
72525
72824
  channel: this.channelId,
72526
72825
  ts: postId
72527
72826
  });
72528
72827
  }
72529
72828
  async pinPost(postId) {
72530
- log40.debug(`Pinning post ${postId.substring(0, 12)}`);
72829
+ log41.debug(`Pinning post ${postId.substring(0, 12)}`);
72531
72830
  try {
72532
72831
  await this.api("POST", "pins.add", {
72533
72832
  channel: this.channelId,
@@ -72535,14 +72834,14 @@ class SlackClient extends BasePlatformClient {
72535
72834
  }, 0, ["already_pinned"]);
72536
72835
  } catch (err) {
72537
72836
  if (err instanceof Error && err.message.includes("already_pinned")) {
72538
- log40.debug(`Post ${postId.substring(0, 12)} already pinned`);
72837
+ log41.debug(`Post ${postId.substring(0, 12)} already pinned`);
72539
72838
  return;
72540
72839
  }
72541
72840
  throw err;
72542
72841
  }
72543
72842
  }
72544
72843
  async unpinPost(postId) {
72545
- log40.debug(`Unpinning post ${postId.substring(0, 12)}`);
72844
+ log41.debug(`Unpinning post ${postId.substring(0, 12)}`);
72546
72845
  try {
72547
72846
  await this.api("POST", "pins.remove", {
72548
72847
  channel: this.channelId,
@@ -72550,7 +72849,7 @@ class SlackClient extends BasePlatformClient {
72550
72849
  }, 0, ["no_pin"]);
72551
72850
  } catch (err) {
72552
72851
  if (err instanceof Error && err.message.includes("no_pin")) {
72553
- log40.debug(`Post ${postId.substring(0, 12)} was not pinned`);
72852
+ log41.debug(`Post ${postId.substring(0, 12)} was not pinned`);
72554
72853
  return;
72555
72854
  }
72556
72855
  throw err;
@@ -72568,7 +72867,7 @@ class SlackClient extends BasePlatformClient {
72568
72867
  if (message.length <= maxLength) {
72569
72868
  return message;
72570
72869
  }
72571
- log40.warn(`Truncating message from ${message.length} to ~${maxLength} chars`);
72870
+ log41.warn(`Truncating message from ${message.length} to ~${maxLength} chars`);
72572
72871
  return truncateMessageSafely(message, maxLength, "_... (truncated)_");
72573
72872
  }
72574
72873
  async getThreadHistory(threadId, options) {
@@ -72592,7 +72891,7 @@ class SlackClient extends BasePlatformClient {
72592
72891
  if (!cursor)
72593
72892
  break;
72594
72893
  if (page === MAX_PAGES - 1 && options?.limit) {
72595
- log40.warn(`Thread ${threadId} exceeds ${MAX_PAGES * 1000} messages — walk stopped early, the NEWEST messages are missing from context`);
72894
+ log41.warn(`Thread ${threadId} exceeds ${MAX_PAGES * 1000} messages — walk stopped early, the NEWEST messages are missing from context`);
72596
72895
  }
72597
72896
  }
72598
72897
  const kept = filtered;
@@ -72609,13 +72908,13 @@ class SlackClient extends BasePlatformClient {
72609
72908
  }
72610
72909
  return messages;
72611
72910
  } catch (err) {
72612
- log40.warn(`Failed to get thread history for ${threadId}: ${err}`);
72911
+ log41.warn(`Failed to get thread history for ${threadId}: ${err}`);
72613
72912
  return [];
72614
72913
  }
72615
72914
  }
72616
72915
  async addReaction(postId, emojiName) {
72617
72916
  const name = getEmojiName(emojiName);
72618
- log40.debug(`Adding reaction :${name}: to post ${postId.substring(0, 12)}`);
72917
+ log41.debug(`Adding reaction :${name}: to post ${postId.substring(0, 12)}`);
72619
72918
  await this.api("POST", "reactions.add", {
72620
72919
  channel: this.channelId,
72621
72920
  timestamp: postId,
@@ -72624,7 +72923,7 @@ class SlackClient extends BasePlatformClient {
72624
72923
  }
72625
72924
  async removeReaction(postId, emojiName) {
72626
72925
  const name = getEmojiName(emojiName);
72627
- log40.debug(`Removing reaction :${name}: from post ${postId.substring(0, 12)}`);
72926
+ log41.debug(`Removing reaction :${name}: from post ${postId.substring(0, 12)}`);
72628
72927
  await this.api("POST", "reactions.remove", {
72629
72928
  channel: this.channelId,
72630
72929
  timestamp: postId,
@@ -72650,7 +72949,7 @@ class SlackClient extends BasePlatformClient {
72650
72949
  }
72651
72950
  sendTyping(_threadId) {}
72652
72951
  async downloadFile(fileId) {
72653
- log40.debug(`Downloading file ${fileId}`);
72952
+ log41.debug(`Downloading file ${fileId}`);
72654
72953
  const fileInfo = await this.api("GET", `files.info?file=${fileId}`);
72655
72954
  const downloadUrl = fileInfo.file.url_private_download || fileInfo.file.url_private;
72656
72955
  if (!downloadUrl) {
@@ -72662,11 +72961,11 @@ class SlackClient extends BasePlatformClient {
72662
72961
  }
72663
72962
  });
72664
72963
  if (!response.ok) {
72665
- log40.warn(`Failed to download file ${fileId}: ${response.status}`);
72964
+ log41.warn(`Failed to download file ${fileId}: ${response.status}`);
72666
72965
  throw new Error(`Failed to download file ${fileId}: ${response.status}`);
72667
72966
  }
72668
72967
  const arrayBuffer = await response.arrayBuffer();
72669
- log40.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
72968
+ log41.debug(`Downloaded file ${fileId}: ${arrayBuffer.byteLength} bytes`);
72670
72969
  return Buffer.from(arrayBuffer);
72671
72970
  }
72672
72971
  async getFileInfo(fileId) {
@@ -73465,285 +73764,6 @@ function slackMessageToMcpPost(message, channelId, username) {
73465
73764
  // src/session/manager.ts
73466
73765
  import { EventEmitter as EventEmitter4 } from "events";
73467
73766
 
73468
- // src/persistence/session-store.ts
73469
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync10 } from "fs";
73470
- init_logger();
73471
- import { homedir as homedir8 } from "os";
73472
- import { join as join15 } from "path";
73473
- var log41 = createLogger("persist");
73474
- var STORE_VERSION3 = 2;
73475
- var DEFAULT_CONFIG_DIR2 = join15(homedir8(), ".config", "claude-threads");
73476
- var DEFAULT_SESSIONS_FILE = join15(DEFAULT_CONFIG_DIR2, "sessions.json");
73477
-
73478
- class SessionStore {
73479
- sessionsFile;
73480
- configDir;
73481
- constructor(sessionsPath) {
73482
- const envPath = process.env.CLAUDE_THREADS_SESSIONS_PATH;
73483
- const effectivePath = sessionsPath ?? envPath;
73484
- if (effectivePath) {
73485
- this.sessionsFile = effectivePath;
73486
- this.configDir = join15(effectivePath, "..");
73487
- } else {
73488
- this.sessionsFile = DEFAULT_SESSIONS_FILE;
73489
- this.configDir = DEFAULT_CONFIG_DIR2;
73490
- }
73491
- if (!existsSync13(this.configDir)) {
73492
- mkdirSync7(this.configDir, { recursive: true });
73493
- }
73494
- }
73495
- load() {
73496
- const sessions = new Map;
73497
- if (!existsSync13(this.sessionsFile)) {
73498
- log41.debug("No sessions file found");
73499
- return sessions;
73500
- }
73501
- try {
73502
- const data = this.loadRaw();
73503
- if (data.version === 1) {
73504
- log41.info("Migrating sessions from v1 to v2 (adding platformId)");
73505
- const newSessions = {};
73506
- for (const [_oldKey, session] of Object.entries(data.sessions)) {
73507
- const v1Session = session;
73508
- if (!v1Session.platformId) {
73509
- v1Session.platformId = "default";
73510
- }
73511
- const newKey = `${v1Session.platformId}:${v1Session.threadId}`;
73512
- newSessions[newKey] = v1Session;
73513
- }
73514
- data.sessions = newSessions;
73515
- data.version = 2;
73516
- this.writeAtomic(data);
73517
- } else if (data.version !== STORE_VERSION3) {
73518
- log41.warn(`Sessions file version ${data.version} not supported, starting fresh`);
73519
- return sessions;
73520
- }
73521
- for (const session of Object.values(data.sessions)) {
73522
- if (session.cleanedAt)
73523
- continue;
73524
- const sessionId = `${session.platformId}:${session.threadId}`;
73525
- sessions.set(sessionId, session);
73526
- }
73527
- log41.debug(`Loaded ${sessions.size} active session(s)`);
73528
- } catch (err) {
73529
- log41.error(`Failed to load sessions: ${err}`);
73530
- }
73531
- return sessions;
73532
- }
73533
- save(sessionId, session) {
73534
- const data = this.loadRaw();
73535
- data.sessions[sessionId] = session;
73536
- this.writeAtomic(data);
73537
- const shortId = sessionId.substring(0, 20);
73538
- log41.debug(`Saved session ${shortId}...`);
73539
- }
73540
- remove(sessionId) {
73541
- const data = this.loadRaw();
73542
- if (data.sessions[sessionId]) {
73543
- delete data.sessions[sessionId];
73544
- this.writeAtomic(data);
73545
- const shortId = sessionId.substring(0, 20);
73546
- log41.debug(`Removed session ${shortId}...`);
73547
- }
73548
- }
73549
- softDelete(sessionId) {
73550
- const data = this.loadRaw();
73551
- if (data.sessions[sessionId]) {
73552
- data.sessions[sessionId].cleanedAt = new Date().toISOString();
73553
- this.writeAtomic(data);
73554
- const shortId = sessionId.substring(0, 20);
73555
- log41.debug(`Soft-deleted session ${shortId}...`);
73556
- }
73557
- }
73558
- cleanStale(maxAgeMs) {
73559
- const data = this.loadRaw();
73560
- const now = Date.now();
73561
- const staleIds = [];
73562
- for (const [sessionId, session] of Object.entries(data.sessions)) {
73563
- if (session.cleanedAt)
73564
- continue;
73565
- const lastActivity = new Date(session.lastActivityAt).getTime();
73566
- if (now - lastActivity > maxAgeMs) {
73567
- staleIds.push(sessionId);
73568
- session.cleanedAt = new Date().toISOString();
73569
- }
73570
- }
73571
- if (staleIds.length > 0) {
73572
- this.writeAtomic(data);
73573
- log41.debug(`Soft-deleted ${staleIds.length} stale session(s)`);
73574
- }
73575
- return staleIds;
73576
- }
73577
- cleanHistory(historyRetentionMs = 3 * 24 * 60 * 60 * 1000) {
73578
- const data = this.loadRaw();
73579
- const now = Date.now();
73580
- let removedCount = 0;
73581
- for (const [sessionId, session] of Object.entries(data.sessions)) {
73582
- if (!session.cleanedAt)
73583
- continue;
73584
- const cleanedTime = new Date(session.cleanedAt).getTime();
73585
- if (now - cleanedTime > historyRetentionMs) {
73586
- delete data.sessions[sessionId];
73587
- removedCount++;
73588
- }
73589
- }
73590
- if (removedCount > 0) {
73591
- this.writeAtomic(data);
73592
- log41.debug(`Permanently removed ${removedCount} old session(s) from history`);
73593
- }
73594
- return removedCount;
73595
- }
73596
- getHistory(platformId, activeSessions) {
73597
- const data = this.loadRaw();
73598
- const historySessions = [];
73599
- for (const [sessionId, session] of Object.entries(data.sessions)) {
73600
- if (session.platformId !== platformId)
73601
- continue;
73602
- if (session.cleanedAt) {
73603
- historySessions.push(session);
73604
- continue;
73605
- }
73606
- if (session.lifecyclePostId && activeSessions && !activeSessions.has(sessionId)) {
73607
- historySessions.push(session);
73608
- }
73609
- }
73610
- return historySessions.sort((a, b) => {
73611
- const aTime = new Date(a.cleanedAt || a.lastActivityAt).getTime();
73612
- const bTime = new Date(b.cleanedAt || b.lastActivityAt).getTime();
73613
- return bTime - aTime;
73614
- });
73615
- }
73616
- clear() {
73617
- const data = this.loadRaw();
73618
- this.writeAtomic({ version: STORE_VERSION3, sessions: {}, stickyPostIds: data.stickyPostIds });
73619
- log41.debug("Cleared all sessions");
73620
- }
73621
- saveStickyPostId(platformId, postId) {
73622
- const data = this.loadRaw();
73623
- if (!data.stickyPostIds) {
73624
- data.stickyPostIds = {};
73625
- }
73626
- data.stickyPostIds[platformId] = postId;
73627
- this.writeAtomic(data);
73628
- log41.debug(`Saved sticky post ID for ${platformId}: ${postId.substring(0, 8)}...`);
73629
- }
73630
- getStickyPostIds() {
73631
- const data = this.loadRaw();
73632
- return new Map(Object.entries(data.stickyPostIds || {}));
73633
- }
73634
- removeStickyPostId(platformId) {
73635
- const data = this.loadRaw();
73636
- if (data.stickyPostIds && data.stickyPostIds[platformId]) {
73637
- delete data.stickyPostIds[platformId];
73638
- this.writeAtomic(data);
73639
- log41.debug(`Removed sticky post ID for ${platformId}`);
73640
- }
73641
- }
73642
- getPlatformEnabledState() {
73643
- const data = this.loadRaw();
73644
- return new Map(Object.entries(data.platformEnabledState || {}));
73645
- }
73646
- isPlatformEnabled(platformId) {
73647
- const data = this.loadRaw();
73648
- return data.platformEnabledState?.[platformId] ?? true;
73649
- }
73650
- setPlatformEnabled(platformId, enabled) {
73651
- const data = this.loadRaw();
73652
- if (!data.platformEnabledState) {
73653
- data.platformEnabledState = {};
73654
- }
73655
- data.platformEnabledState[platformId] = enabled;
73656
- this.writeAtomic(data);
73657
- log41.debug(`Set platform ${platformId} enabled state to ${enabled}`);
73658
- }
73659
- findByThread(platformId, threadId) {
73660
- const sessionId = `${platformId}:${threadId}`;
73661
- const data = this.loadRaw();
73662
- return data.sessions[sessionId];
73663
- }
73664
- findByThreadIdAnyState(threadId, platformId) {
73665
- const data = this.loadRaw();
73666
- for (const session of Object.values(data.sessions)) {
73667
- if (session.threadId !== threadId)
73668
- continue;
73669
- if (platformId !== undefined && session.platformId !== platformId)
73670
- continue;
73671
- return session;
73672
- }
73673
- return;
73674
- }
73675
- findByPostId(platformId, postId) {
73676
- const data = this.loadRaw();
73677
- for (const session of Object.values(data.sessions)) {
73678
- if (session.platformId !== platformId)
73679
- continue;
73680
- if (session.lifecyclePostId === postId || session.sessionStartPostId === postId) {
73681
- return session;
73682
- }
73683
- }
73684
- return;
73685
- }
73686
- getStats() {
73687
- const data = this.loadRaw();
73688
- return data.stats ?? { totalSessionsStarted: 0 };
73689
- }
73690
- recordSessionStarted() {
73691
- const data = this.loadRaw();
73692
- const stats = data.stats ?? { totalSessionsStarted: 0 };
73693
- stats.totalSessionsStarted = (stats.totalSessionsStarted ?? 0) + 1;
73694
- const milestone = milestoneReached(stats.totalSessionsStarted);
73695
- if (milestone) {
73696
- stats.milestone = { n: milestone, reachedAt: new Date().toISOString() };
73697
- }
73698
- data.stats = stats;
73699
- this.writeAtomic(data);
73700
- return stats.totalSessionsStarted;
73701
- }
73702
- lastReadDegraded = false;
73703
- loadRaw() {
73704
- if (!existsSync13(this.sessionsFile)) {
73705
- this.lastReadDegraded = false;
73706
- return { version: STORE_VERSION3, sessions: {} };
73707
- }
73708
- try {
73709
- const raw = readFileSync10(this.sessionsFile, "utf-8");
73710
- if (raw.trim() === "") {
73711
- this.lastReadDegraded = false;
73712
- return { version: STORE_VERSION3, sessions: {} };
73713
- }
73714
- const data = JSON.parse(raw);
73715
- if (!data || typeof data !== "object") {
73716
- this.lastReadDegraded = true;
73717
- return { version: STORE_VERSION3, sessions: {} };
73718
- }
73719
- if (data.sessions === undefined || data.sessions === null) {
73720
- this.lastReadDegraded = false;
73721
- data.sessions = {};
73722
- } else if (typeof data.sessions !== "object") {
73723
- this.lastReadDegraded = true;
73724
- data.sessions = {};
73725
- } else {
73726
- this.lastReadDegraded = false;
73727
- }
73728
- if (!data.version) {
73729
- data.version = STORE_VERSION3;
73730
- }
73731
- return data;
73732
- } catch (err) {
73733
- log41.warn(`Failed to read ${this.sessionsFile}: ${err.message} — reads degrade to empty`);
73734
- this.lastReadDegraded = true;
73735
- return { version: STORE_VERSION3, sessions: {} };
73736
- }
73737
- }
73738
- writeAtomic(data) {
73739
- if (this.lastReadDegraded) {
73740
- log41.error(`Refusing to write ${this.sessionsFile}: the last read of the existing file was degraded — writing would destroy persisted sessions`);
73741
- return;
73742
- }
73743
- writeFileAtomic(this.sessionsFile, JSON.stringify(data, null, 2));
73744
- }
73745
- }
73746
-
73747
73767
  // src/watches/evaluator.ts
73748
73768
  init_quick_query();
73749
73769
 
@@ -74817,6 +74837,10 @@ async function tryResumeFromReaction(deps, platformId, postId, username) {
74817
74837
  const persistedSession = deps.sessionStore.findByPostId(platformId, postId);
74818
74838
  if (!persistedSession)
74819
74839
  return false;
74840
+ if (!isRevivable(persistedSession)) {
74841
+ log50.debug(`Ignoring resume reaction on stopped session ${persistedSession.threadId.substring(0, 8)}...`);
74842
+ return false;
74843
+ }
74820
74844
  const sessionId = `${platformId}:${persistedSession.threadId}`;
74821
74845
  if (deps.registry.hasById(sessionId))
74822
74846
  return false;
@@ -75318,7 +75342,7 @@ class SessionManager extends EventEmitter4 {
75318
75342
  this.sessionStore.save(session.sessionId, state);
75319
75343
  }
75320
75344
  unpersistSession(sessionId) {
75321
- this.sessionStore.softDelete(sessionId);
75345
+ this.sessionStore.softDelete(sessionId, "stopped");
75322
75346
  }
75323
75347
  async updateSessionHeader(session) {
75324
75348
  await updateSessionHeader(session, this.getContext());
@@ -75560,7 +75584,7 @@ class SessionManager extends EventEmitter4 {
75560
75584
  const persisted = this.registry.getPersistedByThreadId(threadId, platformId);
75561
75585
  if (persisted) {
75562
75586
  const sessionId = `${persisted.platformId}:${persisted.threadId}`;
75563
- this.sessionStore.softDelete(sessionId);
75587
+ this.sessionStore.softDelete(sessionId, "stopped");
75564
75588
  }
75565
75589
  }
75566
75590
  async killSession(threadId, unpersist = true) {
@@ -85501,6 +85525,7 @@ init_logger();
85501
85525
  // src/message-handler.ts
85502
85526
  init_logger();
85503
85527
  var ackLog = createLogger("ack");
85528
+ var PAUSED_SAFE_COMMANDS = new Set(["help", "release-notes", "usage"]);
85504
85529
  var BOLD = String.raw`(?:\*{1,2}|_{1,2})?`;
85505
85530
  var STATUS_POST_PATTERNS = [
85506
85531
  /^⚠️\s+\S+ is not authorized\b/u,
@@ -85675,7 +85700,30 @@ async function handleMessage(client, session, post2, user, options) {
85675
85700
  await client.createPost(`\uD83D\uDED1 ${formatter.formatBold("Session cancelled")} by ${formatter.formatUserMention(username)}`, threadRoot);
85676
85701
  }
85677
85702
  }
85703
+ return;
85704
+ }
85705
+ if (!client.isUserAllowed(username)) {
85706
+ logger?.debug?.(`!${pausedParsed.command} from unauthorized @${username} in paused thread ${threadRoot} — dropped`);
85707
+ return;
85708
+ }
85709
+ if (!PAUSED_SAFE_COMMANDS.has(pausedParsed.command)) {
85710
+ logger?.debug?.(`!${pausedParsed.command} from @${username} needs an active session; thread ${threadRoot} is paused — dropped`);
85711
+ return;
85678
85712
  }
85713
+ const immediateCtx = {
85714
+ commandContext: "first-message",
85715
+ threadId: threadRoot,
85716
+ username,
85717
+ client,
85718
+ sessionManager: session,
85719
+ formatter,
85720
+ isAllowed: true,
85721
+ files: post2.metadata?.files
85722
+ };
85723
+ const immediate = await executeCommand(pausedParsed.command, pausedParsed.args, immediateCtx);
85724
+ if (immediate.handled)
85725
+ return;
85726
+ logger?.debug?.(`!${pausedParsed.command} from @${username} needs an active session; thread ${threadRoot} is paused — dropped`);
85679
85727
  return;
85680
85728
  }
85681
85729
  const persistedSession = session.getPersistedSession(threadRoot, platformId);
@@ -85735,6 +85783,14 @@ async function handleMessage(client, session, post2, user, options) {
85735
85783
  isAllowed: true,
85736
85784
  files
85737
85785
  };
85786
+ const firstMessageCommand = parseCommand(prompt);
85787
+ if (firstMessageCommand && !firstMessageCommand.args?.trim()) {
85788
+ const def = COMMAND_REGISTRY.find((c) => c.command === firstMessageCommand.command);
85789
+ if (def && !def.worksInFirstMessage) {
85790
+ logger?.debug?.(`!${firstMessageCommand.command} needs an active session; ${threadRoot} has none — dropped`);
85791
+ return;
85792
+ }
85793
+ }
85738
85794
  let continueProcessing = true;
85739
85795
  while (continueProcessing) {
85740
85796
  continueProcessing = false;