@threadbase-sh/streamer 1.36.1 → 1.36.2

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.d.cts CHANGED
@@ -1285,6 +1285,15 @@ declare class StreamerServer {
1285
1285
  private checkExchangeRateLimit;
1286
1286
  private checkSessionStartRateLimit;
1287
1287
  private checkSessionInputRateLimit;
1288
+ /** Project roots watched for conversation JSONLs (profiles or ~/.claude/projects). */
1289
+ private projectsDirsForFreshnessCheck;
1290
+ /**
1291
+ * Full-glob scan + cache upsert/delete reconcile. Used by ?refresh=1 and by
1292
+ * the automatic freshness path when the directory watcher marked the scanner
1293
+ * stale or shouldRefreshProjectsFromHdd detected disk drift.
1294
+ */
1295
+ private reconcileConversationsCacheFromDisk;
1296
+ private shouldAutoReconcileConversationList;
1288
1297
  private handleListConversations;
1289
1298
  private handleConversationsCount;
1290
1299
  private refreshCountInBackground;
package/dist/index.d.ts CHANGED
@@ -1285,6 +1285,15 @@ declare class StreamerServer {
1285
1285
  private checkExchangeRateLimit;
1286
1286
  private checkSessionStartRateLimit;
1287
1287
  private checkSessionInputRateLimit;
1288
+ /** Project roots watched for conversation JSONLs (profiles or ~/.claude/projects). */
1289
+ private projectsDirsForFreshnessCheck;
1290
+ /**
1291
+ * Full-glob scan + cache upsert/delete reconcile. Used by ?refresh=1 and by
1292
+ * the automatic freshness path when the directory watcher marked the scanner
1293
+ * stale or shouldRefreshProjectsFromHdd detected disk drift.
1294
+ */
1295
+ private reconcileConversationsCacheFromDisk;
1296
+ private shouldAutoReconcileConversationList;
1288
1297
  private handleListConversations;
1289
1298
  private handleConversationsCount;
1290
1299
  private refreshCountInBackground;
package/dist/index.js CHANGED
@@ -2684,14 +2684,14 @@ import {
2684
2684
  createReadStream,
2685
2685
  existsSync as existsSync10,
2686
2686
  watch as fsWatch,
2687
- readdirSync as readdirSync5,
2687
+ readdirSync as readdirSync6,
2688
2688
  readFileSync as readFileSync8,
2689
- statSync as statSync8
2689
+ statSync as statSync9
2690
2690
  } from "fs";
2691
2691
  import { realpath as realpath2 } from "fs/promises";
2692
2692
  import { createServer } from "http";
2693
- import { homedir as homedir8 } from "os";
2694
- import { basename as basename5, dirname as dirname9, join as join17 } from "path";
2693
+ import { homedir as homedir9 } from "os";
2694
+ import { basename as basename5, dirname as dirname9, join as join18 } from "path";
2695
2695
  import { createInterface } from "readline";
2696
2696
 
2697
2697
  // node_modules/nanoid/index.js
@@ -5420,6 +5420,14 @@ function seal(plaintext, recipientPublicKeyBase64) {
5420
5420
  };
5421
5421
  }
5422
5422
 
5423
+ // src/services/cache/cacheMetadata.ts
5424
+ function getCacheMetadata(repo, key) {
5425
+ return repo.getCacheMetadata(key);
5426
+ }
5427
+ function setCacheMetadata(repo, key, value) {
5428
+ repo.setCacheMetadata(key, value);
5429
+ }
5430
+
5423
5431
  // src/services/cache-integrity/cacheIntegrityMonitor.ts
5424
5432
  import { createHash as createHash2 } from "crypto";
5425
5433
  import { existsSync as existsSync8 } from "fs";
@@ -5960,6 +5968,140 @@ function pruneAgentConversations(cache) {
5960
5968
  return { scanned: rows.length, pruned, missing };
5961
5969
  }
5962
5970
 
5971
+ // src/utils/dates.ts
5972
+ import { compareDesc, isValid, parseISO } from "date-fns";
5973
+ function parseIsoDateOrNull(value) {
5974
+ if (!value) return null;
5975
+ const parsed = parseISO(value);
5976
+ return isValid(parsed) ? parsed : null;
5977
+ }
5978
+ function compareIsoDesc(a, b) {
5979
+ const dateA = parseIsoDateOrNull(a);
5980
+ const dateB = parseIsoDateOrNull(b);
5981
+ if (!dateA && !dateB) return 0;
5982
+ if (!dateA) return 1;
5983
+ if (!dateB) return -1;
5984
+ return compareDesc(dateA, dateB);
5985
+ }
5986
+
5987
+ // src/services/projects/ensureProjectsForConversations.ts
5988
+ function ensureProjectsForConversations(repo, conversations) {
5989
+ const conversationsByPath = /* @__PURE__ */ new Map();
5990
+ for (const conversation of conversations) {
5991
+ if (!conversation.projectPath) continue;
5992
+ const canonical = canonicalizeProjectPath(conversation.projectPath);
5993
+ if (!canonical) continue;
5994
+ const existing = conversationsByPath.get(canonical) ?? [];
5995
+ existing.push(conversation);
5996
+ conversationsByPath.set(canonical, existing);
5997
+ }
5998
+ const pathToProjectId = /* @__PURE__ */ new Map();
5999
+ for (const [path, projectConversations] of conversationsByPath) {
6000
+ const latest = pickLatestConversation(projectConversations);
6001
+ const project = repo.upsertProjectByPath(path, {
6002
+ lastConversationId: latest?.id ?? null,
6003
+ lastConversationCreatedAt: latest?.createdAt ?? null,
6004
+ latestMessageAt: latest?.latestMessageAt ?? null
6005
+ });
6006
+ pathToProjectId.set(path, project.id);
6007
+ }
6008
+ return pathToProjectId;
6009
+ }
6010
+ function pickLatestConversation(conversations) {
6011
+ if (conversations.length === 0) return void 0;
6012
+ return [...conversations].sort((a, b) => {
6013
+ const cmp = compareIsoDesc(a.latestMessageAt ?? null, b.latestMessageAt ?? null);
6014
+ if (cmp !== 0) return cmp;
6015
+ return compareIsoDesc(a.createdAt ?? null, b.createdAt ?? null);
6016
+ })[0];
6017
+ }
6018
+
6019
+ // src/services/conversations/refreshConversationCache.ts
6020
+ function refreshConversationCache(deps) {
6021
+ const { projectsRepo, conversationsRepo, cacheMetadataRepo } = deps;
6022
+ const conversations = conversationsRepo.listConversationsForProjectBackfill();
6023
+ const pathToProjectId = ensureProjectsForConversations(
6024
+ projectsRepo,
6025
+ conversations.map((c) => ({
6026
+ id: c.id,
6027
+ projectPath: c.projectPath,
6028
+ latestMessageAt: c.lastActivity ?? null,
6029
+ createdAt: c.lastActivity ?? null
6030
+ }))
6031
+ );
6032
+ let conversationsBackfilled = 0;
6033
+ for (const conversation of conversations) {
6034
+ if (!conversation.projectPath) continue;
6035
+ if (conversation.projectId) continue;
6036
+ const projectId = pathToProjectId.get(canonicalizeProjectPath(conversation.projectPath));
6037
+ if (!projectId) continue;
6038
+ conversationsRepo.updateConversationProjectId({
6039
+ conversationId: conversation.id,
6040
+ projectId
6041
+ });
6042
+ conversationsBackfilled += 1;
6043
+ }
6044
+ const latest = conversationsRepo.getLatestConversation();
6045
+ if (latest) {
6046
+ setCacheMetadata(cacheMetadataRepo, "last_conversation_id", latest.id);
6047
+ if (latest.lastActivity) {
6048
+ setCacheMetadata(cacheMetadataRepo, "last_conversation_created_at", latest.lastActivity);
6049
+ }
6050
+ }
6051
+ setCacheMetadata(cacheMetadataRepo, "conversations_last_indexed_at", (/* @__PURE__ */ new Date()).toISOString());
6052
+ return {
6053
+ projectsTouched: pathToProjectId.size,
6054
+ conversationsBackfilled,
6055
+ latestConversationId: latest?.id ?? null
6056
+ };
6057
+ }
6058
+
6059
+ // src/services/conversations/shouldRefreshProjectsFromHdd.ts
6060
+ import { readdirSync as readdirSync5, statSync as statSync7 } from "fs";
6061
+ import { homedir as homedir8 } from "os";
6062
+ import { join as join16 } from "path";
6063
+ var DEFAULT_PROJECTS_DIR = join16(homedir8(), ".claude", "projects");
6064
+ function maxProjectsTreeMtimeMs(projectsDir) {
6065
+ let maxMs;
6066
+ try {
6067
+ maxMs = statSync7(projectsDir).mtimeMs;
6068
+ } catch {
6069
+ return null;
6070
+ }
6071
+ try {
6072
+ for (const ent of readdirSync5(projectsDir, { withFileTypes: true })) {
6073
+ if (!ent.isDirectory()) continue;
6074
+ try {
6075
+ const childMs = statSync7(join16(projectsDir, ent.name)).mtimeMs;
6076
+ if (childMs > maxMs) maxMs = childMs;
6077
+ } catch {
6078
+ }
6079
+ }
6080
+ } catch {
6081
+ }
6082
+ return maxMs;
6083
+ }
6084
+ function shouldRefreshProjectsFromHdd(conversationsRepo, cacheMetadataRepo, opts = {}) {
6085
+ if (conversationsRepo.hasOrphanRows()) return true;
6086
+ const dirs = /* @__PURE__ */ new Set();
6087
+ if (opts.projectsDirs) {
6088
+ for (const d of opts.projectsDirs) dirs.add(d);
6089
+ }
6090
+ dirs.add(opts.projectsDir ?? DEFAULT_PROJECTS_DIR);
6091
+ let newestMs = null;
6092
+ for (const dir of dirs) {
6093
+ const ms = maxProjectsTreeMtimeMs(dir);
6094
+ if (ms === null) continue;
6095
+ if (newestMs === null || ms > newestMs) newestMs = ms;
6096
+ }
6097
+ if (newestMs === null) return false;
6098
+ const lastIndexedIso = getCacheMetadata(cacheMetadataRepo, "conversations_last_indexed_at");
6099
+ if (!lastIndexedIso) return true;
6100
+ const lastIndexedMs = Date.parse(lastIndexedIso);
6101
+ if (Number.isNaN(lastIndexedMs)) return true;
6102
+ return newestMs > lastIndexedMs;
6103
+ }
6104
+
5963
6105
  // src/services/projectChats/deriveProjectChatTitle.ts
5964
6106
  function deriveProjectChatTitle(input) {
5965
6107
  const trimmed = input.title?.trim();
@@ -6121,7 +6263,7 @@ function resolveAnswer(pending, body) {
6121
6263
  }
6122
6264
 
6123
6265
  // src/services/sessions/conversationBusy.ts
6124
- import { statSync as statSync7 } from "fs";
6266
+ import { statSync as statSync8 } from "fs";
6125
6267
  var RESUME_BUSY_WINDOW_MS = 12e4;
6126
6268
  function resolveResumeBusyWindowMs(env = process.env) {
6127
6269
  const raw = env.THREADBASE_RESUME_BUSY_WINDOW_MS;
@@ -6138,7 +6280,7 @@ function conversationBusy(input) {
6138
6280
  let lastActivityMs = null;
6139
6281
  if (input.jsonlPath) {
6140
6282
  try {
6141
- const mtimeMs = statSync7(input.jsonlPath).mtimeMs;
6283
+ const mtimeMs = statSync8(input.jsonlPath).mtimeMs;
6142
6284
  const age = now - mtimeMs;
6143
6285
  lastActivityMs = Math.max(0, age);
6144
6286
  const isSelfEcho = input.selfPtyEndedAt != null && mtimeMs <= input.selfPtyEndedAt + SELF_ACTIVITY_SKEW_MS;
@@ -6366,7 +6508,7 @@ function discoveredToResponse(d, conversationId) {
6366
6508
  import { randomBytes as randomBytes3 } from "crypto";
6367
6509
  import { mkdir as mkdir3, writeFile } from "fs/promises";
6368
6510
  import heicConvert from "heic-convert";
6369
- import { join as join16 } from "path";
6511
+ import { join as join17 } from "path";
6370
6512
  var UPLOAD_DIR_NAME = ".threadbase-uploads";
6371
6513
  var MAX_BYTES = 25 * 1024 * 1024;
6372
6514
  var HEIC_MIMES = /* @__PURE__ */ new Set(["image/heic", "image/heif"]);
@@ -6399,9 +6541,9 @@ async function saveUploadFile(input) {
6399
6541
  }
6400
6542
  const id = `up_${randomBytes3(8).toString("hex")}`;
6401
6543
  const safeName = sanitizeFilename(originalName) || `file${MIME_TO_EXT[mimeType] ?? ""}`;
6402
- const dir = join16(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
6544
+ const dir = join17(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
6403
6545
  await mkdir3(dir, { recursive: true });
6404
- const filePath = join16(dir, `${Date.now()}-${id}-${safeName}`);
6546
+ const filePath = join17(dir, `${Date.now()}-${id}-${safeName}`);
6405
6547
  await writeFile(filePath, buffer);
6406
6548
  return {
6407
6549
  id,
@@ -6511,14 +6653,6 @@ function computeConversationEtag({
6511
6653
  return `"${digest}"`;
6512
6654
  }
6513
6655
 
6514
- // src/utils/dates.ts
6515
- import { compareDesc, isValid, parseISO } from "date-fns";
6516
- function parseIsoDateOrNull(value) {
6517
- if (!value) return null;
6518
- const parsed = parseISO(value);
6519
- return isValid(parsed) ? parsed : null;
6520
- }
6521
-
6522
6656
  // src/utils/isScannedSnapshotStale.ts
6523
6657
  var STALENESS_TOLERANCE_MS = 1e3;
6524
6658
  function isScannedSnapshotStale(snapshotTimestamp, fileMtimeMs) {
@@ -6819,13 +6953,13 @@ var StreamerServer = class {
6819
6953
  this.disableDb = config.disableDb ?? false;
6820
6954
  this.scannerPersistenceDisabled = config.scannerPersistent === false;
6821
6955
  this.scanProfiles = config.scanProfiles;
6822
- this.codexRoots = config.codexRoots ?? [join17(homedir8(), ".codex", "sessions")];
6956
+ this.codexRoots = config.codexRoots ?? [join18(homedir9(), ".codex", "sessions")];
6823
6957
  this.ptyGracePeriodMs = config.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
6824
6958
  this.defaultSystemPrompt = config.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
6825
6959
  this.defaultPermissionMode = config.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
6826
6960
  this.defaultModel = config.defaultModel ?? "sonnet";
6827
6961
  this.defaultEffort = config.defaultEffort ?? "low";
6828
- this.cacheDir = config.cacheDir ?? loadCacheDir() ?? join17(homedir8(), ".threadbase", "cache");
6962
+ this.cacheDir = config.cacheDir ?? loadCacheDir() ?? join18(homedir9(), ".threadbase", "cache");
6829
6963
  this.tailSize = config.tailSize ?? loadTailSize() ?? 10;
6830
6964
  this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config.directoryScanDebounceMs ?? 1e3;
6831
6965
  this.markScannerStaleDebounced = debounce(() => {
@@ -6866,7 +7000,7 @@ var StreamerServer = class {
6866
7000
  const seqs = cache.extendMessageIndex(
6867
7001
  filePath,
6868
7002
  spans,
6869
- statSync8(filePath),
7003
+ statSync9(filePath),
6870
7004
  readFrom,
6871
7005
  endOffset
6872
7006
  );
@@ -7032,7 +7166,7 @@ var StreamerServer = class {
7032
7166
  temporalClient,
7033
7167
  taskQueue: agentConfig.temporal.taskQueue
7034
7168
  });
7035
- const conversationsBaseDir = agentConfig.conversationsDir || join17(dirname9(this.cacheDir), "conversations");
7169
+ const conversationsBaseDir = agentConfig.conversationsDir || join18(dirname9(this.cacheDir), "conversations");
7036
7170
  conversationWriter = createConversationWriter({
7037
7171
  baseDir: conversationsBaseDir
7038
7172
  });
@@ -7355,7 +7489,7 @@ var StreamerServer = class {
7355
7489
  });
7356
7490
  try {
7357
7491
  this.cache = ConversationCache.open(
7358
- join17(this.cacheDir, "cache.db"),
7492
+ join18(this.cacheDir, "cache.db"),
7359
7493
  this.tailSize,
7360
7494
  void 0,
7361
7495
  {
@@ -7481,6 +7615,20 @@ var StreamerServer = class {
7481
7615
  count: pruned.length,
7482
7616
  event: "cache.prune_ghosts"
7483
7617
  });
7618
+ if (this.projectsRepo && this.conversationsRepo && this.cacheMetadataRepo) {
7619
+ refreshConversationCache({
7620
+ cache: this.cache,
7621
+ projectsRepo: this.projectsRepo,
7622
+ conversationsRepo: this.conversationsRepo,
7623
+ cacheMetadataRepo: this.cacheMetadataRepo
7624
+ });
7625
+ } else if (this.cacheMetadataRepo) {
7626
+ setCacheMetadata(
7627
+ this.cacheMetadataRepo,
7628
+ "conversations_last_indexed_at",
7629
+ (/* @__PURE__ */ new Date()).toISOString()
7630
+ );
7631
+ }
7484
7632
  }
7485
7633
  }).catch((err) => {
7486
7634
  const message = err instanceof Error ? err.message : String(err);
@@ -7720,6 +7868,56 @@ var StreamerServer = class {
7720
7868
  checkSessionInputRateLimit(sessionId) {
7721
7869
  return this.checkRateLimit(this.sessionInputAttempts, sessionId, 500, 6e4);
7722
7870
  }
7871
+ /** Project roots watched for conversation JSONLs (profiles or ~/.claude/projects). */
7872
+ projectsDirsForFreshnessCheck() {
7873
+ if (this.scanProfiles && this.scanProfiles.length > 0) {
7874
+ return this.scanProfiles.filter((p) => p.enabled).map((p) => join18(p.configDir, "projects"));
7875
+ }
7876
+ return [join18(homedir9(), ".claude", "projects")];
7877
+ }
7878
+ /**
7879
+ * Full-glob scan + cache upsert/delete reconcile. Used by ?refresh=1 and by
7880
+ * the automatic freshness path when the directory watcher marked the scanner
7881
+ * stale or shouldRefreshProjectsFromHdd detected disk drift.
7882
+ */
7883
+ async reconcileConversationsCacheFromDisk() {
7884
+ if (!this.cache) return;
7885
+ const scanner = await this.rescanForRefresh();
7886
+ const metas = [...scanner.getMetadataCache().values()];
7887
+ try {
7888
+ this.cache.upsertFromScannerMeta(metas);
7889
+ if (!this.cacheMonitor?.pending) {
7890
+ this.cache.reconcileDeletions(canonicalLivePathSet(metas));
7891
+ }
7892
+ if (this.projectsRepo && this.conversationsRepo && this.cacheMetadataRepo) {
7893
+ refreshConversationCache({
7894
+ cache: this.cache,
7895
+ projectsRepo: this.projectsRepo,
7896
+ conversationsRepo: this.conversationsRepo,
7897
+ cacheMetadataRepo: this.cacheMetadataRepo
7898
+ });
7899
+ } else if (this.cacheMetadataRepo) {
7900
+ setCacheMetadata(
7901
+ this.cacheMetadataRepo,
7902
+ "conversations_last_indexed_at",
7903
+ (/* @__PURE__ */ new Date()).toISOString()
7904
+ );
7905
+ }
7906
+ } catch (err) {
7907
+ this.log.warn(
7908
+ `refresh reconcile failed: ${err instanceof Error ? err.message : String(err)}`,
7909
+ { event: "conversations.reconcile_failed" }
7910
+ );
7911
+ }
7912
+ }
7913
+ shouldAutoReconcileConversationList() {
7914
+ if (!this.cache) return false;
7915
+ if (this.scannerStale) return true;
7916
+ if (!this.conversationsRepo || !this.cacheMetadataRepo) return false;
7917
+ return shouldRefreshProjectsFromHdd(this.conversationsRepo, this.cacheMetadataRepo, {
7918
+ projectsDirs: this.projectsDirsForFreshnessCheck()
7919
+ });
7920
+ }
7723
7921
  async handleListConversations(url, res) {
7724
7922
  if (this.rejectIfWarmingUp(res)) return;
7725
7923
  const limit = intParam(url, "limit", 50);
@@ -7728,19 +7926,14 @@ var StreamerServer = class {
7728
7926
  const project = url.searchParams.get("project") ?? void 0;
7729
7927
  const providerFilter = url.searchParams.get("provider") ?? void 0;
7730
7928
  const bustCache = url.searchParams.get("refresh") === "1";
7731
- if (bustCache && this.cache) {
7732
- const scanner2 = await this.withWarmup("conversation_refresh", () => this.rescanForRefresh());
7733
- const metas2 = [...scanner2.getMetadataCache().values()];
7734
- try {
7735
- this.cache.upsertFromScannerMeta(metas2);
7736
- if (!this.cacheMonitor?.pending) {
7737
- this.cache.reconcileDeletions(canonicalLivePathSet(metas2));
7738
- }
7739
- } catch (err) {
7740
- this.log.warn(
7741
- `refresh reconcile failed: ${err instanceof Error ? err.message : String(err)}`,
7742
- { event: "conversations.reconcile_failed" }
7929
+ if (this.cache && (bustCache || this.shouldAutoReconcileConversationList())) {
7930
+ if (bustCache) {
7931
+ await this.withWarmup(
7932
+ "conversation_refresh",
7933
+ () => this.reconcileConversationsCacheFromDisk()
7743
7934
  );
7935
+ } else {
7936
+ await this.reconcileConversationsCacheFromDisk();
7744
7937
  }
7745
7938
  }
7746
7939
  if (this.cache) {
@@ -8002,21 +8195,21 @@ var StreamerServer = class {
8002
8195
  */
8003
8196
  projectsDirs() {
8004
8197
  if (this.scanProfiles && this.scanProfiles.length > 0) {
8005
- return this.scanProfiles.filter((p) => p.enabled).map((p) => join17(p.configDir, "projects"));
8198
+ return this.scanProfiles.filter((p) => p.enabled).map((p) => join18(p.configDir, "projects"));
8006
8199
  }
8007
- return [join17(homedir8(), ".claude", "projects")];
8200
+ return [join18(homedir9(), ".claude", "projects")];
8008
8201
  }
8009
8202
  findJsonlPath(uuid) {
8010
8203
  const filename = `${uuid}.jsonl`;
8011
8204
  for (const projectsDir of this.projectsDirs()) {
8012
8205
  if (!existsSync10(projectsDir)) continue;
8013
- for (const dir of readdirSync5(projectsDir)) {
8014
- const fp = join17(projectsDir, dir, filename);
8206
+ for (const dir of readdirSync6(projectsDir)) {
8207
+ const fp = join18(projectsDir, dir, filename);
8015
8208
  if (existsSync10(fp)) return fp;
8016
- const projectDir = join17(projectsDir, dir);
8209
+ const projectDir = join18(projectsDir, dir);
8017
8210
  try {
8018
- for (const sub of readdirSync5(projectDir)) {
8019
- const subagentPath = join17(projectDir, sub, "subagents", filename);
8211
+ for (const sub of readdirSync6(projectDir)) {
8212
+ const subagentPath = join18(projectDir, sub, "subagents", filename);
8020
8213
  if (existsSync10(subagentPath)) return subagentPath;
8021
8214
  }
8022
8215
  } catch {
@@ -8117,7 +8310,7 @@ var StreamerServer = class {
8117
8310
  if (this.isManagedTailPath(key)) return;
8118
8311
  let mtimeMs;
8119
8312
  try {
8120
- mtimeMs = statSync8(filePath).mtimeMs;
8313
+ mtimeMs = statSync9(filePath).mtimeMs;
8121
8314
  } catch {
8122
8315
  return;
8123
8316
  }
@@ -8299,7 +8492,7 @@ var StreamerServer = class {
8299
8492
  if (!conv.filePath) return false;
8300
8493
  let mtimeMs = null;
8301
8494
  try {
8302
- mtimeMs = statSync8(conv.filePath).mtimeMs;
8495
+ mtimeMs = statSync9(conv.filePath).mtimeMs;
8303
8496
  } catch {
8304
8497
  return false;
8305
8498
  }
@@ -9219,7 +9412,7 @@ var StreamerServer = class {
9219
9412
  sessionStore: this.sessionStore,
9220
9413
  // biome-ignore lint/style/noNonNullAssertion: agentClient is set when agentConfig.enabled is true
9221
9414
  agentClient: this.agentClient,
9222
- conversationsDir: this.cacheDir ? join17(dirname9(this.cacheDir), "conversations") : "",
9415
+ conversationsDir: this.cacheDir ? join18(dirname9(this.cacheDir), "conversations") : "",
9223
9416
  agentConfig: this.agentConfig
9224
9417
  });
9225
9418
  json(res, result.status, result.body);
@@ -9378,9 +9571,9 @@ var StreamerServer = class {
9378
9571
  // was passed to Claude via --session-id so the filename matches from the start.
9379
9572
  watchForJsonl(sessionId, projectPath) {
9380
9573
  const encoded = projectPath.replace(/[/\\:.]/g, "-");
9381
- const projectsDir = join17(homedir8(), ".claude", "projects", encoded);
9574
+ const projectsDir = join18(homedir9(), ".claude", "projects", encoded);
9382
9575
  const expectedFile = `${sessionId}.jsonl`;
9383
- const filePath = join17(projectsDir, expectedFile);
9576
+ const filePath = join18(projectsDir, expectedFile);
9384
9577
  const deadline = Date.now() + 12e4;
9385
9578
  let watcher = null;
9386
9579
  const cleanup = () => {
@@ -9402,10 +9595,10 @@ var StreamerServer = class {
9402
9595
  if (!resolvedFilePath && existsSync10(projectsDir)) {
9403
9596
  try {
9404
9597
  const now = Date.now();
9405
- const match = readdirSync5(projectsDir).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, mtime: statSync8(join17(projectsDir, f)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).filter(
9406
- ({ f }) => basename5(f, ".jsonl") === sessionId || this.readFirstLineSessionId(join17(projectsDir, f)) === sessionId
9598
+ const match = readdirSync6(projectsDir).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, mtime: statSync9(join18(projectsDir, f)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).filter(
9599
+ ({ f }) => basename5(f, ".jsonl") === sessionId || this.readFirstLineSessionId(join18(projectsDir, f)) === sessionId
9407
9600
  ).sort((a, b) => b.mtime - a.mtime)[0];
9408
- if (match) resolvedFilePath = join17(projectsDir, match.f);
9601
+ if (match) resolvedFilePath = join18(projectsDir, match.f);
9409
9602
  } catch {
9410
9603
  }
9411
9604
  }
@@ -9453,7 +9646,7 @@ var StreamerServer = class {
9453
9646
  watchForCodexRollout(sessionId, projectPath) {
9454
9647
  const deadline = Date.now() + 12e4;
9455
9648
  const now = /* @__PURE__ */ new Date();
9456
- const dateDir = join17(
9649
+ const dateDir = join18(
9457
9650
  String(now.getFullYear()),
9458
9651
  String(now.getMonth() + 1).padStart(2, "0"),
9459
9652
  String(now.getDate()).padStart(2, "0")
@@ -9494,18 +9687,18 @@ var StreamerServer = class {
9494
9687
  this.sessionStore.listManaged().filter((s) => s.id !== sessionId && s.boundConversationId != null).map((s) => s.boundConversationId)
9495
9688
  );
9496
9689
  for (const root of this.codexRoots) {
9497
- const sessionsDir = join17(root, dateDir);
9690
+ const sessionsDir = join18(root, dateDir);
9498
9691
  if (!existsSync10(sessionsDir)) continue;
9499
9692
  let candidateFiles;
9500
9693
  try {
9501
- candidateFiles = readdirSync5(sessionsDir).filter((f) => f.endsWith(".jsonl"));
9694
+ candidateFiles = readdirSync6(sessionsDir).filter((f) => f.endsWith(".jsonl"));
9502
9695
  } catch {
9503
9696
  continue;
9504
9697
  }
9505
9698
  const nowMs = Date.now();
9506
- const recentCandidates = candidateFiles.map((f) => ({ f, mtime: statSync8(join17(sessionsDir, f)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b) => b.mtime - a.mtime);
9699
+ const recentCandidates = candidateFiles.map((f) => ({ f, mtime: statSync9(join18(sessionsDir, f)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b) => b.mtime - a.mtime);
9507
9700
  for (const { f } of recentCandidates) {
9508
- const candidatePath = join17(sessionsDir, f);
9701
+ const candidatePath = join18(sessionsDir, f);
9509
9702
  const match = matchesProjectPath(candidatePath);
9510
9703
  if (!match) continue;
9511
9704
  if (boundElsewhere.has(match.id)) continue;