chatroom-cli 1.98.2 → 1.98.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -110218,7 +110218,8 @@ var init_file_content_fulfillment = __esm(() => {
110218
110218
  return exports_Effect.succeed(Buffer.from("[Error reading file]"));
110219
110219
  });
110220
110220
  if (fileNotFound) {
110221
- console.log(`[${formatTimestamp()}] ⏳ File not on disk yet, deferring content sync: ${filePath}`);
110221
+ yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[File not found]", false);
110222
+ console.log(`[${formatTimestamp()}] \uD83D\uDCC4 File not found, fulfilled content request: ${filePath}`);
110222
110223
  continue;
110223
110224
  }
110224
110225
  const classification2 = classifyFileContent(filePath, new Uint8Array(buffer));
@@ -110470,6 +110471,214 @@ var init_sharded_snapshot_publish = __esm(() => {
110470
110471
  init_file_tree_partition();
110471
110472
  });
110472
110473
 
110474
+ // src/infrastructure/services/workspace/file-tree-sync-ranker.ts
110475
+ function fileExtension(p) {
110476
+ const b = (p.split("/").pop() ?? p).toLowerCase();
110477
+ if (b.endsWith(".min.js"))
110478
+ return ".min.js";
110479
+ if (b.endsWith(".min.css"))
110480
+ return ".min.css";
110481
+ const i2 = b.lastIndexOf(".");
110482
+ return i2 <= 0 ? "" : b.slice(i2);
110483
+ }
110484
+ function pathSegments(p) {
110485
+ return p.split("/").filter(Boolean);
110486
+ }
110487
+ function parentDir(p) {
110488
+ const i2 = p.lastIndexOf("/");
110489
+ return i2 < 0 ? "" : p.slice(0, i2);
110490
+ }
110491
+ function isSourceDirPath(p) {
110492
+ return pathSegments(p).slice(0, -1).some((s) => SOURCE_DIR_NAMES.has(s));
110493
+ }
110494
+ function isBuildDirPath(p) {
110495
+ return pathSegments(p).some((s) => BUILD_DIR_NAMES.has(s));
110496
+ }
110497
+ function isMarkdownFile(p) {
110498
+ return MARKDOWN_EXTENSIONS.has(fileExtension(p));
110499
+ }
110500
+ function isConfigFile(p) {
110501
+ return CONFIG_BASENAMES.has((p.split("/").pop() ?? "").toLowerCase()) || CONFIG_EXTENSIONS.has(fileExtension(p));
110502
+ }
110503
+ function isSourceFile(p) {
110504
+ return SOURCE_EXTENSIONS.has(fileExtension(p));
110505
+ }
110506
+ function isBuildOutputFile(p) {
110507
+ return BUILD_OUTPUT_EXTENSIONS.has(fileExtension(p));
110508
+ }
110509
+ function isDataFile(p) {
110510
+ return !isConfigFile(p) && DATA_EXTENSIONS.has(fileExtension(p));
110511
+ }
110512
+ function countDataFilesByParent(ps) {
110513
+ const m = new Map;
110514
+ for (const p of ps)
110515
+ if (isDataFile(p)) {
110516
+ const d = parentDir(p);
110517
+ m.set(d, (m.get(d) ?? 0) + 1);
110518
+ }
110519
+ return m;
110520
+ }
110521
+ function scoreFile(p, m) {
110522
+ let s = 0;
110523
+ if (isSourceFile(p))
110524
+ s += isSourceDirPath(p) ? SCORE_SOURCE_IN_SOURCE_DIR : SCORE_SOURCE_ELSEWHERE;
110525
+ if (isMarkdownFile(p))
110526
+ s += SCORE_MARKDOWN;
110527
+ if (isConfigFile(p))
110528
+ s += SCORE_CONFIG;
110529
+ if (isBuildDirPath(p))
110530
+ s -= PENALTY_BUILD_DIR;
110531
+ if (isBuildOutputFile(p))
110532
+ s -= PENALTY_BUILD_EXT;
110533
+ const n = m.get(parentDir(p)) ?? 0;
110534
+ return s - n * n;
110535
+ }
110536
+ function compareRankedFiles(a, as7, b, bs) {
110537
+ if (as7 !== bs)
110538
+ return bs - as7;
110539
+ const d = pathSegments(a).length - pathSegments(b).length;
110540
+ return d || a.localeCompare(b);
110541
+ }
110542
+ var SOURCE_DIR_NAMES, BUILD_DIR_NAMES, SOURCE_EXTENSIONS, MARKDOWN_EXTENSIONS, CONFIG_EXTENSIONS, CONFIG_BASENAMES, BUILD_OUTPUT_EXTENSIONS, DATA_EXTENSIONS, SCORE_SOURCE_IN_SOURCE_DIR = 1000, SCORE_MARKDOWN = 800, SCORE_CONFIG = 700, SCORE_SOURCE_ELSEWHERE = 200, PENALTY_BUILD_DIR = 5000, PENALTY_BUILD_EXT = 2000;
110543
+ var init_file_tree_sync_ranker = __esm(() => {
110544
+ SOURCE_DIR_NAMES = new Set([
110545
+ "src",
110546
+ "lib",
110547
+ "app",
110548
+ "apps",
110549
+ "packages",
110550
+ "services",
110551
+ "server",
110552
+ "client",
110553
+ "backend",
110554
+ "frontend",
110555
+ "api",
110556
+ "cmd",
110557
+ "internal",
110558
+ "pkg",
110559
+ "components",
110560
+ "modules",
110561
+ "pages",
110562
+ "routes",
110563
+ "hooks",
110564
+ "domain",
110565
+ "infrastructure",
110566
+ "entry"
110567
+ ]);
110568
+ BUILD_DIR_NAMES = new Set([
110569
+ "dist",
110570
+ "build",
110571
+ "out",
110572
+ "output",
110573
+ "coverage",
110574
+ ".next",
110575
+ ".turbo",
110576
+ ".nx",
110577
+ "_generated",
110578
+ "generated",
110579
+ "__pycache__"
110580
+ ]);
110581
+ SOURCE_EXTENSIONS = new Set([
110582
+ ".ts",
110583
+ ".tsx",
110584
+ ".js",
110585
+ ".jsx",
110586
+ ".mjs",
110587
+ ".cjs",
110588
+ ".py",
110589
+ ".go",
110590
+ ".rs",
110591
+ ".java",
110592
+ ".kt",
110593
+ ".swift",
110594
+ ".rb",
110595
+ ".php",
110596
+ ".cs",
110597
+ ".c",
110598
+ ".cc",
110599
+ ".cpp",
110600
+ ".h",
110601
+ ".hpp",
110602
+ ".vue",
110603
+ ".svelte",
110604
+ ".css",
110605
+ ".scss",
110606
+ ".less"
110607
+ ]);
110608
+ MARKDOWN_EXTENSIONS = new Set([".md", ".mdx", ".markdown"]);
110609
+ CONFIG_EXTENSIONS = new Set([".yml", ".yaml", ".toml", ".jsonc", ".ini", ".cfg"]);
110610
+ CONFIG_BASENAMES = new Set([
110611
+ "package.json",
110612
+ "package-lock.json",
110613
+ "pnpm-lock.yaml",
110614
+ "pnpm-workspace.yaml",
110615
+ "yarn.lock",
110616
+ "bun.lock",
110617
+ "tsconfig.json",
110618
+ "jsconfig.json",
110619
+ "turbo.json",
110620
+ "nx.json",
110621
+ "cargo.toml",
110622
+ "go.mod",
110623
+ "go.sum",
110624
+ "pyproject.toml",
110625
+ "requirements.txt",
110626
+ "dockerfile",
110627
+ "docker-compose.yml",
110628
+ "docker-compose.yaml",
110629
+ "makefile",
110630
+ ".gitignore",
110631
+ ".cursorignore",
110632
+ ".editorconfig",
110633
+ ".prettierrc",
110634
+ ".nvmrc",
110635
+ "eslint.config.mjs",
110636
+ "eslint.config.js",
110637
+ "biome.json",
110638
+ ".fallowrc.jsonc"
110639
+ ]);
110640
+ BUILD_OUTPUT_EXTENSIONS = new Set([
110641
+ ".map",
110642
+ ".min.js",
110643
+ ".min.css",
110644
+ ".pyc",
110645
+ ".pyo",
110646
+ ".o",
110647
+ ".obj",
110648
+ ".wasm",
110649
+ ".class",
110650
+ ".jar",
110651
+ ".exe",
110652
+ ".dll",
110653
+ ".so"
110654
+ ]);
110655
+ DATA_EXTENSIONS = new Set([
110656
+ ".json",
110657
+ ".csv",
110658
+ ".tsv",
110659
+ ".ndjson",
110660
+ ".parquet",
110661
+ ".avro",
110662
+ ".bin",
110663
+ ".dat",
110664
+ ".pb",
110665
+ ".sqlite",
110666
+ ".db",
110667
+ ".log",
110668
+ ".gz",
110669
+ ".zip",
110670
+ ".tar",
110671
+ ".png",
110672
+ ".jpg",
110673
+ ".jpeg",
110674
+ ".webp",
110675
+ ".gif",
110676
+ ".mp4",
110677
+ ".woff",
110678
+ ".woff2"
110679
+ ]);
110680
+ });
110681
+
110473
110682
  // ../../node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
