claude-threads 1.27.0 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -50772,6 +50772,144 @@ class SystemExecutor extends BaseExecutor {
50772
50772
  }
50773
50773
  // src/operations/executors/question-approval.ts
50774
50774
  init_emoji();
50775
+
50776
+ // src/persistence/audit-log.ts
50777
+ import { chmodSync, closeSync, constants as fsConstants, fchmodSync, lstatSync, mkdirSync, openSync, writeSync } from "fs";
50778
+ import { join as join2 } from "path";
50779
+ import { homedir } from "os";
50780
+
50781
+ // src/utils/logger.ts
50782
+ var globalLogHandler = null;
50783
+ var COMPONENT_WIDTH = 10;
50784
+ function createLogger(component, useStderr = false, sessionId) {
50785
+ const isDebug = () => process.env.DEBUG === "1";
50786
+ const consoleLog = useStderr ? console.error : console.log;
50787
+ const paddedComponent = component.length > COMPONENT_WIDTH ? component.substring(0, COMPONENT_WIDTH) : component.padEnd(COMPONENT_WIDTH);
50788
+ const formatMessage = (msg, args) => {
50789
+ if (args.length === 0)
50790
+ return msg;
50791
+ return `${msg} ${args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}`;
50792
+ };
50793
+ const DEFAULT_JSON_MAX_LEN = 60;
50794
+ return {
50795
+ debug: (msg, ...args) => {
50796
+ if (isDebug()) {
50797
+ const fullMsg = formatMessage(msg, args);
50798
+ if (globalLogHandler) {
50799
+ globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
50800
+ } else {
50801
+ consoleLog(`[${paddedComponent}] ${fullMsg}`);
50802
+ }
50803
+ }
50804
+ },
50805
+ debugJson: (label, data, maxLen = DEFAULT_JSON_MAX_LEN) => {
50806
+ if (isDebug()) {
50807
+ const json2 = JSON.stringify(data);
50808
+ const truncated = json2.length > maxLen ? `${json2.substring(0, maxLen)}…` : json2;
50809
+ const fullMsg = `${label}: ${truncated}`;
50810
+ if (globalLogHandler) {
50811
+ globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
50812
+ } else {
50813
+ consoleLog(`[${paddedComponent}] ${fullMsg}`);
50814
+ }
50815
+ }
50816
+ },
50817
+ info: (msg, ...args) => {
50818
+ const fullMsg = formatMessage(msg, args);
50819
+ if (globalLogHandler) {
50820
+ globalLogHandler("info", paddedComponent, fullMsg, sessionId);
50821
+ } else {
50822
+ consoleLog(`[${paddedComponent}] ${fullMsg}`);
50823
+ }
50824
+ },
50825
+ warn: (msg, ...args) => {
50826
+ const fullMsg = formatMessage(msg, args);
50827
+ if (globalLogHandler) {
50828
+ globalLogHandler("warn", paddedComponent, fullMsg, sessionId);
50829
+ } else {
50830
+ console.warn(`[${paddedComponent}] ⚠️ ${fullMsg}`);
50831
+ }
50832
+ },
50833
+ error: (msg, err) => {
50834
+ const fullMsg = err && isDebug() ? `${msg}
50835
+ ${err.stack || err.message}` : msg;
50836
+ if (globalLogHandler) {
50837
+ globalLogHandler("error", paddedComponent, fullMsg, sessionId);
50838
+ } else {
50839
+ console.error(`[${paddedComponent}] ❌ ${msg}`);
50840
+ if (err && isDebug()) {
50841
+ console.error(err);
50842
+ }
50843
+ }
50844
+ },
50845
+ forSession: (sid) => createLogger(component, useStderr, sid)
50846
+ };
50847
+ }
50848
+ var mcpLogger = createLogger("MCP", true);
50849
+ var wsLogger = createLogger("ws", false);
50850
+
50851
+ // src/persistence/audit-log.ts
50852
+ var log = createLogger("audit");
50853
+ var DETAIL_MAX = 500;
50854
+ var enabledPlatforms = new Set;
50855
+ var preparedDirs = new Set;
50856
+ var openFds = new Map;
50857
+ function auditDir() {
50858
+ return process.env.CLAUDE_THREADS_AUDIT_DIR || join2(homedir(), ".claude-threads", "audit");
50859
+ }
50860
+ function openAuditFd(platformId) {
50861
+ const cached3 = openFds.get(platformId);
50862
+ if (cached3 !== undefined)
50863
+ return cached3;
50864
+ const dir = auditDir();
50865
+ if (!preparedDirs.has(dir)) {
50866
+ mkdirSync(dir, { recursive: true, mode: 448 });
50867
+ chmodSync(dir, 448);
50868
+ preparedDirs.add(dir);
50869
+ }
50870
+ const file2 = join2(dir, `${encodeURIComponent(platformId)}.jsonl`);
50871
+ try {
50872
+ const st = lstatSync(file2);
50873
+ if (st.isSymbolicLink() || !st.isFile()) {
50874
+ throw new Error(`audit path exists but is not a regular file: ${file2}`);
50875
+ }
50876
+ } catch (err) {
50877
+ if (err.code !== "ENOENT")
50878
+ throw err;
50879
+ }
50880
+ const noFollow = typeof fsConstants.O_NOFOLLOW === "number" ? fsConstants.O_NOFOLLOW : 0;
50881
+ const fd = openSync(file2, fsConstants.O_WRONLY | fsConstants.O_APPEND | fsConstants.O_CREAT | noFollow, 384);
50882
+ try {
50883
+ fchmodSync(fd, 384);
50884
+ } catch {}
50885
+ openFds.set(platformId, fd);
50886
+ return fd;
50887
+ }
50888
+ function auditLog(platformId, entry) {
50889
+ if (!enabledPlatforms.has(platformId))
50890
+ return;
50891
+ try {
50892
+ const full = {
50893
+ ts: new Date().toISOString(),
50894
+ platformId,
50895
+ ...entry,
50896
+ ...entry.detail !== undefined ? { detail: entry.detail.slice(0, DETAIL_MAX) } : {}
50897
+ };
50898
+ writeSync(openAuditFd(platformId), JSON.stringify(full) + `
50899
+ `);
50900
+ } catch (err) {
50901
+ const fd = openFds.get(platformId);
50902
+ if (fd !== undefined) {
50903
+ openFds.delete(platformId);
50904
+ try {
50905
+ closeSync(fd);
50906
+ } catch {}
50907
+ }
50908
+ log.warn(`audit write failed for ${platformId}: ${err}`);
50909
+ }
50910
+ }
50911
+
50912
+ // src/operations/executors/question-approval.ts
50775
50913
  class QuestionApprovalExecutor extends BaseExecutor {
50776
50914
  constructor(options) {
50777
50915
  super(options, QuestionApprovalExecutor.createInitialState());
@@ -50986,14 +51124,29 @@ class QuestionApprovalExecutor extends BaseExecutor {
50986
51124
  return false;
50987
51125
  }
50988
51126
  if (this.state.pendingApproval?.postId === postId) {
51127
+ const approvalType = this.state.pendingApproval.type;
51128
+ const auditDecision = (approved) => {
51129
+ if (approvalType !== "plan")
51130
+ return;
51131
+ auditLog(ctx.platform.platformId, {
51132
+ threadId: ctx.threadId,
51133
+ sessionId: ctx.sessionId,
51134
+ actor: user,
51135
+ kind: "plan_approval",
51136
+ approved,
51137
+ detail: "via reaction"
51138
+ });
51139
+ };
50989
51140
  if (isApprovalEmoji(emoji4)) {
50990
51141
  ctx.logger.debug(`Approval reaction from @${user}: approved`);
51142
+ auditDecision(true);
50991
51143
  const handled = await this.handleApprovalResponse(postId, true, ctx);
50992
51144
  ctx.logger.debug(`QuestionApprovalExecutor: approval outcome=approved, handled=${handled}`);
50993
51145
  return handled;
50994
51146
  }
50995
51147
  if (isDenialEmoji(emoji4)) {
50996
51148
  ctx.logger.debug(`Approval reaction from @${user}: denied`);
51149
+ auditDecision(false);
50997
51150
  const handled = await this.handleApprovalResponse(postId, false, ctx);
50998
51151
  ctx.logger.debug(`QuestionApprovalExecutor: approval outcome=denied, handled=${handled}`);
50999
51152
  return handled;
@@ -51112,7 +51265,8 @@ class PromptExecutor extends BaseExecutor {
51112
51265
  pendingContextPrompt: null,
51113
51266
  pendingExistingWorktreePrompt: null,
51114
51267
  pendingUpdatePrompt: null,
51115
- pendingRoutinePrompt: null
51268
+ pendingRoutinePrompt: null,
51269
+ pendingWatchPrompt: null
51116
51270
  };
51117
51271
  }
51118
51272
  getInitialState() {
@@ -51123,7 +51277,8 @@ class PromptExecutor extends BaseExecutor {
51123
51277
  pendingContextPrompt: this.state.pendingContextPrompt ? { ...this.state.pendingContextPrompt } : null,
51124
51278
  pendingExistingWorktreePrompt: this.state.pendingExistingWorktreePrompt ? { ...this.state.pendingExistingWorktreePrompt } : null,
51125
51279
  pendingUpdatePrompt: this.state.pendingUpdatePrompt ? { ...this.state.pendingUpdatePrompt } : null,
51126
- pendingRoutinePrompt: this.state.pendingRoutinePrompt ? { ...this.state.pendingRoutinePrompt } : null
51280
+ pendingRoutinePrompt: this.state.pendingRoutinePrompt ? { ...this.state.pendingRoutinePrompt } : null,
51281
+ pendingWatchPrompt: this.state.pendingWatchPrompt ? { ...this.state.pendingWatchPrompt } : null
51127
51282
  };
51128
51283
  }
51129
51284
  hydrateState(persisted) {
@@ -51131,7 +51286,8 @@ class PromptExecutor extends BaseExecutor {
51131
51286
  pendingContextPrompt: persisted.pendingContextPrompt ?? null,
51132
51287
  pendingExistingWorktreePrompt: persisted.pendingExistingWorktreePrompt ?? null,
51133
51288
  pendingUpdatePrompt: persisted.pendingUpdatePrompt ?? null,
51134
- pendingRoutinePrompt: null
51289
+ pendingRoutinePrompt: null,
51290
+ pendingWatchPrompt: null
51135
51291
  };
51136
51292
  }
51137
51293
  setPendingContextPrompt(prompt) {
@@ -51267,24 +51423,36 @@ class PromptExecutor extends BaseExecutor {
51267
51423
  hasPendingRoutinePrompt() {
51268
51424
  return this.state.pendingRoutinePrompt !== null;
51269
51425
  }
51270
- async handleRoutinePromptResponse(postId, approved, username, ctx) {
51271
- if (!this.state.pendingRoutinePrompt)
51272
- return false;
51273
- if (this.state.pendingRoutinePrompt.postId !== postId)
51426
+ async completeCreationPrompt(pending, label, clear, emit, postId, approved, username, ctx) {
51427
+ if (!pending || pending.postId !== postId)
51274
51428
  return false;
51275
- const { parsed, requestedBy } = this.state.pendingRoutinePrompt;
51276
- const statusMessage = approved ? `✅ ${ctx.formatter.formatBold(`Routine "${parsed.name}" confirmed`)} by ${ctx.formatter.formatUserMention(username)} — saving...` : `❌ ${ctx.formatter.formatBold(`Routine "${parsed.name}" discarded`)} by ${ctx.formatter.formatUserMention(username)}`;
51429
+ const { parsed, requestedBy } = pending;
51430
+ const statusMessage = approved ? `✅ ${ctx.formatter.formatBold(`${label} "${parsed.name}" confirmed`)} by ${ctx.formatter.formatUserMention(username)} — saving...` : `❌ ${ctx.formatter.formatBold(`${label} "${parsed.name}" discarded`)} by ${ctx.formatter.formatUserMention(username)}`;
51277
51431
  try {
51278
51432
  await ctx.platform.updatePost(postId, statusMessage);
51279
51433
  } catch (err) {
51280
- ctx.logger.debug(`Failed to update routine prompt post: ${err}`);
51281
- }
51282
- this.state.pendingRoutinePrompt = null;
51283
- if (this.events) {
51284
- this.events.emit("routine-prompt:complete", { approved, parsed, requestedBy, postId });
51434
+ ctx.logger.debug(`Failed to update ${label.toLowerCase()} prompt post: ${err}`);
51285
51435
  }
51436
+ clear();
51437
+ emit({ approved, parsed, requestedBy, postId });
51286
51438
  return true;
51287
51439
  }
51440
+ handleRoutinePromptResponse(postId, approved, username, ctx) {
51441
+ return this.completeCreationPrompt(this.state.pendingRoutinePrompt, "Routine", () => {
51442
+ this.state.pendingRoutinePrompt = null;
51443
+ }, (payload) => this.events?.emit("routine-prompt:complete", payload), postId, approved, username, ctx);
51444
+ }
51445
+ setPendingWatchPrompt(prompt) {
51446
+ this.state.pendingWatchPrompt = prompt;
51447
+ }
51448
+ hasPendingWatchPrompt() {
51449
+ return this.state.pendingWatchPrompt !== null;
51450
+ }
51451
+ handleWatchPromptResponse(postId, approved, username, ctx) {
51452
+ return this.completeCreationPrompt(this.state.pendingWatchPrompt, "Watch", () => {
51453
+ this.state.pendingWatchPrompt = null;
51454
+ }, (payload) => this.events?.emit("watch-prompt:complete", payload), postId, approved, username, ctx);
51455
+ }
51288
51456
  async handleReaction(postId, emoji4, user, action, ctx) {
51289
51457
  ctx.logger.debug(`PromptExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji4}, user=${user}, action=${action}`);
51290
51458
  if (action !== "added") {
@@ -51357,6 +51525,18 @@ class PromptExecutor extends BaseExecutor {
51357
51525
  ctx.logger.debug(`PromptExecutor: emoji ${emoji4} not valid for routine prompt, ignoring`);
51358
51526
  return false;
51359
51527
  }
51528
+ if (this.state.pendingWatchPrompt?.postId === postId) {
51529
+ if (isApprovalEmoji(emoji4)) {
51530
+ ctx.logger.debug(`Watch prompt reaction from @${user}: approve`);
51531
+ return this.handleWatchPromptResponse(postId, true, user, ctx);
51532
+ }
51533
+ if (isDenialEmoji(emoji4)) {
51534
+ ctx.logger.debug(`Watch prompt reaction from @${user}: discard`);
51535
+ return this.handleWatchPromptResponse(postId, false, user, ctx);
51536
+ }
51537
+ ctx.logger.debug(`PromptExecutor: emoji ${emoji4} not valid for watch prompt, ignoring`);
51538
+ return false;
51539
+ }
51360
51540
  ctx.logger.debug(`PromptExecutor: no pending prompt state matches postId=${postId.substring(0, 8)}`);
51361
51541
  return false;
51362
51542
  }
@@ -51450,79 +51630,7 @@ class BugReportExecutor extends BaseExecutor {
51450
51630
  }
51451
51631
  // src/operations/executors/worktree-prompt.ts
51452
51632
  init_emoji();
51453
-
51454
- // src/utils/logger.ts
51455
- var globalLogHandler = null;
51456
- var COMPONENT_WIDTH = 10;
51457
- function createLogger(component, useStderr = false, sessionId) {
51458
- const isDebug = () => process.env.DEBUG === "1";
51459
- const consoleLog = useStderr ? console.error : console.log;
51460
- const paddedComponent = component.length > COMPONENT_WIDTH ? component.substring(0, COMPONENT_WIDTH) : component.padEnd(COMPONENT_WIDTH);
51461
- const formatMessage = (msg, args) => {
51462
- if (args.length === 0)
51463
- return msg;
51464
- return `${msg} ${args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}`;
51465
- };
51466
- const DEFAULT_JSON_MAX_LEN = 60;
51467
- return {
51468
- debug: (msg, ...args) => {
51469
- if (isDebug()) {
51470
- const fullMsg = formatMessage(msg, args);
51471
- if (globalLogHandler) {
51472
- globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
51473
- } else {
51474
- consoleLog(`[${paddedComponent}] ${fullMsg}`);
51475
- }
51476
- }
51477
- },
51478
- debugJson: (label, data, maxLen = DEFAULT_JSON_MAX_LEN) => {
51479
- if (isDebug()) {
51480
- const json2 = JSON.stringify(data);
51481
- const truncated = json2.length > maxLen ? `${json2.substring(0, maxLen)}…` : json2;
51482
- const fullMsg = `${label}: ${truncated}`;
51483
- if (globalLogHandler) {
51484
- globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
51485
- } else {
51486
- consoleLog(`[${paddedComponent}] ${fullMsg}`);
51487
- }
51488
- }
51489
- },
51490
- info: (msg, ...args) => {
51491
- const fullMsg = formatMessage(msg, args);
51492
- if (globalLogHandler) {
51493
- globalLogHandler("info", paddedComponent, fullMsg, sessionId);
51494
- } else {
51495
- consoleLog(`[${paddedComponent}] ${fullMsg}`);
51496
- }
51497
- },
51498
- warn: (msg, ...args) => {
51499
- const fullMsg = formatMessage(msg, args);
51500
- if (globalLogHandler) {
51501
- globalLogHandler("warn", paddedComponent, fullMsg, sessionId);
51502
- } else {
51503
- console.warn(`[${paddedComponent}] ⚠️ ${fullMsg}`);
51504
- }
51505
- },
51506
- error: (msg, err) => {
51507
- const fullMsg = err && isDebug() ? `${msg}
51508
- ${err.stack || err.message}` : msg;
51509
- if (globalLogHandler) {
51510
- globalLogHandler("error", paddedComponent, fullMsg, sessionId);
51511
- } else {
51512
- console.error(`[${paddedComponent}] ❌ ${msg}`);
51513
- if (err && isDebug()) {
51514
- console.error(err);
51515
- }
51516
- }
51517
- },
51518
- forSession: (sid) => createLogger(component, useStderr, sid)
51519
- };
51520
- }
51521
- var mcpLogger = createLogger("MCP", true);
51522
- var wsLogger = createLogger("ws", false);
51523
-
51524
- // src/operations/executors/worktree-prompt.ts
51525
- var log = createLogger("wt-prompt");
51633
+ var log2 = createLogger("wt-prompt");
51526
51634
  // src/operations/message-manager-events.ts
51527
51635
  import { EventEmitter } from "events";
51528
51636
 
@@ -51571,7 +51679,7 @@ function formatBytes(bytes) {
51571
51679
  }
51572
51680
 
51573
51681
  // src/operations/streaming/handler.ts
51574
- var log2 = createLogger("streaming");
51682
+ var log3 = createLogger("streaming");
51575
51683
  async function postSkippedFilesFeedback(platform, threadId, skipped) {
51576
51684
  if (skipped.length === 0)
51577
51685
  return;
@@ -51639,7 +51747,7 @@ function formatRelativeTime(date8) {
51639
51747
  return `${diffMin} min ago`;
51640
51748
  }
51641
51749
  // src/operations/message-manager.ts
51642
- var log3 = createLogger("msg-mgr");
51750
+ var log4 = createLogger("msg-mgr");
51643
51751
 
51644
51752
  class MessageManager {
51645
51753
  platform;
@@ -51732,7 +51840,7 @@ class MessageManager {
51732
51840
  });
51733
51841
  }
51734
51842
  async handleEvent(event) {
51735
- const logger = log3.forSession(this.sessionId);
51843
+ const logger = log4.forSession(this.sessionId);
51736
51844
  const transformCtx = {
51737
51845
  sessionId: this.sessionId,
51738
51846
  formatter: this.platform.getFormatter(),
@@ -51786,7 +51894,7 @@ class MessageManager {
51786
51894
  }
51787
51895
  }
51788
51896
  async executeOperation(op) {
51789
- const logger = log3.forSession(this.sessionId);
51897
+ const logger = log4.forSession(this.sessionId);
51790
51898
  const ctx = this.getExecutorContext();
51791
51899
  try {
51792
51900
  if (isContentOp(op)) {
@@ -51854,7 +51962,7 @@ class MessageManager {
51854
51962
  threadId: this.threadId,
51855
51963
  platform: this.platform,
51856
51964
  formatter: this.platform.getFormatter(),
51857
- logger: log3.forSession(this.sessionId),
51965
+ logger: log4.forSession(this.sessionId),
51858
51966
  postTracker: this.postTracker,
51859
51967
  contentBreaker: this.contentBreaker,
51860
51968
  threadLogger: this.session.threadLogger,
@@ -51976,6 +52084,9 @@ class MessageManager {
51976
52084
  setPendingRoutinePrompt(prompt) {
51977
52085
  this.promptExecutor.setPendingRoutinePrompt(prompt);
51978
52086
  }
52087
+ setPendingWatchPrompt(prompt) {
52088
+ this.promptExecutor.setPendingWatchPrompt(prompt);
52089
+ }
51979
52090
  setPendingBugReport(report) {
51980
52091
  this.bugReportExecutor.setPendingBugReport(report);
51981
52092
  }
@@ -52074,13 +52185,13 @@ class MessageManager {
52074
52185
  return this.systemExecutor.postSuccess(message, this.getExecutorContext());
52075
52186
  }
52076
52187
  async prepareForUserMessage() {
52077
- const logger = log3.forSession(this.sessionId);
52188
+ const logger = log4.forSession(this.sessionId);
52078
52189
  logger.debug("Preparing for new user message");
52079
52190
  await this.closeCurrentPost();
52080
52191
  await this.bumpTaskList();
52081
52192
  }
52082
52193
  async handleUserMessage(message, files, username, displayName) {
52083
- const logger = log3.forSession(this.sessionId);
52194
+ const logger = log4.forSession(this.sessionId);
52084
52195
  if (!this.session.claude.isRunning()) {
52085
52196
  logger.debug("Claude not running, ignoring user message");
52086
52197
  return false;
@@ -52123,7 +52234,7 @@ class MessageManager {
52123
52234
  ];
52124
52235
  }
52125
52236
  async handleReaction(postId, emoji4, user, action) {
52126
- const logger = log3.forSession(this.sessionId);
52237
+ const logger = log4.forSession(this.sessionId);
52127
52238
  const ctx = this.getExecutorContext();
52128
52239
  logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji4}, user=${user}, action=${action}`);
52129
52240
  for (const { name, executor } of this.reactionDispatchList()) {
@@ -52220,7 +52331,7 @@ class MessageManager {
52220
52331
  }
52221
52332
  }
52222
52333
  // src/session/lifecycle-fsm.ts
52223
- var log4 = createLogger("fsm");
52334
+ var log5 = createLogger("fsm");
52224
52335
  var ALLOWED_TRANSITIONS = {
52225
52336
  starting: new Set(["active", "paused", "interrupted", "cancelling", "restarting"]),
52226
52337
  active: new Set([
@@ -52247,7 +52358,7 @@ var ALLOWED_TRANSITIONS = {
52247
52358
  };
52248
52359
  // src/config/index.ts
52249
52360
  import { resolve as resolve2, dirname as dirname2 } from "path";
52250
- import { homedir } from "os";
52361
+ import { homedir as homedir2 } from "os";
52251
52362
 
52252
52363
  // node_modules/js-yaml/dist/js-yaml.mjs
52253
52364
  function getDefaultExportFromCjs(x) {
@@ -55359,7 +55470,7 @@ var jsYamlExports = requireJsYaml();
55359
55470
  var yaml = /* @__PURE__ */ getDefaultExportFromCjs(jsYamlExports);
55360
55471
 
55361
55472
  // src/config/index.ts
55362
- var CONFIG_PATH = resolve2(homedir(), ".config", "claude-threads", "config.yaml");
55473
+ var CONFIG_PATH = resolve2(homedir2(), ".config", "claude-threads", "config.yaml");
55363
55474
 
55364
55475
  // src/utils/battery.ts
55365
55476
  import { exec } from "child_process";
@@ -55464,7 +55575,7 @@ function formatReleaseNotes(notes, formatter) {
55464
55575
 
55465
55576
  // src/utils/keep-alive.ts
55466
55577
  import { spawn } from "child_process";
55467
- var log5 = createLogger("keepalive");
55578
+ var log6 = createLogger("keepalive");
55468
55579
  function keepAliveSpawnSpec(platform, parentPid) {
55469
55580
  switch (platform) {
55470
55581
  case "darwin":
@@ -55520,7 +55631,7 @@ class KeepAliveManager {
55520
55631
  if (!enabled && this.keepAliveProcess) {
55521
55632
  this.stopKeepAlive();
55522
55633
  }
55523
- log5.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
55634
+ log6.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
55524
55635
  }
55525
55636
  isEnabled() {
55526
55637
  return this.enabled;
@@ -55530,7 +55641,7 @@ class KeepAliveManager {
55530
55641
  }
55531
55642
  sessionStarted() {
55532
55643
  this.activeSessionCount++;
55533
- log5.debug(`Session started (${this.activeSessionCount} active)`);
55644
+ log6.debug(`Session started (${this.activeSessionCount} active)`);
55534
55645
  if (this.activeSessionCount === 1) {
55535
55646
  this.startKeepAlive();
55536
55647
  }
@@ -55539,7 +55650,7 @@ class KeepAliveManager {
55539
55650
  if (this.activeSessionCount > 0) {
55540
55651
  this.activeSessionCount--;
55541
55652
  }
55542
- log5.debug(`Session ended (${this.activeSessionCount} active)`);
55653
+ log6.debug(`Session ended (${this.activeSessionCount} active)`);
55543
55654
  if (this.activeSessionCount === 0) {
55544
55655
  this.stopKeepAlive();
55545
55656
  }
@@ -55553,11 +55664,11 @@ class KeepAliveManager {
55553
55664
  }
55554
55665
  startKeepAlive() {
55555
55666
  if (!this.enabled) {
55556
- log5.debug("Keep-alive disabled, skipping");
55667
+ log6.debug("Keep-alive disabled, skipping");
55557
55668
  return;
55558
55669
  }
55559
55670
  if (this.keepAliveProcess) {
55560
- log5.debug("Keep-alive already running");
55671
+ log6.debug("Keep-alive already running");
55561
55672
  return;
55562
55673
  }
55563
55674
  switch (this.platform) {
@@ -55571,12 +55682,12 @@ class KeepAliveManager {
55571
55682
  this.startWindowsKeepAlive();
55572
55683
  break;
55573
55684
  default:
55574
- log5.warn(`Keep-alive not supported on ${this.platform}`);
55685
+ log6.warn(`Keep-alive not supported on ${this.platform}`);
55575
55686
  }
55576
55687
  }
55577
55688
  stopKeepAlive() {
55578
55689
  if (this.keepAliveProcess) {
55579
- log5.debug("Stopping keep-alive");
55690
+ log6.debug("Stopping keep-alive");
55580
55691
  this.keepAliveProcess.kill();
55581
55692
  this.keepAliveProcess = null;
55582
55693
  }
@@ -55591,18 +55702,18 @@ class KeepAliveManager {
55591
55702
  detached: false
55592
55703
  });
55593
55704
  this.keepAliveProcess.on("error", (err) => {
55594
- log5.error(`Failed to start caffeinate: ${err.message}`);
55705
+ log6.error(`Failed to start caffeinate: ${err.message}`);
55595
55706
  this.keepAliveProcess = null;
55596
55707
  });
55597
55708
  this.keepAliveProcess.on("exit", (code) => {
55598
55709
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
55599
- log5.debug(`caffeinate exited with code ${code}`);
55710
+ log6.debug(`caffeinate exited with code ${code}`);
55600
55711
  }
55601
55712
  this.keepAliveProcess = null;
55602
55713
  });
55603
- log5.info("Sleep prevention active (caffeinate)");
55714
+ log6.info("Sleep prevention active (caffeinate)");
55604
55715
  } catch (err) {
55605
- log5.error(`Failed to start caffeinate: ${err}`);
55716
+ log6.error(`Failed to start caffeinate: ${err}`);
55606
55717
  }
55607
55718
  }
55608
55719
  startLinuxKeepAlive() {
@@ -55615,19 +55726,19 @@ class KeepAliveManager {
55615
55726
  detached: false
55616
55727
  });
55617
55728
  this.keepAliveProcess.on("error", (err) => {
55618
- log5.debug(`systemd-inhibit not available: ${err.message}`);
55729
+ log6.debug(`systemd-inhibit not available: ${err.message}`);
55619
55730
  this.keepAliveProcess = null;
55620
55731
  this.startLinuxKeepAliveFallback();
55621
55732
  });
55622
55733
  this.keepAliveProcess.on("exit", (code) => {
55623
55734
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
55624
- log5.debug(`systemd-inhibit exited with code ${code}`);
55735
+ log6.debug(`systemd-inhibit exited with code ${code}`);
55625
55736
  }
55626
55737
  this.keepAliveProcess = null;
55627
55738
  });
55628
- log5.info("Sleep prevention active (systemd-inhibit)");
55739
+ log6.info("Sleep prevention active (systemd-inhibit)");
55629
55740
  } catch (err) {
55630
- log5.debug(`Failed to start systemd-inhibit: ${err}`);
55741
+ log6.debug(`Failed to start systemd-inhibit: ${err}`);
55631
55742
  this.startLinuxKeepAliveFallback();
55632
55743
  }
55633
55744
  }
@@ -55638,15 +55749,15 @@ class KeepAliveManager {
55638
55749
  detached: false
55639
55750
  });
55640
55751
  this.keepAliveProcess.on("error", (err) => {
55641
- log5.warn(`Linux keep-alive fallback not available: ${err.message}`);
55752
+ log6.warn(`Linux keep-alive fallback not available: ${err.message}`);
55642
55753
  this.keepAliveProcess = null;
55643
55754
  });
55644
55755
  this.keepAliveProcess.on("exit", () => {
55645
55756
  this.keepAliveProcess = null;
55646
55757
  });
55647
- log5.info("Sleep prevention active (xdg-screensaver)");
55758
+ log6.info("Sleep prevention active (xdg-screensaver)");
55648
55759
  } catch (err) {
55649
- log5.warn(`Linux keep-alive not available: ${err}`);
55760
+ log6.warn(`Linux keep-alive not available: ${err}`);
55650
55761
  }
55651
55762
  }
55652
55763
  startWindowsKeepAlive() {
@@ -55658,18 +55769,18 @@ class KeepAliveManager {
55658
55769
  windowsHide: true
55659
55770
  });
55660
55771
  this.keepAliveProcess.on("error", (err) => {
55661
- log5.warn(`Windows keep-alive not available: ${err.message}`);
55772
+ log6.warn(`Windows keep-alive not available: ${err.message}`);
55662
55773
  this.keepAliveProcess = null;
55663
55774
  });
55664
55775
  this.keepAliveProcess.on("exit", (code) => {
55665
55776
  if (code !== null && code !== 0 && this.activeSessionCount > 0) {
55666
- log5.debug(`PowerShell keep-alive exited with code ${code}`);
55777
+ log6.debug(`PowerShell keep-alive exited with code ${code}`);
55667
55778
  }
55668
55779
  this.keepAliveProcess = null;
55669
55780
  });
55670
- log5.info("Sleep prevention active (SetThreadExecutionState)");
55781
+ log6.info("Sleep prevention active (SetThreadExecutionState)");
55671
55782
  } catch (err) {
55672
- log5.warn(`Windows keep-alive not available: ${err}`);
55783
+ log6.warn(`Windows keep-alive not available: ${err}`);
55673
55784
  }
55674
55785
  }
55675
55786
  }
@@ -55683,7 +55794,7 @@ function formatSponsorFooter(formatter) {
55683
55794
  }
55684
55795
 
55685
55796
  // src/operations/sticky-message/handler.ts
55686
- var log6 = createLogger("sticky");
55797
+ var log7 = createLogger("sticky");
55687
55798
  var botStartedAt = new Date;
55688
55799
  var stickyPostIds = new Map;
55689
55800
  var needsBump = new Map;
@@ -55922,10 +56033,10 @@ class Redactor {
55922
56033
  }
55923
56034
 
55924
56035
  // src/persistence/thread-logger.ts
55925
- import { homedir as homedir2 } from "os";
55926
- import { join as join2, dirname as dirname4 } from "path";
55927
- var log7 = createLogger("thread-log");
55928
- var LOGS_BASE_DIR = join2(homedir2(), ".claude-threads", "logs");
56036
+ import { homedir as homedir3 } from "os";
56037
+ import { join as join3, dirname as dirname4 } from "path";
56038
+ var log8 = createLogger("thread-log");
56039
+ var LOGS_BASE_DIR = join3(homedir3(), ".claude-threads", "logs");
55929
56040
 
55930
56041
  // src/operations/bug-report/handler.ts
55931
56042
  var piiRedactor = new Redactor({ aggressive: true });
@@ -55948,7 +56059,7 @@ class UsernameAnonymizer {
55948
56059
  // src/mcp/decision-bridge.ts
55949
56060
  import { createServer, createConnection } from "node:net";
55950
56061
  import { tmpdir } from "node:os";
55951
- import { join as join3 } from "node:path";
56062
+ import { join as join4 } from "node:path";
55952
56063
  import { randomUUID } from "node:crypto";
55953
56064
  import { mkdtempSync } from "node:fs";
55954
56065
  import { rm } from "node:fs/promises";
@@ -55959,8 +56070,8 @@ function bridgeSocketPath() {
55959
56070
  if (process.platform === "win32") {
55960
56071
  return `\\\\.\\pipe\\ctb-${randomUUID()}`;
55961
56072
  }
55962
- const dir = mkdtempSync(join3(tmpdir(), "ctb-"));
55963
- return join3(dir, "b.sock");
56073
+ const dir = mkdtempSync(join4(tmpdir(), "ctb-"));
56074
+ return join4(dir, "b.sock");
55964
56075
  }
55965
56076
 
55966
56077
  class DecisionBridgeServer {
@@ -56030,7 +56141,7 @@ class DecisionBridgeServer {
56030
56141
  });
56031
56142
  } catch (err) {
56032
56143
  if (process.platform !== "win32") {
56033
- await rm(join3(path, ".."), { recursive: true, force: true }).catch(() => {});
56144
+ await rm(join4(path, ".."), { recursive: true, force: true }).catch(() => {});
56034
56145
  }
56035
56146
  throw err;
56036
56147
  }
@@ -56043,7 +56154,7 @@ class DecisionBridgeServer {
56043
56154
  socket.destroy();
56044
56155
  await new Promise((resolve4) => this.server.close(() => resolve4()));
56045
56156
  if (process.platform !== "win32") {
56046
- await rm(join3(this.path, ".."), { recursive: true, force: true }).catch(() => {});
56157
+ await rm(join4(this.path, ".."), { recursive: true, force: true }).catch(() => {});
56047
56158
  }
56048
56159
  }
56049
56160
  }
@@ -56106,7 +56217,7 @@ import { resolve as resolve4, dirname as dirname5 } from "path";
56106
56217
  import { fileURLToPath as fileURLToPath3 } from "url";
56107
56218
  import { existsSync as existsSync4, readFileSync as readFileSync3, watchFile, unwatchFile, unlinkSync, statSync, readdirSync, writeFileSync } from "fs";
56108
56219
  import { tmpdir as tmpdir2 } from "os";
56109
- import { join as join4 } from "path";
56220
+ import { join as join5 } from "path";
56110
56221
 
56111
56222
  // src/mcp/outbound-env.ts
56112
56223
  var OUTBOUND_ENV = {
@@ -56197,25 +56308,25 @@ function parseRateLimitEvent(event, now = Date.now()) {
56197
56308
  }
56198
56309
 
56199
56310
  // src/claude/cli.ts
56200
- var log8 = createLogger("claude");
56311
+ var log9 = createLogger("claude");
56201
56312
  function cleanupBrowserBridgeSockets() {
56202
56313
  try {
56203
56314
  const tempDir = tmpdir2();
56204
56315
  const files = readdirSync(tempDir);
56205
56316
  for (const file2 of files) {
56206
56317
  if (file2.startsWith("claude-mcp-browser-bridge-")) {
56207
- const filePath = join4(tempDir, file2);
56318
+ const filePath = join5(tempDir, file2);
56208
56319
  try {
56209
56320
  const stats = statSync(filePath);
56210
56321
  if (stats.isSocket()) {
56211
56322
  unlinkSync(filePath);
56212
- log8.debug(`Removed stale browser bridge socket: ${file2}`);
56323
+ log9.debug(`Removed stale browser bridge socket: ${file2}`);
56213
56324
  }
56214
56325
  } catch {}
56215
56326
  }
56216
56327
  }
56217
56328
  } catch (err) {
56218
- log8.debug(`Browser bridge cleanup failed: ${err}`);
56329
+ log9.debug(`Browser bridge cleanup failed: ${err}`);
56219
56330
  }
56220
56331
  }
56221
56332
  function buildClaudeChildEnv(parentEnv, account, opts) {
@@ -56274,7 +56385,7 @@ function materializeMcpConfig(config3, sessionId, opts = {}) {
56274
56385
  return { mode: "inline", value: JSON.stringify(config3) };
56275
56386
  }
56276
56387
  const dir = opts.tmpDirOverride ?? tmpdir2();
56277
- const path = join4(dir, `claude-threads-mcp-${sessionId ?? process.pid}-${Date.now()}.json`);
56388
+ const path = join5(dir, `claude-threads-mcp-${sessionId ?? process.pid}-${Date.now()}.json`);
56278
56389
  writeFileSync(path, JSON.stringify(config3), { mode: 384 });
56279
56390
  return { mode: "file", path };
56280
56391
  }
@@ -56457,7 +56568,7 @@ class ClaudeCli extends EventEmitter2 {
56457
56568
  }
56458
56569
  let statusLineCommand;
56459
56570
  if (this.options.sessionId) {
56460
- this.statusFilePath = join4(tmpdir2(), `claude-threads-status-${this.options.sessionId}.json`);
56571
+ this.statusFilePath = join5(tmpdir2(), `claude-threads-status-${this.options.sessionId}.json`);
56461
56572
  const statusLineWriterPath = this.getStatusLineWriterPath();
56462
56573
  const runtime = runtimeForScriptPath(statusLineWriterPath);
56463
56574
  statusLineCommand = `${runtime} ${statusLineWriterPath} ${this.options.sessionId}`;
@@ -56876,6 +56987,27 @@ var COMMAND_REGISTRY = [
56876
56987
  { name: "run", description: "Run a routine now, outside its schedule", args: "<n>" }
56877
56988
  ]
56878
56989
  },
56990
+ {
56991
+ command: "watch",
56992
+ description: "Create an event trigger from a natural-language request (confirmed with \uD83D\uDC4D before saving)",
56993
+ args: "<when ..., task>",
56994
+ category: "settings",
56995
+ audience: "user",
56996
+ claudeNotes: "User decisions, not yours"
56997
+ },
56998
+ {
56999
+ command: "watches",
57000
+ description: "List event triggers; pause/resume/delete manage them",
57001
+ args: "[pause|resume|delete <n>]",
57002
+ category: "settings",
57003
+ audience: "user",
57004
+ claudeNotes: "User decisions, not yours",
57005
+ subcommands: [
57006
+ { name: "pause", description: "Pause a watch", args: "<n>" },
57007
+ { name: "resume", description: "Resume a paused watch", args: "<n>" },
57008
+ { name: "delete", description: "Delete a watch", args: "<n>" }
57009
+ ]
57010
+ },
56879
57011
  {
56880
57012
  command: "update",
56881
57013
  description: "Show auto-update status",
@@ -57174,6 +57306,30 @@ var handleRoutines = async (ctx, args) => {
57174
57306
  await ctx.sessionManager.manageRoutines(ctx.threadId, args, ctx.username);
57175
57307
  return { handled: true };
57176
57308
  };
57309
+ var handleWatch = async (ctx, args) => {
57310
+ if (ctx.commandContext === "first-message") {
57311
+ return { handled: false };
57312
+ }
57313
+ if (!ctx.isAllowed) {
57314
+ return { handled: true };
57315
+ }
57316
+ if (!args?.trim()) {
57317
+ await ctx.client.createPost(`⚠️ Usage: ${ctx.formatter.formatCode("!watch when <something happens>, <task>")}`, ctx.threadId);
57318
+ return { handled: true };
57319
+ }
57320
+ await ctx.sessionManager.createWatch(ctx.threadId, args, ctx.username);
57321
+ return { handled: true };
57322
+ };
57323
+ var handleWatches = async (ctx, args) => {
57324
+ if (ctx.commandContext === "first-message") {
57325
+ return { handled: false };
57326
+ }
57327
+ if (!ctx.isAllowed) {
57328
+ return { handled: true };
57329
+ }
57330
+ await ctx.sessionManager.manageWatches(ctx.threadId, args, ctx.username);
57331
+ return { handled: true };
57332
+ };
57177
57333
  var handleCd = async (ctx, args) => {
57178
57334
  if (!args) {
57179
57335
  return { handled: false };
@@ -57367,6 +57523,8 @@ handlers.set("remember", handleRemember);
57367
57523
  handlers.set("memory", handleMemory);
57368
57524
  handlers.set("routine", handleRoutine);
57369
57525
  handlers.set("routines", handleRoutines);
57526
+ handlers.set("watch", handleWatch);
57527
+ handlers.set("watches", handleWatches);
57370
57528
  handlers.set("cd", handleCd);
57371
57529
  handlers.set("permissions", handlePermissions);
57372
57530
  handlers.set("mentions", handleMentions);
@@ -57379,7 +57537,7 @@ handlers.set("compact", createPassthroughHandler("compact"));
57379
57537
  handlers.set("model", createPassthroughHandler("model"));
57380
57538
  handlers.set("effort", createPassthroughHandler("effort"));
57381
57539
  // src/commands/system-prompt-generator.ts
57382
- var log9 = createLogger("system-prompt");
57540
+ var log10 = createLogger("system-prompt");
57383
57541
  function formatUserCommand(cmd) {
57384
57542
  const cmdStr = cmd.args ? `\`!${cmd.command} ${cmd.args}\`` : `\`!${cmd.command}\``;
57385
57543
  const description = cmd.description;
@@ -57464,7 +57622,7 @@ ${avoidCommands.map((c) => `- \`!${c.command}\` - ${c.reason}`).join(`
57464
57622
  `.trim();
57465
57623
  }
57466
57624
  // src/utils/error-handler/index.ts
57467
- var log10 = createLogger("error");
57625
+ var log11 = createLogger("error");
57468
57626
 
57469
57627
  // src/utils/session-log.ts
57470
57628
  function createSessionLog(baseLog) {
@@ -57481,45 +57639,64 @@ init_emoji();
57481
57639
 
57482
57640
  // src/git/worktree.ts
57483
57641
  import * as path from "path";
57484
- import { homedir as homedir3 } from "os";
57485
- var log11 = createLogger("git-wt");
57486
- var WORKTREES_DIR = path.join(homedir3(), ".claude-threads", "worktrees");
57487
- var METADATA_STORE_PATH = path.join(homedir3(), ".claude-threads", "worktree-metadata.json");
57642
+ import { homedir as homedir4 } from "os";
57643
+ var log12 = createLogger("git-wt");
57644
+ var WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
57645
+ var METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
57488
57646
 
57489
57647
  // src/operations/post-helpers/index.ts
57490
- var log12 = createLogger("helpers");
57491
- var sessionLog = createSessionLog(log12);
57648
+ var log13 = createLogger("helpers");
57649
+ var sessionLog = createSessionLog(log13);
57492
57650
 
57493
57651
  // src/claude/quick-query.ts
57494
- var log13 = createLogger("query");
57652
+ var log14 = createLogger("query");
57495
57653
 
57496
57654
  // src/operations/suggestions/title.ts
57497
- var log14 = createLogger("title");
57655
+ var log15 = createLogger("title");
57498
57656
 
57499
57657
  // src/operations/suggestions/tag.ts
57500
- var log15 = createLogger("tags");
57658
+ var log16 = createLogger("tags");
57501
57659
 
57502
57660
  // src/operations/context-prompt/handler.ts
57503
57661
  init_emoji();
57504
- var log16 = createLogger("context");
57505
- var sessionLog2 = createSessionLog(log16);
57662
+ var log17 = createLogger("context");
57663
+ var sessionLog2 = createSessionLog(log17);
57506
57664
  var contextPromptTimeouts = new Map;
57507
57665
  var contextPromptFiles = new Map;
57508
57666
  // src/memory/store.ts
57509
57667
  import { createHash } from "crypto";
57510
57668
  import {
57511
- chmodSync,
57512
57669
  existsSync as existsSync5,
57513
- mkdirSync,
57670
+ mkdirSync as mkdirSync2,
57514
57671
  readFileSync as readFileSync4,
57515
- renameSync,
57516
- realpathSync,
57517
- writeFileSync as writeFileSync2
57672
+ realpathSync
57518
57673
  } from "fs";
57519
- import { homedir as homedir4 } from "os";
57520
- import { basename as basename3, dirname as dirname7, join as join6, sep as sep2 } from "path";
57521
- var log17 = createLogger("memory");
57522
- var DEFAULT_ROOT = join6(homedir4(), ".config", "claude-threads", "memory");
57674
+ import { homedir as homedir5 } from "os";
57675
+ import { basename as basename3, dirname as dirname7, join as join7, sep as sep2 } from "path";
57676
+
57677
+ // src/persistence/atomic-file.ts
57678
+ import { chmodSync as chmodSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
57679
+
57680
+ class SerialQueue {
57681
+ tail = Promise.resolve();
57682
+ run(fn) {
57683
+ const next = this.tail.then(fn, fn);
57684
+ this.tail = next.catch(() => {
57685
+ return;
57686
+ });
57687
+ return next;
57688
+ }
57689
+ }
57690
+ function writeFileAtomic(file2, content) {
57691
+ const tempFile = `${file2}.tmp`;
57692
+ writeFileSync2(tempFile, content, { encoding: "utf-8", mode: 384 });
57693
+ renameSync(tempFile, file2);
57694
+ chmodSync2(file2, 384);
57695
+ }
57696
+
57697
+ // src/memory/store.ts
57698
+ var log18 = createLogger("memory");
57699
+ var DEFAULT_ROOT = join7(homedir5(), ".config", "claude-threads", "memory");
57523
57700
  var CHANNEL_BLOCK_MAX_LINES = 200;
57524
57701
  var CHANNEL_BLOCK_MAX_BYTES = 25 * 1024;
57525
57702
  var CHANNEL_FILE_MAX_ENTRIES = 400;
@@ -57562,10 +57739,10 @@ class MemoryStore {
57562
57739
  return this.root;
57563
57740
  }
57564
57741
  channelMemoryPath(platformId) {
57565
- return join6(this.root, platformSegment(platformId), "channel", "MEMORY.md");
57742
+ return join7(this.root, platformSegment(platformId), "channel", "MEMORY.md");
57566
57743
  }
57567
57744
  repoMemoryDir(platformId, repoKey) {
57568
- const dir = join6(this.root, platformSegment(platformId), "repos", repoKey);
57745
+ const dir = join7(this.root, platformSegment(platformId), "repos", repoKey);
57569
57746
  this.ensureDir(dir);
57570
57747
  return dir;
57571
57748
  }
@@ -57612,7 +57789,7 @@ class MemoryStore {
57612
57789
  if (result.added.length > 0) {
57613
57790
  this.enforceFileCap(lines);
57614
57791
  this.writeLines(platformId, lines);
57615
- log17.debug(`Channel memory for ${platformId}: +${result.added.length} entries` + (result.duplicates.length ? ` (${result.duplicates.length} duplicates skipped)` : ""));
57792
+ log18.debug(`Channel memory for ${platformId}: +${result.added.length} entries` + (result.duplicates.length ? ` (${result.duplicates.length} duplicates skipped)` : ""));
57616
57793
  }
57617
57794
  return result;
57618
57795
  });
@@ -57651,14 +57828,14 @@ class MemoryStore {
57651
57828
  }
57652
57829
  lines.splice(target.lineIndex, 1);
57653
57830
  this.writeLines(platformId, lines);
57654
- log17.debug(`Channel memory for ${platformId}: removed one entry`);
57831
+ log18.debug(`Channel memory for ${platformId}: removed one entry`);
57655
57832
  return { ok: true, removed: target.entry };
57656
57833
  });
57657
57834
  }
57658
57835
  clearChannel(platformId) {
57659
57836
  return this.runExclusive(platformId, () => {
57660
57837
  this.writeLines(platformId, []);
57661
- log17.debug(`Channel memory for ${platformId}: cleared`);
57838
+ log18.debug(`Channel memory for ${platformId}: cleared`);
57662
57839
  });
57663
57840
  }
57664
57841
  buildChannelMemoryBlock(platformId) {
@@ -57666,7 +57843,7 @@ class MemoryStore {
57666
57843
  try {
57667
57844
  lines = this.loadLines(platformId);
57668
57845
  } catch (err) {
57669
- log17.warn(`Failed to read channel memory for ${platformId}: ${err.message}`);
57846
+ log18.warn(`Failed to read channel memory for ${platformId}: ${err.message}`);
57670
57847
  return null;
57671
57848
  }
57672
57849
  if (lines.length === 0)
@@ -57690,12 +57867,12 @@ class MemoryStore {
57690
57867
  _(older entries omitted — \`!memory\` shows all)_` : rendered;
57691
57868
  }
57692
57869
  runExclusive(platformId, fn) {
57693
- const tail = this.locks.get(platformId) ?? Promise.resolve();
57694
- const next = tail.then(fn, fn);
57695
- this.locks.set(platformId, next.catch(() => {
57696
- return;
57697
- }));
57698
- return next;
57870
+ let queue = this.locks.get(platformId);
57871
+ if (!queue) {
57872
+ queue = new SerialQueue;
57873
+ this.locks.set(platformId, queue);
57874
+ }
57875
+ return queue.run(fn);
57699
57876
  }
57700
57877
  loadLines(platformId) {
57701
57878
  const file2 = this.channelMemoryPath(platformId);
@@ -57738,24 +57915,132 @@ _(older entries omitted — \`!memory\` shows all)_` : rendered;
57738
57915
  const content = [FILE_HEADER, ...lines.map((l) => l.raw)].join(`
57739
57916
  `) + `
57740
57917
  `;
57741
- const tempFile = `${file2}.tmp`;
57742
- writeFileSync2(tempFile, content, { encoding: "utf-8", mode: 384 });
57743
- renameSync(tempFile, file2);
57744
- chmodSync(file2, 384);
57918
+ writeFileAtomic(file2, content);
57745
57919
  }
57746
57920
  ensureDir(dir) {
57747
57921
  if (!existsSync5(dir)) {
57748
- mkdirSync(dir, { recursive: true, mode: 448 });
57922
+ mkdirSync2(dir, { recursive: true, mode: 448 });
57749
57923
  }
57750
57924
  }
57751
57925
  }
57752
57926
 
57753
57927
  // src/memory/distiller.ts
57754
- var log18 = createLogger("memory");
57928
+ var log19 = createLogger("memory");
57929
+
57930
+ // src/session/registry.ts
57931
+ function compositeSessionId(platformId, threadId) {
57932
+ return `${platformId}:${threadId}`;
57933
+ }
57934
+
57935
+ class SessionRegistry {
57936
+ sessions = new Map;
57937
+ postIndex = new Map;
57938
+ sessionStore;
57939
+ constructor(sessionStore) {
57940
+ this.sessionStore = sessionStore;
57941
+ }
57942
+ getSessionId(platformId, threadId) {
57943
+ return compositeSessionId(platformId, threadId);
57944
+ }
57945
+ parseSessionId(sessionId) {
57946
+ const colonIndex = sessionId.indexOf(":");
57947
+ if (colonIndex === -1)
57948
+ return null;
57949
+ return {
57950
+ platformId: sessionId.substring(0, colonIndex),
57951
+ threadId: sessionId.substring(colonIndex + 1)
57952
+ };
57953
+ }
57954
+ find(platformId, threadId) {
57955
+ return this.sessions.get(this.getSessionId(platformId, threadId));
57956
+ }
57957
+ findByThreadId(threadId) {
57958
+ for (const session of this.sessions.values()) {
57959
+ if (session.threadId === threadId) {
57960
+ return session;
57961
+ }
57962
+ }
57963
+ return;
57964
+ }
57965
+ findByPost(postId) {
57966
+ const threadId = this.postIndex.get(postId);
57967
+ if (!threadId)
57968
+ return;
57969
+ return this.findByThreadId(threadId);
57970
+ }
57971
+ get(sessionId) {
57972
+ return this.sessions.get(sessionId);
57973
+ }
57974
+ has(platformId, threadId) {
57975
+ return this.sessions.has(this.getSessionId(platformId, threadId));
57976
+ }
57977
+ isActiveThread(threadId) {
57978
+ return this.findByThreadId(threadId) !== undefined;
57979
+ }
57980
+ register(session) {
57981
+ this.sessions.set(session.sessionId, session);
57982
+ }
57983
+ unregister(sessionId) {
57984
+ this.sessions.delete(sessionId);
57985
+ }
57986
+ registerPost(postId, threadId) {
57987
+ this.postIndex.set(postId, threadId);
57988
+ }
57989
+ unregisterPost(postId) {
57990
+ this.postIndex.delete(postId);
57991
+ }
57992
+ clearPostsForThread(threadId) {
57993
+ for (const [postId, tid] of this.postIndex.entries()) {
57994
+ if (tid === threadId) {
57995
+ this.postIndex.delete(postId);
57996
+ }
57997
+ }
57998
+ }
57999
+ getAll() {
58000
+ return Array.from(this.sessions.values());
58001
+ }
58002
+ getActiveThreadIds() {
58003
+ return Array.from(this.sessions.values()).map((s) => s.threadId);
58004
+ }
58005
+ get size() {
58006
+ return this.sessions.size;
58007
+ }
58008
+ getForPlatform(platformId) {
58009
+ return Array.from(this.sessions.values()).filter((s) => s.sessionId.startsWith(`${platformId}:`));
58010
+ }
58011
+ hasPaused(platformId, threadId) {
58012
+ return this.sessionStore.findByThread(platformId, threadId) !== undefined;
58013
+ }
58014
+ getPersisted(platformId, threadId) {
58015
+ return this.sessionStore.findByThread(platformId, threadId);
58016
+ }
58017
+ getPersistedByThreadId(threadId) {
58018
+ return this.sessionStore.findByThreadIdAnyState(threadId);
58019
+ }
58020
+ getSessionStore() {
58021
+ return this.sessionStore;
58022
+ }
58023
+ hasById(sessionId) {
58024
+ return this.sessions.has(sessionId);
58025
+ }
58026
+ clear() {
58027
+ this.sessions.clear();
58028
+ this.postIndex.clear();
58029
+ }
58030
+ getThreadIdForPost(postId) {
58031
+ return this.postIndex.get(postId);
58032
+ }
58033
+ getSessions() {
58034
+ return this.sessions;
58035
+ }
58036
+ getPostIndex() {
58037
+ return this.postIndex;
58038
+ }
58039
+ }
57755
58040
 
57756
58041
  // src/session/lifecycle.ts
57757
- var log19 = createLogger("lifecycle");
57758
- var sessionLog3 = createSessionLog(log19);
58042
+ var log20 = createLogger("lifecycle");
58043
+ var sessionLog3 = createSessionLog(log20);
57759
58044
  var _inFlightSessionStarts = new Map;
57760
58045
  var CHAT_PLATFORM_PROMPT = generateChatPlatformPrompt();
57761
58046
  // src/update-notifier.ts
@@ -57765,206 +58050,181 @@ var import_semver2 = __toESM(require_semver2(), 1);
57765
58050
  init_emoji();
57766
58051
 
57767
58052
  // src/persistence/github-emails-store.ts
57768
- import { homedir as homedir5 } from "os";
57769
- import { join as join7 } from "path";
57770
- var log20 = createLogger("gh-emails");
57771
- var DEFAULT_CONFIG_DIR = join7(homedir5(), ".config", "claude-threads");
57772
- var DEFAULT_FILE = join7(DEFAULT_CONFIG_DIR, "github-emails.yaml");
57773
-
57774
- // src/persistence/routines-store.ts
57775
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5 } from "fs";
57776
58053
  import { homedir as homedir6 } from "os";
57777
58054
  import { join as join8 } from "path";
57778
- import { randomUUID as randomUUID2 } from "crypto";
57779
-
57780
- // src/persistence/atomic-file.ts
57781
- import { chmodSync as chmodSync2, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
57782
-
57783
- class SerialQueue {
57784
- tail = Promise.resolve();
57785
- run(fn) {
57786
- const next = this.tail.then(fn, fn);
57787
- this.tail = next.catch(() => {
57788
- return;
57789
- });
57790
- return next;
57791
- }
57792
- }
57793
- function writeFileAtomic(file2, content) {
57794
- const tempFile = `${file2}.tmp`;
57795
- writeFileSync3(tempFile, content, { encoding: "utf-8", mode: 384 });
57796
- renameSync2(tempFile, file2);
57797
- chmodSync2(file2, 384);
57798
- }
58055
+ var log21 = createLogger("gh-emails");
58056
+ var DEFAULT_CONFIG_DIR = join8(homedir6(), ".config", "claude-threads");
58057
+ var DEFAULT_FILE = join8(DEFAULT_CONFIG_DIR, "github-emails.yaml");
57799
58058
 
57800
58059
  // src/persistence/routines-store.ts
57801
- var log21 = createLogger("routines");
57802
- var DEFAULT_CONFIG_DIR2 = join8(homedir6(), ".config", "claude-threads");
57803
- var DEFAULT_FILE2 = join8(DEFAULT_CONFIG_DIR2, "routines.yaml");
58060
+ import { join as join10 } from "path";
58061
+
58062
+ // src/persistence/platform-list-store.ts
58063
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
58064
+ import { homedir as homedir7 } from "os";
58065
+ import { join as join9 } from "path";
58066
+ var STORES_CONFIG_DIR = join9(homedir7(), ".config", "claude-threads");
57804
58067
  var STORE_VERSION = 1;
57805
- var DEFAULT_MAX_ROUTINES = 10;
57806
- var SCHEDULE_PRESETS = ["hourly", "daily", "weekdays", "weekly"];
57807
- var TIME_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
57808
- function isValidTimezone(tz) {
57809
- if (typeof tz !== "string" || !tz)
57810
- return false;
57811
- try {
57812
- new Intl.DateTimeFormat("en-US", { timeZone: tz });
57813
- return true;
57814
- } catch {
57815
- return false;
57816
- }
57817
- }
57818
- function validateSchedule(schedule) {
57819
- if (!SCHEDULE_PRESETS.includes(schedule.preset)) {
57820
- return `unknown preset "${String(schedule.preset)}" (expected ${SCHEDULE_PRESETS.join("/")})`;
57821
- }
57822
- if (!isValidTimezone(schedule.timezone)) {
57823
- return `invalid timezone "${String(schedule.timezone)}"`;
57824
- }
57825
- if (schedule.preset === "hourly") {
57826
- return null;
57827
- }
57828
- if (!schedule.time || !TIME_RE.test(schedule.time)) {
57829
- return `invalid time "${String(schedule.time)}" (expected HH:MM, 24h)`;
57830
- }
57831
- if (schedule.preset === "weekly") {
57832
- const weekday = schedule.weekday;
57833
- if (typeof weekday !== "number" || !Number.isInteger(weekday) || weekday < 1 || weekday > 7) {
57834
- return `invalid weekday "${String(weekday)}" (expected 1=Mon … 7=Sun)`;
57835
- }
57836
- }
57837
- return null;
57838
- }
57839
- class RoutinesStore {
58068
+
58069
+ class PlatformListStore {
57840
58070
  file;
57841
58071
  configDir;
57842
58072
  queue = new SerialQueue;
57843
- constructor(filePath) {
57844
- const effective = filePath ?? process.env.CLAUDE_THREADS_ROUTINES_PATH;
57845
- if (effective) {
57846
- this.file = effective;
57847
- this.configDir = join8(effective, "..");
58073
+ collectionKey;
58074
+ cache = null;
58075
+ constructor(collectionKey, defaultFile, filePath) {
58076
+ this.collectionKey = collectionKey;
58077
+ if (filePath) {
58078
+ this.file = filePath;
58079
+ this.configDir = join9(filePath, "..");
57848
58080
  } else {
57849
- this.file = DEFAULT_FILE2;
57850
- this.configDir = DEFAULT_CONFIG_DIR2;
57851
- }
57852
- if (!existsSync6(this.configDir)) {
57853
- mkdirSync2(this.configDir, { recursive: true, mode: 448 });
58081
+ this.file = defaultFile;
58082
+ this.configDir = STORES_CONFIG_DIR;
57854
58083
  }
58084
+ mkdirSync3(this.configDir, { recursive: true, mode: 448 });
57855
58085
  }
57856
58086
  list(platformId) {
57857
- return this.loadRaw().routines[platformId] ?? [];
58087
+ return structuredClone(this.loadRaw().items[platformId] ?? []);
57858
58088
  }
57859
58089
  get(platformId, id) {
57860
- return this.list(platformId).find((r) => r.id === id);
58090
+ const item = (this.loadRaw().items[platformId] ?? []).find((i) => i.id === id);
58091
+ return item === undefined ? undefined : structuredClone(item);
57861
58092
  }
57862
- add(platformId, routine, maxRoutines = DEFAULT_MAX_ROUTINES) {
58093
+ addItem(platformId, max, capNoun, build) {
57863
58094
  return this.runExclusive(() => {
57864
- const scheduleError = validateSchedule(routine.schedule);
57865
- if (scheduleError)
57866
- return { ok: false, error: scheduleError };
57867
- const name = routine.name.trim().slice(0, 80);
57868
- const prompt = routine.prompt.trim().slice(0, 2000);
57869
- if (!name || !prompt)
57870
- return { ok: false, error: "name and prompt are required" };
57871
- const data = this.loadRaw();
57872
- const existing = data.routines[platformId] ?? [];
57873
- if (existing.length >= maxRoutines) {
57874
- return { ok: false, error: `routine limit reached (${maxRoutines}); delete one first` };
57875
- }
57876
- const full = {
57877
- ...routine,
57878
- name,
57879
- prompt,
57880
- id: randomUUID2().slice(0, 8),
57881
- createdAt: new Date().toISOString(),
57882
- enabled: true,
57883
- consecutiveFailures: 0
57884
- };
57885
- data.routines[platformId] = [...existing, full];
58095
+ const built = build();
58096
+ if (typeof built === "string")
58097
+ return { ok: false, error: built };
58098
+ const data = this.loadRaw(true);
58099
+ const existing = data.items[platformId] ?? [];
58100
+ if (existing.length >= max) {
58101
+ return { ok: false, error: `${capNoun} limit reached (${max}); delete one first` };
58102
+ }
58103
+ data.items[platformId] = [...existing, built];
57886
58104
  this.writeAtomic(data);
57887
- log21.info(`Routine "${full.name}" created on ${platformId} by @${full.createdBy}`);
57888
- return { ok: true, routine: full };
58105
+ return { ok: true, item: structuredClone(built) };
57889
58106
  });
57890
58107
  }
57891
58108
  update(platformId, id, patch) {
57892
58109
  return this.runExclusive(() => {
57893
- const data = this.loadRaw();
57894
- const routines = data.routines[platformId] ?? [];
57895
- const idx = routines.findIndex((r) => r.id === id);
58110
+ const data = this.loadRaw(true);
58111
+ const items = data.items[platformId] ?? [];
58112
+ const idx = items.findIndex((item) => item.id === id);
57896
58113
  if (idx < 0)
57897
58114
  return;
57898
- routines[idx] = { ...routines[idx], ...patch };
58115
+ items[idx] = { ...items[idx], ...patch };
57899
58116
  this.writeAtomic(data);
57900
- return routines[idx];
58117
+ return structuredClone(items[idx]);
57901
58118
  });
57902
58119
  }
57903
58120
  remove(platformId, id) {
57904
58121
  return this.runExclusive(() => {
57905
- const data = this.loadRaw();
57906
- const routines = data.routines[platformId] ?? [];
57907
- const idx = routines.findIndex((r) => r.id === id);
58122
+ const data = this.loadRaw(true);
58123
+ const items = data.items[platformId] ?? [];
58124
+ const idx = items.findIndex((item) => item.id === id);
57908
58125
  if (idx < 0)
57909
58126
  return;
57910
- const [removed] = routines.splice(idx, 1);
57911
- if (routines.length === 0)
57912
- delete data.routines[platformId];
58127
+ const [removed] = items.splice(idx, 1);
58128
+ if (items.length === 0)
58129
+ delete data.items[platformId];
57913
58130
  this.writeAtomic(data);
57914
- log21.info(`Routine "${removed.name}" removed from ${platformId}`);
58131
+ this.onRemoved(platformId, removed);
57915
58132
  return removed;
57916
58133
  });
57917
58134
  }
58135
+ onRemoved(_platformId, _item) {}
57918
58136
  runExclusive(fn) {
57919
58137
  return this.queue.run(fn);
57920
58138
  }
57921
- loadRaw() {
58139
+ loadRaw(forWrite = false) {
57922
58140
  if (!existsSync6(this.file)) {
57923
- return { version: STORE_VERSION, routines: {} };
58141
+ this.cache = null;
58142
+ return { version: STORE_VERSION, items: {} };
57924
58143
  }
57925
58144
  try {
57926
- const parsed = yaml.load(readFileSync5(this.file, "utf-8"));
57927
- if (!parsed || typeof parsed !== "object") {
57928
- return { version: STORE_VERSION, routines: {} };
57929
- }
57930
- const routines = parsed.routines && typeof parsed.routines === "object" ? parsed.routines : {};
57931
- for (const list of Object.values(routines)) {
57932
- for (const r of list) {
57933
- r.enabled = r.enabled ?? true;
57934
- r.consecutiveFailures = r.consecutiveFailures ?? 0;
57935
- }
58145
+ const stat = statSync2(this.file);
58146
+ if (this.cache && this.cache.mtimeMs === stat.mtimeMs && this.cache.size === stat.size) {
58147
+ return this.cache.data;
57936
58148
  }
57937
- return { version: parsed.version ?? STORE_VERSION, routines };
58149
+ const parsed = yaml.load(readFileSync5(this.file, "utf-8"));
58150
+ const rawItems = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed[this.collectionKey] : undefined;
58151
+ if (rawItems !== null && (rawItems === undefined || typeof rawItems !== "object" || Array.isArray(rawItems))) {
58152
+ this.cache = null;
58153
+ const problem = `unexpected shape (missing or non-map '${this.collectionKey}' key)`;
58154
+ if (forWrite) {
58155
+ throw new Error(`refusing to write over unreadable ${this.file}: ${problem}`);
58156
+ }
58157
+ this.warn(`Failed to read ${this.file}: ${problem} — starting empty`);
58158
+ return { version: STORE_VERSION, items: {} };
58159
+ }
58160
+ const items = rawItems ?? {};
58161
+ for (const list of Object.values(items)) {
58162
+ for (const item of list)
58163
+ this.applyItemDefaults(item);
58164
+ }
58165
+ const data = { version: parsed?.version ?? STORE_VERSION, items };
58166
+ this.cache = { mtimeMs: stat.mtimeMs, size: stat.size, data };
58167
+ return data;
57938
58168
  } catch (err) {
57939
- log21.warn(`Failed to read ${this.file}: ${err.message} — starting empty`);
57940
- return { version: STORE_VERSION, routines: {} };
58169
+ this.cache = null;
58170
+ if (forWrite) {
58171
+ throw new Error(`refusing to write over unreadable ${this.file}: ${err.message}`, { cause: err });
58172
+ }
58173
+ this.warn(`Failed to read ${this.file}: ${err.message} — starting empty`);
58174
+ return { version: STORE_VERSION, items: {} };
57941
58175
  }
57942
58176
  }
57943
58177
  writeAtomic(data) {
57944
- writeFileAtomic(this.file, yaml.dump(data, { sortKeys: true, lineWidth: -1 }));
58178
+ try {
58179
+ this.persistFile(yaml.dump({ version: data.version, [this.collectionKey]: data.items }, { sortKeys: true, lineWidth: -1 }));
58180
+ } catch (err) {
58181
+ this.cache = null;
58182
+ throw err;
58183
+ }
58184
+ try {
58185
+ const stat = statSync2(this.file);
58186
+ this.cache = { mtimeMs: stat.mtimeMs, size: stat.size, data };
58187
+ } catch {
58188
+ this.cache = null;
58189
+ }
58190
+ }
58191
+ persistFile(content) {
58192
+ writeFileAtomic(this.file, content);
57945
58193
  }
57946
58194
  }
57947
58195
 
57948
- // src/routines/parser.ts
58196
+ // src/persistence/routines-store.ts
57949
58197
  var log22 = createLogger("routines");
58198
+ var DEFAULT_FILE2 = join10(STORES_CONFIG_DIR, "routines.yaml");
58199
+
58200
+ // src/routines/parser.ts
58201
+ var log23 = createLogger("routines");
58202
+
58203
+ // src/persistence/watches-store.ts
58204
+ import { join as join11 } from "path";
58205
+ var log24 = createLogger("watches");
58206
+ var DEFAULT_FILE3 = join11(STORES_CONFIG_DIR, "watches.yaml");
58207
+
58208
+ // src/watches/parser.ts
58209
+ var log25 = createLogger("watches");
57950
58210
 
57951
58211
  // src/operations/commands/handler.ts
57952
- var log23 = createLogger("commands");
57953
- var sessionLog4 = createSessionLog(log23);
58212
+ var log26 = createLogger("commands");
58213
+ var sessionLog4 = createSessionLog(log26);
57954
58214
  // src/operations/suggestions/branch.ts
57955
58215
  import { exec as exec2 } from "child_process";
57956
58216
  import { promisify as promisify2 } from "util";
57957
58217
  var execAsync2 = promisify2(exec2);
57958
- var log24 = createLogger("branch");
58218
+ var log27 = createLogger("branch");
57959
58219
 
57960
58220
  // src/operations/worktree/handler.ts
57961
- var log25 = createLogger("worktree");
57962
- var sessionLog5 = createSessionLog(log25);
58221
+ var log28 = createLogger("worktree");
58222
+ var sessionLog5 = createSessionLog(log28);
57963
58223
  // src/operations/events/handler.ts
57964
- var log26 = createLogger("events");
57965
- var sessionLog6 = createSessionLog(log26);
58224
+ var log29 = createLogger("events");
58225
+ var sessionLog6 = createSessionLog(log29);
57966
58226
  // src/operations/monitor/handler.ts
57967
- var log27 = createLogger("monitor");
58227
+ var log30 = createLogger("monitor");
57968
58228
  var DEFAULT_INTERVAL_MS = 60 * 1000;
57969
58229
  // src/utils/websocket.ts
57970
58230
  var WS;
@@ -58046,7 +58306,7 @@ ${code}
58046
58306
 
58047
58307
  // src/platform/mattermost/upload.ts
58048
58308
  import { readFile } from "fs/promises";
58049
- var log28 = createLogger("mm-upload");
58309
+ var log31 = createLogger("mm-upload");
58050
58310
  async function uploadFileMattermost(args) {
58051
58311
  const { url: url2, token, channelId, threadId, filePath, filename, caption } = args;
58052
58312
  const buffer = await readFile(filePath);
@@ -58054,7 +58314,7 @@ async function uploadFileMattermost(args) {
58054
58314
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
58055
58315
  const formData = new FormData;
58056
58316
  formData.append("files", new Blob([arrayBuffer]), filename);
58057
- log28.debug(`POST /files (${buffer.length} bytes, ${filename})`);
58317
+ log31.debug(`POST /files (${buffer.length} bytes, ${filename})`);
58058
58318
  const uploadResponse = await fetch(uploadUrl, {
58059
58319
  method: "POST",
58060
58320
  headers: {
@@ -58078,7 +58338,7 @@ async function uploadFileMattermost(args) {
58078
58338
  root_id: resolvePostThreadId(threadId),
58079
58339
  file_ids: [fileInfo.id]
58080
58340
  };
58081
- log28.debug(`POST /posts (file_ids=[${fileInfo.id}])`);
58341
+ log31.debug(`POST /posts (file_ids=[${fileInfo.id}])`);
58082
58342
  const postResponse = await fetch(postUrl, {
58083
58343
  method: "POST",
58084
58344
  headers: {
@@ -58572,7 +58832,7 @@ ${code}
58572
58832
 
58573
58833
  // src/platform/slack/upload.ts
58574
58834
  import { readFile as readFile2 } from "fs/promises";
58575
- var log29 = createLogger("slack-upload");
58835
+ var log32 = createLogger("slack-upload");
58576
58836
  var DEFAULT_API_URL = "https://slack.com/api";
58577
58837
  async function uploadFileSlack(args) {
58578
58838
  const { botToken, channelId, threadTs, filePath, filename, caption } = args;
@@ -58580,7 +58840,7 @@ async function uploadFileSlack(args) {
58580
58840
  const buffer = await readFile2(filePath);
58581
58841
  const params = new URLSearchParams({ filename, length: String(buffer.length) });
58582
58842
  const step1Url = `${apiUrl}/files.getUploadURLExternal?${params.toString()}`;
58583
- log29.debug(`GET files.getUploadURLExternal (${buffer.length} bytes, ${filename})`);
58843
+ log32.debug(`GET files.getUploadURLExternal (${buffer.length} bytes, ${filename})`);
58584
58844
  const step1Response = await fetch(step1Url, {
58585
58845
  method: "GET",
58586
58846
  headers: {
@@ -58598,7 +58858,7 @@ async function uploadFileSlack(args) {
58598
58858
  const uploadUrl = step1Data.upload_url;
58599
58859
  const fileId = step1Data.file_id;
58600
58860
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
58601
- log29.debug(`POST <upload_url>`);
58861
+ log32.debug(`POST <upload_url>`);
58602
58862
  const step2Response = await fetch(uploadUrl, {
58603
58863
  method: "POST",
58604
58864
  headers: {
@@ -58618,7 +58878,7 @@ async function uploadFileSlack(args) {
58618
58878
  if (caption !== undefined) {
58619
58879
  step3Body.initial_comment = caption;
58620
58880
  }
58621
- log29.debug(`POST files.completeUploadExternal (file_id=${fileId}, thread_ts=${threadTs})`);
58881
+ log32.debug(`POST files.completeUploadExternal (file_id=${fileId}, thread_ts=${threadTs})`);
58622
58882
  const step3Response = await fetch(`${apiUrl}/files.completeUploadExternal`, {
58623
58883
  method: "POST",
58624
58884
  headers: {
@@ -58636,7 +58896,7 @@ async function uploadFileSlack(args) {
58636
58896
  throw new Error(`Slack completeUploadExternal error: ${step3Data.error || "unknown"}`);
58637
58897
  }
58638
58898
  if (!step3Data.ts) {
58639
- log29.warn(`Slack completeUploadExternal returned no ts; using fileId ${fileId} as postId. ` + `Do not use this id for updatePost/addReaction.`);
58899
+ log32.warn(`Slack completeUploadExternal returned no ts; using fileId ${fileId} as postId. ` + `Do not use this id for updatePost/addReaction.`);
58640
58900
  }
58641
58901
  return { fileId, postId: step3Data.ts ?? fileId };
58642
58902
  }