@threadbase-sh/streamer 1.36.2 → 1.36.3

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
@@ -144085,6 +144085,10 @@ var StreamerServer = class {
144085
144085
  // Set by onConversationChanged while a scan is in-flight; getScanner() does
144086
144086
  // a single rescan after the current one completes instead of restarting it.
144087
144087
  scannerStale = false;
144088
+ // Single-flight guard for the background disk reconcile: a burst of list
144089
+ // polls during active session writes shares one rescan instead of queueing
144090
+ // a full rescan per request.
144091
+ conversationReconcileInFlight = null;
144088
144092
  // Single-flight + TTL guard around scanner.refreshFile (see refreshFileGuarded).
144089
144093
  // A live file's mtime is always newer than the snapshot, so an unguarded
144090
144094
  // refresh fires on every request and re-parses the whole file from byte 0.
@@ -145102,9 +145106,9 @@ var StreamerServer = class {
145102
145106
  * the automatic freshness path when the directory watcher marked the scanner
145103
145107
  * stale or shouldRefreshProjectsFromHdd detected disk drift.
145104
145108
  */
145105
- async reconcileConversationsCacheFromDisk() {
145109
+ async reconcileConversationsCacheFromDisk(onProgress) {
145106
145110
  if (!this.cache) return;
145107
- const scanner = await this.rescanForRefresh();
145111
+ const scanner = await this.rescanForRefresh(onProgress);
145108
145112
  const metas = [...scanner.getMetadataCache().values()];
145109
145113
  try {
145110
145114
  this.cache.upsertFromScannerMeta(metas);
@@ -145132,6 +145136,18 @@ var StreamerServer = class {
145132
145136
  );
145133
145137
  }
145134
145138
  }
145139
+ // Reconcile the cache from disk without blocking the caller. Single-flighted
145140
+ // so a burst of list polls during active session writes shares one rescan
145141
+ // rather than queueing a full rescan each; tracked so close() awaits the
145142
+ // in-flight cache write before shutting the DB.
145143
+ startBackgroundConversationReconcile() {
145144
+ if (this.conversationReconcileInFlight) return;
145145
+ const task = this.reconcileConversationsCacheFromDisk().finally(() => {
145146
+ this.conversationReconcileInFlight = null;
145147
+ });
145148
+ this.conversationReconcileInFlight = task;
145149
+ this.trackCacheWrite(task);
145150
+ }
145135
145151
  shouldAutoReconcileConversationList() {
145136
145152
  if (!this.cache) return false;
145137
145153
  if (this.scannerStale) return true;
@@ -145149,13 +145165,19 @@ var StreamerServer = class {
145149
145165
  const providerFilter = url2.searchParams.get("provider") ?? void 0;
145150
145166
  const bustCache = url2.searchParams.get("refresh") === "1";
145151
145167
  if (this.cache && (bustCache || this.shouldAutoReconcileConversationList())) {
145152
- if (bustCache) {
145168
+ const canServeStale = !bustCache && this.cache.listConversations({ limit: 0, offset: 0 }).total > 0;
145169
+ if (canServeStale) {
145170
+ this.startBackgroundConversationReconcile();
145171
+ } else {
145172
+ const shouldEmitProgress = createScanProgressThrottle();
145153
145173
  await this.withWarmup(
145154
145174
  "conversation_refresh",
145155
- () => this.reconcileConversationsCacheFromDisk()
145175
+ () => this.reconcileConversationsCacheFromDisk((scanned, total2) => {
145176
+ if (shouldEmitProgress(scanned, total2)) {
145177
+ this.wsHub.broadcast({ type: "scan_progress", scanned, total: total2 });
145178
+ }
145179
+ })
145156
145180
  );
145157
- } else {
145158
- await this.reconcileConversationsCacheFromDisk();
145159
145181
  }
145160
145182
  }
145161
145183
  if (this.cache) {
@@ -145391,7 +145413,7 @@ var StreamerServer = class {
145391
145413
  // getFreshScanner() this does NOT discard the warm scanner. scannerReady is
145392
145414
  // only ever reassigned to a live scan promise (never nulled mid-scan), so the
145393
145415
  // getScanner() anti-infinite-loop guard is preserved.
145394
- async rescanForRefresh() {
145416
+ async rescanForRefresh(onProgress) {
145395
145417
  if (this.scannerReady) await this.scannerReady;
145396
145418
  this.scannerStale = false;
145397
145419
  if (!this.scanner) {
@@ -145402,7 +145424,8 @@ var StreamerServer = class {
145402
145424
  this.scannerReady = scanner.scan({
145403
145425
  ...this.scanProfiles ? { profiles: this.scanProfiles } : {},
145404
145426
  ...this.codexScanOpts(),
145405
- fullRescan: true
145427
+ fullRescan: true,
145428
+ ...onProgress ? { onProgress } : {}
145406
145429
  });
145407
145430
  await this.scannerReady;
145408
145431
  return scanner;