110474
110683
  var require_ignore = __commonJS((exports, module) => {
110475
110684
  function makeArray(subject) {
@@ -110800,10 +111009,68 @@ var require_ignore = __commonJS((exports, module) => {
110800
111009
  // src/infrastructure/services/workspace/workspace-ignore.ts
110801
111010
  import { promises as fsPromises } from "node:fs";
110802
111011
  import { readFile as readFile10 } from "node:fs/promises";
111012
+ import { homedir as homedir7 } from "node:os";
110803
111013
  import path3 from "node:path";
110804
111014
  function normalizeRelativePath(relativePath) {
110805
111015
  return relativePath.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "");
110806
111016
  }
111017
+ function expandUserPath(filePath) {
111018
+ if (filePath === "~")
111019
+ return homedir7();
111020
+ if (filePath.startsWith("~/"))
111021
+ return path3.join(homedir7(), filePath.slice(2));
111022
+ return filePath;
111023
+ }
111024
+ function readExcludesFileFromGitconfig(content) {
111025
+ let inCore = false;
111026
+ for (const line of content.split(/\r?\n/)) {
111027
+ const section = line.match(/^\s*\[([^\]]+)\]/);
111028
+ if (section) {
111029
+ inCore = section[1].trim().toLowerCase() === "core";
111030
+ continue;
111031
+ }
111032
+ if (!inCore)
111033
+ continue;
111034
+ const match17 = line.match(/^\s*excludesfile\s*=\s*(.*?)\s*(?:[#;].*)?$/i);
111035
+ if (!match17)
111036
+ continue;
111037
+ const value = match17[1].trim().replace(/^(["'])(.*)\1$/, "$2");
111038
+ return value ? expandUserPath(value) : null;
111039
+ }
111040
+ return null;
111041
+ }
111042
+ async function readGitconfigExcludesFilePath(configPath) {
111043
+ const content = await readIgnoreFile(configPath);
111044
+ if (content === null)
111045
+ return null;
111046
+ const raw = readExcludesFileFromGitconfig(content);
111047
+ if (!raw)
111048
+ return null;
111049
+ return path3.isAbsolute(raw) ? expandUserPath(raw) : path3.resolve(path3.dirname(configPath), raw);
111050
+ }
111051
+ async function resolveGitExtraExcludeFilePaths(rootDir) {
111052
+ const globalConfig2 = process.env.GIT_CONFIG_GLOBAL?.length ? process.env.GIT_CONFIG_GLOBAL : path3.join(homedir7(), ".gitconfig");
111053
+ const localConfig = path3.join(rootDir, ".git", "config");
111054
+ let excludesFile = await readGitconfigExcludesFilePath(localConfig) ?? await readGitconfigExcludesFilePath(globalConfig2);
111055
+ if (!excludesFile) {
111056
+ excludesFile = path3.join(process.env.XDG_CONFIG_HOME?.length ? process.env.XDG_CONFIG_HOME : path3.join(homedir7(), ".config"), "git", "ignore");
111057
+ }
111058
+ const candidates = [excludesFile, path3.join(rootDir, ".git", "info", "exclude")];
111059
+ const existing = [];
111060
+ for (const candidate of candidates)
111061
+ if (await readIgnoreFile(candidate) !== null)
111062
+ existing.push(candidate);
111063
+ return existing;
111064
+ }
111065
+ async function loadGitExtraExcludeRuleSets(rootDir) {
111066
+ const result = [];
111067
+ for (const filePath of await resolveGitExtraExcludeFilePaths(rootDir)) {
111068
+ const content = await readIgnoreFile(filePath);
111069
+ if (content !== null)
111070
+ result.push({ baseDir: "", matcher: import_ignore.default().add(content) });
111071
+ }
111072
+ return result;
111073
+ }
110807
111074
  async function readIgnoreFile(filePath) {
110808
111075
  try {
110809
111076
  return await readFile10(filePath, "utf-8");
@@ -110815,7 +111082,7 @@ async function loadDirectoryIgnoreRuleSets(rootDir, relativeDir) {
110815
111082
  const normalizedDir = normalizeRelativePath(relativeDir);
110816
111083
  const absoluteDir = normalizedDir ? path3.join(rootDir, normalizedDir) : rootDir;
110817
111084
  const names = normalizedDir ? [".gitignore"] : IGNORE_FILES;
110818
- const result = [];
111085
+ const result = normalizedDir ? [] : await loadGitExtraExcludeRuleSets(rootDir);
110819
111086
  for (const name of names) {
110820
111087
  const content = await readIgnoreFile(path3.join(absoluteDir, name));
110821
111088
  if (content === null)
@@ -110912,48 +111179,57 @@ async function walkWorkspaceFiles(rootDir, options) {
110912
111179
  const filePaths = [];
110913
111180
  const directoryStubs = [];
110914
111181
  let truncated = false;
110915
- async function visitDir(relDir, inheritedRuleSets, siblingCount) {
110916
- if (truncated || filePaths.length >= maxFilePaths) {
111182
+ let currentLevel = [{ relDir: "", inheritedRuleSets: [], siblingCount: 0 }];
111183
+ while (currentLevel.length > 0) {
111184
+ if (filePaths.length >= maxFilePaths) {
110917
111185
  truncated = true;
110918
- return;
111186
+ break;
110919
111187
  }
110920
- const localRuleSets = await loadDirectoryIgnoreRuleSets(rootDir, relDir);
110921
- const ruleSets = mergeWorkspaceIgnoreRuleSets(inheritedRuleSets, localRuleSets);
110922
- const dirents = await readWorkspaceDirectoryDirents(rootDir, relDir);
110923
- if (!dirents)
110924
- return;
110925
- for (const ent of dirents) {
110926
- if (truncated || filePaths.length >= maxFilePaths) {
111188
+ const nextLevel = [];
111189
+ for (const frame of currentLevel) {
111190
+ if (filePaths.length >= maxFilePaths) {
110927
111191
  truncated = true;
110928
- return;
111192
+ break;
110929
111193
  }
110930
- const relativePath = relDir ? `${relDir}/${ent.name}` : ent.name;
110931
- if (!isPathVisible(relativePath))
111194
+ const localRuleSets = await loadDirectoryIgnoreRuleSets(rootDir, frame.relDir);
111195
+ const ruleSets = mergeWorkspaceIgnoreRuleSets(frame.inheritedRuleSets, localRuleSets);
111196
+ const dirents = await readWorkspaceDirectoryDirents(rootDir, frame.relDir);
111197
+ if (!dirents)
110932
111198
  continue;
110933
- if (ent.isDirectory()) {
110934
- if (isPathIgnoredByRuleSets(ruleSets, relativePath))
111199
+ for (const ent of dirents) {
111200
+ const relativePath = frame.relDir ? `${frame.relDir}/${ent.name}` : ent.name;
111201
+ if (!isPathVisible(relativePath))
110935
111202
  continue;
110936
- const syncMode = classifyDirectorySyncMode(ent.name, {
110937
- relativePath,
110938
- immediateSiblingCount: siblingCount,
110939
- immediateChildCount: dirents.length
110940
- });
110941
- if (syncMode === "hidden")
110942
- continue;
110943
- directoryStubs.push(relativePath);
110944
- if (syncMode === "shallow")
110945
- continue;
110946
- await visitDir(relativePath, ruleSets, dirents.length);
110947
- } else if (ent.isFile()) {
110948
- if (isPathIgnoredByRuleSets(ruleSets, relativePath))
110949
- continue;
110950
- filePaths.push(relativePath);
110951
- if (filePaths.length >= maxFilePaths)
110952
- truncated = true;
111203
+ if (ent.isDirectory()) {
111204
+ if (isPathIgnoredByRuleSets(ruleSets, relativePath))
111205
+ continue;
111206
+ const syncMode = classifyDirectorySyncMode(ent.name, {
111207
+ relativePath,
111208
+ immediateSiblingCount: frame.siblingCount,
111209
+ immediateChildCount: dirents.length
111210
+ });
111211
+ if (syncMode === "hidden")
111212
+ continue;
111213
+ directoryStubs.push(relativePath);
111214
+ if (syncMode === "full")
111215
+ nextLevel.push({
111216
+ relDir: relativePath,
111217
+ inheritedRuleSets: ruleSets,
111218
+ siblingCount: dirents.length
111219
+ });
111220
+ } else if (ent.isFile() && !isPathIgnoredByRuleSets(ruleSets, relativePath)) {
111221
+ if (filePaths.length >= maxFilePaths) {
111222
+ truncated = true;
111223
+ continue;
111224
+ }
111225
+ filePaths.push(relativePath);
111226
+ if (filePaths.length >= maxFilePaths)
111227
+ truncated = true;
111228
+ }
110953
111229
  }
110954
111230
  }
111231
+ currentLevel = truncated ? [] : nextLevel;
110955
111232
  }
110956
- await visitDir("", [], 0);
110957
111233
  return { filePaths, directoryStubs, truncated };
110958
111234
  }
110959
111235
  var init_workspace_file_walk = __esm(() => {
@@ -110965,7 +111241,7 @@ var init_workspace_file_walk = __esm(() => {
110965
111241
  async function scanFileTree(rootDir, options) {
110966
111242
  const maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES;
110967
111243
  const scannedAt = Date.now();
110968
- const walk = await walkWorkspaceFiles(rootDir, { maxFilePaths: maxEntries });
111244
+ const walk = await walkWorkspaceFiles(rootDir, { maxFilePaths: DEFAULT_CANDIDATE_FILE_PATHS });
110969
111245
  const filteredPaths = walk.filePaths.filter((p) => !isExcluded(p));
110970
111246
  const filteredStubs = walk.directoryStubs.filter((p) => !isExcluded(p));
110971
111247
  const entries2 = buildEntries(filteredPaths, filteredStubs, maxEntries);
@@ -110982,30 +111258,35 @@ function entryDepth(entryPath) {
110982
111258
  return entryPath.split("/").length;
110983
111259
  }
110984
111260
  function buildEntries(filePaths, directoryStubs, maxEntries) {
110985
- const directories = new Set(directoryStubs);
110986
- for (const filePath of filePaths) {
110987
- const parts2 = filePath.split("/");
110988
- for (let i2 = 1;i2 < parts2.length; i2++) {
110989
- directories.add(parts2.slice(0, i2).join("/"));
110990
- }
111261
+ const selected = new Map;
111262
+ const data = countDataFilesByParent(filePaths);
111263
+ const ranked = [...filePaths].sort((a, b) => compareRankedFiles(a, scoreFile(a, data), b, scoreFile(b, data)));
111264
+ for (const file2 of ranked) {
111265
+ const parts2 = file2.split("/");
111266
+ const needed = [file2, ...parts2.slice(0, -1).map((_, i2) => parts2.slice(0, i2 + 1).join("/"))];
111267
+ const missing = needed.filter((p) => !selected.has(p));
111268
+ if (selected.size + missing.length > maxEntries)
111269
+ continue;
111270
+ for (const p of needed)
111271
+ selected.set(p, { path: p, type: p === file2 ? "file" : "directory" });
110991
111272
  }
110992
- const entries2 = [
110993
- ...Array.from(directories).map((dir) => ({ path: dir, type: "directory" })),
110994
- ...filePaths.map((file2) => ({ path: file2, type: "file" }))
110995
- ];
110996
- const uniqueByPath = new Map;
110997
- for (const entry of entries2) {
110998
- uniqueByPath.set(entry.path, entry);
111273
+ for (const dir of [...directoryStubs].sort((a, b) => entryDepth(a) - entryDepth(b) || a.localeCompare(b))) {
111274
+ if (selected.has(dir))
111275
+ continue;
111276
+ if (selected.size >= maxEntries)
111277
+ break;
111278
+ selected.set(dir, { path: dir, type: "directory" });
110999
111279
  }
111000
- return Array.from(uniqueByPath.values()).sort((left3, right3) => {
111280
+ return Array.from(selected.values()).sort((left3, right3) => {
111001
111281
  const depthDelta = entryDepth(left3.path) - entryDepth(right3.path);
111002
111282
  if (depthDelta !== 0)
111003
111283
  return depthDelta;
111004
111284
  return left3.path.localeCompare(right3.path);
111005
- }).slice(0, maxEntries);
111285
+ });
111006
111286
  }
111007
- var DEFAULT_MAX_ENTRIES = 1e4;
111287
+ var DEFAULT_MAX_ENTRIES = 1e4, DEFAULT_CANDIDATE_FILE_PATHS = 50000;
111008
111288
  var init_file_tree_scanner = __esm(() => {
111289
+ init_file_tree_sync_ranker();
111009
111290
  init_workspace_file_walk();
111010
111291
  init_workspace_visibility_policy();
111011
111292
  });
@@ -112041,12 +112322,12 @@ class NodeFsHandler {
112041
112322
  });
112042
112323
  }
112043
112324
  async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath3) {
112044
- const parentDir = this.fsw._getWatchedDir(sp.dirname(dir));
112045
- const tracked = parentDir.has(sp.basename(dir));
112325
+ const parentDir2 = this.fsw._getWatchedDir(sp.dirname(dir));
112326
+ const tracked = parentDir2.has(sp.basename(dir));
112046
112327
  if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
112047
112328
  this.fsw._emit(EV.ADD_DIR, dir, stats);
112048
112329
  }
112049
- parentDir.add(sp.basename(dir));
112330
+ parentDir2.add(sp.basename(dir));
112050
112331
  this.fsw._getWatchedDir(dir);
112051
112332
  let throttler;
112052
112333
  let closer;
@@ -113319,7 +113600,7 @@ function diffPathIndexes(previous, next4) {
113319
113600
  // src/infrastructure/services/workspace/workspace-sync-state.ts
113320
113601
  import { createHash as createHash8, randomUUID as randomUUID9 } from "node:crypto";
113321
113602
  import * as fs11 from "node:fs/promises";
113322
- import { homedir as homedir7 } from "node:os";
113603
+ import { homedir as homedir8 } from "node:os";
113323
113604
  import { join as join24 } from "node:path";
113324
113605
  function workspaceKeyFor(workingDir) {
113325
113606
  const normalized = normalizeWorkingDirForLookup(workingDir);
@@ -113397,7 +113678,7 @@ function entriesFromPathIndex(paths) {
113397
113678
  }
113398
113679
  var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
113399
113680
  var init_workspace_sync_state = __esm(() => {
113400
- SYNC_STATE_DIR = join24(homedir7(), ".chatroom", "sync-state");
113681
+ SYNC_STATE_DIR = join24(homedir8(), ".chatroom", "sync-state");
113401
113682
  });
113402
113683
 
113403
113684
  // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
@@ -113547,11 +113828,6 @@ async function startWorkspaceFileTreeCoordinator(options) {
113547
113828
  if (!delta)
113548
113829
  break;
113549
113830
  const result = await options.onDelta(delta, manifest.backendRevision);
113550
- if (result.status === "conflict") {
113551
- manifest.backendRevision = result.revision;
113552
- await saveWorkspaceSyncManifest(manifest);
113553
- continue;
113554
- }
113555
113831
  manifest.backendRevision = result.revision;
113556
113832
  manifest.pendingDeltas.shift();
113557
113833
  await saveWorkspaceSyncManifest(manifest);
@@ -113769,12 +114045,450 @@ var init_workspace_sync_queue = __esm(() => {
113769
114045
  lastCompletedAtByKey = new Map;
113770
114046
  });
113771
114047
 
114048
+ // src/daemon/infrastructure/outbox/lib/coalescing-state-outbox.ts
114049
+ function createCoalescingStateOutbox(options) {
114050
+ const durable = [options.store, options.deliveryKey, options.serialize, options.deserialize];
114051
+ if (durable.some(Boolean) && durable.some((value) => !value))
114052
+ throw new Error("Durable outbox requires store, deliveryKey, serialize, and deserialize");
114053
+ const minIntervalMs = options.minIntervalMs ?? DEFAULT_MIN_INTERVAL_MS;
114054
+ const retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
114055
+ const maxRetryDelayMs = options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;
114056
+ let pending3 = null;
114057
+ let inFlight2 = null;
114058
+ let timer = null;
114059
+ let lastSentAt = 0;
114060
+ let retryAttempt = 0;
114061
+ let stopped = false;
114062
+ if (options.store && options.deliveryKey && options.deserialize) {
114063
+ const recovered = options.store.getPending(options.deliveryKey);
114064
+ if (recovered)
114065
+ pending3 = { state: options.deserialize(recovered.payloadJson), waiters: [] };
114066
+ }
114067
+ const rejectWaiters = (waiters, error51) => {
114068
+ for (const waiter of waiters)
114069
+ waiter.reject(error51);
114070
+ };
114071
+ const mergePending = (older, newer) => ({
114072
+ state: newer?.state ?? older.state,
114073
+ waiters: [...older.waiters, ...newer?.waiters ?? []]
114074
+ });
114075
+ const clearTimer = () => {
114076
+ if (timer)
114077
+ clearTimeout(timer);
114078
+ timer = null;
114079
+ };
114080
+ const schedule2 = (delayMs) => {
114081
+ if (stopped || !pending3 || inFlight2 || timer)
114082
+ return;
114083
+ timer = setTimeout(() => {
114084
+ timer = null;
114085
+ drain2().catch(() => {
114086
+ return;
114087
+ });
114088
+ }, Math.max(0, delayMs));
114089
+ timer.unref?.();
114090
+ };
114091
+ if (pending3)
114092
+ schedule2(0);
114093
+ async function drain2() {
114094
+ if (stopped || !pending3 || inFlight2)
114095
+ return;
114096
+ const batch = pending3;
114097
+ pending3 = null;
114098
+ const sendPromise = Promise.resolve().then(() => options.send(batch.state));
114099
+ inFlight2 = { ...batch, promise: sendPromise };
114100
+ let retryDelay = null;
114101
+ try {
114102
+ const result = await sendPromise;
114103
+ lastSentAt = Date.now();
114104
+ retryAttempt = 0;
114105
+ options.store?.markDone(options.deliveryKey);
114106
+ for (const waiter of batch.waiters)
114107
+ waiter.resolve(result);
114108
+ } catch (error51) {
114109
+ options.onError?.(error51);
114110
+ options.store?.markPendingRetry(options.deliveryKey, error51);
114111
+ pending3 = mergePending(batch, pending3);
114112
+ retryDelay = Math.min(retryDelayMs * 2 ** retryAttempt, maxRetryDelayMs);
114113
+ retryAttempt++;
114114
+ throw error51;
114115
+ } finally {
114116
+ inFlight2 = null;
114117
+ if (pending3) {
114118
+ const intervalDelay = Math.max(0, minIntervalMs - (Date.now() - lastSentAt));
114119
+ schedule2(retryDelay ?? intervalDelay);
114120
+ }
114121
+ }
114122
+ }
114123
+ const enqueue = (state) => {
114124
+ if (stopped)
114125
+ return Promise.reject(new Error("Outbox is stopped"));
114126
+ const promise4 = new Promise((resolve8, reject) => {
114127
+ const waiter = { resolve: resolve8, reject };
114128
+ if (pending3) {
114129
+ pending3 = { state, waiters: [...pending3.waiters, waiter] };
114130
+ } else {
114131
+ pending3 = { state, waiters: [waiter] };
114132
+ }
114133
+ options.store?.upsertPending(options.deliveryKey, options.serialize(state));
114134
+ });
114135
+ if (!inFlight2 && !timer) {
114136
+ const delay3 = lastSentAt === 0 ? 0 : Math.max(0, minIntervalMs - (Date.now() - lastSentAt));
114137
+ schedule2(delay3);
114138
+ }
114139
+ return promise4;
114140
+ };
114141
+ const flushNow = async () => {
114142
+ if (stopped)
114143
+ return;
114144
+ clearTimer();
114145
+ if (inFlight2) {
114146
+ await inFlight2.promise.catch(() => {
114147
+ return;
114148
+ });
114149
+ }
114150
+ if (pending3)
114151
+ await drain2();
114152
+ };
114153
+ const stop4 = async () => {
114154
+ if (stopped)
114155
+ return;
114156
+ await flushNow();
114157
+ stopped = true;
114158
+ clearTimer();
114159
+ if (inFlight2)
114160
+ await inFlight2.promise.catch(() => {
114161
+ return;
114162
+ });
114163
+ const unsent = pending3;
114164
+ pending3 = null;
114165
+ if (unsent)
114166
+ rejectWaiters(unsent.waiters, new Error("Outbox stopped before send completed"));
114167
+ };
114168
+ return { enqueue, flushNow, stop: stop4 };
114169
+ }
114170
+ var DEFAULT_MIN_INTERVAL_MS = 5000, DEFAULT_RETRY_DELAY_MS = 500, DEFAULT_MAX_RETRY_DELAY_MS = 5000;
114171
+
114172
+ // src/daemon/infrastructure/outbox/lib/keyed-coalescing-state-outbox-registry.ts
114173
+ function createKeyedCoalescingStateOutboxRegistry(options) {
114174
+ const outboxes = new Map;
114175
+ const getOrCreate = (key) => {
114176
+ let outbox = outboxes.get(key);
114177
+ if (!outbox) {
114178
+ outbox = createCoalescingStateOutbox({
114179
+ send: (state) => options.createSend(key)(state),
114180
+ minIntervalMs: options.minIntervalMs,
114181
+ retryDelayMs: options.retryDelayMs,
114182
+ maxRetryDelayMs: options.maxRetryDelayMs,
114183
+ onError: (error51) => options.onError?.(key, error51),
114184
+ ...options.store ? { store: options.store, deliveryKey: key, serialize: options.serialize, deserialize: options.deserialize } : {}
114185
+ });
114186
+ outboxes.set(key, outbox);
114187
+ }
114188
+ return outbox;
114189
+ };
114190
+ const stop4 = async (key) => {
114191
+ const outbox = outboxes.get(key);
114192
+ outboxes.delete(key);
114193
+ await outbox?.flushNow().catch(() => {
114194
+ return;
114195
+ });
114196
+ await outbox?.stop().catch(() => {
114197
+ return;
114198
+ });
114199
+ };
114200
+ return {
114201
+ enqueue: (key, state) => getOrCreate(key).enqueue(state),
114202
+ stop: stop4,
114203
+ stopAll: async () => {
114204
+ for (const key of [...outboxes.keys()])
114205
+ await stop4(key);
114206
+ options.store?.close();
114207
+ }
114208
+ };
114209
+ }
114210
+ var init_keyed_coalescing_state_outbox_registry = () => {};
114211
+
114212
+ // src/daemon/infrastructure/outbox/outbox-db-path.ts
114213
+ import { homedir as homedir9 } from "node:os";
114214
+ import { join as join25 } from "node:path";
114215
+ function resolveOutboxDbPath(machineId, kind) {
114216
+ return join25(homedir9(), ".chatroom", "daemon", machineId, `outbox-${kind}.sqlite`);
114217
+ }
114218
+ var init_outbox_db_path = () => {};
114219
+
114220
+ // src/daemon/infrastructure/outbox/lib/durable-coalescing-state-schema.ts
114221
+ var DURABLE_COALESCING_STATE_MIGRATIONS;
114222
+ var init_durable_coalescing_state_schema = __esm(() => {
114223
+ DURABLE_COALESCING_STATE_MIGRATIONS = [
114224
+ `CREATE TABLE IF NOT EXISTS schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`,
114225
+ `CREATE TABLE IF NOT EXISTS coalescing_outbox_entries (delivery_key TEXT PRIMARY KEY, payload_json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', attempts INTEGER NOT NULL DEFAULT 0, last_error TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`
114226
+ ];
114227
+ });
114228
+
114229
+ // src/daemon/infrastructure/outbox/lib/durable-coalescing-state-store.ts
114230
+ import { mkdirSync as mkdirSync6 } from "node:fs";
114231
+ import { dirname as dirname12 } from "node:path";
114232
+ import { DatabaseSync } from "node:sqlite";
114233
+ function openDurableCoalescingStateStore(path8) {
114234
+ mkdirSync6(dirname12(path8), { recursive: true });
114235
+ const db = new DatabaseSync(path8);
114236
+ for (const sql of DURABLE_COALESCING_STATE_MIGRATIONS)
114237
+ db.exec(sql);
114238
+ db.prepare("UPDATE coalescing_outbox_entries SET status='pending' WHERE status='in_flight'").run();
114239
+ return { upsertPending: (key, payload) => {
114240
+ const now = Date.now();
114241
+ db.prepare("INSERT INTO coalescing_outbox_entries(delivery_key,payload_json,status,created_at,updated_at) VALUES(?,?,'pending',?,?) ON CONFLICT(delivery_key) DO UPDATE SET payload_json=excluded.payload_json,status='pending',updated_at=excluded.updated_at").run(key, payload, now, now);
114242
+ }, getPending: (key) => db.prepare("SELECT payload_json as payloadJson, attempts FROM coalescing_outbox_entries WHERE delivery_key=? AND status IN ('pending','in_flight')").get(key) ?? null, markDone: (key) => {
114243
+ db.prepare("DELETE FROM coalescing_outbox_entries WHERE delivery_key=?").run(key);
114244
+ }, markPendingRetry: (key, error51) => {
114245
+ db.prepare("UPDATE coalescing_outbox_entries SET status='pending',last_error=?,attempts=attempts+1,updated_at=? WHERE delivery_key=?").run(String(error51), Date.now(), key);
114246
+ }, listPendingKeys: () => db.prepare("SELECT delivery_key as key FROM coalescing_outbox_entries WHERE status IN ('pending','in_flight')").all().map((r) => String(r.key)), close: () => db.close() };
114247
+ }
114248
+ var init_durable_coalescing_state_store = __esm(() => {
114249
+ init_durable_coalescing_state_schema();
114250
+ });
114251
+
114252
+ // src/daemon/infrastructure/outbox/workspace-file-tree-checkpoint-outbox.ts
114253
+ function createWorkspaceFileTreeCheckpointOutboxRegistry(machineId, createSend, options) {
114254
+ return createKeyedCoalescingStateOutboxRegistry({
114255
+ store: openDurableCoalescingStateStore(resolveOutboxDbPath(machineId, "file-tree-checkpoint")),
114256
+ serialize: JSON.stringify,
114257
+ deserialize: JSON.parse,
114258
+ createSend,
114259
+ minIntervalMs: WORKSPACE_FILE_TREE_CHECKPOINT_OUTBOX_MIN_INTERVAL_MS,
114260
+ onError: options?.onError
114261
+ });
114262
+ }
114263
+ var WORKSPACE_FILE_TREE_CHECKPOINT_OUTBOX_MIN_INTERVAL_MS = 5000;
114264
+ var init_workspace_file_tree_checkpoint_outbox = __esm(() => {
114265
+ init_keyed_coalescing_state_outbox_registry();
114266
+ init_outbox_db_path();
114267
+ init_durable_coalescing_state_store();
114268
+ });
114269
+
114270
+ // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-schema.ts
114271
+ var DURABLE_FIFO_QUEUE_MIGRATIONS;
114272
+ var init_durable_fifo_queue_schema = __esm(() => {
114273
+ DURABLE_FIFO_QUEUE_MIGRATIONS = [
114274
+ `CREATE TABLE IF NOT EXISTS schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`,
114275
+ `CREATE TABLE IF NOT EXISTS fifo_outbox_entries (id INTEGER PRIMARY KEY AUTOINCREMENT, delivery_key TEXT NOT NULL, payload_json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', attempts INTEGER NOT NULL DEFAULT 0, last_error TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
114276
+ `CREATE INDEX IF NOT EXISTS idx_fifo_outbox_pending ON fifo_outbox_entries(delivery_key, status, id)`
114277
+ ];
114278
+ });
114279
+
114280
+ // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-store.ts
114281
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
114282
+ import { mkdirSync as mkdirSync7 } from "node:fs";
114283
+ import { dirname as dirname13 } from "node:path";
114284
+ function openDurableFifoQueueStore(dbPath) {
114285
+ mkdirSync7(dirname13(dbPath), { recursive: true });
114286
+ const db = new DatabaseSync2(dbPath);
114287
+ for (const sql of DURABLE_FIFO_QUEUE_MIGRATIONS)
114288
+ db.exec(sql);
114289
+ db.prepare("UPDATE fifo_outbox_entries SET status='pending', updated_at=? WHERE status='in_flight'").run(Date.now());
114290
+ return {
114291
+ enqueue: (key, payloadJson) => {
114292
+ const now = Date.now();
114293
+ return Number(db.prepare("INSERT INTO fifo_outbox_entries(delivery_key,payload_json,created_at,updated_at) VALUES(?,?,?,?) RETURNING id").get(key, payloadJson, now, now).id);
114294
+ },
114295
+ claimNextBatch: (key, limit) => db.prepare("UPDATE fifo_outbox_entries SET status='in_flight', attempts=attempts+1, updated_at=? WHERE id IN (SELECT id FROM fifo_outbox_entries WHERE delivery_key=? AND status='pending' ORDER BY id LIMIT ?) RETURNING id, delivery_key as deliveryKey, payload_json as payloadJson, attempts").all(Date.now(), key, limit),
114296
+ markDone: (id3) => {
114297
+ db.prepare("DELETE FROM fifo_outbox_entries WHERE id=?").run(id3);
114298
+ },
114299
+ markPending: (id3) => {
114300
+ db.prepare("UPDATE fifo_outbox_entries SET status='pending', last_error=NULL, updated_at=? WHERE id=?").run(Date.now(), id3);
114301
+ },
114302
+ updatePayload: (id3, payloadJson) => {
114303
+ db.prepare("UPDATE fifo_outbox_entries SET payload_json=?, updated_at=? WHERE id=?").run(payloadJson, Date.now(), id3);
114304
+ },
114305
+ markPendingRetry: (id3, error51) => {
114306
+ db.prepare("UPDATE fifo_outbox_entries SET status='pending', last_error=?, updated_at=? WHERE id=?").run(String(error51), Date.now(), id3);
114307
+ },
114308
+ listPendingForRecovery: (key) => db.prepare("SELECT id, delivery_key as deliveryKey, payload_json as payloadJson, attempts FROM fifo_outbox_entries WHERE delivery_key=? AND status IN ('pending','in_flight') ORDER BY id").all(key),
114309
+ close: () => db.close()
114310
+ };
114311
+ }
114312
+ var init_durable_fifo_queue_store = __esm(() => {
114313
+ init_durable_fifo_queue_schema();
114314
+ });
114315
+
114316
+ // src/daemon/infrastructure/outbox/lib/fifo-batched-outbox.ts
114317
+ function createFifoBatchedOutbox(o) {
114318
+ const waiters = new Map;
114319
+ let running3;
114320
+ let stopped = false;
114321
+ let retryTimer;
114322
+ let retryAttempt = 0;
114323
+ const retryDelay = () => Math.min((o.retryDelayMs ?? 500) * 2 ** retryAttempt++, o.maxRetryDelayMs ?? 5000);
114324
+ const scheduleRetry = () => {
114325
+ if (!stopped && !retryTimer) {
114326
+ retryTimer = setTimeout(() => {
114327
+ retryTimer = undefined;
114328
+ drain2().catch(() => {
114329
+ return;
114330
+ });
114331
+ }, retryDelay());
114332
+ retryTimer.unref?.();
114333
+ }
114334
+ };
114335
+ const drain2 = async () => {
114336
+ if (running3)
114337
+ return running3;
114338
+ running3 = (async () => {
114339
+ while (true) {
114340
+ const rows2 = o.store.claimNextBatch(o.deliveryKey, o.batchSize);
114341
+ if (!rows2.length)
114342
+ break;
114343
+ try {
114344
+ const results = await o.send(rows2.map((r) => o.deserialize(r.payloadJson)));
114345
+ let hasRetry = false;
114346
+ for (let i2 = 0;i2 < results.length; i2++) {
114347
+ const item = o.deserialize(rows2[i2].payloadJson);
114348
+ const outcome = o.classifyOutcome?.(results[i2], item) ?? { kind: "success" };
114349
+ if (outcome.kind === "retry") {
114350
+ o.store.updatePayload(rows2[i2].id, o.serialize(outcome.item));
114351
+ o.store.markPending(rows2[i2].id);
114352
+ hasRetry = true;
114353
+ continue;
114354
+ }
114355
+ o.store.markDone(rows2[i2].id);
114356
+ waiters.get(rows2[i2].id)?.resolve(results[i2]);
114357
+ waiters.delete(rows2[i2].id);
114358
+ }
114359
+ for (let i2 = results.length;i2 < rows2.length; i2++)
114360
+ o.store.markPending(rows2[i2].id);
114361
+ if (hasRetry) {
114362
+ scheduleRetry();
114363
+ break;
114364
+ }
114365
+ } catch (e) {
114366
+ o.onError?.(e);
114367
+ for (const r of rows2)
114368
+ o.store.markPendingRetry(r.id, e);
114369
+ scheduleRetry();
114370
+ break;
114371
+ }
114372
+ }
114373
+ })().finally(() => {
114374
+ running3 = undefined;
114375
+ });
114376
+ return running3;
114377
+ };
114378
+ const enqueue = (item) => {
114379
+ if (stopped)
114380
+ return Promise.reject(new Error("Outbox is stopped"));
114381
+ const id3 = o.store.enqueue(o.deliveryKey, o.serialize(item));
114382
+ const p = new Promise((resolve8, reject) => waiters.set(id3, { resolve: resolve8, reject }));
114383
+ drain2().catch(() => {
114384
+ return;
114385
+ });
114386
+ return p;
114387
+ };
114388
+ const flushNow = async () => {
114389
+ while (o.store.listPendingForRecovery(o.deliveryKey).length) {
114390
+ await drain2();
114391
+ }
114392
+ };
114393
+ const stop4 = async () => {
114394
+ if (stopped)
114395
+ return;
114396
+ if (retryTimer)
114397
+ clearTimeout(retryTimer);
114398
+ retryTimer = undefined;
114399
+ await flushNow();
114400
+ stopped = true;
114401
+ };
114402
+ return { enqueue, flushNow, stop: stop4 };
114403
+ }
114404
+
114405
+ // src/daemon/infrastructure/outbox/lib/keyed-fifo-batched-outbox-registry.ts
114406
+ function createKeyedFifoBatchedOutboxRegistry(o) {
114407
+ const boxes = new Map;
114408
+ const get13 = (key) => {
114409
+ let b = boxes.get(key);
114410
+ if (!b) {
114411
+ b = createFifoBatchedOutbox({
114412
+ batchSize: o.batchSize,
114413
+ send: o.createSend(key),
114414
+ store: o.store,
114415
+ deliveryKey: key,
114416
+ serialize: o.serialize,
114417
+ deserialize: o.deserialize,
114418
+ onError: (e) => o.onError?.(key, e),
114419
+ classifyOutcome: o.classifyOutcome
114420
+ });
114421
+ boxes.set(key, b);
114422
+ }
114423
+ return b;
114424
+ };
114425
+ const stop4 = async (k) => {
114426
+ const b = boxes.get(k);
114427
+ if (b) {
114428
+ await b.stop();
114429
+ boxes.delete(k);
114430
+ }
114431
+ };
114432
+ return {
114433
+ enqueue: (k, i2) => get13(k).enqueue(i2),
114434
+ flushNow: async (k) => {
114435
+ if (k)
114436
+ await get13(k).flushNow();
114437
+ else
114438
+ for (const b of boxes.values())
114439
+ await b.flushNow();
114440
+ },
114441
+ stop: stop4,
114442
+ stopAll: async () => {
114443
+ for (const k of [...boxes.keys()])
114444
+ await stop4(k);
114445
+ o.store.close();
114446
+ }
114447
+ };
114448
+ }
114449
+ var init_keyed_fifo_batched_outbox_registry = __esm(() => {
114450
+ init_durable_fifo_queue_store();
114451
+ });
114452
+
114453
+ // src/daemon/infrastructure/outbox/workspace-file-tree-delta-outbox.ts
114454
+ function createWorkspaceFileTreeDeltaOutboxRegistry(machineId, createSend, options) {
114455
+ const store = openDurableFifoQueueStore(resolveOutboxDbPath(machineId, "file-tree-delta"));
114456
+ return createKeyedFifoBatchedOutboxRegistry({
114457
+ store,
114458
+ batchSize: options?.batchSize ?? WORKSPACE_FILE_TREE_DELTA_OUTBOX_BATCH_SIZE,
114459
+ createSend: (key) => async (units) => {
114460
+ const out = [];
114461
+ for (const u of units) {
114462
+ const r = await createSend(key)(u);
114463
+ out.push(r);
114464
+ }
114465
+ return out;
114466
+ },
114467
+ serialize: JSON.stringify,
114468
+ deserialize: JSON.parse,
114469
+ onError: options?.onError,
114470
+ classifyOutcome: (result, unit) => result.status === "conflict" ? { kind: "retry", item: { ...unit, baseRevision: result.revision } } : { kind: "success" }
114471
+ });
114472
+ }
114473
+ var WORKSPACE_FILE_TREE_DELTA_OUTBOX_BATCH_SIZE = 5;
114474
+ var init_workspace_file_tree_delta_outbox = __esm(() => {
114475
+ init_outbox_db_path();
114476
+ init_durable_fifo_queue_store();
114477
+ init_keyed_fifo_batched_outbox_registry();
114478
+ });
114479
+
114480
+ // src/daemon/infrastructure/outbox/index.ts
114481
+ var init_outbox = __esm(() => {
114482
+ init_workspace_file_tree_checkpoint_outbox();
114483
+ init_workspace_file_tree_delta_outbox();
114484
+ });
114485
+
113772
114486
  // src/daemon/entry/files/file-tree-subscription.ts
113773
114487
  import { randomUUID as randomUUID11 } from "node:crypto";
113774
114488
  function logSubscriptionWarn(label, err) {
113775
114489
  console.warn(`[${formatTimestamp()}] ⚠️ ${label}: ${getErrorMessage(err)}`);
113776
114490
  }
113777
- async function stopCoordinatorForWorkingDir(coordinators, normalized) {
114491
+ async function stopCoordinatorForWorkingDir(coordinators, checkpointOutboxRegistry, deltaOutboxRegistry, normalized) {
113778
114492
  const coordinatorPromise = coordinators.get(normalized);
113779
114493
  if (!coordinatorPromise)
113780
114494
  return;
@@ -113782,8 +114496,10 @@ async function stopCoordinatorForWorkingDir(coordinators, normalized) {
113782
114496
  await coordinatorPromise.then((coordinator2) => coordinator2.stop()).catch(() => {
113783
114497
  return;
113784
114498
  });
114499
+ await checkpointOutboxRegistry.stop(normalized);
114500
+ await deltaOutboxRegistry.stop(normalized);
113785
114501
  }
113786
- async function drainPendingFileTreeReleaseRequests(session2, coordinators) {
114502
+ async function drainPendingFileTreeReleaseRequests(session2, coordinators, checkpointOutboxRegistry, deltaOutboxRegistry) {
113787
114503
  const releases = await session2.backend.query(api.workspaceFiles.getPendingFileTreeReleaseRequests, {
113788
114504
  sessionId: session2.sessionId,
113789
114505
  machineId: session2.machineId
@@ -113795,7 +114511,7 @@ async function drainPendingFileTreeReleaseRequests(session2, coordinators) {
113795
114511
  releasesByDir.add(normalizeWorkingDirForLookup(release.workingDir));
113796
114512
  }
113797
114513
  for (const normalized of releasesByDir) {
113798
- await stopCoordinatorForWorkingDir(coordinators, normalized).then(() => session2.backend.mutation(api.workspaceFiles.fulfillFileTreeReleaseRequest, {
114514
+ await stopCoordinatorForWorkingDir(coordinators, checkpointOutboxRegistry, deltaOutboxRegistry, normalized).then(() => session2.backend.mutation(api.workspaceFiles.fulfillFileTreeReleaseRequest, {
113799
114515
  sessionId: session2.sessionId,
113800
114516
  machineId: session2.machineId,
113801
114517
  workingDir: normalized
@@ -113893,6 +114609,22 @@ async function publishCheckpoint(session2, normalizedWorkingDir, tree, revision)
113893
114609
  var startFileTreeSubscriptionEffect = () => exports_Effect.gen(function* () {
113894
114610
  const session2 = yield* DaemonSessionService;
113895
114611
  const coordinators = new Map;
114612
+ const checkpointOutboxRegistry = createWorkspaceFileTreeCheckpointOutboxRegistry(session2.machineId, (normalized) => (state) => publishCheckpoint(session2, normalized, state.tree, state.revision), {
114613
+ onError: (normalized, error51) => logSubscriptionWarn(`File tree checkpoint outbox failed for ${normalized}`, error51)
114614
+ });
114615
+ const deltaOutboxRegistry = createWorkspaceFileTreeDeltaOutboxRegistry(session2.machineId, (normalized) => async (unit) => {
114616
+ const result = await session2.backend.mutation(api.workspaceFiles.applyFileTreeDeltaBatch, {
114617
+ sessionId: session2.sessionId,
114618
+ machineId: session2.machineId,
114619
+ workingDir: normalized,
114620
+ operationId: unit.delta.operationId,
114621
+ baseRevision: unit.baseRevision,
114622
+ operations: toDeltaOperations(unit.delta)
114623
+ });
114624
+ if (result.status === "resync-required")
114625
+ return { status: "conflict", revision: result.expectedRevision };
114626
+ return result;
114627
+ });
113896
114628
  const ensureCoordinator = (workingDir, forceReconcile) => {
113897
114629
  const normalized = normalizeWorkingDirForLookup(workingDir);
113898
114630
  let coordinatorPromise = coordinators.get(normalized);
@@ -113900,23 +114632,8 @@ var startFileTreeSubscriptionEffect = () => exports_Effect.gen(function* () {
113900
114632
  coordinatorPromise = startWorkspaceFileTreeCoordinator({
113901
114633
  machineId: session2.machineId,
113902
114634
  workingDir: normalized,
113903
- onDelta: async (delta, baseRevision) => {
113904
- const operations = toDeltaOperations(delta);
113905
- const result = await session2.backend.mutation(api.workspaceFiles.applyFileTreeDeltaBatch, {
113906
- sessionId: session2.sessionId,
113907
- machineId: session2.machineId,
113908
- workingDir: normalized,
113909
- operationId: delta.operationId,
113910
- baseRevision,
113911
- operations
113912
- });
113913
- if (result.status === "resync-required") {
113914
- return { status: "conflict", revision: result.expectedRevision };
113915
- }
113916
- console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree delta: ${normalized} (${operations.length} operations, ${Buffer.byteLength(JSON.stringify(operations))} bytes, revision ${result.revision})`);
113917
- return result;
113918
- },
113919
- onCheckpoint: (tree, revision) => publishCheckpoint(session2, normalized, tree, revision),
114635
+ onDelta: (delta, baseRevision) => deltaOutboxRegistry.enqueue(normalized, { delta, baseRevision }),
114636
+ onCheckpoint: (tree, revision) => checkpointOutboxRegistry.enqueue(normalized, { tree, revision }),
113920
114637
  onError: (error51) => logSubscriptionWarn(`File tree coordinator failed for ${normalized}`, error51),
113921
114638
  onReconciled: (correctedPathCount) => {
113922
114639
  console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree reconciled: ${normalized} (${correctedPathCount} corrected paths)`);
@@ -113942,12 +114659,15 @@ var startFileTreeSubscriptionEffect = () => exports_Effect.gen(function* () {
113942
114659
  };
113943
114660
  return {
113944
114661
  drainPendingFileTreeRequests: () => drainPendingFileTreeRequests(session2, coordinators, ensureCoordinator),
113945
- drainPendingFileTreeReleaseRequests: () => drainPendingFileTreeReleaseRequests(session2, coordinators),
114662
+ drainPendingFileTreeReleaseRequests: () => drainPendingFileTreeReleaseRequests(session2, coordinators, checkpointOutboxRegistry, deltaOutboxRegistry),
113946
114663
  stop: () => {
113947
- Promise.all([...coordinators.values()].map((coordinator2) => coordinator2.then((handle) => handle.stop()).catch(() => {
113948
- return;
113949
- })));
113950
- coordinators.clear();
114664
+ (async () => {
114665
+ const keys3 = [...coordinators.keys()];
114666
+ await Promise.all(keys3.map((normalized) => stopCoordinatorForWorkingDir(coordinators, checkpointOutboxRegistry, deltaOutboxRegistry, normalized)));
114667
+ await deltaOutboxRegistry.stopAll();
114668
+ await checkpointOutboxRegistry.stopAll();
114669
+ coordinators.clear();
114670
+ })();
113951
114671
  }
113952
114672
  };
113953
114673
  });
@@ -113961,6 +114681,7 @@ var init_file_tree_subscription = __esm(() => {
113961
114681
  init_workspace_file_tree_coordinator();
113962
114682
  init_workspace_sync_queue();
113963
114683
  init_convex_error();
114684
+ init_outbox();
113964
114685
  init_daemon_services();
113965
114686
  });
113966
114687
 
@@ -114074,7 +114795,7 @@ var init_file_write_request_priority = __esm(() => {
114074
114795
 
114075
114796
  // src/daemon/entry/files/file-write-fulfillment.ts
114076
114797
  import { access as access4, mkdir as mkdir10, rename as rename4, rm as rm4, writeFile as writeFile9 } from "node:fs/promises";
114077
- import { dirname as dirname12 } from "node:path";
114798
+ import { dirname as dirname14 } from "node:path";
114078
114799
  function isTerminalFileWriteError(errorMessage) {
114079
114800
  const terminalMessages = new Set([
114080
114801
  "Invalid file path",
@@ -114173,7 +114894,7 @@ async function validateWriteOperation(operation, absolutePath) {
114173
114894
  }
114174
114895
  async function writePayloadToDisk(absolutePath, operation, content) {
114175
114896
  if (operation === "create") {
114176
- await mkdir10(dirname12(absolutePath), { recursive: true });
114897
+ await mkdir10(dirname14(absolutePath), { recursive: true });
114177
114898
  await writeFile9(absolutePath, content, { flag: "wx" });
114178
114899
  return;
114179
114900
  }
@@ -114251,7 +114972,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
114251
114972
  });
114252
114973
  return;
114253
114974
  }
114254
- await mkdir10(dirname12(targetResolved.absolutePath), { recursive: true });
114975
+ await mkdir10(dirname14(targetResolved.absolutePath), { recursive: true });
114255
114976
  await rename4(resolved.absolutePath, targetResolved.absolutePath);
114256
114977
  await completeWriteRequest(session2, claimed._id, claimed.revision, { status: "done" });
114257
114978
  const elapsed4 = Date.now() - startTime;
@@ -116551,10 +117272,10 @@ var init_deps = __esm(() => {
116551
117272
  });
116552
117273
 
116553
117274
  // src/daemon/entry/persistence-path.ts
116554
- import { homedir as homedir8 } from "node:os";
116555
- import { join as join25 } from "node:path";
117275
+ import { homedir as homedir10 } from "node:os";
117276
+ import { join as join26 } from "node:path";
116556
117277
  function resolvePersistenceDbPath(machineId) {
116557
- return join25(homedir8(), ".chatroom", "daemon", machineId, "events.sqlite");
117278
+ return join26(homedir10(), ".chatroom", "daemon", machineId, "events.sqlite");
116558
117279
  }
116559
117280
  var init_persistence_path = () => {};
116560
117281
 
@@ -117590,10 +118311,10 @@ function resolveEnvSlug(convexUrl) {
117590
118311
  }
117591
118312
 
117592
118313
  // src/infrastructure/log-server/resolve-logs-db-path.ts
117593
- import { homedir as homedir9 } from "node:os";
117594
- import { join as join26 } from "node:path";
118314
+ import { homedir as homedir11 } from "node:os";
118315
+ import { join as join27 } from "node:path";
117595
118316
  function resolveLogsDbPath(convexUrl) {
117596
- return join26(homedir9(), ".chatroom", resolveEnvSlug(convexUrl ?? getConvexUrl()), "logs.sqlite");
118317
+ return join27(homedir11(), ".chatroom", resolveEnvSlug(convexUrl ?? getConvexUrl()), "logs.sqlite");
117597
118318
  }
117598
118319
  var init_resolve_logs_db_path = __esm(() => {
117599
118320
  init_client2();
@@ -117626,12 +118347,12 @@ var init_log_schema = __esm(() => {
117626
118347
  });
117627
118348
 
117628
118349
  // src/infrastructure/log-server/open-log-database.ts
117629
- import { mkdirSync as mkdirSync6 } from "node:fs";
117630
- import { dirname as dirname13 } from "node:path";
117631
- import { DatabaseSync } from "node:sqlite";
118350
+ import { mkdirSync as mkdirSync8 } from "node:fs";
118351
+ import { dirname as dirname15 } from "node:path";
118352
+ import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
117632
118353
  function openLogDatabase(dbPath) {
117633
- mkdirSync6(dirname13(dbPath), { recursive: true });
117634
- const db = new DatabaseSync(dbPath);
118354
+ mkdirSync8(dirname15(dbPath), { recursive: true });
118355
+ const db = new DatabaseSync3(dbPath);
117635
118356
  db.exec("PRAGMA journal_mode = WAL");
117636
118357
  db.exec("PRAGMA busy_timeout = 5000");
117637
118358
  db.exec("PRAGMA synchronous = NORMAL");
@@ -117857,12 +118578,12 @@ var init_schema = __esm(() => {
117857
118578
  });
117858
118579
 
117859
118580
  // src/daemon/infrastructure/persistence/open-database.ts
117860
- import { mkdirSync as mkdirSync7 } from "node:fs";
117861
- import { dirname as dirname14 } from "node:path";
117862
- import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
118581
+ import { mkdirSync as mkdirSync9 } from "node:fs";
118582
+ import { dirname as dirname16 } from "node:path";
118583
+ import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
117863
118584
  function openDatabase(dbPath) {
117864
- mkdirSync7(dirname14(dbPath), { recursive: true });
117865
- const db = new DatabaseSync2(dbPath);
118585
+ mkdirSync9(dirname16(dbPath), { recursive: true });
118586
+ const db = new DatabaseSync4(dbPath);
117866
118587
  for (const sql of MIGRATIONS2) {
117867
118588
  db.exec(sql);
117868
118589
  }
@@ -131572,19 +132293,19 @@ function routeRequest(req, res, deps) {
131572
132293
 
131573
132294
  // src/daemon/local-web/server/serve-static.ts
131574
132295
  import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
131575
- import { dirname as dirname15, join as join27 } from "node:path";
132296
+ import { dirname as dirname17, join as join28 } from "node:path";
131576
132297
  import { fileURLToPath as fileURLToPath7 } from "node:url";
131577
132298
  function clientDistCandidates(here) {
131578
132299
  return [
131579
- join27(here, "client/build"),
131580
- join27(here, "../client/build"),
131581
- join27(here, "../src/daemon/local-web/client/build")
132300
+ join28(here, "client/build"),
132301
+ join28(here, "../client/build"),
132302
+ join28(here, "../src/daemon/local-web/client/build")
131582
132303
  ];
131583
132304
  }
131584
- function resolveClientDistDir(here = dirname15(fileURLToPath7(import.meta.url))) {
132305
+ function resolveClientDistDir(here = dirname17(fileURLToPath7(import.meta.url))) {
131585
132306
  const candidates = clientDistCandidates(here);
131586
132307
  for (const dir of candidates) {
131587
- if (existsSync10(join27(dir, "index.html")))
132308
+ if (existsSync10(join28(dir, "index.html")))
131588
132309
  return dir;
131589
132310
  }
131590
132311
  return candidates[0];
@@ -131597,7 +132318,7 @@ function tryServeStatic(req, res, distDir) {
131597
132318
  return false;
131598
132319
  }
131599
132320
  const safePath = pathname === "/" ? "/index.html" : pathname;
131600
- const filePath = join27(distDir, safePath);
132321
+ const filePath = join28(distDir, safePath);
131601
132322
  if (!filePath.startsWith(distDir)) {
131602
132323
  res.writeHead(403);
131603
132324
  res.end();
@@ -131614,7 +132335,7 @@ function tryServeStatic(req, res, distDir) {
131614
132335
  }
131615
132336
  return true;
131616
132337
  }
131617
- const indexPath = join27(distDir, "index.html");
132338
+ const indexPath = join28(distDir, "index.html");
131618
132339
  if (existsSync10(indexPath)) {
131619
132340
  res.writeHead(200, { "Content-Type": "text/html" });
131620
132341
  if (req.method === "HEAD") {
@@ -133345,4 +134066,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
133345
134066
  });
133346
134067
  program2.parse();
133347
134068
 
133348
- //# debugId=FF157482FA3E83C464756E2164756E21
134069
+ //# debugId=DE791FABFBB4390C64756E2164756E21