@threadbase-sh/streamer 1.67.0 → 1.67.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.
@@ -60,6 +60,35 @@ export declare class ConversationHandlers {
60
60
  handleGetPopularProjects(url: URL, res: ServerResponse): void;
61
61
  handleGetProjectSummaries(url: URL, res: ServerResponse): void;
62
62
  findJsonlPath(uuid: string): string | null;
63
+ /**
64
+ * Resolve a conversation id to a JSONL path, in order of authority.
65
+ *
66
+ * `findJsonlPath` alone answers 64.0% of this machine's 961 conversations and
67
+ * 0 of 343 Codex ones — it reconstructs `<projectsDir>/<dir>/<uuid>.jsonl`,
68
+ * which is Claude Code's layout, and a Codex rollout is
69
+ * `rollout-<ts>-<uuid>.jsonl` under a date path, so that walk cannot match one
70
+ * by construction. The ladder below answers 99.7%, leaving only the three
71
+ * whose file is genuinely gone.
72
+ */
73
+ locateJsonlPath(uuid: string, lookupId: string): Promise<string | null>;
74
+ /**
75
+ * Does `filePath` actually hold the conversation `requestedId` names?
76
+ *
77
+ * The filename settles it for both providers: Claude writes `<uuid>.jsonl`
78
+ * (or `agent-<agentId>.jsonl`), Codex writes `rollout-<ts>-<uuid>.jsonl`.
79
+ * Only when the name says nothing do we open the file — and there the naive
80
+ * rule is wrong, because **a Claude subagent transcript carries the PARENT's
81
+ * `sessionId`**. Matching on `sessionId` alone therefore verifies exactly the
82
+ * file this check exists to reject, so a sidechain is refused outright unless
83
+ * it was asked for by its own `agent-<agentId>` name, which the filename
84
+ * branch above already covers.
85
+ */
86
+ isJsonlPathFor(filePath: string, requestedId: string): Promise<boolean>;
87
+ /** First parseable JSONL line, for identity checks. Null on an empty or unreadable file. */
88
+ readFirstJsonlEntry(filePath: string): Promise<{
89
+ sessionId?: string;
90
+ isSidechain?: boolean;
91
+ } | null>;
63
92
  readCwdFromJsonl(filePath: string): Promise<string | null>;
64
93
  findConversationByUuid(uuid: string): Promise<Conversation | null>;
65
94
  handleGetConversation(id: string, url: URL, res: ServerResponse, ifNoneMatch?: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"conversations.handlers.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/conversations.handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversations.handlers.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/conversations.handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,YAAY,EAKlB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAG5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAO3C,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAazE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAe1C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACxC,KAAK,EAAE,MAAM,iBAAiB,GAAG,IAAI,CAAC;IACtC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,iBAAiB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;IACpD,UAAU,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACrF,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAClD,2BAA2B,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtD,uBAAuB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACzD,uBAAuB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;CACvD,CAAC;AAEF;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,IAAI;IAAxB,YAAoB,IAAI,EAAE,wBAAwB,EAAI;IAEtD,OAAO,KAAK,cAAc,GAEzB;IAED,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,KAAK,GAEhB;IAED,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,KAAK,GAEhB;IAED,OAAO,KAAK,GAAG,GAEd;IAEK,uBAAuB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsH1E;IAEK,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC3E;IAKD,OAAO,CAAC,wBAAwB;IAmBhC,uBAAuB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CA4B3D;IAED,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAQ5D;IAED,yBAAyB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAgB7D;IAED,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBzC;IAED;;;;;;;;;OASG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgB5E;IAED;;;;;;;;;;;OAWG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ5E;IAED,4FAA4F;IACtF,mBAAmB,CACvB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC,CAoB/D;IAEK,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsB/D;IAEK,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA8IvE;IAEK,qBAAqB,CACzB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,cAAc,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAmZf;IAYK,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsD7F;IAEK,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAuE/D;CACF"}
package/dist/cli.cjs CHANGED
@@ -99746,8 +99746,8 @@ var require_pattern = __commonJS({
99746
99746
  }
99747
99747
  exports2.endsWithSlashGlobStar = endsWithSlashGlobStar;
99748
99748
  function isAffectDepthOfReadingPattern(pattern) {
99749
- const basename12 = path2.basename(pattern);
99750
- return endsWithSlashGlobStar(pattern) || isStaticPattern(basename12);
99749
+ const basename13 = path2.basename(pattern);
99750
+ return endsWithSlashGlobStar(pattern) || isStaticPattern(basename13);
99751
99751
  }
99752
99752
  exports2.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
99753
99753
  function expandPatternsWithBraceExpansion(patterns2) {
@@ -139150,10 +139150,10 @@ var ReaddirpStream = class extends import_node_stream.Readable {
139150
139150
  }
139151
139151
  async _formatEntry(dirent, path2) {
139152
139152
  let entry;
139153
- const basename12 = this._isDirent ? dirent.name : dirent;
139153
+ const basename13 = this._isDirent ? dirent.name : dirent;
139154
139154
  try {
139155
- const fullPath = (0, import_node_path8.resolve)((0, import_node_path8.join)(path2, basename12));
139156
- entry = { path: (0, import_node_path8.relative)(this._root, fullPath), fullPath, basename: basename12 };
139155
+ const fullPath = (0, import_node_path8.resolve)((0, import_node_path8.join)(path2, basename13));
139156
+ entry = { path: (0, import_node_path8.relative)(this._root, fullPath), fullPath, basename: basename13 };
139157
139157
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
139158
139158
  } catch (err) {
139159
139159
  this._onError(err);
@@ -139694,9 +139694,9 @@ var NodeFsHandler = class {
139694
139694
  _watchWithNodeFs(path2, listener) {
139695
139695
  const opts = this.fsw.options;
139696
139696
  const directory = sp.dirname(path2);
139697
- const basename12 = sp.basename(path2);
139697
+ const basename13 = sp.basename(path2);
139698
139698
  const parent = this.fsw._getWatchedDir(directory);
139699
- parent.add(basename12);
139699
+ parent.add(basename13);
139700
139700
  const absolutePath = sp.resolve(path2);
139701
139701
  const options = {
139702
139702
  persistent: opts.persistent
@@ -139706,7 +139706,7 @@ var NodeFsHandler = class {
139706
139706
  let closer;
139707
139707
  if (opts.usePolling) {
139708
139708
  const enableBin = opts.interval !== opts.binaryInterval;
139709
- options.interval = enableBin && isBinaryPath(basename12) ? opts.binaryInterval : opts.interval;
139709
+ options.interval = enableBin && isBinaryPath(basename13) ? opts.binaryInterval : opts.interval;
139710
139710
  closer = setFsWatchFileListener(path2, absolutePath, options, {
139711
139711
  listener,
139712
139712
  rawEmitter: this.fsw._emitRaw
@@ -139729,10 +139729,10 @@ var NodeFsHandler = class {
139729
139729
  return;
139730
139730
  }
139731
139731
  const dirname18 = sp.dirname(file2);
139732
- const basename12 = sp.basename(file2);
139732
+ const basename13 = sp.basename(file2);
139733
139733
  const parent = this.fsw._getWatchedDir(dirname18);
139734
139734
  let prevStats = stats;
139735
- if (parent.has(basename12))
139735
+ if (parent.has(basename13))
139736
139736
  return;
139737
139737
  const listener = async (path2, newStats) => {
139738
139738
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
@@ -139757,9 +139757,9 @@ var NodeFsHandler = class {
139757
139757
  prevStats = newStats2;
139758
139758
  }
139759
139759
  } catch (error51) {
139760
- this.fsw._remove(dirname18, basename12);
139760
+ this.fsw._remove(dirname18, basename13);
139761
139761
  }
139762
- } else if (parent.has(basename12)) {
139762
+ } else if (parent.has(basename13)) {
139763
139763
  const at2 = newStats.atimeMs;
139764
139764
  const mt2 = newStats.mtimeMs;
139765
139765
  if (!at2 || at2 <= mt2 || mt2 !== prevStats.mtimeMs) {
@@ -143610,8 +143610,18 @@ var IndexQueue = class {
143610
143610
  };
143611
143611
  var BATCH_SIZE2 = 12;
143612
143612
  var DEFAULT_CONFIG_PATH2 = "~/.config/threadbase-scanner";
143613
+ function isTestProcess() {
143614
+ return Boolean(process.env.VITEST) || Boolean(process.env.JEST_WORKER_ID) || process.env.NODE_ENV === "test";
143615
+ }
143613
143616
  function defaultDbPath() {
143614
- return process.env.TB_SCANNER_DB ?? (0, import_path10.join)((0, import_os8.homedir)(), ".config", "threadbase-scanner", "index.db");
143617
+ const override = process.env.TB_SCANNER_DB;
143618
+ if (override) return override;
143619
+ if (isTestProcess()) {
143620
+ throw new Error(
143621
+ "TB_SCANNER_DB must be set under a test runner. Refusing to open the default index at ~/.config/threadbase-scanner/index.db, because test fixtures written there are indistinguishable from real conversations and corrupt every measurement of the index. Point it at a temp directory in your test setup, or pass persistent.dbPath explicitly."
143622
+ );
143623
+ }
143624
+ return (0, import_path10.join)((0, import_os8.homedir)(), ".config", "threadbase-scanner", "index.db");
143615
143625
  }
143616
143626
  function capForMemory(doc, max) {
143617
143627
  const combined = combineSearchContent(doc);
@@ -144602,6 +144612,7 @@ CREATE TABLE IF NOT EXISTS session_names (
144602
144612
  );
144603
144613
  `;
144604
144614
  var cacheLog = getLogger("cache");
144615
+ var PARSE_STATE_LOOKBACK = 8;
144605
144616
  var ConversationCache = class _ConversationCache {
144606
144617
  db;
144607
144618
  tailSize;
@@ -145118,7 +145129,7 @@ var ConversationCache = class _ConversationCache {
145118
145129
  if (to2 <= from) return { messages: [], total, fromIndex: from };
145119
145130
  const rows = this.getMessageIndexWindow(
145120
145131
  _ConversationCache.conversationIdForFile(filePath),
145121
- from,
145132
+ Math.max(0, from - PARSE_STATE_LOOKBACK),
145122
145133
  to2
145123
145134
  );
145124
145135
  if (rows.length === 0) return { messages: [], total, fromIndex: from };
@@ -145130,7 +145141,7 @@ var ConversationCache = class _ConversationCache {
145130
145141
  const buf = Buffer.alloc(row.byte_length);
145131
145142
  (0, import_fs18.readSync)(fd, buf, 0, row.byte_length, row.byte_offset);
145132
145143
  const msg = parseJsonlLine(buf.toString("utf-8"), state);
145133
- if (msg) messages.push(msg);
145144
+ if (msg && row.message_index >= from) messages.push(msg);
145134
145145
  }
145135
145146
  } finally {
145136
145147
  (0, import_fs18.closeSync)(fd);
@@ -146482,6 +146493,64 @@ var ConversationHandlers = class {
146482
146493
  }
146483
146494
  return null;
146484
146495
  }
146496
+ /**
146497
+ * Resolve a conversation id to a JSONL path, in order of authority.
146498
+ *
146499
+ * `findJsonlPath` alone answers 64.0% of this machine's 961 conversations and
146500
+ * 0 of 343 Codex ones — it reconstructs `<projectsDir>/<dir>/<uuid>.jsonl`,
146501
+ * which is Claude Code's layout, and a Codex rollout is
146502
+ * `rollout-<ts>-<uuid>.jsonl` under a date path, so that walk cannot match one
146503
+ * by construction. The ladder below answers 99.7%, leaving only the three
146504
+ * whose file is genuinely gone.
146505
+ */
146506
+ async locateJsonlPath(uuid3, lookupId) {
146507
+ const live = this.deps.findLiveSessionFilePath(uuid3) ?? this.deps.findLiveSessionFilePath(lookupId);
146508
+ if (live) return live;
146509
+ const cached4 = this.cache?.getMetaById(lookupId)?.filePath;
146510
+ if (cached4 && await this.isJsonlPathFor(cached4, lookupId)) return cached4;
146511
+ return this.findJsonlPath(lookupId);
146512
+ }
146513
+ /**
146514
+ * Does `filePath` actually hold the conversation `requestedId` names?
146515
+ *
146516
+ * The filename settles it for both providers: Claude writes `<uuid>.jsonl`
146517
+ * (or `agent-<agentId>.jsonl`), Codex writes `rollout-<ts>-<uuid>.jsonl`.
146518
+ * Only when the name says nothing do we open the file — and there the naive
146519
+ * rule is wrong, because **a Claude subagent transcript carries the PARENT's
146520
+ * `sessionId`**. Matching on `sessionId` alone therefore verifies exactly the
146521
+ * file this check exists to reject, so a sidechain is refused outright unless
146522
+ * it was asked for by its own `agent-<agentId>` name, which the filename
146523
+ * branch above already covers.
146524
+ */
146525
+ async isJsonlPathFor(filePath, requestedId) {
146526
+ if (!(0, import_fs20.existsSync)(filePath)) return false;
146527
+ const stem = (0, import_path18.basename)(filePath).replace(/\.jsonl$/, "");
146528
+ if (stem === requestedId || stem.includes(requestedId)) return true;
146529
+ const first = await this.readFirstJsonlEntry(filePath);
146530
+ if (!first || first.isSidechain === true) return false;
146531
+ return first.sessionId === requestedId;
146532
+ }
146533
+ /** First parseable JSONL line, for identity checks. Null on an empty or unreadable file. */
146534
+ async readFirstJsonlEntry(filePath) {
146535
+ return new Promise((resolve4) => {
146536
+ const rl = (0, import_readline4.createInterface)({ input: (0, import_fs20.createReadStream)(filePath), crlfDelay: Infinity });
146537
+ let done = false;
146538
+ rl.on("line", (line) => {
146539
+ if (done) return;
146540
+ try {
146541
+ const entry = JSON.parse(line);
146542
+ done = true;
146543
+ rl.close();
146544
+ resolve4(entry);
146545
+ } catch {
146546
+ }
146547
+ });
146548
+ rl.on("close", () => {
146549
+ if (!done) resolve4(null);
146550
+ });
146551
+ rl.on("error", () => resolve4(null));
146552
+ });
146553
+ }
146485
146554
  async readCwdFromJsonl(filePath) {
146486
146555
  return new Promise((resolve4) => {
146487
146556
  const rl = (0, import_readline4.createInterface)({ input: (0, import_fs20.createReadStream)(filePath), crlfDelay: Infinity });
@@ -146507,7 +146576,7 @@ var ConversationHandlers = class {
146507
146576
  async findConversationByUuid(uuid3) {
146508
146577
  const lookupId = this.deps.resolveConversationLookupId(uuid3);
146509
146578
  if (!this.scannerManager.ready && !this.scanProfiles) {
146510
- const filePath2 = this.findJsonlPath(lookupId) ?? this.deps.findLiveSessionFilePath(uuid3) ?? this.deps.findLiveSessionFilePath(lookupId);
146579
+ const filePath2 = await this.locateJsonlPath(uuid3, lookupId);
146511
146580
  if (filePath2) {
146512
146581
  const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;
146513
146582
  const coldScanner = this.scannerManager.current ?? this.scannerManager.newScanner();
@@ -146559,7 +146628,7 @@ var ConversationHandlers = class {
146559
146628
  return fromIndex;
146560
146629
  }
146561
146630
  if (this.scanProfiles) return null;
146562
- const filePath = this.findJsonlPath(lookupId) ?? this.deps.findLiveSessionFilePath(uuid3) ?? this.deps.findLiveSessionFilePath(lookupId);
146631
+ const filePath = await this.locateJsonlPath(uuid3, lookupId);
146563
146632
  if (!filePath) return null;
146564
146633
  if (this.scannerManager.ready) {
146565
146634
  const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;