@threadbase-sh/streamer 1.27.0 → 1.27.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/cli.cjs CHANGED
@@ -99922,8 +99922,8 @@ var require_pattern = __commonJS({
99922
99922
  }
99923
99923
  exports2.endsWithSlashGlobStar = endsWithSlashGlobStar;
99924
99924
  function isAffectDepthOfReadingPattern(pattern) {
99925
- const basename11 = path2.basename(pattern);
99926
- return endsWithSlashGlobStar(pattern) || isStaticPattern(basename11);
99925
+ const basename12 = path2.basename(pattern);
99926
+ return endsWithSlashGlobStar(pattern) || isStaticPattern(basename12);
99927
99927
  }
99928
99928
  exports2.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
99929
99929
  function expandPatternsWithBraceExpansion(patterns2) {
@@ -123750,14 +123750,14 @@ __export(prompt_exports, {
123750
123750
  interactivePermissionModePrompt: () => interactivePermissionModePrompt,
123751
123751
  interactivePrompt: () => interactivePrompt
123752
123752
  });
123753
- var import_node_process2, import_promises18, interactivePrompt, interactivePermissionModePrompt;
123753
+ var import_node_process2, import_promises20, interactivePrompt, interactivePermissionModePrompt;
123754
123754
  var init_prompt = __esm({
123755
123755
  "src/lifecycle/prompt.ts"() {
123756
123756
  "use strict";
123757
123757
  import_node_process2 = require("process");
123758
- import_promises18 = require("readline/promises");
123758
+ import_promises20 = require("readline/promises");
123759
123759
  interactivePrompt = async ({ prodPort, suggestedAltPort, prodActive }) => {
123760
- const rl = (0, import_promises18.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
123760
+ const rl = (0, import_promises20.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
123761
123761
  try {
123762
123762
  if (!prodActive) {
123763
123763
  import_node_process2.stdout.write(
@@ -123792,7 +123792,7 @@ The supervised prod streamer is already holding port ${prodPort}.
123792
123792
  }
123793
123793
  };
123794
123794
  interactivePermissionModePrompt = async () => {
123795
- const rl = (0, import_promises18.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
123795
+ const rl = (0, import_promises20.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
123796
123796
  try {
123797
123797
  import_node_process2.stdout.write(
123798
123798
  "\nWhich permission mode should spawned Claude Code sessions use?\n [a] acceptEdits \u2014 auto-approve file edits, still prompt for shell commands (default)\n [m] manual \u2014 prompt for every edit and command\n"
@@ -128528,35 +128528,38 @@ var flexsearch_bundle_module_min_default = { Index: N, Document: U, Worker: S, r
128528
128528
 
128529
128529
  // node_modules/@threadbase-sh/scanner/dist/index.js
128530
128530
  var import_fs5 = require("fs");
128531
- var import_promises4 = require("fs/promises");
128532
- var import_os4 = require("os");
128533
128531
  var import_path3 = require("path");
128534
- var import_fast_glob = __toESM(require_out4(), 1);
128535
- var import_fs6 = require("fs");
128536
- var import_promises5 = require("fs/promises");
128537
- var import_path4 = require("path");
128538
128532
  var import_readline = require("readline");
128539
- var import_fast_glob2 = __toESM(require_out4(), 1);
128540
- var import_promises6 = require("fs/promises");
128533
+ var import_path4 = require("path");
128534
+ var import_fs6 = require("fs");
128535
+ var import_promises4 = require("fs/promises");
128536
+ var import_os4 = require("os");
128541
128537
  var import_path5 = require("path");
128538
+ var import_fast_glob = __toESM(require_out4(), 1);
128542
128539
  var import_fs7 = require("fs");
128540
+ var import_promises5 = require("fs/promises");
128543
128541
  var import_path6 = require("path");
128544
128542
  var import_readline2 = require("readline");
128543
+ var import_fast_glob2 = __toESM(require_out4(), 1);
128544
+ var import_promises6 = require("fs/promises");
128545
128545
  var import_events2 = require("events");
128546
128546
  var import_fs8 = require("fs");
128547
128547
  var import_os5 = require("os");
128548
128548
  var import_path7 = require("path");
128549
- var import_crypto2 = require("crypto");
128549
+ var import_path8 = require("path");
128550
128550
  var import_fs9 = require("fs");
128551
+ var import_promises7 = require("timers/promises");
128551
128552
  var import_fs10 = require("fs");
128552
- var import_readline3 = require("readline");
128553
- var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
128553
+ var import_promises8 = require("timers/promises");
128554
+ var import_crypto2 = require("crypto");
128554
128555
  var import_fs11 = require("fs");
128555
- var import_path8 = require("path");
128556
- var import_promises7 = require("fs/promises");
128557
128556
  var import_fs12 = require("fs");
128557
+ var import_readline3 = require("readline");
128558
+ var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
128558
128559
  var import_fs13 = require("fs");
128559
128560
  var import_path9 = require("path");
128561
+ var import_promises9 = require("fs/promises");
128562
+ var import_path10 = require("path");
128560
128563
 
128561
128564
  // node_modules/@threadbase-sh/scanner/node_modules/chokidar/esm/index.js
128562
128565
  var import_fs3 = require("fs");
@@ -128711,10 +128714,10 @@ var ReaddirpStream = class extends import_node_stream.Readable {
128711
128714
  }
128712
128715
  async _formatEntry(dirent, path2) {
128713
128716
  let entry;
128714
- const basename11 = this._isDirent ? dirent.name : dirent;
128717
+ const basename12 = this._isDirent ? dirent.name : dirent;
128715
128718
  try {
128716
- const fullPath = (0, import_node_path5.resolve)((0, import_node_path5.join)(path2, basename11));
128717
- entry = { path: (0, import_node_path5.relative)(this._root, fullPath), fullPath, basename: basename11 };
128719
+ const fullPath = (0, import_node_path5.resolve)((0, import_node_path5.join)(path2, basename12));
128720
+ entry = { path: (0, import_node_path5.relative)(this._root, fullPath), fullPath, basename: basename12 };
128718
128721
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
128719
128722
  } catch (err) {
128720
128723
  this._onError(err);
@@ -129253,9 +129256,9 @@ var NodeFsHandler = class {
129253
129256
  _watchWithNodeFs(path2, listener) {
129254
129257
  const opts = this.fsw.options;
129255
129258
  const directory = sysPath.dirname(path2);
129256
- const basename11 = sysPath.basename(path2);
129259
+ const basename12 = sysPath.basename(path2);
129257
129260
  const parent = this.fsw._getWatchedDir(directory);
129258
- parent.add(basename11);
129261
+ parent.add(basename12);
129259
129262
  const absolutePath = sysPath.resolve(path2);
129260
129263
  const options = {
129261
129264
  persistent: opts.persistent
@@ -129265,7 +129268,7 @@ var NodeFsHandler = class {
129265
129268
  let closer;
129266
129269
  if (opts.usePolling) {
129267
129270
  const enableBin = opts.interval !== opts.binaryInterval;
129268
- options.interval = enableBin && isBinaryPath(basename11) ? opts.binaryInterval : opts.interval;
129271
+ options.interval = enableBin && isBinaryPath(basename12) ? opts.binaryInterval : opts.interval;
129269
129272
  closer = setFsWatchFileListener(path2, absolutePath, options, {
129270
129273
  listener,
129271
129274
  rawEmitter: this.fsw._emitRaw
@@ -129288,10 +129291,10 @@ var NodeFsHandler = class {
129288
129291
  return;
129289
129292
  }
129290
129293
  const dirname17 = sysPath.dirname(file2);
129291
- const basename11 = sysPath.basename(file2);
129294
+ const basename12 = sysPath.basename(file2);
129292
129295
  const parent = this.fsw._getWatchedDir(dirname17);
129293
129296
  let prevStats = stats;
129294
- if (parent.has(basename11))
129297
+ if (parent.has(basename12))
129295
129298
  return;
129296
129299
  const listener = async (path2, newStats) => {
129297
129300
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
@@ -129316,9 +129319,9 @@ var NodeFsHandler = class {
129316
129319
  prevStats = newStats2;
129317
129320
  }
129318
129321
  } catch (error51) {
129319
- this.fsw._remove(dirname17, basename11);
129322
+ this.fsw._remove(dirname17, basename12);
129320
129323
  }
129321
- } else if (parent.has(basename11)) {
129324
+ } else if (parent.has(basename12)) {
129322
129325
  const at2 = newStats.atimeMs;
129323
129326
  const mt2 = newStats.mtimeMs;
129324
129327
  if (!at2 || at2 <= mt2 || mt2 !== prevStats.mtimeMs) {
@@ -130514,6 +130517,435 @@ var SearchIndexer = class {
130514
130517
  getLogger2().trace("indexer: cleared");
130515
130518
  }
130516
130519
  };
130520
+ var CLAUDE_CODE_PROVIDER = "claude-code";
130521
+ var CODEX_CLI_PROVIDER = "codex-cli";
130522
+ function initialReducerState() {
130523
+ return {
130524
+ sessionId: "",
130525
+ sessionName: "",
130526
+ latestTimestamp: "",
130527
+ cwd: "",
130528
+ teamName: "",
130529
+ model: null,
130530
+ messageCount: 0,
130531
+ lastMessageSender: "user",
130532
+ isTeammate: false,
130533
+ firstUserSeen: false,
130534
+ firstMessage: null,
130535
+ lastMessage: null,
130536
+ lastPrompt: "",
130537
+ pageMessageCount: 0,
130538
+ toolNames: [],
130539
+ previewParts: [],
130540
+ snippetParts: [],
130541
+ previewLength: 0,
130542
+ snippetLength: 0,
130543
+ badJsonLines: 0
130544
+ };
130545
+ }
130546
+ function reduceLine(state, entry, tier) {
130547
+ if (entry.cwd && !state.cwd) state.cwd = entry.cwd;
130548
+ if (entry.sessionId && !state.sessionId) state.sessionId = entry.sessionId;
130549
+ if (entry.slug && !state.sessionName) state.sessionName = entry.slug;
130550
+ if (entry.teamName && !state.teamName) state.teamName = entry.teamName;
130551
+ if (entry.timestamp) {
130552
+ const ts2 = entry.timestamp;
130553
+ if (!state.latestTimestamp || ts2 > state.latestTimestamp) state.latestTimestamp = ts2;
130554
+ }
130555
+ const type = entry.type;
130556
+ if (type === "last-prompt") {
130557
+ if (entry.lastPrompt && !state.lastPrompt) state.lastPrompt = entry.lastPrompt;
130558
+ return;
130559
+ }
130560
+ if (type !== "user" && type !== "assistant") return;
130561
+ if (entry.isMeta) return;
130562
+ const msg = entry.message;
130563
+ if (state.model === null && msg?.model) state.model = msg.model;
130564
+ if (type === "user" && !state.firstUserSeen) {
130565
+ state.firstUserSeen = true;
130566
+ if (isTeammateContent(msg?.content)) state.isTeammate = true;
130567
+ }
130568
+ const content = extractTextContent(msg?.content);
130569
+ const hasToolUseResult = type === "user" && entry.toolUseResult != null;
130570
+ const isOnlyToolResult = hasToolUseResult && isOnlyToolResultContent(msg?.content);
130571
+ const toolSet = new Set(state.toolNames);
130572
+ collectToolNames(msg?.content, toolSet);
130573
+ state.toolNames = Array.from(toolSet);
130574
+ const toolUseBlocks = extractToolUseBlocks(msg?.content);
130575
+ const thinking = type === "assistant" ? extractThinking(msg?.content) : null;
130576
+ const hasThinking = !!(thinking?.content || thinking?.signature);
130577
+ if (content || isOnlyToolResult || toolUseBlocks.length > 0 || hasThinking) {
130578
+ state.pageMessageCount++;
130579
+ }
130580
+ if (content || isOnlyToolResult) {
130581
+ state.messageCount++;
130582
+ state.lastMessageSender = type;
130583
+ if (content) {
130584
+ const ts2 = entry.timestamp || "";
130585
+ if (!state.firstMessage) state.firstMessage = { text: content.slice(0, 200), timestamp: ts2 };
130586
+ state.lastMessage = { text: content.slice(0, 200), timestamp: ts2 };
130587
+ if (state.previewLength < tier.previewMax) {
130588
+ state.previewParts.push(content);
130589
+ state.previewLength += content.length;
130590
+ }
130591
+ if (state.snippetLength < tier.snippetMax) {
130592
+ const remaining = tier.snippetMax - state.snippetLength;
130593
+ const chunk = content.length > remaining ? content.slice(0, remaining) : content;
130594
+ state.snippetParts.push(chunk);
130595
+ state.snippetLength += chunk.length;
130596
+ }
130597
+ }
130598
+ }
130599
+ }
130600
+ function finalizeMeta(state, filePath, account, tier) {
130601
+ if (state.messageCount === 0) return null;
130602
+ const isSubagent = filePath.includes("/subagents/");
130603
+ let parentSessionId = null;
130604
+ if (isSubagent) {
130605
+ const uuidDir = (0, import_path4.dirname)((0, import_path4.dirname)(filePath));
130606
+ parentSessionId = (0, import_path4.join)((0, import_path4.dirname)(uuidDir), `${(0, import_path4.basename)(uuidDir)}.jsonl`);
130607
+ }
130608
+ const projectPath = state.cwd;
130609
+ return {
130610
+ id: filePath,
130611
+ filePath,
130612
+ provider: CLAUDE_CODE_PROVIDER,
130613
+ sessionId: state.sessionId || (0, import_path4.basename)(filePath, ".jsonl"),
130614
+ sessionName: state.sessionName,
130615
+ projectPath,
130616
+ projectName: getShortProjectName(projectPath),
130617
+ account,
130618
+ timestamp: state.latestTimestamp || (/* @__PURE__ */ new Date()).toISOString(),
130619
+ messageCount: state.messageCount,
130620
+ lastMessageSender: state.lastMessageSender,
130621
+ preview: state.previewParts.join(" ").slice(0, tier.previewMax),
130622
+ contentSnippet: state.snippetParts.join(" "),
130623
+ gitBranch: null,
130624
+ model: state.model,
130625
+ isSubagent,
130626
+ parentSessionId,
130627
+ isTeammate: state.isTeammate,
130628
+ teamName: state.teamName || null,
130629
+ toolNames: state.toolNames,
130630
+ firstMessage: state.firstMessage,
130631
+ lastMessage: state.lastMessage,
130632
+ lastPrompt: state.lastPrompt || void 0
130633
+ };
130634
+ }
130635
+ function getShortProjectName(fullPath) {
130636
+ const parts = fullPath.split("/").filter(Boolean);
130637
+ return parts.slice(-3).join("/");
130638
+ }
130639
+ var SYSTEM_TAGS = [
130640
+ "system-reminder",
130641
+ "command-name",
130642
+ "command-message",
130643
+ "command-args",
130644
+ "ide_selection",
130645
+ "ide_opened_file",
130646
+ "local-command-stdout",
130647
+ "local-command-caveat",
130648
+ "retrieval_status",
130649
+ "task_id",
130650
+ "task_type",
130651
+ "task-id",
130652
+ "task-notification",
130653
+ "fast_mode_info",
130654
+ "persisted-output",
130655
+ "tool_use_error",
130656
+ "user-prompt-submit-hook",
130657
+ "thinking",
130658
+ "ask_user",
130659
+ "teammate-message"
130660
+ ];
130661
+ var SYSTEM_TAG_RE = new RegExp(`<(${SYSTEM_TAGS.join("|")})[^>]*>[\\s\\S]*?<\\/\\1>`, "g");
130662
+ function cleanSystemTags(text) {
130663
+ return text.replace(SYSTEM_TAG_RE, "").replace(/[^\S\n]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
130664
+ }
130665
+ async function parseMeta(filePath, account, tier) {
130666
+ const log8 = getLogger2();
130667
+ log8.trace({ filePath, account, tier: tier.name }, "parseMeta: start");
130668
+ const state = initialReducerState();
130669
+ const fileStream = (0, import_fs5.createReadStream)(filePath);
130670
+ const rl = (0, import_readline.createInterface)({ input: fileStream, crlfDelay: Infinity });
130671
+ try {
130672
+ for await (const line of rl) {
130673
+ if (!line.trim()) continue;
130674
+ let entry;
130675
+ try {
130676
+ entry = JSON.parse(line);
130677
+ } catch {
130678
+ state.badJsonLines++;
130679
+ continue;
130680
+ }
130681
+ reduceLine(state, entry, tier);
130682
+ }
130683
+ } catch (err) {
130684
+ log8.warn({ filePath, err }, "parseMeta: read failed");
130685
+ return null;
130686
+ }
130687
+ if (state.badJsonLines > 0) {
130688
+ log8.warn(
130689
+ { filePath, badJsonLines: state.badJsonLines },
130690
+ "parseMeta: skipped malformed JSON lines"
130691
+ );
130692
+ }
130693
+ const meta3 = finalizeMeta(state, filePath, account, tier);
130694
+ if (!meta3) log8.trace({ filePath }, "parseMeta: no messages");
130695
+ return meta3;
130696
+ }
130697
+ async function parseConversation(filePath, account) {
130698
+ const log8 = getLogger2();
130699
+ log8.trace({ filePath, account }, "parseConversation: start");
130700
+ const messages = [];
130701
+ let badJsonLines = 0;
130702
+ const textParts = [];
130703
+ const turnDurations = [];
130704
+ const state = initialConvState();
130705
+ const fileStream = (0, import_fs5.createReadStream)(filePath);
130706
+ const rl = (0, import_readline.createInterface)({ input: fileStream, crlfDelay: Infinity });
130707
+ try {
130708
+ for await (const line of rl) {
130709
+ if (!line.trim()) continue;
130710
+ let entry;
130711
+ try {
130712
+ entry = JSON.parse(line);
130713
+ } catch {
130714
+ badJsonLines++;
130715
+ continue;
130716
+ }
130717
+ if (entry.type === "system" && entry.subtype === "turn_duration" && typeof entry.durationMs === "number") {
130718
+ turnDurations.push({
130719
+ durationMs: entry.durationMs,
130720
+ messageCount: entry.messageCount || 0,
130721
+ uuid: entry.uuid
130722
+ });
130723
+ continue;
130724
+ }
130725
+ const message = reduceConvLine(state, entry);
130726
+ if (message) {
130727
+ messages.push(message);
130728
+ if (message.text) textParts.push(message.text);
130729
+ }
130730
+ }
130731
+ } catch (err) {
130732
+ log8.warn({ filePath, err }, "parseConversation: read failed");
130733
+ return null;
130734
+ }
130735
+ if (badJsonLines > 0) {
130736
+ log8.warn({ filePath, badJsonLines }, "parseConversation: skipped malformed JSON lines");
130737
+ }
130738
+ if (messages.length === 0) {
130739
+ log8.trace({ filePath }, "parseConversation: no messages");
130740
+ return null;
130741
+ }
130742
+ log8.debug({ filePath, messageCount: messages.length }, "parseConversation: complete");
130743
+ applyTeamInfo(messages, state);
130744
+ return {
130745
+ id: filePath,
130746
+ filePath,
130747
+ projectPath: state.cwd,
130748
+ projectName: getShortProjectName2(state.cwd),
130749
+ sessionId: state.sessionId || (0, import_path3.basename)(filePath, ".jsonl"),
130750
+ sessionName: state.sessionName,
130751
+ messages,
130752
+ fullText: textParts.join(" "),
130753
+ timestamp: state.latestTimestamp || (/* @__PURE__ */ new Date()).toISOString(),
130754
+ messageCount: messages.length,
130755
+ account,
130756
+ turnDurations: turnDurations.length > 0 ? turnDurations : void 0,
130757
+ lastPrompt: state.lastPrompt || void 0
130758
+ };
130759
+ }
130760
+ function extractTextContent(content) {
130761
+ if (!content) return "";
130762
+ if (typeof content === "string") return cleanSystemTags(content);
130763
+ if (Array.isArray(content)) {
130764
+ return content.map((item) => {
130765
+ if (typeof item === "string") return item;
130766
+ if (item?.type === "text" && item?.text) return item.text;
130767
+ if (item?.type === "tool_result" && typeof item?.content === "string") return item.content;
130768
+ return "";
130769
+ }).filter(Boolean).map(cleanSystemTags).join(" ");
130770
+ }
130771
+ return "";
130772
+ }
130773
+ function extractToolUseNames(content) {
130774
+ if (!Array.isArray(content)) return [];
130775
+ return content.filter((item) => item?.type === "tool_use" && item?.name).map((item) => item.name);
130776
+ }
130777
+ function extractToolUseBlocks(content) {
130778
+ if (!Array.isArray(content)) return [];
130779
+ return content.filter((item) => item?.type === "tool_use" && item?.name && item?.id).map((item) => ({
130780
+ id: item.id,
130781
+ name: item.name,
130782
+ input: item.input || {}
130783
+ }));
130784
+ }
130785
+ var TOOL_NAME_TO_TYPE = {
130786
+ Edit: "edit",
130787
+ Write: "write",
130788
+ Read: "read",
130789
+ Bash: "bash",
130790
+ Grep: "grep",
130791
+ Glob: "glob",
130792
+ Agent: "taskAgent",
130793
+ TaskCreate: "taskCreate",
130794
+ TaskUpdate: "taskUpdate"
130795
+ };
130796
+ function extractToolResultBlocks(content, pendingToolUses) {
130797
+ if (!Array.isArray(content)) return [];
130798
+ return content.filter((item) => item?.type === "tool_result" && item?.tool_use_id).map((item) => {
130799
+ const toolName = pendingToolUses.get(item.tool_use_id)?.name ?? "";
130800
+ return {
130801
+ toolUseId: item.tool_use_id,
130802
+ type: TOOL_NAME_TO_TYPE[toolName] ?? "generic",
130803
+ content: typeof item.content === "string" ? { text: item.content } : item.content ?? {},
130804
+ isError: typeof item.is_error === "boolean" ? item.is_error : void 0
130805
+ };
130806
+ });
130807
+ }
130808
+ function collectToolNames(content, toolSet) {
130809
+ if (!Array.isArray(content)) return;
130810
+ for (const item of content) {
130811
+ if (item?.type === "tool_use" && item?.name) {
130812
+ toolSet.add(item.name);
130813
+ }
130814
+ }
130815
+ }
130816
+ function isOnlyToolResultContent(content) {
130817
+ if (!Array.isArray(content)) return false;
130818
+ return content.length > 0 && content.every((item) => item?.type === "tool_result");
130819
+ }
130820
+ function isTeammateContent(content) {
130821
+ const raw2 = typeof content === "string" ? content : Array.isArray(content) ? content.map(
130822
+ (item) => typeof item === "string" ? item : item?.type === "text" ? item.text ?? "" : ""
130823
+ ).join("") : "";
130824
+ return raw2.includes("<teammate-message");
130825
+ }
130826
+ function extractThinking(content) {
130827
+ if (!Array.isArray(content)) return { content: "", signature: "" };
130828
+ const blocks = content.filter((item) => item?.type === "thinking");
130829
+ return {
130830
+ content: blocks.map((b2) => b2.thinking).filter(Boolean).join("\n\n"),
130831
+ signature: blocks.map((b2) => b2.signature).filter(Boolean).join("")
130832
+ };
130833
+ }
130834
+ function hasImageBlocks(content) {
130835
+ if (!Array.isArray(content)) return false;
130836
+ return content.some(
130837
+ (item) => item?.type === "image" && (item?.source?.type === "base64" || item?.file?.base64 !== void 0)
130838
+ );
130839
+ }
130840
+ function parseTeammateMessageTag(content) {
130841
+ const match2 = content.match(/<teammate-message\s+([^>]*)>/);
130842
+ if (!match2) return null;
130843
+ const attrs = match2[1];
130844
+ const id = attrs.match(/teammate_id="([^"]*)"/)?.[1];
130845
+ if (!id) return null;
130846
+ const summary = attrs.match(/summary="([^"]*)"/)?.[1];
130847
+ const color = attrs.match(/color="([^"]*)"/)?.[1];
130848
+ return { teammateId: id, summary, color };
130849
+ }
130850
+ function getShortProjectName2(fullPath) {
130851
+ const parts = fullPath.split("/").filter(Boolean);
130852
+ return parts.slice(-3).join("/");
130853
+ }
130854
+ function initialConvState() {
130855
+ return {
130856
+ cwd: "",
130857
+ sessionId: "",
130858
+ sessionName: "",
130859
+ latestTimestamp: "",
130860
+ lastPrompt: "",
130861
+ pendingToolUses: {},
130862
+ teamInfo: {}
130863
+ };
130864
+ }
130865
+ function reduceConvLine(state, entry) {
130866
+ if (entry.cwd && !state.cwd) state.cwd = entry.cwd;
130867
+ if (entry.sessionId && !state.sessionId) state.sessionId = entry.sessionId;
130868
+ if (entry.slug && !state.sessionName) state.sessionName = entry.slug;
130869
+ if (entry.timestamp) {
130870
+ const ts2 = entry.timestamp;
130871
+ if (!state.latestTimestamp || ts2 > state.latestTimestamp) state.latestTimestamp = ts2;
130872
+ }
130873
+ const type = entry.type;
130874
+ if (type === "last-prompt") {
130875
+ if (entry.lastPrompt && !state.lastPrompt) state.lastPrompt = entry.lastPrompt;
130876
+ return null;
130877
+ }
130878
+ if (type !== "user" && type !== "assistant") return null;
130879
+ if (entry.isMeta) return null;
130880
+ const msg = entry.message;
130881
+ const toolUseBlocks = extractToolUseBlocks(msg?.content);
130882
+ for (const block of toolUseBlocks) state.pendingToolUses[block.id] = block;
130883
+ const hasToolUseResult = type === "user" && entry.toolUseResult != null;
130884
+ const isToolResultOnly = hasToolUseResult && isOnlyToolResultContent(msg?.content);
130885
+ const content = extractTextContent(msg?.content);
130886
+ const thinking = type === "assistant" ? extractThinking(msg?.content) : null;
130887
+ const hasThinking = !!(thinking?.content || thinking?.signature);
130888
+ if (!(content || isToolResultOnly || toolUseBlocks.length > 0 || hasThinking)) return null;
130889
+ const metadata = {};
130890
+ if (msg?.model) metadata.model = msg.model;
130891
+ if (msg?.stop_reason !== void 0) metadata.stopReason = msg.stop_reason;
130892
+ if (entry.gitBranch) metadata.gitBranch = entry.gitBranch;
130893
+ if (entry.version) metadata.version = entry.version;
130894
+ const usage = msg?.usage;
130895
+ if (usage) {
130896
+ if (usage.input_tokens) metadata.inputTokens = usage.input_tokens;
130897
+ if (usage.output_tokens) metadata.outputTokens = usage.output_tokens;
130898
+ if (usage.cache_read_input_tokens) metadata.cacheReadTokens = usage.cache_read_input_tokens;
130899
+ if (usage.cache_creation_input_tokens)
130900
+ metadata.cacheCreationTokens = usage.cache_creation_input_tokens;
130901
+ }
130902
+ const toolUseNames = extractToolUseNames(msg?.content);
130903
+ if (toolUseNames.length > 0) metadata.toolUses = toolUseNames;
130904
+ if (toolUseBlocks.length > 0) metadata.toolUseBlocks = toolUseBlocks;
130905
+ if (isToolResultOnly) {
130906
+ const pending = new Map(Object.entries(state.pendingToolUses));
130907
+ const toolResultBlocks = extractToolResultBlocks(msg?.content, pending);
130908
+ if (toolResultBlocks.length > 0) {
130909
+ metadata.toolResults = toolResultBlocks;
130910
+ for (const block of toolResultBlocks) delete state.pendingToolUses[block.toolUseId];
130911
+ }
130912
+ }
130913
+ if (entry.teamName) {
130914
+ metadata.teamName = entry.teamName;
130915
+ if (!state.teamInfo[metadata.teamName] && content) {
130916
+ const info = parseTeammateMessageTag(content);
130917
+ if (info) state.teamInfo[metadata.teamName] = info;
130918
+ }
130919
+ }
130920
+ const thinkingContent = thinking?.content || void 0;
130921
+ const thinkingSignature = thinking?.signature || void 0;
130922
+ const hasMetadata = Object.keys(metadata).length > 0;
130923
+ return {
130924
+ role: type,
130925
+ text: content || "",
130926
+ timestamp: entry.timestamp || "",
130927
+ uuid: entry.uuid || void 0,
130928
+ metadata: hasMetadata ? metadata : void 0,
130929
+ isToolResult: isToolResultOnly || void 0,
130930
+ isThinking: thinkingContent || thinkingSignature ? true : void 0,
130931
+ thinkingContent,
130932
+ thinkingSignature,
130933
+ parentUuid: entry.parentUuid !== void 0 ? entry.parentUuid : void 0,
130934
+ requestId: type === "assistant" ? entry.requestId : void 0,
130935
+ promptId: type === "user" ? entry.promptId : void 0,
130936
+ isSidechain: typeof entry.isSidechain === "boolean" ? entry.isSidechain : void 0,
130937
+ permissionMode: type === "user" ? entry.permissionMode : void 0,
130938
+ hasImages: hasImageBlocks(msg?.content) || void 0,
130939
+ attachment: entry.attachment !== void 0 ? entry.attachment : void 0
130940
+ };
130941
+ }
130942
+ function applyTeamInfo(messages, state) {
130943
+ if (Object.keys(state.teamInfo).length === 0) return;
130944
+ for (const m2 of messages) {
130945
+ const name = m2.metadata?.teamName;
130946
+ if (name && state.teamInfo[name] && m2.metadata) m2.metadata.teamInfo = state.teamInfo[name];
130947
+ }
130948
+ }
130517
130949
  var SIDECAR_VERSION = 1;
130518
130950
  function sidecarPath(jsonlPath) {
130519
130951
  return `${jsonlPath}.idx.json`;
@@ -130539,7 +130971,7 @@ function buildSidecar(meta3, cursor, updatedAt) {
130539
130971
  }
130540
130972
  function writeSidecar(jsonlPath, sidecar) {
130541
130973
  try {
130542
- (0, import_fs5.writeFileSync)(sidecarPath(jsonlPath), JSON.stringify(sidecar, null, 2));
130974
+ (0, import_fs6.writeFileSync)(sidecarPath(jsonlPath), JSON.stringify(sidecar, null, 2));
130543
130975
  } catch (err) {
130544
130976
  getLogger2().warn({ jsonlPath, err }, "sidecar: write failed");
130545
130977
  }
@@ -130549,13 +130981,13 @@ function resolveConfigDir(configDir2) {
130549
130981
  return configDir2.replace(/^~/, (0, import_os4.homedir)());
130550
130982
  }
130551
130983
  function getProjectsDir(profile) {
130552
- return (0, import_path3.join)(resolveConfigDir(profile.configDir), "projects");
130984
+ return (0, import_path5.join)(resolveConfigDir(profile.configDir), "projects");
130553
130985
  }
130554
130986
  async function detectDefaultProfile() {
130555
130987
  return {
130556
130988
  id: "default",
130557
130989
  label: "Default",
130558
- configDir: (0, import_path3.join)((0, import_os4.homedir)(), ".claude"),
130990
+ configDir: (0, import_path5.join)((0, import_os4.homedir)(), ".claude"),
130559
130991
  enabled: true,
130560
130992
  emoji: "\u{1F916}"
130561
130993
  };
@@ -130564,7 +130996,7 @@ async function loadProfiles(configPath) {
130564
130996
  const log8 = getLogger2();
130565
130997
  try {
130566
130998
  const resolved = resolveConfigDir(configPath);
130567
- const data = await (0, import_promises4.readFile)((0, import_path3.join)(resolved, PROFILES_FILE), "utf-8");
130999
+ const data = await (0, import_promises4.readFile)((0, import_path5.join)(resolved, PROFILES_FILE), "utf-8");
130568
131000
  const profiles = JSON.parse(data);
130569
131001
  log8.debug({ configPath, count: profiles.length }, "profiles: loaded");
130570
131002
  return profiles;
@@ -130574,34 +131006,6 @@ async function loadProfiles(configPath) {
130574
131006
  return [defaultProfile];
130575
131007
  }
130576
131008
  }
130577
- var SYSTEM_TAGS = [
130578
- "system-reminder",
130579
- "command-name",
130580
- "command-message",
130581
- "command-args",
130582
- "ide_selection",
130583
- "ide_opened_file",
130584
- "local-command-stdout",
130585
- "local-command-caveat",
130586
- "retrieval_status",
130587
- "task_id",
130588
- "task_type",
130589
- "task-id",
130590
- "task-notification",
130591
- "fast_mode_info",
130592
- "persisted-output",
130593
- "tool_use_error",
130594
- "user-prompt-submit-hook",
130595
- "thinking",
130596
- "ask_user",
130597
- "teammate-message"
130598
- ];
130599
- var SYSTEM_TAG_RE = new RegExp(`<(${SYSTEM_TAGS.join("|")})[^>]*>[\\s\\S]*?<\\/\\1>`, "g");
130600
- function cleanSystemTags(text) {
130601
- return text.replace(SYSTEM_TAG_RE, "").replace(/[^\S\n]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
130602
- }
130603
- var CLAUDE_CODE_PROVIDER = "claude-code";
130604
- var CODEX_CLI_PROVIDER = "codex-cli";
130605
131009
  var CodexCliProvider = class {
130606
131010
  name = CODEX_CLI_PROVIDER;
130607
131011
  async discover(roots) {
@@ -130734,7 +131138,7 @@ function reduceCodexEntry(acc, entry, tier) {
130734
131138
  }
130735
131139
  function finalizeCodexMeta(acc, filePath, account, tier) {
130736
131140
  if (acc.messageCount === 0) return null;
130737
- const sessionId = acc.sessionId || (0, import_path4.basename)(filePath, ".jsonl");
131141
+ const sessionId = acc.sessionId || (0, import_path6.basename)(filePath, ".jsonl");
130738
131142
  const projectPath = acc.cwd;
130739
131143
  const kind = acc.lastAssistant === null && acc.toolNames.length > 0 ? "task" : "conversation";
130740
131144
  return {
@@ -130746,7 +131150,7 @@ function finalizeCodexMeta(acc, filePath, account, tier) {
130746
131150
  sessionId,
130747
131151
  sessionName: "",
130748
131152
  projectPath,
130749
- projectName: getShortProjectName(projectPath),
131153
+ projectName: getShortProjectName3(projectPath),
130750
131154
  account,
130751
131155
  timestamp: acc.latestTimestamp || (/* @__PURE__ */ new Date()).toISOString(),
130752
131156
  messageCount: acc.messageCount,
@@ -130765,7 +131169,7 @@ function finalizeCodexMeta(acc, filePath, account, tier) {
130765
131169
  lastPrompt: acc.lastUser?.text || void 0
130766
131170
  };
130767
131171
  }
130768
- function getShortProjectName(fullPath) {
131172
+ function getShortProjectName3(fullPath) {
130769
131173
  return fullPath.split("/").filter(Boolean).slice(-3).join("/");
130770
131174
  }
130771
131175
  async function parseCodexConversation(filePath, account) {
@@ -130776,7 +131180,7 @@ async function parseCodexConversation(filePath, account) {
130776
131180
  let cwd = "";
130777
131181
  let latestTimestamp = "";
130778
131182
  let lastUserText = "";
130779
- const rl = (0, import_readline.createInterface)({ input: (0, import_fs6.createReadStream)(filePath), crlfDelay: Infinity });
131183
+ const rl = (0, import_readline2.createInterface)({ input: (0, import_fs7.createReadStream)(filePath), crlfDelay: Infinity });
130780
131184
  try {
130781
131185
  for await (const line of rl) {
130782
131186
  if (!line.trim()) continue;
@@ -130813,8 +131217,8 @@ async function parseCodexConversation(filePath, account) {
130813
131217
  id: filePath,
130814
131218
  filePath,
130815
131219
  projectPath: cwd,
130816
- projectName: getShortProjectName(cwd),
130817
- sessionId: sessionId || (0, import_path4.basename)(filePath, ".jsonl"),
131220
+ projectName: getShortProjectName3(cwd),
131221
+ sessionId: sessionId || (0, import_path6.basename)(filePath, ".jsonl"),
130818
131222
  sessionName: "",
130819
131223
  messages,
130820
131224
  fullText: textParts.join(" "),
@@ -130886,404 +131290,6 @@ async function discoverJsonlFiles(dirs, onProgress) {
130886
131290
  log8.debug({ totalFiles: results.length, dirs: dirs.length }, "discovery: complete");
130887
131291
  return results;
130888
131292
  }
130889
- function initialConvState() {
130890
- return {
130891
- cwd: "",
130892
- sessionId: "",
130893
- sessionName: "",
130894
- latestTimestamp: "",
130895
- lastPrompt: "",
130896
- pendingToolUses: {},
130897
- teamInfo: {}
130898
- };
130899
- }
130900
- function reduceConvLine(state, entry) {
130901
- if (entry.cwd && !state.cwd) state.cwd = entry.cwd;
130902
- if (entry.sessionId && !state.sessionId) state.sessionId = entry.sessionId;
130903
- if (entry.slug && !state.sessionName) state.sessionName = entry.slug;
130904
- if (entry.timestamp) {
130905
- const ts2 = entry.timestamp;
130906
- if (!state.latestTimestamp || ts2 > state.latestTimestamp) state.latestTimestamp = ts2;
130907
- }
130908
- const type = entry.type;
130909
- if (type === "last-prompt") {
130910
- if (entry.lastPrompt && !state.lastPrompt) state.lastPrompt = entry.lastPrompt;
130911
- return null;
130912
- }
130913
- if (type !== "user" && type !== "assistant") return null;
130914
- if (entry.isMeta) return null;
130915
- const msg = entry.message;
130916
- const toolUseBlocks = extractToolUseBlocks(msg?.content);
130917
- for (const block of toolUseBlocks) state.pendingToolUses[block.id] = block;
130918
- const hasToolUseResult = type === "user" && entry.toolUseResult != null;
130919
- const isToolResultOnly = hasToolUseResult && isOnlyToolResultContent(msg?.content);
130920
- const content = extractTextContent(msg?.content);
130921
- const thinking = type === "assistant" ? extractThinking(msg?.content) : null;
130922
- const hasThinking = !!(thinking?.content || thinking?.signature);
130923
- if (!(content || isToolResultOnly || toolUseBlocks.length > 0 || hasThinking)) return null;
130924
- const metadata = {};
130925
- if (msg?.model) metadata.model = msg.model;
130926
- if (msg?.stop_reason !== void 0) metadata.stopReason = msg.stop_reason;
130927
- if (entry.gitBranch) metadata.gitBranch = entry.gitBranch;
130928
- if (entry.version) metadata.version = entry.version;
130929
- const usage = msg?.usage;
130930
- if (usage) {
130931
- if (usage.input_tokens) metadata.inputTokens = usage.input_tokens;
130932
- if (usage.output_tokens) metadata.outputTokens = usage.output_tokens;
130933
- if (usage.cache_read_input_tokens) metadata.cacheReadTokens = usage.cache_read_input_tokens;
130934
- if (usage.cache_creation_input_tokens)
130935
- metadata.cacheCreationTokens = usage.cache_creation_input_tokens;
130936
- }
130937
- const toolUseNames = extractToolUseNames(msg?.content);
130938
- if (toolUseNames.length > 0) metadata.toolUses = toolUseNames;
130939
- if (toolUseBlocks.length > 0) metadata.toolUseBlocks = toolUseBlocks;
130940
- if (isToolResultOnly) {
130941
- const pending = new Map(Object.entries(state.pendingToolUses));
130942
- const toolResultBlocks = extractToolResultBlocks(msg?.content, pending);
130943
- if (toolResultBlocks.length > 0) metadata.toolResults = toolResultBlocks;
130944
- }
130945
- if (entry.teamName) {
130946
- metadata.teamName = entry.teamName;
130947
- if (!state.teamInfo[metadata.teamName] && content) {
130948
- const info = parseTeammateMessageTag(content);
130949
- if (info) state.teamInfo[metadata.teamName] = info;
130950
- }
130951
- }
130952
- const thinkingContent = thinking?.content || void 0;
130953
- const thinkingSignature = thinking?.signature || void 0;
130954
- const hasMetadata = Object.keys(metadata).length > 0;
130955
- return {
130956
- role: type,
130957
- text: content || "",
130958
- timestamp: entry.timestamp || "",
130959
- uuid: entry.uuid || void 0,
130960
- metadata: hasMetadata ? metadata : void 0,
130961
- isToolResult: isToolResultOnly || void 0,
130962
- isThinking: thinkingContent || thinkingSignature ? true : void 0,
130963
- thinkingContent,
130964
- thinkingSignature,
130965
- parentUuid: entry.parentUuid !== void 0 ? entry.parentUuid : void 0,
130966
- requestId: type === "assistant" ? entry.requestId : void 0,
130967
- promptId: type === "user" ? entry.promptId : void 0,
130968
- isSidechain: typeof entry.isSidechain === "boolean" ? entry.isSidechain : void 0,
130969
- permissionMode: type === "user" ? entry.permissionMode : void 0,
130970
- hasImages: hasImageBlocks(msg?.content) || void 0,
130971
- attachment: entry.attachment !== void 0 ? entry.attachment : void 0
130972
- };
130973
- }
130974
- function applyTeamInfo(messages, state) {
130975
- if (Object.keys(state.teamInfo).length === 0) return;
130976
- for (const m2 of messages) {
130977
- const name = m2.metadata?.teamName;
130978
- if (name && state.teamInfo[name] && m2.metadata) m2.metadata.teamInfo = state.teamInfo[name];
130979
- }
130980
- }
130981
- async function parseMeta(filePath, account, tier) {
130982
- const log8 = getLogger2();
130983
- log8.trace({ filePath, account, tier: tier.name }, "parseMeta: start");
130984
- const state = initialReducerState();
130985
- const fileStream = (0, import_fs7.createReadStream)(filePath);
130986
- const rl = (0, import_readline2.createInterface)({ input: fileStream, crlfDelay: Infinity });
130987
- try {
130988
- for await (const line of rl) {
130989
- if (!line.trim()) continue;
130990
- let entry;
130991
- try {
130992
- entry = JSON.parse(line);
130993
- } catch {
130994
- state.badJsonLines++;
130995
- continue;
130996
- }
130997
- reduceLine(state, entry, tier);
130998
- }
130999
- } catch (err) {
131000
- log8.warn({ filePath, err }, "parseMeta: read failed");
131001
- return null;
131002
- }
131003
- if (state.badJsonLines > 0) {
131004
- log8.warn(
131005
- { filePath, badJsonLines: state.badJsonLines },
131006
- "parseMeta: skipped malformed JSON lines"
131007
- );
131008
- }
131009
- const meta3 = finalizeMeta(state, filePath, account, tier);
131010
- if (!meta3) log8.trace({ filePath }, "parseMeta: no messages");
131011
- return meta3;
131012
- }
131013
- async function parseConversation(filePath, account) {
131014
- const log8 = getLogger2();
131015
- log8.trace({ filePath, account }, "parseConversation: start");
131016
- const messages = [];
131017
- let badJsonLines = 0;
131018
- const textParts = [];
131019
- const turnDurations = [];
131020
- const state = initialConvState();
131021
- const fileStream = (0, import_fs7.createReadStream)(filePath);
131022
- const rl = (0, import_readline2.createInterface)({ input: fileStream, crlfDelay: Infinity });
131023
- try {
131024
- for await (const line of rl) {
131025
- if (!line.trim()) continue;
131026
- let entry;
131027
- try {
131028
- entry = JSON.parse(line);
131029
- } catch {
131030
- badJsonLines++;
131031
- continue;
131032
- }
131033
- if (entry.type === "system" && entry.subtype === "turn_duration" && typeof entry.durationMs === "number") {
131034
- turnDurations.push({
131035
- durationMs: entry.durationMs,
131036
- messageCount: entry.messageCount || 0,
131037
- uuid: entry.uuid
131038
- });
131039
- continue;
131040
- }
131041
- const message = reduceConvLine(state, entry);
131042
- if (message) {
131043
- messages.push(message);
131044
- if (message.text) textParts.push(message.text);
131045
- }
131046
- }
131047
- } catch (err) {
131048
- log8.warn({ filePath, err }, "parseConversation: read failed");
131049
- return null;
131050
- }
131051
- if (badJsonLines > 0) {
131052
- log8.warn({ filePath, badJsonLines }, "parseConversation: skipped malformed JSON lines");
131053
- }
131054
- if (messages.length === 0) {
131055
- log8.trace({ filePath }, "parseConversation: no messages");
131056
- return null;
131057
- }
131058
- log8.debug({ filePath, messageCount: messages.length }, "parseConversation: complete");
131059
- applyTeamInfo(messages, state);
131060
- return {
131061
- id: filePath,
131062
- filePath,
131063
- projectPath: state.cwd,
131064
- projectName: getShortProjectName2(state.cwd),
131065
- sessionId: state.sessionId || (0, import_path6.basename)(filePath, ".jsonl"),
131066
- sessionName: state.sessionName,
131067
- messages,
131068
- fullText: textParts.join(" "),
131069
- timestamp: state.latestTimestamp || (/* @__PURE__ */ new Date()).toISOString(),
131070
- messageCount: messages.length,
131071
- account,
131072
- turnDurations: turnDurations.length > 0 ? turnDurations : void 0,
131073
- lastPrompt: state.lastPrompt || void 0
131074
- };
131075
- }
131076
- function extractTextContent(content) {
131077
- if (!content) return "";
131078
- if (typeof content === "string") return cleanSystemTags(content);
131079
- if (Array.isArray(content)) {
131080
- return content.map((item) => {
131081
- if (typeof item === "string") return item;
131082
- if (item?.type === "text" && item?.text) return item.text;
131083
- if (item?.type === "tool_result" && typeof item?.content === "string") return item.content;
131084
- return "";
131085
- }).filter(Boolean).map(cleanSystemTags).join(" ");
131086
- }
131087
- return "";
131088
- }
131089
- function extractToolUseNames(content) {
131090
- if (!Array.isArray(content)) return [];
131091
- return content.filter((item) => item?.type === "tool_use" && item?.name).map((item) => item.name);
131092
- }
131093
- function extractToolUseBlocks(content) {
131094
- if (!Array.isArray(content)) return [];
131095
- return content.filter((item) => item?.type === "tool_use" && item?.name && item?.id).map((item) => ({
131096
- id: item.id,
131097
- name: item.name,
131098
- input: item.input || {}
131099
- }));
131100
- }
131101
- var TOOL_NAME_TO_TYPE = {
131102
- Edit: "edit",
131103
- Write: "write",
131104
- Read: "read",
131105
- Bash: "bash",
131106
- Grep: "grep",
131107
- Glob: "glob",
131108
- Agent: "taskAgent",
131109
- TaskCreate: "taskCreate",
131110
- TaskUpdate: "taskUpdate"
131111
- };
131112
- function extractToolResultBlocks(content, pendingToolUses) {
131113
- if (!Array.isArray(content)) return [];
131114
- return content.filter((item) => item?.type === "tool_result" && item?.tool_use_id).map((item) => {
131115
- const toolName = pendingToolUses.get(item.tool_use_id)?.name ?? "";
131116
- return {
131117
- toolUseId: item.tool_use_id,
131118
- type: TOOL_NAME_TO_TYPE[toolName] ?? "generic",
131119
- content: typeof item.content === "string" ? { text: item.content } : item.content ?? {},
131120
- isError: typeof item.is_error === "boolean" ? item.is_error : void 0
131121
- };
131122
- });
131123
- }
131124
- function collectToolNames(content, toolSet) {
131125
- if (!Array.isArray(content)) return;
131126
- for (const item of content) {
131127
- if (item?.type === "tool_use" && item?.name) {
131128
- toolSet.add(item.name);
131129
- }
131130
- }
131131
- }
131132
- function isOnlyToolResultContent(content) {
131133
- if (!Array.isArray(content)) return false;
131134
- return content.length > 0 && content.every((item) => item?.type === "tool_result");
131135
- }
131136
- function isTeammateContent(content) {
131137
- const raw2 = typeof content === "string" ? content : Array.isArray(content) ? content.map(
131138
- (item) => typeof item === "string" ? item : item?.type === "text" ? item.text ?? "" : ""
131139
- ).join("") : "";
131140
- return raw2.includes("<teammate-message");
131141
- }
131142
- function extractThinking(content) {
131143
- if (!Array.isArray(content)) return { content: "", signature: "" };
131144
- const blocks = content.filter((item) => item?.type === "thinking");
131145
- return {
131146
- content: blocks.map((b2) => b2.thinking).filter(Boolean).join("\n\n"),
131147
- signature: blocks.map((b2) => b2.signature).filter(Boolean).join("")
131148
- };
131149
- }
131150
- function hasImageBlocks(content) {
131151
- if (!Array.isArray(content)) return false;
131152
- return content.some(
131153
- (item) => item?.type === "image" && (item?.source?.type === "base64" || item?.file?.base64 !== void 0)
131154
- );
131155
- }
131156
- function parseTeammateMessageTag(content) {
131157
- const match2 = content.match(/<teammate-message\s+([^>]*)>/);
131158
- if (!match2) return null;
131159
- const attrs = match2[1];
131160
- const id = attrs.match(/teammate_id="([^"]*)"/)?.[1];
131161
- if (!id) return null;
131162
- const summary = attrs.match(/summary="([^"]*)"/)?.[1];
131163
- const color = attrs.match(/color="([^"]*)"/)?.[1];
131164
- return { teammateId: id, summary, color };
131165
- }
131166
- function getShortProjectName2(fullPath) {
131167
- const parts = fullPath.split("/").filter(Boolean);
131168
- return parts.slice(-3).join("/");
131169
- }
131170
- function initialReducerState() {
131171
- return {
131172
- sessionId: "",
131173
- sessionName: "",
131174
- latestTimestamp: "",
131175
- cwd: "",
131176
- teamName: "",
131177
- model: null,
131178
- messageCount: 0,
131179
- lastMessageSender: "user",
131180
- isTeammate: false,
131181
- firstUserSeen: false,
131182
- firstMessage: null,
131183
- lastMessage: null,
131184
- lastPrompt: "",
131185
- pageMessageCount: 0,
131186
- toolNames: [],
131187
- previewParts: [],
131188
- snippetParts: [],
131189
- previewLength: 0,
131190
- snippetLength: 0,
131191
- badJsonLines: 0
131192
- };
131193
- }
131194
- function reduceLine(state, entry, tier) {
131195
- if (entry.cwd && !state.cwd) state.cwd = entry.cwd;
131196
- if (entry.sessionId && !state.sessionId) state.sessionId = entry.sessionId;
131197
- if (entry.slug && !state.sessionName) state.sessionName = entry.slug;
131198
- if (entry.teamName && !state.teamName) state.teamName = entry.teamName;
131199
- if (entry.timestamp) {
131200
- const ts2 = entry.timestamp;
131201
- if (!state.latestTimestamp || ts2 > state.latestTimestamp) state.latestTimestamp = ts2;
131202
- }
131203
- const type = entry.type;
131204
- if (type === "last-prompt") {
131205
- if (entry.lastPrompt && !state.lastPrompt) state.lastPrompt = entry.lastPrompt;
131206
- return;
131207
- }
131208
- if (type !== "user" && type !== "assistant") return;
131209
- if (entry.isMeta) return;
131210
- const msg = entry.message;
131211
- if (state.model === null && msg?.model) state.model = msg.model;
131212
- if (type === "user" && !state.firstUserSeen) {
131213
- state.firstUserSeen = true;
131214
- if (isTeammateContent(msg?.content)) state.isTeammate = true;
131215
- }
131216
- const content = extractTextContent(msg?.content);
131217
- const hasToolUseResult = type === "user" && entry.toolUseResult != null;
131218
- const isOnlyToolResult = hasToolUseResult && isOnlyToolResultContent(msg?.content);
131219
- const toolSet = new Set(state.toolNames);
131220
- collectToolNames(msg?.content, toolSet);
131221
- state.toolNames = Array.from(toolSet);
131222
- const toolUseBlocks = extractToolUseBlocks(msg?.content);
131223
- const thinking = type === "assistant" ? extractThinking(msg?.content) : null;
131224
- const hasThinking = !!(thinking?.content || thinking?.signature);
131225
- if (content || isOnlyToolResult || toolUseBlocks.length > 0 || hasThinking) {
131226
- state.pageMessageCount++;
131227
- }
131228
- if (content || isOnlyToolResult) {
131229
- state.messageCount++;
131230
- state.lastMessageSender = type;
131231
- if (content) {
131232
- const ts2 = entry.timestamp || "";
131233
- if (!state.firstMessage) state.firstMessage = { text: content.slice(0, 200), timestamp: ts2 };
131234
- state.lastMessage = { text: content.slice(0, 200), timestamp: ts2 };
131235
- if (state.previewLength < tier.previewMax) {
131236
- state.previewParts.push(content);
131237
- state.previewLength += content.length;
131238
- }
131239
- if (state.snippetLength < tier.snippetMax) {
131240
- const remaining = tier.snippetMax - state.snippetLength;
131241
- const chunk = content.length > remaining ? content.slice(0, remaining) : content;
131242
- state.snippetParts.push(chunk);
131243
- state.snippetLength += chunk.length;
131244
- }
131245
- }
131246
- }
131247
- }
131248
- function finalizeMeta(state, filePath, account, tier) {
131249
- if (state.messageCount === 0) return null;
131250
- const isSubagent = filePath.includes("/subagents/");
131251
- let parentSessionId = null;
131252
- if (isSubagent) {
131253
- const uuidDir = (0, import_path5.dirname)((0, import_path5.dirname)(filePath));
131254
- parentSessionId = (0, import_path5.join)((0, import_path5.dirname)(uuidDir), `${(0, import_path5.basename)(uuidDir)}.jsonl`);
131255
- }
131256
- const projectPath = state.cwd;
131257
- return {
131258
- id: filePath,
131259
- filePath,
131260
- provider: CLAUDE_CODE_PROVIDER,
131261
- sessionId: state.sessionId || (0, import_path5.basename)(filePath, ".jsonl"),
131262
- sessionName: state.sessionName,
131263
- projectPath,
131264
- projectName: getShortProjectName3(projectPath),
131265
- account,
131266
- timestamp: state.latestTimestamp || (/* @__PURE__ */ new Date()).toISOString(),
131267
- messageCount: state.messageCount,
131268
- lastMessageSender: state.lastMessageSender,
131269
- preview: state.previewParts.join(" ").slice(0, tier.previewMax),
131270
- contentSnippet: state.snippetParts.join(" "),
131271
- gitBranch: null,
131272
- model: state.model,
131273
- isSubagent,
131274
- parentSessionId,
131275
- isTeammate: state.isTeammate,
131276
- teamName: state.teamName || null,
131277
- toolNames: state.toolNames,
131278
- firstMessage: state.firstMessage,
131279
- lastMessage: state.lastMessage,
131280
- lastPrompt: state.lastPrompt || void 0
131281
- };
131282
- }
131283
- function getShortProjectName3(fullPath) {
131284
- const parts = fullPath.split("/").filter(Boolean);
131285
- return parts.slice(-3).join("/");
131286
- }
131287
131293
  var ThreadbaseProvider = class {
131288
131294
  name = CLAUDE_CODE_PROVIDER;
131289
131295
  // Roots are passed as "<projectsDir>\0<account>" so the scanner can carry the
@@ -131352,32 +131358,219 @@ var LRUCache = class {
131352
131358
  return this.map.size;
131353
131359
  }
131354
131360
  };
131361
+ var YIELD_EVERY_LINES = 500;
131362
+ async function tailReduce(filePath, startOffset, startLine, state, tier) {
131363
+ const stream = (0, import_fs10.createReadStream)(filePath, { start: startOffset, encoding: "utf8" });
131364
+ let buffer = "";
131365
+ let offset = startOffset;
131366
+ let line = startLine;
131367
+ let parsedLines = 0;
131368
+ let sinceYield = 0;
131369
+ for await (const chunk of stream) {
131370
+ buffer += chunk;
131371
+ let nl;
131372
+ while ((nl = buffer.indexOf("\n")) >= 0) {
131373
+ const lineWithNewline = buffer.slice(0, nl + 1);
131374
+ const text = lineWithNewline.trimEnd();
131375
+ buffer = buffer.slice(nl + 1);
131376
+ if (text.length > 0) {
131377
+ try {
131378
+ reduceLine(state, JSON.parse(text), tier);
131379
+ } catch {
131380
+ state.badJsonLines++;
131381
+ }
131382
+ parsedLines++;
131383
+ }
131384
+ offset += Buffer.byteLength(lineWithNewline, "utf8");
131385
+ line++;
131386
+ if (++sinceYield >= YIELD_EVERY_LINES) {
131387
+ sinceYield = 0;
131388
+ await (0, import_promises8.setImmediate)();
131389
+ }
131390
+ }
131391
+ }
131392
+ return { newOffset: offset, newLine: line, parsedLines, badJsonLines: state.badJsonLines };
131393
+ }
131394
+ var CHECKPOINT_INTERVAL = 500;
131395
+ async function streamMessages(filePath, startOffset, startLine, state, onMessage, onEntry) {
131396
+ const stream = (0, import_fs9.createReadStream)(filePath, { start: startOffset, encoding: "utf8" });
131397
+ let buffer = "";
131398
+ let offset = startOffset;
131399
+ let line = startLine;
131400
+ let sinceYield = 0;
131401
+ for await (const chunk of stream) {
131402
+ buffer += chunk;
131403
+ let nl;
131404
+ while ((nl = buffer.indexOf("\n")) >= 0) {
131405
+ const lineWithNewline = buffer.slice(0, nl + 1);
131406
+ const text = lineWithNewline.trimEnd();
131407
+ buffer = buffer.slice(nl + 1);
131408
+ offset += Buffer.byteLength(lineWithNewline, "utf8");
131409
+ line += 1;
131410
+ if (++sinceYield >= YIELD_EVERY_LINES) {
131411
+ sinceYield = 0;
131412
+ await (0, import_promises7.setImmediate)();
131413
+ }
131414
+ if (text.length === 0) continue;
131415
+ let entry;
131416
+ try {
131417
+ entry = JSON.parse(text);
131418
+ } catch {
131419
+ continue;
131420
+ }
131421
+ if (onEntry?.(entry)) continue;
131422
+ const message = reduceConvLine(state, entry);
131423
+ if (message && onMessage(message, offset, line)) {
131424
+ stream.destroy();
131425
+ return { offset, line };
131426
+ }
131427
+ }
131428
+ }
131429
+ return { offset, line };
131430
+ }
131431
+ async function buildCheckpoints(filePath, interval = CHECKPOINT_INTERVAL, from = null) {
131432
+ const checkpoints = [];
131433
+ const state = from ? from.state : initialConvState();
131434
+ let index = from ? from.messageIndex : 0;
131435
+ await streamMessages(
131436
+ filePath,
131437
+ from?.byteOffset ?? 0,
131438
+ from?.lineNumber ?? 0,
131439
+ state,
131440
+ (_msg, nextOffset, nextLine) => {
131441
+ index += 1;
131442
+ if (index % interval === 0) {
131443
+ checkpoints.push({
131444
+ messageIndex: index,
131445
+ byteOffset: nextOffset,
131446
+ lineNumber: nextLine,
131447
+ state: structuredClone(state)
131448
+ });
131449
+ }
131450
+ return false;
131451
+ }
131452
+ );
131453
+ return checkpoints;
131454
+ }
131455
+ async function readPage(filePath, total, options, floor) {
131456
+ const beforeIndex = options.beforeIndex ?? total;
131457
+ const fromIndex = Math.max(0, beforeIndex - options.limit);
131458
+ const state = floor ? structuredClone(floor.state) : initialConvState();
131459
+ const startOffset = floor ? floor.byteOffset : 0;
131460
+ const startLine = floor ? floor.lineNumber : 0;
131461
+ let index = floor ? floor.messageIndex : 0;
131462
+ const window2 = [];
131463
+ await streamMessages(filePath, startOffset, startLine, state, (message) => {
131464
+ const current = index;
131465
+ index += 1;
131466
+ if (current >= fromIndex && current < beforeIndex) window2.push(message);
131467
+ return index >= beforeIndex;
131468
+ });
131469
+ applyTeamInfo(window2, state);
131470
+ return { messages: window2, total, fromIndex };
131471
+ }
131472
+ async function foldTail(filePath, resume) {
131473
+ const messages = [];
131474
+ const textParts = [];
131475
+ const turnDurations = [];
131476
+ const end = await streamMessages(
131477
+ filePath,
131478
+ resume.offset,
131479
+ resume.line,
131480
+ resume.state,
131481
+ (message) => {
131482
+ messages.push(message);
131483
+ if (message.text) textParts.push(message.text);
131484
+ return false;
131485
+ },
131486
+ (entry) => {
131487
+ if (entry.type === "system" && entry.subtype === "turn_duration" && typeof entry.durationMs === "number") {
131488
+ turnDurations.push({
131489
+ durationMs: entry.durationMs,
131490
+ messageCount: entry.messageCount || 0,
131491
+ uuid: entry.uuid
131492
+ });
131493
+ return true;
131494
+ }
131495
+ return false;
131496
+ }
131497
+ );
131498
+ return { messages, textParts, turnDurations, end };
131499
+ }
131500
+ function assemble(filePath, account, messages, fullText, turnDurations, state) {
131501
+ return {
131502
+ id: filePath,
131503
+ filePath,
131504
+ projectPath: state.cwd,
131505
+ projectName: getShortProjectName2(state.cwd),
131506
+ sessionId: state.sessionId || (0, import_path8.basename)(filePath, ".jsonl"),
131507
+ sessionName: state.sessionName,
131508
+ messages,
131509
+ fullText,
131510
+ timestamp: state.latestTimestamp || (/* @__PURE__ */ new Date()).toISOString(),
131511
+ messageCount: messages.length,
131512
+ account,
131513
+ turnDurations: turnDurations.length > 0 ? turnDurations : void 0,
131514
+ lastPrompt: state.lastPrompt || void 0
131515
+ };
131516
+ }
131517
+ async function parseConversationResumable(filePath, account) {
131518
+ const resume = { state: initialConvState(), offset: 0, line: 0 };
131519
+ const { messages, textParts, turnDurations, end } = await foldTail(filePath, resume);
131520
+ if (messages.length === 0) return null;
131521
+ applyTeamInfo(messages, resume.state);
131522
+ const conversation = assemble(
131523
+ filePath,
131524
+ account,
131525
+ messages,
131526
+ textParts.join(" "),
131527
+ turnDurations,
131528
+ resume.state
131529
+ );
131530
+ return { conversation, resume: { state: resume.state, offset: end.offset, line: end.line } };
131531
+ }
131532
+ async function extendConversation(previous, resume, filePath, account) {
131533
+ const { messages: fresh, textParts, turnDurations, end } = await foldTail(filePath, resume);
131534
+ const messages = fresh.length > 0 ? previous.messages.concat(fresh) : previous.messages;
131535
+ applyTeamInfo(messages, resume.state);
131536
+ const fullText = textParts.length === 0 ? previous.fullText : previous.fullText ? `${previous.fullText} ${textParts.join(" ")}` : textParts.join(" ");
131537
+ const allTurnDurations = (previous.turnDurations ?? []).concat(turnDurations);
131538
+ const conversation = assemble(
131539
+ filePath,
131540
+ account,
131541
+ messages,
131542
+ fullText,
131543
+ allTurnDurations,
131544
+ resume.state
131545
+ );
131546
+ return { conversation, resume: { state: resume.state, offset: end.offset, line: end.line } };
131547
+ }
131355
131548
  var FP_BYTES = 4096;
131356
131549
  function fingerprint(filePath, size) {
131357
131550
  const hash2 = (0, import_crypto2.createHash)("sha1");
131358
131551
  hash2.update(String(size));
131359
- const fd = (0, import_fs9.openSync)(filePath, "r");
131552
+ const fd = (0, import_fs11.openSync)(filePath, "r");
131360
131553
  try {
131361
131554
  const head = Buffer.alloc(Math.min(FP_BYTES, size));
131362
131555
  if (head.length > 0) {
131363
- (0, import_fs9.readSync)(fd, head, 0, head.length, 0);
131556
+ (0, import_fs11.readSync)(fd, head, 0, head.length, 0);
131364
131557
  hash2.update(head);
131365
131558
  }
131366
131559
  if (size > FP_BYTES) {
131367
131560
  const tailLen = Math.min(FP_BYTES, size);
131368
131561
  const tail = Buffer.alloc(tailLen);
131369
- (0, import_fs9.readSync)(fd, tail, 0, tailLen, size - tailLen);
131562
+ (0, import_fs11.readSync)(fd, tail, 0, tailLen, size - tailLen);
131370
131563
  hash2.update(tail);
131371
131564
  }
131372
131565
  } finally {
131373
- (0, import_fs9.closeSync)(fd);
131566
+ (0, import_fs11.closeSync)(fd);
131374
131567
  }
131375
131568
  return hash2.digest("hex");
131376
131569
  }
131377
131570
  function classify(filePath, existing) {
131378
131571
  let stat42;
131379
131572
  try {
131380
- const s3 = (0, import_fs9.statSync)(filePath);
131573
+ const s3 = (0, import_fs11.statSync)(filePath);
131381
131574
  stat42 = { size: s3.size, mtimeMs: s3.mtimeMs };
131382
131575
  } catch {
131383
131576
  return { change: "vanished" };
@@ -131407,7 +131600,7 @@ async function parseMetaWithProvider(provider, filePath, account, tier) {
131407
131600
  const log8 = getLogger2();
131408
131601
  const acc = provider.createEmptyAccumulator();
131409
131602
  const rl = (0, import_readline3.createInterface)({
131410
- input: (0, import_fs10.createReadStream)(filePath),
131603
+ input: (0, import_fs12.createReadStream)(filePath),
131411
131604
  crlfDelay: Infinity
131412
131605
  });
131413
131606
  try {
@@ -131630,7 +131823,7 @@ function hasColumn(db, table, column) {
131630
131823
  }
131631
131824
  function openDatabase(dbPath) {
131632
131825
  if (dbPath !== ":memory:") {
131633
- (0, import_fs11.mkdirSync)((0, import_path8.dirname)(dbPath), { recursive: true });
131826
+ (0, import_fs13.mkdirSync)((0, import_path9.dirname)(dbPath), { recursive: true });
131634
131827
  }
131635
131828
  const db = new import_better_sqlite3.default(dbPath);
131636
131829
  db.pragma("journal_mode = WAL");
@@ -131656,7 +131849,7 @@ async function discoverJsonlFilesGated(dirs, files, scannedDirs, options = {}) {
131656
131849
  for (const projectDir of resolved.entries) {
131657
131850
  let dirStat;
131658
131851
  try {
131659
- dirStat = await (0, import_promises7.stat)(projectDir);
131852
+ dirStat = await (0, import_promises9.stat)(projectDir);
131660
131853
  } catch {
131661
131854
  scannedDirs.remove(projectDir);
131662
131855
  continue;
@@ -131694,7 +131887,7 @@ async function discoverJsonlFilesGated(dirs, files, scannedDirs, options = {}) {
131694
131887
  async function resolveProjectDirs(projectsDir, scannedDirs) {
131695
131888
  let rootStat;
131696
131889
  try {
131697
- rootStat = await (0, import_promises7.stat)(projectsDir);
131890
+ rootStat = await (0, import_promises9.stat)(projectsDir);
131698
131891
  } catch {
131699
131892
  return null;
131700
131893
  }
@@ -131704,7 +131897,7 @@ async function resolveProjectDirs(projectsDir, scannedDirs) {
131704
131897
  }
131705
131898
  let entries;
131706
131899
  try {
131707
- entries = (await (0, import_promises7.readdir)(projectsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => joinPath(projectsDir, e.name));
131900
+ entries = (await (0, import_promises9.readdir)(projectsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => joinPath(projectsDir, e.name));
131708
131901
  } catch {
131709
131902
  return null;
131710
131903
  }
@@ -131717,98 +131910,6 @@ function dirnameOf(filePath) {
131717
131910
  function joinPath(dir, name) {
131718
131911
  return dir.endsWith("/") ? `${dir}${name}` : `${dir}/${name}`;
131719
131912
  }
131720
- async function tailReduce(filePath, startOffset, startLine, state, tier) {
131721
- const stream = (0, import_fs12.createReadStream)(filePath, { start: startOffset, encoding: "utf8" });
131722
- let buffer = "";
131723
- let offset = startOffset;
131724
- let line = startLine;
131725
- let parsedLines = 0;
131726
- for await (const chunk of stream) {
131727
- buffer += chunk;
131728
- let nl;
131729
- while ((nl = buffer.indexOf("\n")) >= 0) {
131730
- const lineWithNewline = buffer.slice(0, nl + 1);
131731
- const text = lineWithNewline.trimEnd();
131732
- buffer = buffer.slice(nl + 1);
131733
- if (text.length > 0) {
131734
- try {
131735
- reduceLine(state, JSON.parse(text), tier);
131736
- } catch {
131737
- state.badJsonLines++;
131738
- }
131739
- parsedLines++;
131740
- }
131741
- offset += Buffer.byteLength(lineWithNewline, "utf8");
131742
- line++;
131743
- }
131744
- }
131745
- return { newOffset: offset, newLine: line, parsedLines, badJsonLines: state.badJsonLines };
131746
- }
131747
- var CHECKPOINT_INTERVAL = 500;
131748
- async function streamMessages(filePath, startOffset, startLine, state, onMessage) {
131749
- const stream = (0, import_fs13.createReadStream)(filePath, { start: startOffset, encoding: "utf8" });
131750
- let buffer = "";
131751
- let offset = startOffset;
131752
- let line = startLine;
131753
- for await (const chunk of stream) {
131754
- buffer += chunk;
131755
- let nl;
131756
- while ((nl = buffer.indexOf("\n")) >= 0) {
131757
- const lineWithNewline = buffer.slice(0, nl + 1);
131758
- const text = lineWithNewline.trimEnd();
131759
- buffer = buffer.slice(nl + 1);
131760
- offset += Buffer.byteLength(lineWithNewline, "utf8");
131761
- line += 1;
131762
- if (text.length === 0) continue;
131763
- let entry;
131764
- try {
131765
- entry = JSON.parse(text);
131766
- } catch {
131767
- continue;
131768
- }
131769
- const message = reduceConvLine(state, entry);
131770
- if (message && onMessage(message, offset, line)) {
131771
- stream.destroy();
131772
- return;
131773
- }
131774
- }
131775
- }
131776
- }
131777
- async function buildCheckpoints(filePath, interval = CHECKPOINT_INTERVAL) {
131778
- const checkpoints = [];
131779
- const state = initialConvState();
131780
- let index = 0;
131781
- await streamMessages(filePath, 0, 0, state, (_msg, nextOffset, nextLine) => {
131782
- index += 1;
131783
- if (index % interval === 0) {
131784
- checkpoints.push({
131785
- messageIndex: index,
131786
- byteOffset: nextOffset,
131787
- lineNumber: nextLine,
131788
- state: structuredClone(state)
131789
- });
131790
- }
131791
- return false;
131792
- });
131793
- return checkpoints;
131794
- }
131795
- async function readPage(filePath, total, options, floor) {
131796
- const beforeIndex = options.beforeIndex ?? total;
131797
- const fromIndex = Math.max(0, beforeIndex - options.limit);
131798
- const state = floor ? structuredClone(floor.state) : initialConvState();
131799
- const startOffset = floor ? floor.byteOffset : 0;
131800
- const startLine = floor ? floor.lineNumber : 0;
131801
- let index = floor ? floor.messageIndex : 0;
131802
- const window2 = [];
131803
- await streamMessages(filePath, startOffset, startLine, state, (message) => {
131804
- const current = index;
131805
- index += 1;
131806
- if (current >= fromIndex && current < beforeIndex) window2.push(message);
131807
- return index >= beforeIndex;
131808
- });
131809
- applyTeamInfo(window2, state);
131810
- return { messages: window2, total, fromIndex };
131811
- }
131812
131913
  var CheckpointsRepo = class {
131813
131914
  constructor(db) {
131814
131915
  this.db = db;
@@ -131828,6 +131929,22 @@ var CheckpointsRepo = class {
131828
131929
  });
131829
131930
  tx();
131830
131931
  }
131932
+ // Insert checkpoints without touching existing rows. Appends never invalidate
131933
+ // the chain covering the immutable prefix (Kafka sparse-index style); rows are
131934
+ // only ever removed on truncation/replace or deletion.
131935
+ append(sourcePath, checkpoints) {
131936
+ const tx = this.db.transaction(() => {
131937
+ const insert = this.db.prepare(
131938
+ `INSERT INTO message_checkpoints
131939
+ (source_path, message_index, byte_offset, line_number, parser_state)
131940
+ VALUES (?, ?, ?, ?, ?)`
131941
+ );
131942
+ for (const c of checkpoints) {
131943
+ insert.run(sourcePath, c.messageIndex, c.byteOffset, c.lineNumber, JSON.stringify(c.state));
131944
+ }
131945
+ });
131946
+ tx();
131947
+ }
131831
131948
  // The latest checkpoint at or before `messageIndex`, or null if none (read
131832
131949
  // from the file start). Lets a page seek to the nearest prior anchor.
131833
131950
  floor(sourcePath, messageIndex) {
@@ -131839,6 +131956,17 @@ var CheckpointsRepo = class {
131839
131956
  ).get(sourcePath, messageIndex);
131840
131957
  return row ? toCheckpoint(row) : null;
131841
131958
  }
131959
+ // The highest-index checkpoint for a file, or null if none. The resume point
131960
+ // for extending the chain after an append.
131961
+ last(sourcePath) {
131962
+ const row = this.db.prepare(
131963
+ `SELECT message_index, byte_offset, line_number, parser_state
131964
+ FROM message_checkpoints
131965
+ WHERE source_path = ?
131966
+ ORDER BY message_index DESC LIMIT 1`
131967
+ ).get(sourcePath);
131968
+ return row ? toCheckpoint(row) : null;
131969
+ }
131842
131970
  count(sourcePath) {
131843
131971
  return this.db.prepare("SELECT COUNT(*) AS n FROM message_checkpoints WHERE source_path = ?").get(sourcePath).n;
131844
131972
  }
@@ -131870,7 +131998,7 @@ var ConversationFilesRepo = class {
131870
131998
  const info = this.db.prepare(
131871
131999
  `INSERT INTO conversation_files (absolute_path, parent_dir, file_name, account)
131872
132000
  VALUES (?, ?, ?, ?)`
131873
- ).run(absolutePath, (0, import_path9.dirname)(absolutePath), (0, import_path9.basename)(absolutePath), account);
132001
+ ).run(absolutePath, (0, import_path10.dirname)(absolutePath), (0, import_path10.basename)(absolutePath), account);
131874
132002
  return Number(info.lastInsertRowid);
131875
132003
  }
131876
132004
  // Advance the cursor + persisted reducer state after a successful index pass.
@@ -132221,6 +132349,9 @@ var PersistentEngine = class {
132221
132349
  // restart just means the first few post-restart scans don't force an early
132222
132350
  // backstop pass, which is harmless (watermarks themselves persist in the DB).
132223
132351
  scanCount = 0;
132352
+ // In-flight checkpoint build/extension per file, so concurrent getPage
132353
+ // callers share one stream instead of each walking the file.
132354
+ checkpointBuilds = /* @__PURE__ */ new Map();
132224
132355
  constructor(dbPath, options = {}) {
132225
132356
  this.db = openDatabase(dbPath);
132226
132357
  this.files = new ConversationFilesRepo(this.db);
@@ -132273,7 +132404,7 @@ var PersistentEngine = class {
132273
132404
  const batch = discovered.slice(i, i + BATCH_SIZE);
132274
132405
  const results = await Promise.all(
132275
132406
  batch.map(async ({ filePath, account, provider }) => {
132276
- const meta3 = await this.indexFile(
132407
+ const { meta: meta3 } = await this.indexFile(
132277
132408
  filePath,
132278
132409
  account,
132279
132410
  tier.name,
@@ -132308,7 +132439,9 @@ var PersistentEngine = class {
132308
132439
  // unchanged → return the stored summary; appended → resume the fold and read
132309
132440
  // only new bytes; reindex/force → fold from offset 0. Writes the summary +
132310
132441
  // cursor + reducer state in one transaction so a crash never leaves a
132311
- // half-written row or an over-advanced cursor.
132442
+ // half-written row or an over-advanced cursor. Returns the classification
132443
+ // alongside the meta so callers (refreshFile) can keep, extend, or evict
132444
+ // their own per-file caches without re-stat'ing the file (racy) themselves.
132312
132445
  async indexFile(filePath, account, tierName, customTiers, resolveGitBranch, force = false, provider) {
132313
132446
  const log8 = getLogger2();
132314
132447
  const tier = resolveTier(tierName, customTiers);
@@ -132316,13 +132449,21 @@ var PersistentEngine = class {
132316
132449
  const { change, stat: stat42 } = classify(filePath, existing);
132317
132450
  if (change === "vanished" || !stat42) {
132318
132451
  this.markDeleted(filePath);
132319
- return null;
132452
+ return { meta: null, change: "vanished" };
132320
132453
  }
132321
132454
  if (change === "unchanged" && !force) {
132322
- return this.conversations.getBySourcePath(filePath);
132455
+ return { meta: this.conversations.getBySourcePath(filePath), change };
132323
132456
  }
132324
132457
  if (provider && provider.name !== CLAUDE_CODE_PROVIDER) {
132325
- return this.indexFileWithProvider(provider, filePath, account, tier, stat42, resolveGitBranch);
132458
+ const meta22 = await this.indexFileWithProvider(
132459
+ provider,
132460
+ filePath,
132461
+ account,
132462
+ tier,
132463
+ stat42,
132464
+ resolveGitBranch
132465
+ );
132466
+ return { meta: meta22, change };
132326
132467
  }
132327
132468
  const resume = change === "appended" && !force && existing?.reducer_state;
132328
132469
  const state = resume ? JSON.parse(existing.reducer_state) : initialReducerState();
@@ -132333,12 +132474,12 @@ var PersistentEngine = class {
132333
132474
  result = await tailReduce(filePath, startOffset, startLine, state, tier);
132334
132475
  } catch (err) {
132335
132476
  log8.warn({ filePath, err }, "persistent: tail read failed");
132336
- return null;
132477
+ return { meta: null, change };
132337
132478
  }
132338
132479
  const meta3 = finalizeMeta(state, filePath, account, tier);
132339
132480
  if (!meta3) {
132340
132481
  this.markDeleted(filePath);
132341
- return null;
132482
+ return { meta: null, change };
132342
132483
  }
132343
132484
  meta3.gitBranch = resolveGitBranch(meta3.projectPath);
132344
132485
  const fp = stat42.size > 0 ? fingerprint(filePath, stat42.size) : null;
@@ -132346,7 +132487,7 @@ var PersistentEngine = class {
132346
132487
  const upsert = this.db.transaction(() => {
132347
132488
  this.conversations.upsert(fileId, meta3, state.pageMessageCount);
132348
132489
  this.fts.upsert(meta3);
132349
- this.checkpoints.remove(filePath);
132490
+ if (!resume) this.checkpoints.remove(filePath);
132350
132491
  this.files.updateCursor(fileId, {
132351
132492
  sizeBytes: stat42.size,
132352
132493
  mtimeMs: stat42.mtimeMs,
@@ -132379,7 +132520,7 @@ var PersistentEngine = class {
132379
132520
  { filePath, change, bytesRead: result.newOffset - startOffset, msgs: meta3.messageCount },
132380
132521
  "persistent: indexed file"
132381
132522
  );
132382
- return meta3;
132523
+ return { meta: meta3, change };
132383
132524
  }
132384
132525
  // Index a non-Threadbase provider file: full reparse from offset 0 through the
132385
132526
  // provider's reducer/finalize, then the same upsert + FTS write + cursor bump
@@ -132479,15 +132620,34 @@ var PersistentEngine = class {
132479
132620
  return { messages: messages.slice(fromIndex2, beforeIndex2), total: total2, fromIndex: fromIndex2 };
132480
132621
  }
132481
132622
  const total = this.conversations.pageMessageCount(filePath);
132482
- if (total > CHECKPOINT_INTERVAL && this.checkpoints.count(filePath) === 0) {
132483
- const built = await buildCheckpoints(filePath);
132484
- if (built.length > 0) this.checkpoints.replaceAll(filePath, built);
132485
- }
132623
+ await this.ensureCheckpoints(filePath, total);
132486
132624
  const beforeIndex = options.beforeIndex ?? total;
132487
132625
  const fromIndex = Math.max(0, beforeIndex - options.limit);
132488
132626
  const floor = this.checkpoints.floor(filePath, fromIndex);
132489
132627
  return readPage(filePath, total, options, floor);
132490
132628
  }
132629
+ // Build or extend the checkpoint chain so it covers `total` messages. Cold
132630
+ // file → full build; a file that grew → extend from the last persisted
132631
+ // checkpoint (reads only past its offset, never the prefix). Single-flighted
132632
+ // per path: concurrent getPage callers await the same build instead of
132633
+ // streaming the file in parallel.
132634
+ ensureCheckpoints(filePath, total) {
132635
+ if (total <= CHECKPOINT_INTERVAL) return Promise.resolve();
132636
+ const inFlight = this.checkpointBuilds.get(filePath);
132637
+ if (inFlight) return inFlight;
132638
+ const build2 = (async () => {
132639
+ const last = this.checkpoints.last(filePath);
132640
+ if (last && total < last.messageIndex + CHECKPOINT_INTERVAL) return;
132641
+ const fresh = await buildCheckpoints(filePath, CHECKPOINT_INTERVAL, last);
132642
+ if (fresh.length === 0) return;
132643
+ if (last) this.checkpoints.append(filePath, fresh);
132644
+ else this.checkpoints.replaceAll(filePath, fresh);
132645
+ })().finally(() => {
132646
+ if (this.checkpointBuilds.get(filePath) === build2) this.checkpointBuilds.delete(filePath);
132647
+ });
132648
+ this.checkpointBuilds.set(filePath, build2);
132649
+ return build2;
132650
+ }
132491
132651
  };
132492
132652
  var EXCLUDED_SEGMENTS2 = ["/memory/", "/tool-results/"];
132493
132653
  var FileWatcher = class {
@@ -132586,6 +132746,8 @@ function defaultDbPath() {
132586
132746
  }
132587
132747
  var ConversationScanner = class {
132588
132748
  metadataCache = /* @__PURE__ */ new Map();
132749
+ // Parsed conversations plus (persistent claude-code entries only) the resume
132750
+ // point that lets refreshFile extend them in place when the file grows.
132589
132751
  conversationLRU;
132590
132752
  // session_id is NOT unique, so this maps a sessionId to every active meta that
132591
132753
  // carries it. Resolution picks deterministically (newest timestamp, then path
@@ -132617,7 +132779,9 @@ var ConversationScanner = class {
132617
132779
  // can't hit a closed DB (the watch-mode half of Bug #4).
132618
132780
  inFlightReconcile = null;
132619
132781
  constructor(options) {
132620
- this.conversationLRU = new LRUCache(options?.conversationCacheSize ?? 5);
132782
+ this.conversationLRU = new LRUCache(
132783
+ options?.conversationCacheSize ?? 5
132784
+ );
132621
132785
  if (options?.persistent === false) {
132622
132786
  this.dbPath = null;
132623
132787
  this.sidecarEnabled = false;
@@ -132874,7 +133038,7 @@ var ConversationScanner = class {
132874
133038
  const cached3 = this.conversationLRU.get(id);
132875
133039
  if (cached3) {
132876
133040
  log8.debug({ id }, "getConversation: cache hit");
132877
- return cached3;
133041
+ return cached3.conversation;
132878
133042
  }
132879
133043
  const meta3 = this.persistent ? this.engine().getByIdOrSession(id) : this.metadataCache.get(id) ?? this.resolveSessionId(id);
132880
133044
  if (!meta3) {
@@ -132883,9 +133047,14 @@ var ConversationScanner = class {
132883
133047
  }
132884
133048
  log8.debug({ id, filePath: meta3.filePath }, "getConversation: cache miss, parsing");
132885
133049
  try {
133050
+ if (this.persistent && meta3.provider !== CODEX_CLI_PROVIDER) {
133051
+ const parsed = await parseConversationResumable(meta3.filePath, meta3.account);
133052
+ if (parsed) this.conversationLRU.set(id, parsed);
133053
+ return parsed?.conversation ?? null;
133054
+ }
132886
133055
  const conversation = meta3.provider === CODEX_CLI_PROVIDER ? await parseCodexConversation(meta3.filePath, meta3.account) : await parseConversation(meta3.filePath, meta3.account);
132887
133056
  if (conversation) {
132888
- this.conversationLRU.set(id, conversation);
133057
+ this.conversationLRU.set(id, { conversation });
132889
133058
  }
132890
133059
  return conversation;
132891
133060
  } catch (err) {
@@ -132957,20 +133126,30 @@ var ConversationScanner = class {
132957
133126
  // not seen before. Returns the fresh ConversationMeta, or null when the file
132958
133127
  // no longer parses (missing/empty) — in which case any prior entry for it is
132959
133128
  // dropped from all indexes.
132960
- async refreshFile(filePath, account) {
133129
+ //
133130
+ // Single-flighted per path: concurrent callers (stacked client retries, a
133131
+ // watcher tick racing a caller) await the one in-flight refresh instead of
133132
+ // each re-reading the file.
133133
+ refreshesInFlight = /* @__PURE__ */ new Map();
133134
+ refreshFile(filePath, account) {
133135
+ const inFlight = this.refreshesInFlight.get(filePath);
133136
+ if (inFlight) return inFlight;
133137
+ const refresh = this.doRefreshFile(filePath, account).finally(() => {
133138
+ if (this.refreshesInFlight.get(filePath) === refresh) {
133139
+ this.refreshesInFlight.delete(filePath);
133140
+ }
133141
+ });
133142
+ this.refreshesInFlight.set(filePath, refresh);
133143
+ return refresh;
133144
+ }
133145
+ async doRefreshFile(filePath, account) {
132961
133146
  const log8 = getLogger2();
132962
133147
  if (this.persistent) {
132963
133148
  const engine = this.engine();
132964
133149
  const previous2 = engine.getByIdOrSession(filePath);
132965
133150
  const resolvedAccount2 = account ?? previous2?.account ?? "default";
132966
- const evict2 = (m2) => {
132967
- if (!m2) return;
132968
- this.conversationLRU.delete(m2.id);
132969
- this.conversationLRU.delete(m2.sessionId);
132970
- };
132971
- evict2(previous2);
132972
133151
  const provider = await this.resolveProviderForFile(filePath, previous2);
132973
- const meta22 = await engine.indexFile(
133152
+ const { meta: meta22, change } = await engine.indexFile(
132974
133153
  filePath,
132975
133154
  resolvedAccount2,
132976
133155
  this.lastTier.name,
@@ -132979,8 +133158,19 @@ var ConversationScanner = class {
132979
133158
  false,
132980
133159
  provider
132981
133160
  );
132982
- evict2(meta22);
132983
- log8.debug({ filePath, kept: !!meta22 }, "refreshFile: updated persistent index");
133161
+ const cacheKeys = /* @__PURE__ */ new Set();
133162
+ for (const m2 of [previous2, meta22]) {
133163
+ if (m2) {
133164
+ cacheKeys.add(m2.id);
133165
+ cacheKeys.add(m2.sessionId);
133166
+ }
133167
+ }
133168
+ if (!meta22 || change === "reindex" || change === "vanished") {
133169
+ for (const key of cacheKeys) this.conversationLRU.delete(key);
133170
+ } else if (change === "appended") {
133171
+ await this.extendCachedConversations(cacheKeys, filePath, meta22.account);
133172
+ }
133173
+ log8.debug({ filePath, change, kept: !!meta22 }, "refreshFile: updated persistent index");
132984
133174
  return meta22;
132985
133175
  }
132986
133176
  const previous = this.metadataCache.get(filePath);
@@ -133024,6 +133214,39 @@ var ConversationScanner = class {
133024
133214
  );
133025
133215
  return meta3;
133026
133216
  }
133217
+ // Advance every cached parse of an appended file by folding only the new
133218
+ // bytes through the conversation reducer — the in-memory analogue of the
133219
+ // persisted metadata fold. Entries without resume state (Codex) and entries
133220
+ // whose extension fails are evicted so the next read re-parses from scratch.
133221
+ async extendCachedConversations(cacheKeys, filePath, account) {
133222
+ const wrappers = /* @__PURE__ */ new Map();
133223
+ for (const key of cacheKeys) {
133224
+ const wrapper = this.conversationLRU.get(key);
133225
+ if (!wrapper) continue;
133226
+ const keys = wrappers.get(wrapper) ?? [];
133227
+ keys.push(key);
133228
+ wrappers.set(wrapper, keys);
133229
+ }
133230
+ for (const [wrapper, keys] of wrappers) {
133231
+ if (!wrapper.resume) {
133232
+ for (const key of keys) this.conversationLRU.delete(key);
133233
+ continue;
133234
+ }
133235
+ try {
133236
+ const extended = await extendConversation(
133237
+ wrapper.conversation,
133238
+ wrapper.resume,
133239
+ filePath,
133240
+ account
133241
+ );
133242
+ wrapper.conversation = extended.conversation;
133243
+ wrapper.resume = extended.resume;
133244
+ } catch (err) {
133245
+ getLogger2().warn({ filePath, err }, "refreshFile: cache extension failed, evicting");
133246
+ for (const key of keys) this.conversationLRU.delete(key);
133247
+ }
133248
+ }
133249
+ }
133027
133250
  getMetadataCache() {
133028
133251
  if (this.persistent) {
133029
133252
  const map2 = /* @__PURE__ */ new Map();
@@ -133290,10 +133513,10 @@ async function search(query, options, scanner) {
133290
133513
  // src/server.ts
133291
133514
  var import_events3 = require("events");
133292
133515
  var import_fs24 = require("fs");
133293
- var import_promises15 = require("fs/promises");
133516
+ var import_promises17 = require("fs/promises");
133294
133517
  var import_http = require("http");
133295
133518
  var import_os10 = require("os");
133296
- var import_path21 = require("path");
133519
+ var import_path22 = require("path");
133297
133520
  var import_readline4 = require("readline");
133298
133521
 
133299
133522
  // src/agent/agent-client.ts
@@ -133382,7 +133605,7 @@ function readAgentConfig(env = process.env) {
133382
133605
  }
133383
133606
 
133384
133607
  // src/agent/conversation-writer.ts
133385
- var import_promises8 = require("fs/promises");
133608
+ var import_promises10 = require("fs/promises");
133386
133609
  var import_node_path6 = require("path");
133387
133610
  function createConversationWriter(opts) {
133388
133611
  const { baseDir } = opts;
@@ -133392,7 +133615,7 @@ function createConversationWriter(opts) {
133392
133615
  throw new Error("ConversationWriter: refusing to write empty assistant turn");
133393
133616
  }
133394
133617
  const file2 = (0, import_node_path6.join)(baseDir, `${args.sessionId}.jsonl`);
133395
- await (0, import_promises8.mkdir)((0, import_node_path6.dirname)(file2), { recursive: true });
133618
+ await (0, import_promises10.mkdir)((0, import_node_path6.dirname)(file2), { recursive: true });
133396
133619
  const record2 = {
133397
133620
  role: "assistant",
133398
133621
  turnId: args.turnId,
@@ -133402,7 +133625,7 @@ function createConversationWriter(opts) {
133402
133625
  };
133403
133626
  const line = `${JSON.stringify(record2)}
133404
133627
  `;
133405
- await (0, import_promises8.appendFile)(file2, line, { encoding: "utf8" });
133628
+ await (0, import_promises10.appendFile)(file2, line, { encoding: "utf8" });
133406
133629
  }
133407
133630
  };
133408
133631
  }
@@ -136383,8 +136606,8 @@ var createHonoApp = (deps, upgradeWebSocket) => {
136383
136606
  init_auth();
136384
136607
 
136385
136608
  // src/browse.ts
136386
- var import_promises9 = require("fs/promises");
136387
- var import_path10 = require("path");
136609
+ var import_promises11 = require("fs/promises");
136610
+ var import_path11 = require("path");
136388
136611
  var BrowsePathNotFoundError = class extends Error {
136389
136612
  constructor(message) {
136390
136613
  super(message);
@@ -136392,20 +136615,20 @@ var BrowsePathNotFoundError = class extends Error {
136392
136615
  }
136393
136616
  };
136394
136617
  async function resolveBrowsePath(browseRoot, relativePath) {
136395
- const normalizedRoot = (0, import_path10.resolve)(browseRoot);
136618
+ const normalizedRoot = (0, import_path11.resolve)(browseRoot);
136396
136619
  let sanitized;
136397
136620
  if (process.platform !== "win32" && relativePath.startsWith("/") && relativePath.length > 1 && relativePath.includes("/", 1)) {
136398
136621
  sanitized = relativePath;
136399
136622
  } else {
136400
136623
  sanitized = relativePath.replace(/^[/\\]+/, "");
136401
136624
  }
136402
- const target = sanitized ? (0, import_path10.resolve)(normalizedRoot, sanitized) : normalizedRoot;
136403
- const rootPrefix = normalizedRoot.endsWith(import_path10.sep) ? normalizedRoot : `${normalizedRoot}${import_path10.sep}`;
136625
+ const target = sanitized ? (0, import_path11.resolve)(normalizedRoot, sanitized) : normalizedRoot;
136626
+ const rootPrefix = normalizedRoot.endsWith(import_path11.sep) ? normalizedRoot : `${normalizedRoot}${import_path11.sep}`;
136404
136627
  if (!target.startsWith(rootPrefix) && target !== normalizedRoot) {
136405
136628
  throw new Error("Path outside browse root");
136406
136629
  }
136407
136630
  try {
136408
- await (0, import_promises9.realpath)(target);
136631
+ await (0, import_promises11.realpath)(target);
136409
136632
  } catch (err) {
136410
136633
  if (err?.code === "ENOENT") {
136411
136634
  throw new BrowsePathNotFoundError(`Path not found: ${target}`);
@@ -136415,37 +136638,37 @@ async function resolveBrowsePath(browseRoot, relativePath) {
136415
136638
  return target;
136416
136639
  }
136417
136640
  async function listDirectories(absolutePath) {
136418
- const entries = await (0, import_promises9.readdir)(absolutePath, { withFileTypes: true });
136641
+ const entries = await (0, import_promises11.readdir)(absolutePath, { withFileTypes: true });
136419
136642
  return entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name })).sort((a, b2) => a.name.localeCompare(b2.name));
136420
136643
  }
136421
136644
  async function createDirectory(parentAbsolutePath, name) {
136422
136645
  if (name.includes("/") || name.includes("\\") || name === ".." || name === ".") {
136423
136646
  throw new Error("Invalid directory name");
136424
136647
  }
136425
- const target = (0, import_path10.join)(parentAbsolutePath, name);
136648
+ const target = (0, import_path11.join)(parentAbsolutePath, name);
136426
136649
  try {
136427
- const s3 = await (0, import_promises9.stat)(target);
136650
+ const s3 = await (0, import_promises11.stat)(target);
136428
136651
  if (s3.isDirectory()) throw new Error("Directory already exists");
136429
136652
  } catch (err) {
136430
136653
  if (err.code !== "ENOENT") throw err;
136431
136654
  }
136432
- await (0, import_promises9.mkdir)(target);
136655
+ await (0, import_promises11.mkdir)(target);
136433
136656
  return target;
136434
136657
  }
136435
136658
 
136436
136659
  // src/conversation-cache.ts
136437
136660
  var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
136438
136661
  var import_fs16 = require("fs");
136439
- var import_path12 = require("path");
136662
+ var import_path13 = require("path");
136440
136663
 
136441
136664
  // src/db/sqlite-migrate.ts
136442
136665
  var import_fs14 = require("fs");
136443
- var import_path11 = require("path");
136666
+ var import_path12 = require("path");
136444
136667
  var import_url6 = require("url");
136445
136668
  var import_meta = {};
136446
136669
  function getMigrationsDir() {
136447
136670
  if (typeof import_meta !== "undefined" && import_meta.url) {
136448
- return (0, import_path11.dirname)((0, import_url6.fileURLToPath)(import_meta.url));
136671
+ return (0, import_path12.dirname)((0, import_url6.fileURLToPath)(import_meta.url));
136449
136672
  }
136450
136673
  return __dirname;
136451
136674
  }
@@ -136457,7 +136680,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations (
136457
136680
  `;
136458
136681
  function runSqliteMigrations(db, migrationsDir) {
136459
136682
  db.exec(SCHEMA_MIGRATIONS_SQL);
136460
- const dir = migrationsDir ?? (0, import_path11.join)(getMigrationsDir(), "migrations");
136683
+ const dir = migrationsDir ?? (0, import_path12.join)(getMigrationsDir(), "migrations");
136461
136684
  const files = (0, import_fs14.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
136462
136685
  const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
136463
136686
  const appliedSet = new Set(appliedRows.map((r) => r.id));
@@ -136469,7 +136692,7 @@ function runSqliteMigrations(db, migrationsDir) {
136469
136692
  skipped.push(file2);
136470
136693
  continue;
136471
136694
  }
136472
- const sql = (0, import_fs14.readFileSync)((0, import_path11.join)(dir, file2), "utf-8");
136695
+ const sql = (0, import_fs14.readFileSync)((0, import_path12.join)(dir, file2), "utf-8");
136473
136696
  const tx = db.transaction(() => {
136474
136697
  db.exec(sql);
136475
136698
  recordApplied.run(file2, (/* @__PURE__ */ new Date()).toISOString());
@@ -136486,6 +136709,9 @@ var CODEX_CLI_PROVIDER2 = "codex-cli";
136486
136709
  function isProviderName(value) {
136487
136710
  return value === CLAUDE_CODE_PROVIDER2 || value === CODEX_CLI_PROVIDER2;
136488
136711
  }
136712
+ function coerceProviderForRunner(value) {
136713
+ return isProviderName(value) ? value : CLAUDE_CODE_PROVIDER2;
136714
+ }
136489
136715
  function isProviderResumable(_provider, availabilityResumable) {
136490
136716
  return availabilityResumable;
136491
136717
  }
@@ -136811,7 +137037,7 @@ var ConversationCache = class _ConversationCache {
136811
137037
  return this.classifyAgentFile(filePath, s3.mtimeMs, s3.size);
136812
137038
  }
136813
137039
  static open(dbPath, tailSize = 10, migrationsDir, options) {
136814
- (0, import_fs16.mkdirSync)((0, import_path12.dirname)(dbPath), { recursive: true });
137040
+ (0, import_fs16.mkdirSync)((0, import_path13.dirname)(dbPath), { recursive: true });
136815
137041
  const db = new import_better_sqlite32.default(dbPath);
136816
137042
  db.pragma("journal_mode = WAL");
136817
137043
  db.pragma("foreign_keys = ON");
@@ -137392,12 +137618,12 @@ function getDbConfig() {
137392
137618
 
137393
137619
  // src/db/migrations.ts
137394
137620
  var import_fs17 = require("fs");
137395
- var import_path13 = require("path");
137621
+ var import_path14 = require("path");
137396
137622
  var import_url7 = require("url");
137397
137623
  var import_meta2 = {};
137398
137624
  function getMigrationsDir2() {
137399
137625
  if (import_meta2.url) {
137400
- return (0, import_path13.dirname)((0, import_url7.fileURLToPath)(import_meta2.url));
137626
+ return (0, import_path14.dirname)((0, import_url7.fileURLToPath)(import_meta2.url));
137401
137627
  }
137402
137628
  return __dirname;
137403
137629
  }
@@ -137413,11 +137639,11 @@ async function runMigrations2(pool2, migrationsDir) {
137413
137639
  "SELECT name FROM _migrations ORDER BY name"
137414
137640
  );
137415
137641
  const appliedSet = new Set(applied.map((r) => r.name));
137416
- const dir = migrationsDir ?? (0, import_path13.join)(getMigrationsDir2(), "pg-migrations");
137642
+ const dir = migrationsDir ?? (0, import_path14.join)(getMigrationsDir2(), "pg-migrations");
137417
137643
  const files = (0, import_fs17.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
137418
137644
  for (const file2 of files) {
137419
137645
  if (appliedSet.has(file2)) continue;
137420
- const sql = (0, import_fs17.readFileSync)((0, import_path13.join)(dir, file2), "utf-8");
137646
+ const sql = (0, import_fs17.readFileSync)((0, import_path14.join)(dir, file2), "utf-8");
137421
137647
  await pool2.query(sql);
137422
137648
  await pool2.query("INSERT INTO _migrations (name) VALUES ($1)", [file2]);
137423
137649
  }
@@ -137654,18 +137880,18 @@ async function recordUpload(pool2, instanceId, row) {
137654
137880
  // src/handlers/handleListProjects.ts
137655
137881
  var import_fs18 = require("fs");
137656
137882
  var import_os7 = require("os");
137657
- var import_path14 = require("path");
137883
+ var import_path15 = require("path");
137658
137884
  function decodeProjectPath(dirName) {
137659
137885
  return dirName.replace(/-/g, "/");
137660
137886
  }
137661
137887
  function handleListProjects(url2, res) {
137662
137888
  const limit = Math.max(1, parseInt(url2.searchParams.get("limit") ?? "50", 10) || 50);
137663
137889
  const offset = Math.max(0, parseInt(url2.searchParams.get("offset") ?? "0", 10) || 0);
137664
- const projectsDir = (0, import_path14.join)((0, import_os7.homedir)(), ".claude", "projects");
137890
+ const projectsDir = (0, import_path15.join)((0, import_os7.homedir)(), ".claude", "projects");
137665
137891
  let entries;
137666
137892
  try {
137667
137893
  entries = (0, import_fs18.readdirSync)(projectsDir).map((dirName) => {
137668
- const fullPath = (0, import_path14.join)(projectsDir, dirName);
137894
+ const fullPath = (0, import_path15.join)(projectsDir, dirName);
137669
137895
  let mtime = 0;
137670
137896
  try {
137671
137897
  mtime = (0, import_fs18.statSync)(fullPath).mtimeMs;
@@ -137687,20 +137913,20 @@ function handleListProjects(url2, res) {
137687
137913
  }
137688
137914
 
137689
137915
  // src/live-session-manager.ts
137690
- var import_path18 = require("path");
137916
+ var import_path19 = require("path");
137691
137917
 
137692
137918
  // src/codex-pty-runner.ts
137693
137919
  var import_headless = __toESM(require_xterm_headless(), 1);
137694
137920
  var import_crypto6 = require("crypto");
137695
137921
  var import_fs20 = require("fs");
137696
- var import_path16 = require("path");
137922
+ var import_path17 = require("path");
137697
137923
  init_logger();
137698
137924
 
137699
137925
  // src/platform.ts
137700
137926
  var import_child_process = require("child_process");
137701
137927
  var import_fs19 = require("fs");
137702
137928
  var import_os8 = require("os");
137703
- var import_path15 = require("path");
137929
+ var import_path16 = require("path");
137704
137930
  var isWindows2 = (0, import_os8.platform)() === "win32";
137705
137931
  var _claudeExe;
137706
137932
  function resolveClaudeExe() {
@@ -137719,9 +137945,9 @@ function resolveClaudeExe() {
137719
137945
  } catch {
137720
137946
  }
137721
137947
  const candidates = [
137722
- (0, import_path15.join)((0, import_os8.homedir)(), ".local", "bin", "claude.exe"),
137723
- (0, import_path15.join)(
137724
- process.env.LOCALAPPDATA ?? (0, import_path15.join)((0, import_os8.homedir)(), "AppData", "Local"),
137948
+ (0, import_path16.join)((0, import_os8.homedir)(), ".local", "bin", "claude.exe"),
137949
+ (0, import_path16.join)(
137950
+ process.env.LOCALAPPDATA ?? (0, import_path16.join)((0, import_os8.homedir)(), "AppData", "Local"),
137725
137951
  "Microsoft",
137726
137952
  "WindowsApps",
137727
137953
  "claude.exe"
@@ -137748,7 +137974,7 @@ function resolveClaudeExe() {
137748
137974
  const candidates = [
137749
137975
  "/opt/homebrew/bin/claude",
137750
137976
  "/usr/local/bin/claude",
137751
- (0, import_path15.join)((0, import_os8.homedir)(), ".local", "bin", "claude")
137977
+ (0, import_path16.join)((0, import_os8.homedir)(), ".local", "bin", "claude")
137752
137978
  ];
137753
137979
  for (const p2 of candidates) {
137754
137980
  if ((0, import_fs19.existsSync)(p2)) {
@@ -137777,9 +138003,9 @@ function resolveCodexExe() {
137777
138003
  } catch {
137778
138004
  }
137779
138005
  const candidates = [
137780
- (0, import_path15.join)((0, import_os8.homedir)(), ".local", "bin", "codex.exe"),
137781
- (0, import_path15.join)(
137782
- process.env.LOCALAPPDATA ?? (0, import_path15.join)((0, import_os8.homedir)(), "AppData", "Local"),
138006
+ (0, import_path16.join)((0, import_os8.homedir)(), ".local", "bin", "codex.exe"),
138007
+ (0, import_path16.join)(
138008
+ process.env.LOCALAPPDATA ?? (0, import_path16.join)((0, import_os8.homedir)(), "AppData", "Local"),
137783
138009
  "Microsoft",
137784
138010
  "WindowsApps",
137785
138011
  "codex.exe"
@@ -137806,7 +138032,7 @@ function resolveCodexExe() {
137806
138032
  const candidates = [
137807
138033
  "/opt/homebrew/bin/codex",
137808
138034
  "/usr/local/bin/codex",
137809
- (0, import_path15.join)((0, import_os8.homedir)(), ".local", "bin", "codex")
138035
+ (0, import_path16.join)((0, import_os8.homedir)(), ".local", "bin", "codex")
137810
138036
  ];
137811
138037
  for (const p2 of candidates) {
137812
138038
  if ((0, import_fs19.existsSync)(p2)) {
@@ -137902,7 +138128,7 @@ var CodexPtyRunner = class {
137902
138128
  }
137903
138129
  async doStart(sessionId, options) {
137904
138130
  const nodePty = await loadPty();
137905
- const projectName = options.projectName ?? (0, import_path16.basename)(options.projectPath);
138131
+ const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
137906
138132
  const proc = nodePty.spawn(
137907
138133
  resolveCodexExe(),
137908
138134
  ["resume", sessionId, "--cd", options.projectPath, "--no-alt-screen"],
@@ -137947,7 +138173,7 @@ var CodexPtyRunner = class {
137947
138173
  async startFresh(options) {
137948
138174
  const nodePty = await loadPty();
137949
138175
  const sessionId = (0, import_crypto6.randomUUID)();
137950
- const projectName = options.projectName ?? (0, import_path16.basename)(options.projectPath);
138176
+ const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
137951
138177
  const args = ["--cd", options.projectPath, "--no-alt-screen"];
137952
138178
  if (options.systemPrompt) {
137953
138179
  args.push(options.systemPrompt);
@@ -138277,7 +138503,7 @@ function stripAnsi(str) {
138277
138503
  var import_headless2 = __toESM(require_xterm_headless(), 1);
138278
138504
  var import_crypto7 = require("crypto");
138279
138505
  var import_fs21 = require("fs");
138280
- var import_path17 = require("path");
138506
+ var import_path18 = require("path");
138281
138507
  init_logger();
138282
138508
 
138283
138509
  // src/services/questions/detectPermissionGate.ts
@@ -138629,7 +138855,7 @@ var PTYManager = class {
138629
138855
  }
138630
138856
  async doStart(sessionId, options) {
138631
138857
  const nodePty = await loadPty2();
138632
- const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
138858
+ const projectName = options.projectName ?? (0, import_path18.basename)(options.projectPath);
138633
138859
  const proc = nodePty.spawn(
138634
138860
  resolveClaudeExe(),
138635
138861
  [
@@ -138680,7 +138906,7 @@ var PTYManager = class {
138680
138906
  async startFresh(options) {
138681
138907
  const nodePty = await loadPty2();
138682
138908
  const sessionId = (0, import_crypto7.randomUUID)();
138683
- const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
138909
+ const projectName = options.projectName ?? (0, import_path18.basename)(options.projectPath);
138684
138910
  const args = [
138685
138911
  "--permission-mode",
138686
138912
  options.permissionMode ?? "acceptEdits",
@@ -139252,7 +139478,7 @@ var LiveSessionManager = class {
139252
139478
  const runner = this.runners.get(provider);
139253
139479
  if (runner) return runner;
139254
139480
  const err = new Error(
139255
- `Live ${provider} sessions are not implemented yet for ${(0, import_path18.basename)(projectPath)}`
139481
+ `Live ${provider} sessions are not implemented yet for ${(0, import_path19.basename)(projectPath)}`
139256
139482
  );
139257
139483
  err.statusCode = 501;
139258
139484
  throw err;
@@ -139319,7 +139545,7 @@ var PairTokenStore = class {
139319
139545
  // src/process-discovery.ts
139320
139546
  var import_child_process2 = require("child_process");
139321
139547
  var import_os9 = require("os");
139322
- var import_path19 = require("path");
139548
+ var import_path20 = require("path");
139323
139549
  async function discoverClaudeProcesses() {
139324
139550
  if ((0, import_os9.platform)() === "win32") return discoverWindows();
139325
139551
  return discoverUnix();
@@ -139338,7 +139564,7 @@ async function discoverUnix() {
139338
139564
  return {
139339
139565
  pid,
139340
139566
  projectPath: cwd,
139341
- projectName: (0, import_path19.basename)(cwd),
139567
+ projectName: (0, import_path20.basename)(cwd),
139342
139568
  branch: await readGitBranch2(cwd),
139343
139569
  conversationId,
139344
139570
  startedAt
@@ -139360,7 +139586,7 @@ async function discoverWindows() {
139360
139586
  return {
139361
139587
  pid,
139362
139588
  projectPath: info.cwd,
139363
- projectName: (0, import_path19.basename)(info.cwd),
139589
+ projectName: (0, import_path20.basename)(info.cwd),
139364
139590
  branch: await readGitBranch2(info.cwd),
139365
139591
  conversationId: extractResumeId(info.args),
139366
139592
  startedAt: info.startedAt
@@ -139441,7 +139667,7 @@ async function getProcessInfoWindows(pid) {
139441
139667
  const startedAt = /* @__PURE__ */ new Date(`${year}-${month}-${day}T${hour}:${min}:${sec}`);
139442
139668
  if (Number.isNaN(startedAt.getTime())) return null;
139443
139669
  const exePath = parts[3] ?? "";
139444
- const cwd = exePath ? (0, import_path19.dirname)(exePath) : "";
139670
+ const cwd = exePath ? (0, import_path20.dirname)(exePath) : "";
139445
139671
  return { cwd, args, startedAt };
139446
139672
  } catch {
139447
139673
  return null;
@@ -139483,11 +139709,11 @@ function seal(plaintext, recipientPublicKeyBase64) {
139483
139709
  // node_modules/chokidar/index.js
139484
139710
  var import_node_events2 = require("events");
139485
139711
  var import_node_fs7 = require("fs");
139486
- var import_promises12 = require("fs/promises");
139712
+ var import_promises14 = require("fs/promises");
139487
139713
  var sp2 = __toESM(require("path"), 1);
139488
139714
 
139489
139715
  // node_modules/readdirp/index.js
139490
- var import_promises10 = require("fs/promises");
139716
+ var import_promises12 = require("fs/promises");
139491
139717
  var import_node_path9 = require("path");
139492
139718
  var import_node_stream2 = require("stream");
139493
139719
  var EntryTypes2 = {
@@ -139568,7 +139794,7 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
139568
139794
  const { root, type } = opts;
139569
139795
  this._fileFilter = normalizeFilter2(opts.fileFilter);
139570
139796
  this._directoryFilter = normalizeFilter2(opts.directoryFilter);
139571
- const statMethod = opts.lstat ? import_promises10.lstat : import_promises10.stat;
139797
+ const statMethod = opts.lstat ? import_promises12.lstat : import_promises12.stat;
139572
139798
  if (wantBigintFsStats2) {
139573
139799
  this._stat = (path2) => statMethod(path2, { bigint: true });
139574
139800
  } else {
@@ -139639,7 +139865,7 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
139639
139865
  async _exploreDir(path2, depth) {
139640
139866
  let files;
139641
139867
  try {
139642
- files = await (0, import_promises10.readdir)(path2, this._rdOptions);
139868
+ files = await (0, import_promises12.readdir)(path2, this._rdOptions);
139643
139869
  } catch (error51) {
139644
139870
  this._onError(error51);
139645
139871
  }
@@ -139647,10 +139873,10 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
139647
139873
  }
139648
139874
  async _formatEntry(dirent, path2) {
139649
139875
  let entry;
139650
- const basename11 = this._isDirent ? dirent.name : dirent;
139876
+ const basename12 = this._isDirent ? dirent.name : dirent;
139651
139877
  try {
139652
- const fullPath = (0, import_node_path9.resolve)((0, import_node_path9.join)(path2, basename11));
139653
- entry = { path: (0, import_node_path9.relative)(this._root, fullPath), fullPath, basename: basename11 };
139878
+ const fullPath = (0, import_node_path9.resolve)((0, import_node_path9.join)(path2, basename12));
139879
+ entry = { path: (0, import_node_path9.relative)(this._root, fullPath), fullPath, basename: basename12 };
139654
139880
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
139655
139881
  } catch (err) {
139656
139882
  this._onError(err);
@@ -139677,8 +139903,8 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
139677
139903
  if (stats && stats.isSymbolicLink()) {
139678
139904
  const full = entry.fullPath;
139679
139905
  try {
139680
- const entryRealPath = await (0, import_promises10.realpath)(full);
139681
- const entryRealPathStats = await (0, import_promises10.lstat)(entryRealPath);
139906
+ const entryRealPath = await (0, import_promises12.realpath)(full);
139907
+ const entryRealPathStats = await (0, import_promises12.lstat)(entryRealPath);
139682
139908
  if (entryRealPathStats.isFile()) {
139683
139909
  return "file";
139684
139910
  }
@@ -139721,7 +139947,7 @@ function readdirp2(root, options = {}) {
139721
139947
 
139722
139948
  // node_modules/chokidar/handler.js
139723
139949
  var import_node_fs6 = require("fs");
139724
- var import_promises11 = require("fs/promises");
139950
+ var import_promises13 = require("fs/promises");
139725
139951
  var import_node_os3 = require("os");
139726
139952
  var sp = __toESM(require("path"), 1);
139727
139953
  var STR_DATA2 = "data";
@@ -139748,7 +139974,7 @@ var EVENTS2 = {
139748
139974
  };
139749
139975
  var EV2 = EVENTS2;
139750
139976
  var THROTTLE_MODE_WATCH2 = "watch";
139751
- var statMethods2 = { lstat: import_promises11.lstat, stat: import_promises11.stat };
139977
+ var statMethods2 = { lstat: import_promises13.lstat, stat: import_promises13.stat };
139752
139978
  var KEY_LISTENERS2 = "listeners";
139753
139979
  var KEY_ERR2 = "errHandlers";
139754
139980
  var KEY_RAW2 = "rawEmitters";
@@ -140105,7 +140331,7 @@ var setFsWatchListener2 = (path2, fullPath, options, handlers) => {
140105
140331
  cont.watcherUnusable = true;
140106
140332
  if (isWindows3 && error51.code === "EPERM") {
140107
140333
  try {
140108
- const fd = await (0, import_promises11.open)(path2, "r");
140334
+ const fd = await (0, import_promises13.open)(path2, "r");
140109
140335
  await fd.close();
140110
140336
  broadcastErr(error51);
140111
140337
  } catch (err) {
@@ -140191,9 +140417,9 @@ var NodeFsHandler2 = class {
140191
140417
  _watchWithNodeFs(path2, listener) {
140192
140418
  const opts = this.fsw.options;
140193
140419
  const directory = sp.dirname(path2);
140194
- const basename11 = sp.basename(path2);
140420
+ const basename12 = sp.basename(path2);
140195
140421
  const parent = this.fsw._getWatchedDir(directory);
140196
- parent.add(basename11);
140422
+ parent.add(basename12);
140197
140423
  const absolutePath = sp.resolve(path2);
140198
140424
  const options = {
140199
140425
  persistent: opts.persistent
@@ -140203,7 +140429,7 @@ var NodeFsHandler2 = class {
140203
140429
  let closer;
140204
140430
  if (opts.usePolling) {
140205
140431
  const enableBin = opts.interval !== opts.binaryInterval;
140206
- options.interval = enableBin && isBinaryPath2(basename11) ? opts.binaryInterval : opts.interval;
140432
+ options.interval = enableBin && isBinaryPath2(basename12) ? opts.binaryInterval : opts.interval;
140207
140433
  closer = setFsWatchFileListener2(path2, absolutePath, options, {
140208
140434
  listener,
140209
140435
  rawEmitter: this.fsw._emitRaw
@@ -140226,17 +140452,17 @@ var NodeFsHandler2 = class {
140226
140452
  return;
140227
140453
  }
140228
140454
  const dirname17 = sp.dirname(file2);
140229
- const basename11 = sp.basename(file2);
140455
+ const basename12 = sp.basename(file2);
140230
140456
  const parent = this.fsw._getWatchedDir(dirname17);
140231
140457
  let prevStats = stats;
140232
- if (parent.has(basename11))
140458
+ if (parent.has(basename12))
140233
140459
  return;
140234
140460
  const listener = async (path2, newStats) => {
140235
140461
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH2, file2, 5))
140236
140462
  return;
140237
140463
  if (!newStats || newStats.mtimeMs === 0) {
140238
140464
  try {
140239
- const newStats2 = await (0, import_promises11.stat)(file2);
140465
+ const newStats2 = await (0, import_promises13.stat)(file2);
140240
140466
  if (this.fsw.closed)
140241
140467
  return;
140242
140468
  const at2 = newStats2.atimeMs;
@@ -140254,9 +140480,9 @@ var NodeFsHandler2 = class {
140254
140480
  prevStats = newStats2;
140255
140481
  }
140256
140482
  } catch (error51) {
140257
- this.fsw._remove(dirname17, basename11);
140483
+ this.fsw._remove(dirname17, basename12);
140258
140484
  }
140259
- } else if (parent.has(basename11)) {
140485
+ } else if (parent.has(basename12)) {
140260
140486
  const at2 = newStats.atimeMs;
140261
140487
  const mt2 = newStats.mtimeMs;
140262
140488
  if (!at2 || at2 <= mt2 || mt2 !== prevStats.mtimeMs) {
@@ -140291,7 +140517,7 @@ var NodeFsHandler2 = class {
140291
140517
  this.fsw._incrReadyCount();
140292
140518
  let linkPath;
140293
140519
  try {
140294
- linkPath = await (0, import_promises11.realpath)(path2);
140520
+ linkPath = await (0, import_promises13.realpath)(path2);
140295
140521
  } catch (e) {
140296
140522
  this.fsw._emitReady();
140297
140523
  return true;
@@ -140440,7 +140666,7 @@ var NodeFsHandler2 = class {
140440
140666
  let closer;
140441
140667
  if (stats.isDirectory()) {
140442
140668
  const absPath = sp.resolve(path2);
140443
- const targetPath = follow ? await (0, import_promises11.realpath)(path2) : path2;
140669
+ const targetPath = follow ? await (0, import_promises13.realpath)(path2) : path2;
140444
140670
  if (this.fsw.closed)
140445
140671
  return;
140446
140672
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -140450,7 +140676,7 @@ var NodeFsHandler2 = class {
140450
140676
  this.fsw._symlinkPaths.set(absPath, targetPath);
140451
140677
  }
140452
140678
  } else if (stats.isSymbolicLink()) {
140453
- const targetPath = follow ? await (0, import_promises11.realpath)(path2) : path2;
140679
+ const targetPath = follow ? await (0, import_promises13.realpath)(path2) : path2;
140454
140680
  if (this.fsw.closed)
140455
140681
  return;
140456
140682
  const parent = sp.dirname(wh.watchPath);
@@ -140610,7 +140836,7 @@ var DirEntry2 = class {
140610
140836
  return;
140611
140837
  const dir = this.path;
140612
140838
  try {
140613
- await (0, import_promises12.readdir)(dir);
140839
+ await (0, import_promises14.readdir)(dir);
140614
140840
  } catch (err) {
140615
140841
  if (this._removeWatcher) {
140616
140842
  this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
@@ -140960,7 +141186,7 @@ var FSWatcher2 = class extends import_node_events2.EventEmitter {
140960
141186
  const fullPath = opts.cwd ? sp2.join(opts.cwd, path2) : path2;
140961
141187
  let stats2;
140962
141188
  try {
140963
- stats2 = await (0, import_promises12.stat)(fullPath);
141189
+ stats2 = await (0, import_promises14.stat)(fullPath);
140964
141190
  } catch (err) {
140965
141191
  }
140966
141192
  if (!stats2 || this.closed)
@@ -141214,7 +141440,7 @@ var chokidar_default = { watch: watch2, FSWatcher: FSWatcher2 };
141214
141440
 
141215
141441
  // src/services/conversations/conversationWatcher.ts
141216
141442
  var import_fs22 = require("fs");
141217
- var import_promises13 = require("fs/promises");
141443
+ var import_promises15 = require("fs/promises");
141218
141444
  var ConversationWatcher = class {
141219
141445
  files = /* @__PURE__ */ new Map();
141220
141446
  directories = /* @__PURE__ */ new Map();
@@ -141318,12 +141544,12 @@ var ConversationWatcher = class {
141318
141544
  entry.reading = true;
141319
141545
  try {
141320
141546
  for (; ; ) {
141321
- const st2 = await (0, import_promises13.stat)(filePath);
141547
+ const st2 = await (0, import_promises15.stat)(filePath);
141322
141548
  if (st2.size <= entry.offset) break;
141323
141549
  const readFrom = entry.offset;
141324
141550
  const bytesToRead = st2.size - readFrom;
141325
141551
  const buf = Buffer.alloc(bytesToRead);
141326
- const fh = await (0, import_promises13.open)(filePath, "r");
141552
+ const fh = await (0, import_promises15.open)(filePath, "r");
141327
141553
  try {
141328
141554
  await fh.read(buf, 0, bytesToRead, readFrom);
141329
141555
  } finally {
@@ -141772,9 +141998,9 @@ function discoveredToResponse(d, conversationId) {
141772
141998
 
141773
141999
  // src/uploads.ts
141774
142000
  var import_crypto9 = require("crypto");
141775
- var import_promises14 = require("fs/promises");
142001
+ var import_promises16 = require("fs/promises");
141776
142002
  var import_heic_convert = __toESM(require_heic_convert(), 1);
141777
- var import_path20 = require("path");
142003
+ var import_path21 = require("path");
141778
142004
  var UPLOAD_DIR_NAME = ".threadbase-uploads";
141779
142005
  var MAX_BYTES = 25 * 1024 * 1024;
141780
142006
  var HEIC_MIMES = /* @__PURE__ */ new Set(["image/heic", "image/heif"]);
@@ -141807,10 +142033,10 @@ async function saveUploadFile(input) {
141807
142033
  }
141808
142034
  const id = `up_${(0, import_crypto9.randomBytes)(8).toString("hex")}`;
141809
142035
  const safeName = sanitizeFilename(originalName) || `file${MIME_TO_EXT[mimeType] ?? ""}`;
141810
- const dir = (0, import_path20.join)(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
141811
- await (0, import_promises14.mkdir)(dir, { recursive: true });
141812
- const filePath = (0, import_path20.join)(dir, `${Date.now()}-${id}-${safeName}`);
141813
- await (0, import_promises14.writeFile)(filePath, buffer);
142036
+ const dir = (0, import_path21.join)(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
142037
+ await (0, import_promises16.mkdir)(dir, { recursive: true });
142038
+ const filePath = (0, import_path21.join)(dir, `${Date.now()}-${id}-${safeName}`);
142039
+ await (0, import_promises16.writeFile)(filePath, buffer);
141814
142040
  return {
141815
142041
  id,
141816
142042
  filePath,
@@ -142310,11 +142536,11 @@ var StreamerServer = class {
142310
142536
  this.disableDb = config2.disableDb ?? false;
142311
142537
  this.scannerPersistenceDisabled = config2.scannerPersistent === false;
142312
142538
  this.scanProfiles = config2.scanProfiles;
142313
- this.codexRoots = config2.codexRoots ?? [(0, import_path21.join)((0, import_os10.homedir)(), ".codex", "sessions")];
142539
+ this.codexRoots = config2.codexRoots ?? [(0, import_path22.join)((0, import_os10.homedir)(), ".codex", "sessions")];
142314
142540
  this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
142315
142541
  this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
142316
142542
  this.defaultPermissionMode = config2.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
142317
- this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path21.join)((0, import_os10.homedir)(), ".threadbase", "cache");
142543
+ this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path22.join)((0, import_os10.homedir)(), ".threadbase", "cache");
142318
142544
  this.tailSize = config2.tailSize ?? loadTailSize() ?? 10;
142319
142545
  this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config2.directoryScanDebounceMs ?? 1e3;
142320
142546
  this.markScannerStaleDebounced = debounce(() => {
@@ -142325,7 +142551,7 @@ var StreamerServer = class {
142325
142551
  this.agentEntrypoints = parseAgentEntrypointsEnv(process.env.THREADBASE_AGENT_ENTRYPOINTS);
142326
142552
  const rawRoot = process.env.THREADBASE_BROWSE_ROOT ?? loadBrowseRoot() ?? config2.browseRoot;
142327
142553
  if (rawRoot) {
142328
- (0, import_promises15.realpath)(rawRoot).then((resolved) => {
142554
+ (0, import_promises17.realpath)(rawRoot).then((resolved) => {
142329
142555
  this.browseRoot = resolved;
142330
142556
  if (this.verbose) this.log.info(`Browse root: ${resolved}`, { browseRoot: resolved });
142331
142557
  }).catch(() => {
@@ -142481,7 +142707,7 @@ var StreamerServer = class {
142481
142707
  temporalClient,
142482
142708
  taskQueue: agentConfig.temporal.taskQueue
142483
142709
  });
142484
- const conversationsBaseDir = agentConfig.conversationsDir || (0, import_path21.join)((0, import_path21.dirname)(this.cacheDir), "conversations");
142710
+ const conversationsBaseDir = agentConfig.conversationsDir || (0, import_path22.join)((0, import_path22.dirname)(this.cacheDir), "conversations");
142485
142711
  conversationWriter = createConversationWriter({
142486
142712
  baseDir: conversationsBaseDir
142487
142713
  });
@@ -142705,7 +142931,7 @@ var StreamerServer = class {
142705
142931
  });
142706
142932
  try {
142707
142933
  this.cache = ConversationCache.open(
142708
- (0, import_path21.join)(this.cacheDir, "cache.db"),
142934
+ (0, import_path22.join)(this.cacheDir, "cache.db"),
142709
142935
  this.tailSize,
142710
142936
  void 0,
142711
142937
  {
@@ -142731,11 +142957,11 @@ var StreamerServer = class {
142731
142957
  if (this.scanProfiles && this.scanProfiles.length > 0) {
142732
142958
  for (const profile of this.scanProfiles) {
142733
142959
  if (profile.enabled) {
142734
- this.fileWatcher.watchDirectory((0, import_path21.join)(profile.configDir, "projects"));
142960
+ this.fileWatcher.watchDirectory((0, import_path22.join)(profile.configDir, "projects"));
142735
142961
  }
142736
142962
  }
142737
142963
  } else {
142738
- this.fileWatcher.watchDirectory((0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects"));
142964
+ this.fileWatcher.watchDirectory((0, import_path22.join)((0, import_os10.homedir)(), ".claude", "projects"));
142739
142965
  }
142740
142966
  } catch (err) {
142741
142967
  const message = err instanceof Error ? err.message : String(err);
@@ -143289,16 +143515,16 @@ var StreamerServer = class {
143289
143515
  return scanner;
143290
143516
  }
143291
143517
  findJsonlPath(uuid3) {
143292
- const projectsDir = (0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects");
143518
+ const projectsDir = (0, import_path22.join)((0, import_os10.homedir)(), ".claude", "projects");
143293
143519
  if (!(0, import_fs24.existsSync)(projectsDir)) return null;
143294
143520
  const filename = `${uuid3}.jsonl`;
143295
143521
  for (const dir of (0, import_fs24.readdirSync)(projectsDir)) {
143296
- const fp = (0, import_path21.join)(projectsDir, dir, filename);
143522
+ const fp = (0, import_path22.join)(projectsDir, dir, filename);
143297
143523
  if ((0, import_fs24.existsSync)(fp)) return fp;
143298
- const projectDir = (0, import_path21.join)(projectsDir, dir);
143524
+ const projectDir = (0, import_path22.join)(projectsDir, dir);
143299
143525
  try {
143300
143526
  for (const sub of (0, import_fs24.readdirSync)(projectDir)) {
143301
- const subagentPath = (0, import_path21.join)(projectDir, sub, "subagents", filename);
143527
+ const subagentPath = (0, import_path22.join)(projectDir, sub, "subagents", filename);
143302
143528
  if ((0, import_fs24.existsSync)(subagentPath)) return subagentPath;
143303
143529
  }
143304
143530
  } catch {
@@ -143522,7 +143748,7 @@ var StreamerServer = class {
143522
143748
  });
143523
143749
  const conv = conversation;
143524
143750
  const cachedConvMeta = this.cache?.getMetaById(id);
143525
- const convProvider = conv.provider ?? cachedConvMeta?.provider ?? CLAUDE_CODE_PROVIDER2;
143751
+ const convProvider = coerceProviderForRunner(conv.provider ?? cachedConvMeta?.provider);
143526
143752
  const availability = classifyResumability(conv.projectPath);
143527
143753
  const body = {
143528
143754
  meta: {
@@ -143736,7 +143962,7 @@ var StreamerServer = class {
143736
143962
  return;
143737
143963
  }
143738
143964
  const cachedConvMeta = this.cache?.getMetaById(sessionId);
143739
- const provider = conv?.provider ?? cachedConvMeta?.provider ?? CLAUDE_CODE_PROVIDER2;
143965
+ const provider = coerceProviderForRunner(conv?.provider ?? cachedConvMeta?.provider);
143740
143966
  const session = await this.ptyManager.start(sessionId, {
143741
143967
  provider,
143742
143968
  projectPath,
@@ -144091,7 +144317,7 @@ var StreamerServer = class {
144091
144317
  sessionStore: this.sessionStore,
144092
144318
  // biome-ignore lint/style/noNonNullAssertion: agentClient is set when agentConfig.enabled is true
144093
144319
  agentClient: this.agentClient,
144094
- conversationsDir: this.cacheDir ? (0, import_path21.join)((0, import_path21.dirname)(this.cacheDir), "conversations") : "",
144320
+ conversationsDir: this.cacheDir ? (0, import_path22.join)((0, import_path22.dirname)(this.cacheDir), "conversations") : "",
144095
144321
  agentConfig: this.agentConfig
144096
144322
  });
144097
144323
  json2(res, result.status, result.body);
@@ -144232,9 +144458,9 @@ var StreamerServer = class {
144232
144458
  // was passed to Claude via --session-id so the filename matches from the start.
144233
144459
  watchForJsonl(sessionId, projectPath) {
144234
144460
  const encoded = projectPath.replace(/[/\\:.]/g, "-");
144235
- const projectsDir = (0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects", encoded);
144461
+ const projectsDir = (0, import_path22.join)((0, import_os10.homedir)(), ".claude", "projects", encoded);
144236
144462
  const expectedFile = `${sessionId}.jsonl`;
144237
- const filePath = (0, import_path21.join)(projectsDir, expectedFile);
144463
+ const filePath = (0, import_path22.join)(projectsDir, expectedFile);
144238
144464
  const deadline = Date.now() + 12e4;
144239
144465
  let watcher = null;
144240
144466
  const cleanup = () => {
@@ -144256,8 +144482,8 @@ var StreamerServer = class {
144256
144482
  if (!resolvedFilePath && (0, import_fs24.existsSync)(projectsDir)) {
144257
144483
  try {
144258
144484
  const now = Date.now();
144259
- const recent = (0, import_fs24.readdirSync)(projectsDir).filter((f2) => f2.endsWith(".jsonl")).map((f2) => ({ f: f2, mtime: (0, import_fs24.statSync)((0, import_path21.join)(projectsDir, f2)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).sort((a, b2) => b2.mtime - a.mtime)[0];
144260
- if (recent) resolvedFilePath = (0, import_path21.join)(projectsDir, recent.f);
144485
+ const recent = (0, import_fs24.readdirSync)(projectsDir).filter((f2) => f2.endsWith(".jsonl")).map((f2) => ({ f: f2, mtime: (0, import_fs24.statSync)((0, import_path22.join)(projectsDir, f2)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).sort((a, b2) => b2.mtime - a.mtime)[0];
144486
+ if (recent) resolvedFilePath = (0, import_path22.join)(projectsDir, recent.f);
144261
144487
  } catch {
144262
144488
  }
144263
144489
  }
@@ -144308,7 +144534,7 @@ var StreamerServer = class {
144308
144534
  watchForCodexRollout(sessionId, projectPath) {
144309
144535
  const deadline = Date.now() + 12e4;
144310
144536
  const now = /* @__PURE__ */ new Date();
144311
- const dateDir = (0, import_path21.join)(
144537
+ const dateDir = (0, import_path22.join)(
144312
144538
  String(now.getFullYear()),
144313
144539
  String(now.getMonth() + 1).padStart(2, "0"),
144314
144540
  String(now.getDate()).padStart(2, "0")
@@ -144349,7 +144575,7 @@ var StreamerServer = class {
144349
144575
  this.sessionStore.listManaged().filter((s3) => s3.id !== sessionId && s3.boundConversationId != null).map((s3) => s3.boundConversationId)
144350
144576
  );
144351
144577
  for (const root of this.codexRoots) {
144352
- const sessionsDir = (0, import_path21.join)(root, dateDir);
144578
+ const sessionsDir = (0, import_path22.join)(root, dateDir);
144353
144579
  if (!(0, import_fs24.existsSync)(sessionsDir)) continue;
144354
144580
  let candidateFiles;
144355
144581
  try {
@@ -144358,9 +144584,9 @@ var StreamerServer = class {
144358
144584
  continue;
144359
144585
  }
144360
144586
  const nowMs = Date.now();
144361
- const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0, import_fs24.statSync)((0, import_path21.join)(sessionsDir, f2)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b2) => b2.mtime - a.mtime);
144587
+ const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0, import_fs24.statSync)((0, import_path22.join)(sessionsDir, f2)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b2) => b2.mtime - a.mtime);
144362
144588
  for (const { f: f2 } of recentCandidates) {
144363
- const candidatePath = (0, import_path21.join)(sessionsDir, f2);
144589
+ const candidatePath = (0, import_path22.join)(sessionsDir, f2);
144364
144590
  const match2 = matchesProjectPath(candidatePath);
144365
144591
  if (!match2) continue;
144366
144592
  if (boundElsewhere.has(match2.id)) continue;
@@ -144814,7 +145040,7 @@ function isBrewInstall(scriptPath = process.argv[1] ?? "") {
144814
145040
  var import_node_crypto4 = require("crypto");
144815
145041
  var import_node_fs8 = require("fs");
144816
145042
  var import_node_path10 = require("path");
144817
- var import_promises16 = require("stream/promises");
145043
+ var import_promises18 = require("stream/promises");
144818
145044
 
144819
145045
  // src/updater/manifest.ts
144820
145046
  init_zod();
@@ -144889,7 +145115,7 @@ async function downloadAndVerify(opts) {
144889
145115
  }
144890
145116
  });
144891
145117
  try {
144892
- await (0, import_promises16.pipeline)(res.body.pipeThrough(measured), out);
145118
+ await (0, import_promises18.pipeline)(res.body.pipeThrough(measured), out);
144893
145119
  } catch (err) {
144894
145120
  (0, import_node_fs8.rmSync)(targetPath, { force: true });
144895
145121
  throw err;
@@ -145081,7 +145307,7 @@ var import_node_stream3 = __toESM(require("stream"), 1);
145081
145307
  var import_node_string_decoder = require("string_decoder");
145082
145308
  var import_node_path14 = __toESM(require("path"), 1);
145083
145309
  var import_node_fs11 = __toESM(require("fs"), 1);
145084
- var import_path22 = require("path");
145310
+ var import_path23 = require("path");
145085
145311
  var import_events5 = require("events");
145086
145312
  var import_assert = __toESM(require("assert"), 1);
145087
145313
  var import_buffer2 = require("buffer");
@@ -145091,9 +145317,9 @@ var import_node_path15 = require("path");
145091
145317
  var import_node_path16 = require("path");
145092
145318
  var import_fs26 = __toESM(require("fs"), 1);
145093
145319
  var import_fs27 = __toESM(require("fs"), 1);
145094
- var import_path23 = __toESM(require("path"), 1);
145095
- var import_node_path17 = require("path");
145096
145320
  var import_path24 = __toESM(require("path"), 1);
145321
+ var import_node_path17 = require("path");
145322
+ var import_path25 = __toESM(require("path"), 1);
145097
145323
  var import_node_fs12 = __toESM(require("fs"), 1);
145098
145324
  var import_node_assert = __toESM(require("assert"), 1);
145099
145325
  var import_node_crypto5 = require("crypto");
@@ -145103,7 +145329,7 @@ var import_fs28 = __toESM(require("fs"), 1);
145103
145329
  var import_node_fs14 = __toESM(require("fs"), 1);
145104
145330
  var import_node_path19 = __toESM(require("path"), 1);
145105
145331
  var import_node_fs15 = __toESM(require("fs"), 1);
145106
- var import_promises17 = __toESM(require("fs/promises"), 1);
145332
+ var import_promises19 = __toESM(require("fs/promises"), 1);
145107
145333
  var import_node_path20 = __toESM(require("path"), 1);
145108
145334
  var import_node_path21 = require("path");
145109
145335
  var import_node_fs16 = __toESM(require("fs"), 1);
@@ -146458,11 +146684,11 @@ var vn = (s3) => {
146458
146684
  };
146459
146685
  var Qi = (s3, t2) => {
146460
146686
  let e = new Map(t2.map((n) => [ut(n), true])), i = s3.filter, r = (n, o = "") => {
146461
- let h = o || (0, import_path22.parse)(n).root || ".", a;
146687
+ let h = o || (0, import_path23.parse)(n).root || ".", a;
146462
146688
  if (n === h) a = false;
146463
146689
  else {
146464
146690
  let l = e.get(n);
146465
- a = l !== void 0 ? l : r((0, import_path22.dirname)(n), h);
146691
+ a = l !== void 0 ? l : r((0, import_path23.dirname)(n), h);
146466
146692
  }
146467
146693
  return e.set(n, a), a;
146468
146694
  };
@@ -146582,7 +146808,7 @@ var de = class extends A {
146582
146808
  let [o, h] = ce(this.path);
146583
146809
  o && typeof h == "string" && (this.path = h, r = o);
146584
146810
  }
146585
- this.win32 = !!i.win32 || process.platform === "win32", this.win32 && (this.path = Qs(this.path.replaceAll(/\\/g, "/")), t2 = t2.replaceAll(/\\/g, "/")), this.absolute = f(i.absolute || import_path23.default.resolve(this.cwd, t2)), this.path === "" && (this.path = "./"), r && this.warn("TAR_ENTRY_INFO", `stripping ${r} from absolute path`, { entry: this, path: r + this.path });
146811
+ this.win32 = !!i.win32 || process.platform === "win32", this.win32 && (this.path = Qs(this.path.replaceAll(/\\/g, "/")), t2 = t2.replaceAll(/\\/g, "/")), this.absolute = f(i.absolute || import_path24.default.resolve(this.cwd, t2)), this.path === "" && (this.path = "./"), r && this.warn("TAR_ENTRY_INFO", `stripping ${r} from absolute path`, { entry: this, path: r + this.path });
146586
146812
  let n = this.statCache.get(this.absolute);
146587
146813
  n ? this[si](n) : this[ss]();
146588
146814
  }
@@ -146641,7 +146867,7 @@ var de = class extends A {
146641
146867
  }
146642
146868
  [sr](t2) {
146643
146869
  if (!this.stat) throw new Error("cannot create link entry without stat");
146644
- this.type = "Link", this.linkpath = f(import_path23.default.relative(this.cwd, t2)), this.stat.size = 0, this[fe](), this.end();
146870
+ this.type = "Link", this.linkpath = f(import_path24.default.relative(this.cwd, t2)), this.stat.size = 0, this[fe](), this.end();
146645
146871
  }
146646
146872
  [er]() {
146647
146873
  if (!this.stat) throw new Error("cannot create file entry without stat");
@@ -147055,7 +147281,7 @@ var wt = class extends A {
147055
147281
  return typeof t2 == "string" ? this[ci](t2) : this[or](t2), this.flowing;
147056
147282
  }
147057
147283
  [or](t2) {
147058
- let e = f(import_path24.default.resolve(this.cwd, t2.path));
147284
+ let e = f(import_path25.default.resolve(this.cwd, t2.path));
147059
147285
  if (!this.filter(t2.path, t2)) t2.resume();
147060
147286
  else {
147061
147287
  let i = new pi(t2.path, e);
@@ -147064,7 +147290,7 @@ var wt = class extends A {
147064
147290
  this[Ft]();
147065
147291
  }
147066
147292
  [ci](t2) {
147067
- let e = f(import_path24.default.resolve(this.cwd, t2));
147293
+ let e = f(import_path25.default.resolve(this.cwd, t2));
147068
147294
  this[W].push(new pi(t2, e)), this[Ft]();
147069
147295
  }
147070
147296
  [ds](t2) {
@@ -147335,7 +147561,7 @@ var gr = (s3, t2, e) => {
147335
147561
  E2 ? e(E2) : x2 && a ? Es(x2, o, h, (Le) => S2(Le)) : n ? import_node_fs15.default.chmod(s3, r, e) : e();
147336
147562
  };
147337
147563
  if (s3 === d) return no(s3, S2);
147338
- if (l) return import_promises17.default.mkdir(s3, { mode: r, recursive: true }).then((E2) => S2(null, E2 ?? void 0), S2);
147564
+ if (l) return import_promises19.default.mkdir(s3, { mode: r, recursive: true }).then((E2) => S2(null, E2 ?? void 0), S2);
147339
147565
  let D2 = f(import_node_path20.default.relative(d, s3)).split("/");
147340
147566
  Ss(d, D2, r, c, d, void 0, S2);
147341
147567
  };