@threadbase-sh/streamer 1.22.2 → 1.23.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/cli.cjs CHANGED
@@ -137289,7 +137289,10 @@ var ConversationCache = class _ConversationCache {
137289
137289
  model = excluded.model,
137290
137290
  account = excluded.account,
137291
137291
  branch = excluded.branch,
137292
- message_count = excluded.message_count,
137292
+ -- message_count is incremented by live tailing but recounted from
137293
+ -- scratch by a scanner rescan; a stale rescan must not carry a live
137294
+ -- session's count backwards, so take the max instead of overwriting.
137295
+ message_count = MAX(conversation_meta.message_count, excluded.message_count),
137293
137296
  last_activity = excluded.last_activity,
137294
137297
  first_message = excluded.first_message,
137295
137298
  last_message = excluded.last_message,
@@ -137566,6 +137569,7 @@ var ConversationCache = class _ConversationCache {
137566
137569
  fileSize = s3.size;
137567
137570
  } catch {
137568
137571
  }
137572
+ const seq = ++this.tailSeq;
137569
137573
  this.stmts.upsertFull.run({
137570
137574
  id,
137571
137575
  file_path: m2.filePath,
@@ -137580,7 +137584,7 @@ var ConversationCache = class _ConversationCache {
137580
137584
  first_message: m2.firstMessage ? JSON.stringify(m2.firstMessage) : null,
137581
137585
  last_message: m2.lastMessage ? JSON.stringify(m2.lastMessage) : null,
137582
137586
  preview: m2.preview ?? null,
137583
- updated_at: 0,
137587
+ updated_at: seq,
137584
137588
  mtime_ms: mtimeMs,
137585
137589
  file_size: fileSize,
137586
137590
  provider: m2.provider ?? CLAUDE_CODE_PROVIDER2
@@ -137797,9 +137801,25 @@ var ConversationCache = class _ConversationCache {
137797
137801
  this.fileIndex.clear();
137798
137802
  }
137799
137803
  }
137800
- invalidateByFilePath(filePath) {
137804
+ /**
137805
+ * Drop the cached row for a file. Two callers with opposite intent:
137806
+ * - a directory-watch "change" event (the file was appended to) — pass
137807
+ * `skipIfTailed: true`. A cached tail means the row is being actively
137808
+ * maintained from the file's real content by the live-tail
137809
+ * (updateFromLines) or warm-up path, fresher than any scanner-derived
137810
+ * view. Both watchers fire on the same append with no ordering guarantee;
137811
+ * without this guard the invalidate can land after the tail write and wipe
137812
+ * the just-cached row, flickering the conversation out of
137813
+ * /api/conversations on nearly every message (CRITICAL #2). The debounced
137814
+ * rescan still re-derives metadata, so skipping the eager drop loses
137815
+ * nothing.
137816
+ * - a genuine unlink (the file is gone) — leave `skipIfTailed` false so the
137817
+ * row is always removed, otherwise a deleted session ghosts in the cache.
137818
+ */
137819
+ invalidateByFilePath(filePath, opts) {
137801
137820
  const row = this.stmts.getIdByFilePath.get(filePath);
137802
137821
  if (!row) return null;
137822
+ if (opts?.skipIfTailed && this.stmts.hasTail.get(row.id)) return null;
137803
137823
  this.invalidate(row.id);
137804
137824
  return row.id;
137805
137825
  }
@@ -142044,7 +142064,7 @@ var StreamerServer = class {
142044
142064
  }
142045
142065
  },
142046
142066
  onConversationChanged: (filePath) => {
142047
- this.cache?.invalidateByFilePath(filePath);
142067
+ this.cache?.invalidateByFilePath(filePath, { skipIfTailed: true });
142048
142068
  this.markScannerStaleDebounced();
142049
142069
  this.log.debug?.(`Scanner invalidated by directory event: ${filePath}`, {
142050
142070
  filePath,