@adhdev/daemon-standalone 1.0.58-rc.2 → 1.0.58-rc.4

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
@@ -3591,14 +3591,77 @@ var require_dist = __commonJS({
3591
3591
  }
3592
3592
  };
3593
3593
  }
3594
+ var establishedSockets2 = /* @__PURE__ */ new WeakSet();
3594
3595
  var SessionHostClient2 = class {
3595
3596
  endpoint;
3596
3597
  socket = null;
3597
3598
  requestWaiters = /* @__PURE__ */ new Map();
3598
3599
  eventListeners = /* @__PURE__ */ new Set();
3600
+ disconnectListeners = /* @__PURE__ */ new Set();
3601
+ /** Guards against emitting both an 'error'-path and a 'close'-path disconnect for one socket. */
3602
+ disconnectedSockets = /* @__PURE__ */ new WeakSet();
3599
3603
  constructor(options = {}) {
3600
3604
  this.endpoint = options.endpoint || getDefaultSessionHostEndpoint2(options.appName || "adhdev");
3601
3605
  }
3606
+ /**
3607
+ * Fires when an established connection to the session host goes away.
3608
+ *
3609
+ * Before this existed the client registered only 'data' and 'error' handlers,
3610
+ * so a *clean* FIN — the host process exiting normally, or dying to an
3611
+ * uncaught exception that took the socket down without an error event on our
3612
+ * end — ran nothing at all: `this.socket` stayed non-null pointing at a dead
3613
+ * socket, in-flight waiters hung to their 30s timeout, and no caller was told.
3614
+ * That silence is what let a session-host death present downstream as an
3615
+ * unrelated application-level fault (a missing completion marker) instead of
3616
+ * "the host is gone".
3617
+ */
3618
+ onDisconnect(listener) {
3619
+ this.disconnectListeners.add(listener);
3620
+ return () => {
3621
+ this.disconnectListeners.delete(listener);
3622
+ };
3623
+ }
3624
+ handleDisconnect(socket, reason, error48) {
3625
+ if (this.disconnectedSockets.has(socket)) return;
3626
+ this.disconnectedSockets.add(socket);
3627
+ const wasEstablished = establishedSockets2.has(socket);
3628
+ if (!wasEstablished) {
3629
+ if (this.socket === socket) this.socket = null;
3630
+ try {
3631
+ socket.destroy();
3632
+ } catch {
3633
+ }
3634
+ const failure2 = error48 || new Error(`Session host connect failed (${this.endpoint.path})`);
3635
+ for (const waiter of this.requestWaiters.values()) waiter.reject(failure2);
3636
+ this.requestWaiters.clear();
3637
+ return;
3638
+ }
3639
+ const pendingRequests = this.requestWaiters.size;
3640
+ const failure = error48 || new Error(`Session host connection ${reason} (${this.endpoint.path})`);
3641
+ for (const waiter of this.requestWaiters.values()) {
3642
+ waiter.reject(failure);
3643
+ }
3644
+ this.requestWaiters.clear();
3645
+ if (this.socket === socket) {
3646
+ this.socket = null;
3647
+ }
3648
+ try {
3649
+ socket.destroy();
3650
+ } catch {
3651
+ }
3652
+ const info = {
3653
+ reason,
3654
+ endpointPath: this.endpoint.path,
3655
+ pendingRequests,
3656
+ error: error48
3657
+ };
3658
+ for (const listener of this.disconnectListeners) {
3659
+ try {
3660
+ listener(info);
3661
+ } catch {
3662
+ }
3663
+ }
3664
+ }
3602
3665
  async connect() {
3603
3666
  if (this.socket && !this.socket.destroyed) return;
3604
3667
  if (this.socket) {
@@ -3624,20 +3687,19 @@ var require_dist = __commonJS({
3624
3687
  }
3625
3688
  }));
3626
3689
  socket.on("error", (error48) => {
3627
- for (const waiter of this.requestWaiters.values()) {
3628
- waiter.reject(error48);
3629
- }
3630
- this.requestWaiters.clear();
3631
- if (this.socket === socket) {
3632
- this.socket = null;
3633
- }
3634
- try {
3635
- socket.destroy();
3636
- } catch {
3637
- }
3690
+ this.handleDisconnect(socket, "error", error48);
3691
+ });
3692
+ socket.on("close", () => {
3693
+ this.handleDisconnect(socket, "closed");
3694
+ });
3695
+ socket.on("end", () => {
3696
+ this.handleDisconnect(socket, "ended");
3638
3697
  });
3639
3698
  await new Promise((resolve3, reject) => {
3640
- socket.once("connect", () => resolve3());
3699
+ socket.once("connect", () => {
3700
+ establishedSockets2.add(socket);
3701
+ resolve3();
3702
+ });
3641
3703
  socket.once("error", reject);
3642
3704
  });
3643
3705
  }
@@ -3679,6 +3741,7 @@ var require_dist = __commonJS({
3679
3741
  if (!this.socket) return;
3680
3742
  const socket = this.socket;
3681
3743
  this.socket = null;
3744
+ this.disconnectedSockets.add(socket);
3682
3745
  for (const waiter of this.requestWaiters.values()) {
3683
3746
  waiter.reject(new Error("Session host client closed"));
3684
3747
  }
@@ -36768,10 +36831,10 @@ var require_dist3 = __commonJS({
36768
36831
  }
36769
36832
  function getDaemonBuildInfo() {
36770
36833
  if (cached2) return cached2;
36771
- const commit = readInjected(true ? "8e051f2dacc04bd04da37212168709c3ade00877" : void 0) ?? "unknown";
36772
- const commitShort = readInjected(true ? "8e051f2d" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36773
- const version2 = readInjected(true ? "1.0.58-rc.2" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36774
- const builtAt = readInjected(true ? "2026-08-22T14:13:56.874Z" : void 0);
36834
+ const commit = readInjected(true ? "388d6e74b391ae9afd94d860aed141063297dca9" : void 0) ?? "unknown";
36835
+ const commitShort = readInjected(true ? "388d6e74" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36836
+ const version2 = readInjected(true ? "1.0.58-rc.4" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36837
+ const builtAt = readInjected(true ? "2026-08-23T04:11:22.908Z" : void 0);
36775
36838
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36776
36839
  return cached2;
36777
36840
  }
@@ -38186,7 +38249,9 @@ var require_dist3 = __commonJS({
38186
38249
  }
38187
38250
  }
38188
38251
  function sizeRotationPath(logFile, generation) {
38189
- return logFile.replace(/\.log$/, `.${generation}.log`);
38252
+ const match = logFile.match(/^(.*)(\.[^./\\]+)$/);
38253
+ if (!match) return `${logFile}.${generation}`;
38254
+ return `${match[1]}.${generation}${match[2]}`;
38190
38255
  }
38191
38256
  function rotateSizeGenerations(logFile, maxGenerations = MAX_SIZE_ROTATION_GENERATIONS) {
38192
38257
  if (maxGenerations < 1) return;
@@ -38205,6 +38270,20 @@ var require_dist3 = __commonJS({
38205
38270
  }
38206
38271
  fs22.renameSync(logFile, sizeRotationPath(logFile, 1));
38207
38272
  }
38273
+ function rotateCaptureLogIfNeeded(logPath, maxSize = MAX_CAPTURE_LOG_SIZE, maxGenerations = MAX_CAPTURE_LOG_GENERATIONS) {
38274
+ try {
38275
+ const stat2 = fs22.statSync(logPath);
38276
+ if (stat2.size <= maxSize) return false;
38277
+ } catch {
38278
+ return false;
38279
+ }
38280
+ try {
38281
+ rotateSizeGenerations(logPath, maxGenerations);
38282
+ return true;
38283
+ } catch {
38284
+ return false;
38285
+ }
38286
+ }
38208
38287
  function rotateSizeIfNeeded() {
38209
38288
  try {
38210
38289
  const stat2 = fs22.statSync(currentLogFile);
@@ -38343,6 +38422,8 @@ var require_dist3 = __commonJS({
38343
38422
  var currentDate;
38344
38423
  var currentLogDir;
38345
38424
  var currentLogFile;
38425
+ var MAX_CAPTURE_LOG_SIZE;
38426
+ var MAX_CAPTURE_LOG_GENERATIONS;
38346
38427
  var preparedLogDirs;
38347
38428
  var writeCount;
38348
38429
  var currentLogDirEnv;
@@ -38371,6 +38452,8 @@ var require_dist3 = __commonJS({
38371
38452
  currentDate = getDateStr();
38372
38453
  currentLogDir = resolveLogDir();
38373
38454
  currentLogFile = path32.join(currentLogDir, daemonLogFileName(currentDate));
38455
+ MAX_CAPTURE_LOG_SIZE = 10 * 1024 * 1024;
38456
+ MAX_CAPTURE_LOG_GENERATIONS = 2;
38374
38457
  preparedLogDirs = /* @__PURE__ */ new Set();
38375
38458
  writeCount = 0;
38376
38459
  currentLogDirEnv = process.env.ADHDEV_CONFIG_DIR;
@@ -44117,25 +44200,75 @@ ${error48.message || ""}`;
44117
44200
  function getMeshConfigPath() {
44118
44201
  return (0, import_path5.join)(getConfigDir2(), "meshes.json");
44119
44202
  }
44120
- function loadMeshConfig(options = {}) {
44203
+ function sleepBlockingMs(ms) {
44204
+ if (ms <= 0) return;
44205
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
44206
+ }
44207
+ function acquireMeshConfigLock() {
44208
+ const lockPath = `${getMeshConfigPath()}.lock`;
44209
+ const deadline = Date.now() + MESH_CONFIG_LOCK_WAIT_MS;
44210
+ while (Date.now() <= deadline) {
44211
+ try {
44212
+ (0, import_fs5.mkdirSync)(lockPath);
44213
+ return () => {
44214
+ try {
44215
+ (0, import_fs5.rmSync)(lockPath, { recursive: true, force: true });
44216
+ } catch {
44217
+ }
44218
+ };
44219
+ } catch (error48) {
44220
+ if (error48?.code !== "EEXIST") return null;
44221
+ try {
44222
+ const stat2 = (0, import_fs5.statSync)(lockPath);
44223
+ if (Date.now() - stat2.mtimeMs > MESH_CONFIG_LOCK_STALE_MS) {
44224
+ (0, import_fs5.rmSync)(lockPath, { recursive: true, force: true });
44225
+ continue;
44226
+ }
44227
+ } catch {
44228
+ continue;
44229
+ }
44230
+ sleepBlockingMs(MESH_CONFIG_LOCK_POLL_MS);
44231
+ }
44232
+ }
44233
+ return null;
44234
+ }
44235
+ function withMeshConfigWriteLock(fn) {
44236
+ if (meshConfigLockHeldInProcess) return fn();
44237
+ const release2 = acquireMeshConfigLock();
44238
+ if (!release2) return fn();
44239
+ meshConfigLockHeldInProcess = true;
44240
+ try {
44241
+ return fn();
44242
+ } finally {
44243
+ meshConfigLockHeldInProcess = false;
44244
+ release2();
44245
+ }
44246
+ }
44247
+ function readMeshConfigFile() {
44121
44248
  const path60 = getMeshConfigPath();
44122
44249
  if (!(0, import_fs5.existsSync)(path60)) return { meshes: [] };
44123
44250
  try {
44124
44251
  const raw = JSON.parse((0, import_fs5.readFileSync)(path60, "utf-8"));
44125
44252
  if (!raw || !Array.isArray(raw.meshes)) return { meshes: [] };
44126
- const config2 = raw;
44127
- const migrated = migrateLoadedMeshConfig(config2);
44128
- if (migrated && options.persistMigrations !== false) {
44129
- try {
44130
- saveMeshConfig(config2);
44131
- } catch {
44132
- }
44133
- }
44134
- return config2;
44253
+ return raw;
44135
44254
  } catch {
44136
44255
  return { meshes: [] };
44137
44256
  }
44138
44257
  }
44258
+ function loadMeshConfig(options = {}) {
44259
+ const config2 = readMeshConfigFile();
44260
+ const migrated = migrateLoadedMeshConfig(config2);
44261
+ if (migrated && options.persistMigrations !== false) {
44262
+ try {
44263
+ withMeshConfigWriteLock(() => {
44264
+ const fresh = readMeshConfigFile();
44265
+ if (migrateLoadedMeshConfig(fresh)) saveMeshConfig(fresh);
44266
+ });
44267
+ } catch {
44268
+ }
44269
+ }
44270
+ return config2;
44271
+ }
44139
44272
  function migrateLoadedMeshConfig(config2) {
44140
44273
  let changed = false;
44141
44274
  if (foldLegacyTopLevelMeshSetting(config2, "magiKindPanels", "mesh_magi_kind_panel_set({ meshId, task_kind, slots })")) changed = true;
@@ -44220,7 +44353,9 @@ ${error48.message || ""}`;
44220
44353
  }
44221
44354
  function saveMeshConfig(config2) {
44222
44355
  const path60 = getMeshConfigPath();
44223
- (0, import_fs5.writeFileSync)(path60, JSON.stringify(config2, null, 2), { encoding: "utf-8", mode: 384 });
44356
+ const tmpPath = `${path60}.tmp-${process.pid}`;
44357
+ (0, import_fs5.writeFileSync)(tmpPath, JSON.stringify(config2, null, 2), { encoding: "utf-8", mode: 384 });
44358
+ (0, import_fs5.renameSync)(tmpPath, path60);
44224
44359
  }
44225
44360
  function normalizeRepoIdentity(remoteUrl) {
44226
44361
  let identity = remoteUrl.trim().replace(/[?#].*$/, "").replace(/\/+$/, "");
@@ -44260,7 +44395,10 @@ ${error48.message || ""}`;
44260
44395
  const normalized = normalizeRepoIdentity(repoIdentity);
44261
44396
  return loadMeshConfig().meshes.find((m) => normalizeRepoIdentity(m.repoIdentity) === normalized);
44262
44397
  }
44263
- function createMesh(opts) {
44398
+ function createMesh(...args) {
44399
+ return withMeshConfigWriteLock(() => createMeshUnlocked(...args));
44400
+ }
44401
+ function createMeshUnlocked(opts) {
44264
44402
  const config2 = loadMeshConfig();
44265
44403
  if (config2.meshes.length >= 20) {
44266
44404
  throw new Error("Maximum 20 meshes allowed");
@@ -44289,7 +44427,10 @@ ${error48.message || ""}`;
44289
44427
  saveMeshConfig(config2);
44290
44428
  return mesh;
44291
44429
  }
44292
- function updateMesh(meshId, opts) {
44430
+ function updateMesh(...args) {
44431
+ return withMeshConfigWriteLock(() => updateMeshUnlocked(...args));
44432
+ }
44433
+ function updateMeshUnlocked(meshId, opts) {
44293
44434
  const config2 = loadMeshConfig();
44294
44435
  const mesh = config2.meshes.find((m) => m.id === meshId);
44295
44436
  if (!mesh) return void 0;
@@ -44302,7 +44443,10 @@ ${error48.message || ""}`;
44302
44443
  saveMeshConfig(config2);
44303
44444
  return mesh;
44304
44445
  }
44305
- function deleteMesh(meshId) {
44446
+ function deleteMesh(...args) {
44447
+ return withMeshConfigWriteLock(() => deleteMeshUnlocked(...args));
44448
+ }
44449
+ function deleteMeshUnlocked(meshId) {
44306
44450
  const config2 = loadMeshConfig();
44307
44451
  const idx = config2.meshes.findIndex((m) => m.id === meshId);
44308
44452
  if (idx === -1) return false;
@@ -44349,7 +44493,10 @@ ${error48.message || ""}`;
44349
44493
  }
44350
44494
  return { ok: true, tokenId: presentedTokenId };
44351
44495
  }
44352
- function configureMeshHostPairing(meshId, opts) {
44496
+ function configureMeshHostPairing(...args) {
44497
+ return withMeshConfigWriteLock(() => configureMeshHostPairingUnlocked(...args));
44498
+ }
44499
+ function configureMeshHostPairingUnlocked(meshId, opts) {
44353
44500
  const hostAddress = normalizeManualHostAddress(opts.hostAddress);
44354
44501
  const token = opts.token.trim();
44355
44502
  if (!token) throw new Error("token required");
@@ -44373,7 +44520,10 @@ ${error48.message || ""}`;
44373
44520
  saveMeshConfig(config2);
44374
44521
  return { mesh, meshHost, hostAddress };
44375
44522
  }
44376
- function createMeshHostPairingToken(meshId, opts = {}) {
44523
+ function createMeshHostPairingToken(...args) {
44524
+ return withMeshConfigWriteLock(() => createMeshHostPairingTokenUnlocked(...args));
44525
+ }
44526
+ function createMeshHostPairingTokenUnlocked(meshId, opts = {}) {
44377
44527
  const config2 = loadMeshConfig();
44378
44528
  const mesh = config2.meshes.find((m) => m.id === meshId);
44379
44529
  if (!mesh) return void 0;
@@ -44401,7 +44551,10 @@ ${error48.message || ""}`;
44401
44551
  saveMeshConfig(config2);
44402
44552
  return { mesh, meshHost, token, tokenId, ...expiresAt ? { expiresAt } : {} };
44403
44553
  }
44404
- function applyMeshHostJoinRequest(meshId, opts) {
44554
+ function applyMeshHostJoinRequest(...args) {
44555
+ return withMeshConfigWriteLock(() => applyMeshHostJoinRequestUnlocked(...args));
44556
+ }
44557
+ function applyMeshHostJoinRequestUnlocked(meshId, opts) {
44405
44558
  const config2 = loadMeshConfig();
44406
44559
  const mesh = config2.meshes.find((m) => m.id === meshId);
44407
44560
  if (!mesh) return void 0;
@@ -44466,7 +44619,10 @@ ${error48.message || ""}`;
44466
44619
  saveMeshConfig(config2);
44467
44620
  return { accepted: true, mesh, meshHost: mesh.meshHost, node, tokenId: validation.tokenId };
44468
44621
  }
44469
- function setMeshHostPin(meshId, opts) {
44622
+ function setMeshHostPin(...args) {
44623
+ return withMeshConfigWriteLock(() => setMeshHostPinUnlocked(...args));
44624
+ }
44625
+ function setMeshHostPinUnlocked(meshId, opts) {
44470
44626
  const config2 = loadMeshConfig();
44471
44627
  const mesh = config2.meshes.find((m) => m.id === meshId);
44472
44628
  if (!mesh) return void 0;
@@ -44546,7 +44702,10 @@ ${error48.message || ""}`;
44546
44702
  ...mesh.meshHost.hostNodeId ? { hostNodeId: mesh.meshHost.hostNodeId } : {}
44547
44703
  };
44548
44704
  }
44549
- function markMeshHostPairingJoined(meshId, opts) {
44705
+ function markMeshHostPairingJoined(...args) {
44706
+ return withMeshConfigWriteLock(() => markMeshHostPairingJoinedUnlocked(...args));
44707
+ }
44708
+ function markMeshHostPairingJoinedUnlocked(meshId, opts) {
44550
44709
  const config2 = loadMeshConfig();
44551
44710
  const mesh = config2.meshes.find((m) => m.id === meshId);
44552
44711
  if (!mesh) return void 0;
@@ -44570,7 +44729,10 @@ ${error48.message || ""}`;
44570
44729
  saveMeshConfig(config2);
44571
44730
  return { mesh, meshHost: mesh.meshHost };
44572
44731
  }
44573
- function addNode(meshId, opts) {
44732
+ function addNode(...args) {
44733
+ return withMeshConfigWriteLock(() => addNodeUnlocked(...args));
44734
+ }
44735
+ function addNodeUnlocked(meshId, opts) {
44574
44736
  const config2 = loadMeshConfig();
44575
44737
  const mesh = config2.meshes.find((m) => m.id === meshId);
44576
44738
  if (!mesh) return void 0;
@@ -44617,7 +44779,10 @@ ${error48.message || ""}`;
44617
44779
  saveMeshConfig(config2);
44618
44780
  return node;
44619
44781
  }
44620
- function removeNode(meshId, nodeId) {
44782
+ function removeNode(...args) {
44783
+ return withMeshConfigWriteLock(() => removeNodeUnlocked(...args));
44784
+ }
44785
+ function removeNodeUnlocked(meshId, nodeId) {
44621
44786
  const config2 = loadMeshConfig();
44622
44787
  const mesh = config2.meshes.find((m) => m.id === meshId);
44623
44788
  if (!mesh) return false;
@@ -44629,7 +44794,10 @@ ${error48.message || ""}`;
44629
44794
  saveMeshConfig(config2);
44630
44795
  return true;
44631
44796
  }
44632
- function updateNode(meshId, nodeId, opts) {
44797
+ function updateNode(...args) {
44798
+ return withMeshConfigWriteLock(() => updateNodeUnlocked(...args));
44799
+ }
44800
+ function updateNodeUnlocked(meshId, nodeId, opts) {
44633
44801
  const config2 = loadMeshConfig();
44634
44802
  const mesh = config2.meshes.find((m) => m.id === meshId);
44635
44803
  if (!mesh) return void 0;
@@ -44759,7 +44927,10 @@ ${error48.message || ""}`;
44759
44927
  const config2 = loadMeshConfig();
44760
44928
  return resolveScopedMesh(config2, meshId)?.magiKindPanels?.[key2];
44761
44929
  }
44762
- function setMagiKindPanel(kind, slots, meshId) {
44930
+ function setMagiKindPanel(...args) {
44931
+ return withMeshConfigWriteLock(() => setMagiKindPanelUnlocked(...args));
44932
+ }
44933
+ function setMagiKindPanelUnlocked(kind, slots, meshId) {
44763
44934
  const key2 = normalizeMagiTaskKindKey(kind);
44764
44935
  const stored = loadMeshConfig();
44765
44936
  const mesh = resolveScopedMesh(stored, meshId);
@@ -44776,7 +44947,10 @@ ${error48.message || ""}`;
44776
44947
  saveMeshConfig(stored);
44777
44948
  return normalized;
44778
44949
  }
44779
- function removeMagiKindPanel(kind, meshId) {
44950
+ function removeMagiKindPanel(...args) {
44951
+ return withMeshConfigWriteLock(() => removeMagiKindPanelUnlocked(...args));
44952
+ }
44953
+ function removeMagiKindPanelUnlocked(kind, meshId) {
44780
44954
  let key2;
44781
44955
  try {
44782
44956
  key2 = normalizeMagiTaskKindKey(kind);
@@ -44813,7 +44987,10 @@ ${error48.message || ""}`;
44813
44987
  const normalized = normalizeDifficultyBrainMap(stored);
44814
44988
  return Object.keys(normalized).length > 0 ? normalized : { ...DEFAULT_DIFFICULTY_BRAINS };
44815
44989
  }
44816
- function setDifficultyBrains(map3, meshId) {
44990
+ function setDifficultyBrains(...args) {
44991
+ return withMeshConfigWriteLock(() => setDifficultyBrainsUnlocked(...args));
44992
+ }
44993
+ function setDifficultyBrainsUnlocked(map3, meshId) {
44817
44994
  const normalized = normalizeDifficultyBrainMap(map3);
44818
44995
  const stored = loadMeshConfig();
44819
44996
  const mesh = resolveScopedMesh(stored, meshId);
@@ -44859,7 +45036,10 @@ ${error48.message || ""}`;
44859
45036
  const stored = resolveScopedMesh(config2, meshId)?.policy?.quotaRouting;
44860
45037
  return normalizeQuotaRoutingPolicy(stored) ?? {};
44861
45038
  }
44862
- function setMeshQuotaRouting(input, meshId) {
45039
+ function setMeshQuotaRouting(...args) {
45040
+ return withMeshConfigWriteLock(() => setMeshQuotaRoutingUnlocked(...args));
45041
+ }
45042
+ function setMeshQuotaRoutingUnlocked(input, meshId) {
44863
45043
  const overrides = validateQuotaRoutingOverrides(input);
44864
45044
  const stored = loadMeshConfig();
44865
45045
  const mesh = resolveScopedMesh(stored, meshId);
@@ -44876,6 +45056,10 @@ ${error48.message || ""}`;
44876
45056
  var import_fs5;
44877
45057
  var import_path5;
44878
45058
  var import_crypto3;
45059
+ var MESH_CONFIG_LOCK_WAIT_MS;
45060
+ var MESH_CONFIG_LOCK_STALE_MS;
45061
+ var MESH_CONFIG_LOCK_POLL_MS;
45062
+ var meshConfigLockHeldInProcess;
44879
45063
  var mergeMeshPolicy;
44880
45064
  var MAGI_KIND_PANEL_KINDS;
44881
45065
  var MAX_MAGI_KIND_SLOTS;
@@ -44894,6 +45078,10 @@ ${error48.message || ""}`;
44894
45078
  init_dist();
44895
45079
  init_repo_mesh_types();
44896
45080
  init_mesh_host_ownership();
45081
+ MESH_CONFIG_LOCK_WAIT_MS = 2e3;
45082
+ MESH_CONFIG_LOCK_STALE_MS = 15e3;
45083
+ MESH_CONFIG_LOCK_POLL_MS = 25;
45084
+ meshConfigLockHeldInProcess = false;
44897
45085
  mergeMeshPolicy = mergeAndNormalizePolicy;
44898
45086
  MAGI_KIND_PANEL_KINDS = ["claim_audit", "rca", "design", "freeform"];
44899
45087
  MAX_MAGI_KIND_SLOTS = 24;
@@ -49721,6 +49909,176 @@ The instruction it carried was never delivered to anyone. If it still matters, r
49721
49909
  PARK_RETENTION_EXPIRED_REASON = "parked_task_retention_expired";
49722
49910
  }
49723
49911
  });
49912
+ function isWorkerAbsenceDispatchFailure(reason) {
49913
+ const text = readNonEmptyString(reason);
49914
+ if (!text) return false;
49915
+ const normalized = text.toLowerCase();
49916
+ return normalized.includes("cli agent not running") || normalized.includes("no provider instance for session") || normalized.includes("no mesh worker session bound to node");
49917
+ }
49918
+ function resolveTunedReconcileMs(envName, def, min, max) {
49919
+ const raw = readNonEmptyString(process.env[envName]);
49920
+ if (raw) {
49921
+ const parsed = Number.parseInt(raw, 10);
49922
+ if (Number.isFinite(parsed) && parsed >= min && parsed <= max) return parsed;
49923
+ }
49924
+ return def;
49925
+ }
49926
+ function resolveAckedDeathDeadlineMs() {
49927
+ return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_DEATH_DEADLINE_MS", 8 * 6e4, 0, 60 * 6e4);
49928
+ }
49929
+ function resolveAckedTranscriptFastTrackGraceMs() {
49930
+ return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_TRANSCRIPT_FASTTRACK_GRACE_MS", 4e4, 0, 5 * 6e4);
49931
+ }
49932
+ function resolveAckedHoldHardCeilingMs() {
49933
+ return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_HOLD_HARD_CEILING_MS", 90 * 6e4, 0, 24 * 60 * 6e4);
49934
+ }
49935
+ function inFlightSynthKey(meshId, taskId) {
49936
+ return `${meshId}::${taskId}`;
49937
+ }
49938
+ function taskIdFromSynthKey(meshId, synthKey) {
49939
+ const prefix = `${meshId}::`;
49940
+ return synthKey.startsWith(prefix) ? synthKey.slice(prefix.length) : synthKey;
49941
+ }
49942
+ function holdStore() {
49943
+ try {
49944
+ return MeshRuntimeStore.getInstance();
49945
+ } catch {
49946
+ return void 0;
49947
+ }
49948
+ }
49949
+ function getHoldState(synthKey, meshId) {
49950
+ const cached5 = inFlightAckedHoldState.get(synthKey);
49951
+ if (cached5) return cached5;
49952
+ const store = holdStore();
49953
+ if (!store) return void 0;
49954
+ let row;
49955
+ try {
49956
+ row = store.getInflightHold(taskIdFromSynthKey(meshId, synthKey));
49957
+ } catch {
49958
+ return void 0;
49959
+ }
49960
+ if (!row) return void 0;
49961
+ const state = {
49962
+ liveConfirmedSinceAck: row.holdReason === "live",
49963
+ consecutiveReadFailures: row.readFailureCount ?? 0,
49964
+ ...row.firstIdleSinceAck !== null && row.firstIdleSinceAck !== void 0 ? { transcriptIdleSinceMs: row.firstIdleSinceAck } : {},
49965
+ ...row.heldAt !== null && row.heldAt !== void 0 ? { heldSinceMs: row.heldAt } : {}
49966
+ };
49967
+ inFlightAckedHoldState.set(synthKey, state);
49968
+ return state;
49969
+ }
49970
+ function setHoldState(synthKey, meshId, state) {
49971
+ const store = holdStore();
49972
+ let priorHeldSinceMs = state.heldSinceMs ?? inFlightAckedHoldState.get(synthKey)?.heldSinceMs;
49973
+ if (priorHeldSinceMs === void 0 && store) {
49974
+ try {
49975
+ priorHeldSinceMs = store.getInflightHold(taskIdFromSynthKey(meshId, synthKey))?.heldAt ?? void 0;
49976
+ } catch {
49977
+ }
49978
+ }
49979
+ const heldSinceMs = priorHeldSinceMs ?? Date.now();
49980
+ inFlightAckedHoldState.set(synthKey, { ...state, heldSinceMs });
49981
+ if (!store) return;
49982
+ try {
49983
+ store.upsertInflightHold({
49984
+ taskId: taskIdFromSynthKey(meshId, synthKey),
49985
+ meshId,
49986
+ holdReason: state.liveConfirmedSinceAck ? "live" : "unconfirmed",
49987
+ // Only meaningful when the row is NEW — upsertInflightHold deliberately
49988
+ // preserves held_at on conflict, so an existing row keeps its original anchor.
49989
+ heldAt: heldSinceMs,
49990
+ firstIdleSinceAck: state.transcriptIdleSinceMs ?? null,
49991
+ readFailureCount: state.consecutiveReadFailures
49992
+ });
49993
+ } catch {
49994
+ }
49995
+ }
49996
+ function deleteHoldState(synthKey, meshId) {
49997
+ inFlightAckedHoldState.delete(synthKey);
49998
+ const store = holdStore();
49999
+ if (!store) return;
50000
+ try {
50001
+ store.deleteInflightHold(taskIdFromSynthKey(meshId, synthKey));
50002
+ } catch {
50003
+ }
50004
+ }
50005
+ function recordAckedHoldDispatchOutcome(meshId, taskId, outcome) {
50006
+ const key2 = inFlightSynthKey(meshId, taskId);
50007
+ if (outcome.ok || !isWorkerAbsenceDispatchFailure(outcome.reason)) {
50008
+ ackedHoldDispatchFailureStreak.delete(key2);
50009
+ return;
50010
+ }
50011
+ const prior = ackedHoldDispatchFailureStreak.get(key2)?.count ?? 0;
50012
+ ackedHoldDispatchFailureStreak.set(key2, {
50013
+ count: prior + 1,
50014
+ lastReason: readNonEmptyString(outcome.reason) || "worker_absent"
50015
+ });
50016
+ }
50017
+ function getAckedHoldDispatchFailureStreak(meshId, taskId) {
50018
+ return ackedHoldDispatchFailureStreak.get(inFlightSynthKey(meshId, taskId));
50019
+ }
50020
+ function clearAckedHoldDispatchFailureStreak(synthKey) {
50021
+ ackedHoldDispatchFailureStreak.delete(synthKey);
50022
+ }
50023
+ function rehydrateAckedHoldsForMesh(meshId) {
50024
+ if (rehydratedHoldMeshes.has(meshId)) return;
50025
+ rehydratedHoldMeshes.add(meshId);
50026
+ const store = holdStore();
50027
+ if (!store) return;
50028
+ let rows;
50029
+ try {
50030
+ rows = store.listInflightHoldsByMesh(meshId);
50031
+ } catch {
50032
+ return;
50033
+ }
50034
+ for (const row of rows) {
50035
+ const synthKey = inFlightSynthKey(meshId, row.taskId);
50036
+ if (inFlightAckedHoldState.has(synthKey)) continue;
50037
+ inFlightAckedHoldState.set(synthKey, {
50038
+ liveConfirmedSinceAck: row.holdReason === "live",
50039
+ consecutiveReadFailures: row.readFailureCount ?? 0,
50040
+ ...row.firstIdleSinceAck !== null && row.firstIdleSinceAck !== void 0 ? { transcriptIdleSinceMs: row.firstIdleSinceAck } : {},
50041
+ ...row.heldAt !== null && row.heldAt !== void 0 ? { heldSinceMs: row.heldAt } : {}
50042
+ });
50043
+ }
50044
+ if (rows.length > 0) {
50045
+ LOG2.info("MeshReconcile", `Rehydrated ${rows.length} persisted acked-hold row(s) for mesh ${meshId} after (re)start`);
50046
+ }
50047
+ }
50048
+ function collectHeldSynthKeysForMesh(meshId) {
50049
+ const heldKeys = /* @__PURE__ */ new Set();
50050
+ for (const key2 of inFlightAckedHoldState.keys()) {
50051
+ if (key2.startsWith(`${meshId}::`)) heldKeys.add(key2);
50052
+ }
50053
+ const store = holdStore();
50054
+ if (store) {
50055
+ try {
50056
+ for (const row of store.listInflightHoldsByMesh(meshId)) {
50057
+ heldKeys.add(inFlightSynthKey(meshId, row.taskId));
50058
+ }
50059
+ } catch {
50060
+ }
50061
+ }
50062
+ return heldKeys;
50063
+ }
50064
+ var ACKED_DEATH_CONSECUTIVE_READ_FAILURES;
50065
+ var ACKED_DEATH_CONSECUTIVE_DISPATCH_FAILURES;
50066
+ var inFlightAckedHoldState;
50067
+ var rehydratedHoldMeshes;
50068
+ var ackedHoldDispatchFailureStreak;
50069
+ var init_mesh_reconcile_acked_hold = __esm2({
50070
+ "src/mesh/mesh-reconcile-acked-hold.ts"() {
50071
+ "use strict";
50072
+ init_logger();
50073
+ init_mesh_runtime_store();
50074
+ init_mesh_events_utils();
50075
+ ACKED_DEATH_CONSECUTIVE_READ_FAILURES = 3;
50076
+ ACKED_DEATH_CONSECUTIVE_DISPATCH_FAILURES = 3;
50077
+ inFlightAckedHoldState = /* @__PURE__ */ new Map();
50078
+ rehydratedHoldMeshes = /* @__PURE__ */ new Set();
50079
+ ackedHoldDispatchFailureStreak = /* @__PURE__ */ new Map();
50080
+ }
50081
+ });
49724
50082
  function readPayloadTaskId(entry) {
49725
50083
  const value = entry.payload?.taskId;
49726
50084
  return typeof value === "string" && value.trim() ? value.trim() : "";
@@ -50227,6 +50585,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
50227
50585
  normalizeMeshTaskPriority: () => normalizeMeshTaskPriority,
50228
50586
  parkTaskTargetPin: () => parkTaskTargetPin,
50229
50587
  reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
50588
+ recordAckedHoldDispatchOutcome: () => recordAckedHoldDispatchOutcome,
50230
50589
  recordDirectDispatchTask: () => recordDirectDispatchTask,
50231
50590
  recordMeshToolCall: () => recordMeshToolCall,
50232
50591
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
@@ -51125,6 +51484,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
51125
51484
  init_mesh_task_mode_guardrail();
51126
51485
  init_mesh_task_parking();
51127
51486
  init_mesh_task_mode_guardrail();
51487
+ init_mesh_reconcile_acked_hold();
51128
51488
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
51129
51489
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
51130
51490
  MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
@@ -57908,6 +58268,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
57908
58268
  "task_approval_needed",
57909
58269
  "task_question_pending",
57910
58270
  "p2p_dispatch_failed",
58271
+ "dispatch_failed",
57911
58272
  "dispatch_duplicate_rebound",
57912
58273
  "queue_hold_hard_deadline"
57913
58274
  ]);
@@ -61442,6 +61803,10 @@ ${rendered}`, "utf-8");
61442
61803
  if (cachedInline && registry2?.updateInlineMeshNode) {
61443
61804
  registry2.updateInlineMeshNode(req.meshId, cachedInline, node);
61444
61805
  published = true;
61806
+ } else if (!cachedInline && !mesh && registry2?.updateInlineMeshNode) {
61807
+ const shell = { id: req.meshId, nodes: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
61808
+ registry2.updateInlineMeshNode(req.meshId, shell, node);
61809
+ published = true;
61445
61810
  }
61446
61811
  } catch {
61447
61812
  }
@@ -61801,7 +62166,7 @@ ${rendered}`, "utf-8");
61801
62166
  }
61802
62167
  async function registerWorkspaceNodeBestEffort(intent, created, ports, bootstrapStatus) {
61803
62168
  try {
61804
- await ports.registerNode({
62169
+ const published = await ports.registerNode({
61805
62170
  meshId: intent.meshId,
61806
62171
  nodeId: created.nodeId,
61807
62172
  worktreePath: created.worktreePath,
@@ -61809,6 +62174,9 @@ ${rendered}`, "utf-8");
61809
62174
  sourceNodeId: intent.sourceNodeId,
61810
62175
  bootstrapStatus
61811
62176
  });
62177
+ if (!published) {
62178
+ LOG2.warn("MeshGraphWorkspace", `node registration (${bootstrapStatus}) published NOTHING for ${intent.graphId}/${intent.workspaceRef}: node ${created.nodeId} exists on disk at ${created.worktreePath} but is in no membership view (mesh ${intent.meshId} has no config twin on this daemon and no inline-cache seam accepted it) \u2014 mesh_list_nodes will not show it`);
62179
+ }
61812
62180
  } catch (e) {
61813
62181
  LOG2.warn("MeshGraphWorkspace", `node registration (${bootstrapStatus}) failed for ${intent.graphId}/${intent.workspaceRef}: ${e?.message || e}`);
61814
62182
  }
@@ -68019,7 +68387,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
68019
68387
  const base = 1;
68020
68388
  let difficulty = 0;
68021
68389
  const diff = isMeshTaskDifficulty(task.difficulty) ? task.difficulty : void 0;
68022
- if (diff) {
68390
+ if (diff && diff !== "freeform") {
68023
68391
  if (slot.difficulty?.length) {
68024
68392
  difficulty = slot.difficulty.includes(diff) ? 100 : 0;
68025
68393
  } else {
@@ -68690,6 +69058,146 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
68690
69058
  CLASSIFIED_DIFFICULTIES = ["easy", "medium", "difficult"];
68691
69059
  }
68692
69060
  });
69061
+ function normalizeProviderPriority2(policy) {
69062
+ const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
69063
+ if (!Array.isArray(raw)) return [];
69064
+ const seen = /* @__PURE__ */ new Set();
69065
+ return raw.map((type2) => typeof type2 === "string" ? type2.trim() : "").filter(Boolean).filter((type2) => {
69066
+ if (seen.has(type2)) return false;
69067
+ seen.add(type2);
69068
+ return true;
69069
+ });
69070
+ }
69071
+ function isTerminalSessionStatus(status) {
69072
+ return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
69073
+ }
69074
+ function isIdleSessionState3(state) {
69075
+ const status = readNonEmptyString(state?.status).toLowerCase();
69076
+ if (isTerminalSessionStatus(status)) return false;
69077
+ return status === "idle" || state?.activeChat?.status === "waiting_input";
69078
+ }
69079
+ function sessionStateLooksActive(state) {
69080
+ const status = readNonEmptyString(state?.status).toLowerCase();
69081
+ const chatStatus = readNonEmptyString(state?.activeChat?.status).toLowerCase();
69082
+ const active = /* @__PURE__ */ new Set(["generating", "streaming", "no_progress", "long_generating", "working", "starting", "waiting_approval"]);
69083
+ return active.has(status) || active.has(chatStatus);
69084
+ }
69085
+ function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
69086
+ if (nodeHasActiveAssignment(meshId, nodeId)) return true;
69087
+ return components.instanceManager.getByCategory("cli").some((inst) => {
69088
+ const state = inst.getState();
69089
+ const settings = state.settings || {};
69090
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69091
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69092
+ if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69093
+ const sessionId = readNonEmptyString(state.instanceId);
69094
+ if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState3(state)) return false;
69095
+ return sessionStateLooksActive(state);
69096
+ });
69097
+ }
69098
+ function isLaunchableNode(node) {
69099
+ if (!node || node.status === "disabled" || node.status === "removed") return false;
69100
+ if (shouldDeferDispatchForBootstrap(node)) {
69101
+ return false;
69102
+ }
69103
+ return isMeshNodeHealthLaunchable(node);
69104
+ }
69105
+ function isLocalAutoLaunchNode(node) {
69106
+ const daemonId = readNonEmptyString(node?.daemonId);
69107
+ const machineId = readNonEmptyString(node?.machineId);
69108
+ const appConfig = loadConfig2();
69109
+ const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
69110
+ const daemonMatchesLocal = !daemonId || daemonIdsEquivalent(daemonId, localMachineId);
69111
+ const machineMatchesLocal = !machineId || daemonIdsEquivalent(machineId, localMachineId);
69112
+ if (node?.isLocalWorktree === true) {
69113
+ return daemonMatchesLocal && machineMatchesLocal;
69114
+ }
69115
+ if (daemonId || machineId) {
69116
+ return daemonMatchesLocal && machineMatchesLocal;
69117
+ }
69118
+ return true;
69119
+ }
69120
+ function isSessionActivelyGenerating(components, sessionId) {
69121
+ if (!sessionId) return false;
69122
+ const state = components.instanceManager?.getInstance?.(sessionId)?.getState?.();
69123
+ if (!state) return false;
69124
+ return sessionStateLooksActive(state);
69125
+ }
69126
+ function resolveSessionBusyVerdict(components, sessionId) {
69127
+ if (!sessionId) return "UNKNOWN";
69128
+ try {
69129
+ const instances = components.instanceManager?.getByCategory?.("cli") || [];
69130
+ const inst = instances.find((i) => {
69131
+ const sid = readNonEmptyString(i?.getState?.().instanceId);
69132
+ return sid && sessionIdsEquivalent(sid, sessionId);
69133
+ });
69134
+ if (!inst) return "UNKNOWN";
69135
+ const state = inst.getState?.();
69136
+ if (!state) return "UNKNOWN";
69137
+ return sessionStateLooksActive(state) ? "GENERATING" : "IDLE_CONFIRMED";
69138
+ } catch {
69139
+ return "UNKNOWN";
69140
+ }
69141
+ }
69142
+ function liveSessionCountForNode(components, meshId, nodeId) {
69143
+ const localInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
69144
+ const state = inst.getState();
69145
+ const settings = state.settings || {};
69146
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69147
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69148
+ if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69149
+ const status = readNonEmptyString(state.status).toLowerCase();
69150
+ return !isTerminalSessionStatus(status);
69151
+ });
69152
+ let count = localInstances.length;
69153
+ const localSessionIds = localInstances.map((inst) => readNonEmptyString(inst.getState().instanceId)).filter(Boolean);
69154
+ for (const sid of inWindowAutoLaunchSessionIdsForNode(meshId, nodeId)) {
69155
+ if (!localSessionIds.some((local) => sessionIdsEquivalent(local, sid))) count += 1;
69156
+ }
69157
+ return count;
69158
+ }
69159
+ function nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node) {
69160
+ if (inWindowAutoLaunchSessionIdsForNode(meshId, nodeId).length > 0) return true;
69161
+ const busySessionIds = new Set(
69162
+ getQueue(meshId, { status: ["assigned"] }).filter((task2) => daemonIdsEquivalent(task2.assignedNodeId, nodeId)).map((task2) => readNonEmptyString(task2.assignedSessionId)).filter(Boolean)
69163
+ );
69164
+ return components.instanceManager.getByCategory("cli").some((inst) => {
69165
+ const state = inst.getState();
69166
+ const settings = state.settings || {};
69167
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69168
+ if (readNonEmptyString(settings.meshCoordinatorFor) === meshId) return false;
69169
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69170
+ if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69171
+ const status = readNonEmptyString(state.status).toLowerCase();
69172
+ if (isTerminalSessionStatus(status)) return false;
69173
+ const sessionId = readNonEmptyString(state.instanceId);
69174
+ if (sessionId && busySessionIds.has(sessionId)) return false;
69175
+ const sessionProviderType = state.type || readNonEmptyString(settings.providerType);
69176
+ if (task.requiredTags?.length) {
69177
+ if (sessionProviderType && !nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, sessionProviderType))) {
69178
+ return false;
69179
+ }
69180
+ }
69181
+ const allowance = allowedClassifiedDifficultiesForSession(node, resolveNodeCapabilitySlots(node, meshId), sessionProviderType, readSessionModel(state));
69182
+ if (!taskMeetsSessionDifficultyFloor(task, allowance)) return false;
69183
+ return true;
69184
+ });
69185
+ }
69186
+ var init_mesh_candidacy_predicates = __esm2({
69187
+ "src/mesh/mesh-candidacy-predicates.ts"() {
69188
+ "use strict";
69189
+ init_config();
69190
+ init_mesh_work_queue();
69191
+ init_dist();
69192
+ init_mesh_events_utils();
69193
+ init_mesh_node_identity();
69194
+ init_worktree_bootstrap_config();
69195
+ init_mesh_autolaunch_integrity();
69196
+ init_mesh_scheduling_fitness();
69197
+ init_mesh_node_slots();
69198
+ init_mesh_difficulty_floor();
69199
+ }
69200
+ });
68693
69201
  function localCoordinatorDaemonId2() {
68694
69202
  return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
68695
69203
  }
@@ -68886,6 +69394,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
68886
69394
  if (timer) clearTimeout(timer);
68887
69395
  const isQueued = res && typeof res === "object" && res.status === "queued";
68888
69396
  updateSessionDeliveryStatus(delivery.id, isQueued ? "queued" : "delivered");
69397
+ recordAckedHoldDispatchOutcome(ctx.meshId, ctx.task.id, { ok: true });
68889
69398
  if (ctx.task.attemptId) {
68890
69399
  try {
68891
69400
  recordTurnAck({
@@ -68934,6 +69443,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
68934
69443
  }
68935
69444
  LOG2.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
68936
69445
  updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
69446
+ recordAckedHoldDispatchOutcome(ctx.meshId, ctx.task.id, { ok: false, reason: e?.message });
68937
69447
  endTaskDispatchInFlight(ctx.meshId, ctx.task.id);
68938
69448
  try {
68939
69449
  closeAttemptForReassignment({ meshId: ctx.meshId, taskId: ctx.task.id, reason: "dispatch_failed" });
@@ -68954,6 +69464,12 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
68954
69464
  }
68955
69465
  try {
68956
69466
  appendLedgerEntry(ctx.meshId, {
69467
+ // 'dispatch_failed' is a real MeshLedgerKind and a member of
69468
+ // TASK_LIFECYCLE_LEDGER_KINDS, so appendLedgerEntry derives the top-level
69469
+ // taskId from payload.taskId below. It spent its whole life as
69470
+ // `as any` — off the union, hence off the lifecycle set, hence written to
69471
+ // the indexed SQLite task_id column as NULL and unreachable by the
69472
+ // kind+taskId join every reader uses. Do not reintroduce the cast.
68957
69473
  kind: "dispatch_failed",
68958
69474
  nodeId: ctx.nodeId,
68959
69475
  sessionId: ctx.sessionId,
@@ -69299,65 +69815,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
69299
69815
  if (now >= until) autoLaunchCooldownUntil.delete(key2);
69300
69816
  }
69301
69817
  }
69302
- function normalizeProviderPriority2(policy) {
69303
- const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
69304
- if (!Array.isArray(raw)) return [];
69305
- const seen = /* @__PURE__ */ new Set();
69306
- return raw.map((type2) => typeof type2 === "string" ? type2.trim() : "").filter(Boolean).filter((type2) => {
69307
- if (seen.has(type2)) return false;
69308
- seen.add(type2);
69309
- return true;
69310
- });
69311
- }
69312
- function isTerminalSessionStatus(status) {
69313
- return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
69314
- }
69315
- function isIdleSessionState3(state) {
69316
- const status = readNonEmptyString(state?.status).toLowerCase();
69317
- if (isTerminalSessionStatus(status)) return false;
69318
- return status === "idle" || state?.activeChat?.status === "waiting_input";
69319
- }
69320
- function sessionStateLooksActive(state) {
69321
- const status = readNonEmptyString(state?.status).toLowerCase();
69322
- const chatStatus = readNonEmptyString(state?.activeChat?.status).toLowerCase();
69323
- const active = /* @__PURE__ */ new Set(["generating", "streaming", "no_progress", "long_generating", "working", "starting", "waiting_approval"]);
69324
- return active.has(status) || active.has(chatStatus);
69325
- }
69326
- function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
69327
- if (nodeHasActiveAssignment(meshId, nodeId)) return true;
69328
- return components.instanceManager.getByCategory("cli").some((inst) => {
69329
- const state = inst.getState();
69330
- const settings = state.settings || {};
69331
- if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69332
- const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69333
- if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69334
- const sessionId = readNonEmptyString(state.instanceId);
69335
- if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState3(state)) return false;
69336
- return sessionStateLooksActive(state);
69337
- });
69338
- }
69339
- function isLaunchableNode(node) {
69340
- if (!node || node.status === "disabled" || node.status === "removed") return false;
69341
- if (shouldDeferDispatchForBootstrap(node)) {
69342
- return false;
69343
- }
69344
- return isMeshNodeHealthLaunchable(node);
69345
- }
69346
- function isLocalAutoLaunchNode(node) {
69347
- const daemonId = readNonEmptyString(node?.daemonId);
69348
- const machineId = readNonEmptyString(node?.machineId);
69349
- const appConfig = loadConfig2();
69350
- const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
69351
- const daemonMatchesLocal = !daemonId || daemonIdsEquivalent(daemonId, localMachineId);
69352
- const machineMatchesLocal = !machineId || daemonIdsEquivalent(machineId, localMachineId);
69353
- if (node?.isLocalWorktree === true) {
69354
- return daemonMatchesLocal && machineMatchesLocal;
69355
- }
69356
- if (daemonId || machineId) {
69357
- return daemonMatchesLocal && machineMatchesLocal;
69358
- }
69359
- return true;
69360
- }
69361
69818
  function resolveAutoLaunchTarget(components, node) {
69362
69819
  if (isLocalAutoLaunchNode(node)) return { mode: "local" };
69363
69820
  const daemonId = readMeshNodeDaemonId(node ?? {});
@@ -69367,72 +69824,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
69367
69824
  if (!coordinatorDaemonId) return { mode: "skip", reason: "remote_auto_launch_no_coordinator_daemon_id" };
69368
69825
  return { mode: "remote", daemonId, coordinatorDaemonId };
69369
69826
  }
69370
- function isSessionActivelyGenerating(components, sessionId) {
69371
- if (!sessionId) return false;
69372
- const state = components.instanceManager?.getInstance?.(sessionId)?.getState?.();
69373
- if (!state) return false;
69374
- return sessionStateLooksActive(state);
69375
- }
69376
- function resolveSessionBusyVerdict(components, sessionId) {
69377
- if (!sessionId) return "UNKNOWN";
69378
- try {
69379
- const instances = components.instanceManager?.getByCategory?.("cli") || [];
69380
- const inst = instances.find((i) => {
69381
- const sid = readNonEmptyString(i?.getState?.().instanceId);
69382
- return sid && sessionIdsEquivalent(sid, sessionId);
69383
- });
69384
- if (!inst) return "UNKNOWN";
69385
- const state = inst.getState?.();
69386
- if (!state) return "UNKNOWN";
69387
- return sessionStateLooksActive(state) ? "GENERATING" : "IDLE_CONFIRMED";
69388
- } catch {
69389
- return "UNKNOWN";
69390
- }
69391
- }
69392
- function liveSessionCountForNode(components, meshId, nodeId) {
69393
- const localInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
69394
- const state = inst.getState();
69395
- const settings = state.settings || {};
69396
- if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69397
- const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69398
- if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69399
- const status = readNonEmptyString(state.status).toLowerCase();
69400
- return !isTerminalSessionStatus(status);
69401
- });
69402
- let count = localInstances.length;
69403
- const localSessionIds = localInstances.map((inst) => readNonEmptyString(inst.getState().instanceId)).filter(Boolean);
69404
- for (const sid of inWindowAutoLaunchSessionIdsForNode(meshId, nodeId)) {
69405
- if (!localSessionIds.some((local) => sessionIdsEquivalent(local, sid))) count += 1;
69406
- }
69407
- return count;
69408
- }
69409
- function nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node) {
69410
- if (inWindowAutoLaunchSessionIdsForNode(meshId, nodeId).length > 0) return true;
69411
- const busySessionIds = new Set(
69412
- getQueue(meshId, { status: ["assigned"] }).filter((task2) => daemonIdsEquivalent(task2.assignedNodeId, nodeId)).map((task2) => readNonEmptyString(task2.assignedSessionId)).filter(Boolean)
69413
- );
69414
- return components.instanceManager.getByCategory("cli").some((inst) => {
69415
- const state = inst.getState();
69416
- const settings = state.settings || {};
69417
- if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69418
- if (readNonEmptyString(settings.meshCoordinatorFor) === meshId) return false;
69419
- const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69420
- if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69421
- const status = readNonEmptyString(state.status).toLowerCase();
69422
- if (isTerminalSessionStatus(status)) return false;
69423
- const sessionId = readNonEmptyString(state.instanceId);
69424
- if (sessionId && busySessionIds.has(sessionId)) return false;
69425
- const sessionProviderType = state.type || readNonEmptyString(settings.providerType);
69426
- if (task.requiredTags?.length) {
69427
- if (sessionProviderType && !nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, sessionProviderType))) {
69428
- return false;
69429
- }
69430
- }
69431
- const allowance = allowedClassifiedDifficultiesForSession(node, resolveNodeCapabilitySlots(node, meshId), sessionProviderType, readSessionModel(state));
69432
- if (!taskMeetsSessionDifficultyFloor(task, allowance)) return false;
69433
- return true;
69434
- });
69435
- }
69436
69827
  function markAutoLaunch(meshId, taskId, args) {
69437
69828
  const reason = args.reason || args.error;
69438
69829
  const difficultyFloorSkip = args.status === "skipped" && isDifficultyFloorWaitReason(reason);
@@ -70172,6 +70563,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
70172
70563
  init_mesh_routing_decision();
70173
70564
  init_mesh_autolaunch_integrity();
70174
70565
  init_mesh_difficulty_floor();
70566
+ init_mesh_candidacy_predicates();
70175
70567
  init_mesh_difficulty_floor();
70176
70568
  init_mesh_auto_fast_forward();
70177
70569
  init_mesh_skip_notify();
@@ -74573,148 +74965,6 @@ ${cleanBody}`;
74573
74965
  init_mesh_events_utils();
74574
74966
  }
74575
74967
  });
74576
- function resolveTunedReconcileMs(envName, def, min, max) {
74577
- const raw = readNonEmptyString(process.env[envName]);
74578
- if (raw) {
74579
- const parsed = Number.parseInt(raw, 10);
74580
- if (Number.isFinite(parsed) && parsed >= min && parsed <= max) return parsed;
74581
- }
74582
- return def;
74583
- }
74584
- function resolveAckedDeathDeadlineMs() {
74585
- return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_DEATH_DEADLINE_MS", 8 * 6e4, 0, 60 * 6e4);
74586
- }
74587
- function resolveAckedTranscriptFastTrackGraceMs() {
74588
- return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_TRANSCRIPT_FASTTRACK_GRACE_MS", 4e4, 0, 5 * 6e4);
74589
- }
74590
- function resolveAckedHoldHardCeilingMs() {
74591
- return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_HOLD_HARD_CEILING_MS", 90 * 6e4, 0, 24 * 60 * 6e4);
74592
- }
74593
- function inFlightSynthKey(meshId, taskId) {
74594
- return `${meshId}::${taskId}`;
74595
- }
74596
- function taskIdFromSynthKey(meshId, synthKey) {
74597
- const prefix = `${meshId}::`;
74598
- return synthKey.startsWith(prefix) ? synthKey.slice(prefix.length) : synthKey;
74599
- }
74600
- function holdStore() {
74601
- try {
74602
- return MeshRuntimeStore.getInstance();
74603
- } catch {
74604
- return void 0;
74605
- }
74606
- }
74607
- function getHoldState(synthKey, meshId) {
74608
- const cached5 = inFlightAckedHoldState.get(synthKey);
74609
- if (cached5) return cached5;
74610
- const store = holdStore();
74611
- if (!store) return void 0;
74612
- let row;
74613
- try {
74614
- row = store.getInflightHold(taskIdFromSynthKey(meshId, synthKey));
74615
- } catch {
74616
- return void 0;
74617
- }
74618
- if (!row) return void 0;
74619
- const state = {
74620
- liveConfirmedSinceAck: row.holdReason === "live",
74621
- consecutiveReadFailures: row.readFailureCount ?? 0,
74622
- ...row.firstIdleSinceAck !== null && row.firstIdleSinceAck !== void 0 ? { transcriptIdleSinceMs: row.firstIdleSinceAck } : {},
74623
- ...row.heldAt !== null && row.heldAt !== void 0 ? { heldSinceMs: row.heldAt } : {}
74624
- };
74625
- inFlightAckedHoldState.set(synthKey, state);
74626
- return state;
74627
- }
74628
- function setHoldState(synthKey, meshId, state) {
74629
- const store = holdStore();
74630
- let priorHeldSinceMs = state.heldSinceMs ?? inFlightAckedHoldState.get(synthKey)?.heldSinceMs;
74631
- if (priorHeldSinceMs === void 0 && store) {
74632
- try {
74633
- priorHeldSinceMs = store.getInflightHold(taskIdFromSynthKey(meshId, synthKey))?.heldAt ?? void 0;
74634
- } catch {
74635
- }
74636
- }
74637
- const heldSinceMs = priorHeldSinceMs ?? Date.now();
74638
- inFlightAckedHoldState.set(synthKey, { ...state, heldSinceMs });
74639
- if (!store) return;
74640
- try {
74641
- store.upsertInflightHold({
74642
- taskId: taskIdFromSynthKey(meshId, synthKey),
74643
- meshId,
74644
- holdReason: state.liveConfirmedSinceAck ? "live" : "unconfirmed",
74645
- // Only meaningful when the row is NEW — upsertInflightHold deliberately
74646
- // preserves held_at on conflict, so an existing row keeps its original anchor.
74647
- heldAt: heldSinceMs,
74648
- firstIdleSinceAck: state.transcriptIdleSinceMs ?? null,
74649
- readFailureCount: state.consecutiveReadFailures
74650
- });
74651
- } catch {
74652
- }
74653
- }
74654
- function deleteHoldState(synthKey, meshId) {
74655
- inFlightAckedHoldState.delete(synthKey);
74656
- const store = holdStore();
74657
- if (!store) return;
74658
- try {
74659
- store.deleteInflightHold(taskIdFromSynthKey(meshId, synthKey));
74660
- } catch {
74661
- }
74662
- }
74663
- function rehydrateAckedHoldsForMesh(meshId) {
74664
- if (rehydratedHoldMeshes.has(meshId)) return;
74665
- rehydratedHoldMeshes.add(meshId);
74666
- const store = holdStore();
74667
- if (!store) return;
74668
- let rows;
74669
- try {
74670
- rows = store.listInflightHoldsByMesh(meshId);
74671
- } catch {
74672
- return;
74673
- }
74674
- for (const row of rows) {
74675
- const synthKey = inFlightSynthKey(meshId, row.taskId);
74676
- if (inFlightAckedHoldState.has(synthKey)) continue;
74677
- inFlightAckedHoldState.set(synthKey, {
74678
- liveConfirmedSinceAck: row.holdReason === "live",
74679
- consecutiveReadFailures: row.readFailureCount ?? 0,
74680
- ...row.firstIdleSinceAck !== null && row.firstIdleSinceAck !== void 0 ? { transcriptIdleSinceMs: row.firstIdleSinceAck } : {},
74681
- ...row.heldAt !== null && row.heldAt !== void 0 ? { heldSinceMs: row.heldAt } : {}
74682
- });
74683
- }
74684
- if (rows.length > 0) {
74685
- LOG2.info("MeshReconcile", `Rehydrated ${rows.length} persisted acked-hold row(s) for mesh ${meshId} after (re)start`);
74686
- }
74687
- }
74688
- function collectHeldSynthKeysForMesh(meshId) {
74689
- const heldKeys = /* @__PURE__ */ new Set();
74690
- for (const key2 of inFlightAckedHoldState.keys()) {
74691
- if (key2.startsWith(`${meshId}::`)) heldKeys.add(key2);
74692
- }
74693
- const store = holdStore();
74694
- if (store) {
74695
- try {
74696
- for (const row of store.listInflightHoldsByMesh(meshId)) {
74697
- heldKeys.add(inFlightSynthKey(meshId, row.taskId));
74698
- }
74699
- } catch {
74700
- }
74701
- }
74702
- return heldKeys;
74703
- }
74704
- var ACKED_DEATH_CONSECUTIVE_READ_FAILURES;
74705
- var inFlightAckedHoldState;
74706
- var rehydratedHoldMeshes;
74707
- var init_mesh_reconcile_acked_hold = __esm2({
74708
- "src/mesh/mesh-reconcile-acked-hold.ts"() {
74709
- "use strict";
74710
- init_logger();
74711
- init_mesh_runtime_store();
74712
- init_mesh_events_utils();
74713
- ACKED_DEATH_CONSECUTIVE_READ_FAILURES = 3;
74714
- inFlightAckedHoldState = /* @__PURE__ */ new Map();
74715
- rehydratedHoldMeshes = /* @__PURE__ */ new Set();
74716
- }
74717
- });
74718
74968
  function resolveAutoPruneMinAgeMs() {
74719
74969
  const raw = readNonEmptyString(process.env.MESH_AUTO_PRUNE_MIN_AGE_MS);
74720
74970
  if (raw) {
@@ -74946,6 +75196,109 @@ ${cleanBody}`;
74946
75196
  init_mesh_reconcile_identity();
74947
75197
  }
74948
75198
  });
75199
+ function formatBytes(bytes) {
75200
+ if (!Number.isFinite(bytes) || bytes < 0) return "unknown";
75201
+ const units = ["B", "KiB", "MiB", "GiB", "TiB"];
75202
+ let value = bytes;
75203
+ let unit = 0;
75204
+ while (value >= 1024 && unit < units.length - 1) {
75205
+ value /= 1024;
75206
+ unit += 1;
75207
+ }
75208
+ return `${value >= 10 || unit === 0 ? Math.round(value) : value.toFixed(1)}${units[unit]}`;
75209
+ }
75210
+ function classifyDiskSpace(stats) {
75211
+ if (!Number.isFinite(stats.totalBytes) || stats.totalBytes <= 0) return "ok";
75212
+ const { percentFree, freeBytes } = stats;
75213
+ if (percentFree < DISK_CRITICAL_PERCENT_FREE && freeBytes < DISK_CRITICAL_FREE_BYTES) {
75214
+ return "critical";
75215
+ }
75216
+ if (percentFree < DISK_WARNING_PERCENT_FREE && freeBytes < DISK_WARNING_FREE_BYTES) {
75217
+ return "warning";
75218
+ }
75219
+ return "ok";
75220
+ }
75221
+ function describeDiskSpace(stats, level, path60) {
75222
+ const pct = Number.isFinite(stats.percentFree) ? stats.percentFree.toFixed(1) : "?";
75223
+ const used = Number.isFinite(stats.percentFree) ? (100 - stats.percentFree).toFixed(1) : "?";
75224
+ return `disk ${level.toUpperCase()} at ${path60}: ${formatBytes(stats.freeBytes)} free of ${formatBytes(stats.totalBytes)} (${pct}% free / ${used}% used)`;
75225
+ }
75226
+ function readDiskSpace(path60) {
75227
+ try {
75228
+ const stats = (0, import_fs16.statfsSync)(path60);
75229
+ const blockSize = Number(stats.bsize);
75230
+ const freeBytes = Number(stats.bavail) * blockSize;
75231
+ const totalBytes = Number(stats.blocks) * blockSize;
75232
+ if (!Number.isFinite(freeBytes) || !Number.isFinite(totalBytes) || totalBytes <= 0) {
75233
+ return null;
75234
+ }
75235
+ return {
75236
+ freeBytes,
75237
+ totalBytes,
75238
+ percentFree: freeBytes / totalBytes * 100
75239
+ };
75240
+ } catch {
75241
+ return null;
75242
+ }
75243
+ }
75244
+ function checkDiskSpace(path60) {
75245
+ const stats = readDiskSpace(path60);
75246
+ if (!stats) return null;
75247
+ const level = classifyDiskSpace(stats);
75248
+ return { ...stats, level, path: path60, summary: describeDiskSpace(stats, level, path60) };
75249
+ }
75250
+ function logDiskSpaceStatus(status, context) {
75251
+ if (!status) {
75252
+ LOG2.debug("DiskSpace", `${context}: volume not measurable (statfs unavailable)`);
75253
+ return;
75254
+ }
75255
+ switch (status.level) {
75256
+ case "critical":
75257
+ LOG2.error("DiskSpace", `${context}: ${status.summary} \u2014 writes are expected to fail with ENOSPC`);
75258
+ break;
75259
+ case "warning":
75260
+ LOG2.warn("DiskSpace", `${context}: ${status.summary}`);
75261
+ break;
75262
+ default:
75263
+ LOG2.debug("DiskSpace", `${context}: ${status.summary}`);
75264
+ break;
75265
+ }
75266
+ }
75267
+ function preflightDiskSpace(path60, operation, options = {}) {
75268
+ const status = checkDiskSpace(path60);
75269
+ logDiskSpaceStatus(status, `preflight before ${operation}`);
75270
+ if (status?.level === "critical" && options.throwOnCritical !== false) {
75271
+ throw new LowDiskSpaceError(status, operation);
75272
+ }
75273
+ return status;
75274
+ }
75275
+ var import_fs16;
75276
+ var DISK_CRITICAL_PERCENT_FREE;
75277
+ var DISK_CRITICAL_FREE_BYTES;
75278
+ var DISK_WARNING_PERCENT_FREE;
75279
+ var DISK_WARNING_FREE_BYTES;
75280
+ var LowDiskSpaceError;
75281
+ var init_disk_space_preflight = __esm2({
75282
+ "src/diagnostics/disk-space-preflight.ts"() {
75283
+ "use strict";
75284
+ import_fs16 = require("fs");
75285
+ init_logger();
75286
+ DISK_CRITICAL_PERCENT_FREE = 2;
75287
+ DISK_CRITICAL_FREE_BYTES = 1 * 1024 * 1024 * 1024;
75288
+ DISK_WARNING_PERCENT_FREE = 5;
75289
+ DISK_WARNING_FREE_BYTES = 5 * 1024 * 1024 * 1024;
75290
+ LowDiskSpaceError = class extends Error {
75291
+ status;
75292
+ constructor(status, operation) {
75293
+ super(
75294
+ `Refusing to ${operation}: ${status.summary}. Writes would fail with ENOSPC mid-operation. Free space and retry (see \`adhdev doctor\` for the largest reclaimable paths).`
75295
+ );
75296
+ this.name = "LowDiskSpaceError";
75297
+ this.status = status;
75298
+ }
75299
+ };
75300
+ }
75301
+ });
74949
75302
  function selectExpiredLedgerJsonl(files, now, maxAgeMs = LEDGER_JSONL_MAX_AGE_MS) {
74950
75303
  return files.filter((f) => now - f.mtimeMs > maxAgeMs);
74951
75304
  }
@@ -74980,7 +75333,7 @@ ${cleanBody}`;
74980
75333
  }
74981
75334
  function safeUnlink(path60) {
74982
75335
  try {
74983
- (0, import_fs16.unlinkSync)(path60);
75336
+ (0, import_fs17.unlinkSync)(path60);
74984
75337
  return true;
74985
75338
  } catch (e) {
74986
75339
  LOG2.warn("DiskRetention", `Failed to delete ${path60}: ${e?.message || e}`);
@@ -74988,18 +75341,18 @@ ${cleanBody}`;
74988
75341
  }
74989
75342
  }
74990
75343
  function listDirFiles(dir) {
74991
- if (!(0, import_fs16.existsSync)(dir)) return [];
75344
+ if (!(0, import_fs17.existsSync)(dir)) return [];
74992
75345
  const out = [];
74993
75346
  let names;
74994
75347
  try {
74995
- names = (0, import_fs16.readdirSync)(dir);
75348
+ names = (0, import_fs17.readdirSync)(dir);
74996
75349
  } catch {
74997
75350
  return [];
74998
75351
  }
74999
75352
  for (const name of names) {
75000
75353
  const path60 = (0, import_path14.join)(dir, name);
75001
75354
  try {
75002
- const st = (0, import_fs16.statSync)(path60);
75355
+ const st = (0, import_fs17.statSync)(path60);
75003
75356
  if (st.isFile()) out.push({ path: path60, mtimeMs: st.mtimeMs });
75004
75357
  } catch {
75005
75358
  }
@@ -75026,22 +75379,22 @@ ${cleanBody}`;
75026
75379
  }
75027
75380
  function pruneExpiredSessionHostRuntimes(now = Date.now()) {
75028
75381
  const root = (0, import_path14.join)(getConfigDir2(), "session-host");
75029
- if (!(0, import_fs16.existsSync)(root)) return 0;
75382
+ if (!(0, import_fs17.existsSync)(root)) return 0;
75030
75383
  let apps;
75031
75384
  try {
75032
- apps = (0, import_fs16.readdirSync)(root);
75385
+ apps = (0, import_fs17.readdirSync)(root);
75033
75386
  } catch {
75034
75387
  return 0;
75035
75388
  }
75036
75389
  const candidates = [];
75037
75390
  for (const app of apps) {
75038
75391
  const runtimesDir = (0, import_path14.join)(root, app, "runtimes");
75039
- if (!(0, import_fs16.existsSync)(runtimesDir)) continue;
75392
+ if (!(0, import_fs17.existsSync)(runtimesDir)) continue;
75040
75393
  for (const f of listDirFiles(runtimesDir)) {
75041
75394
  if (!f.path.endsWith(".json")) continue;
75042
75395
  let record2 = null;
75043
75396
  try {
75044
- const parsed = JSON.parse((0, import_fs16.readFileSync)(f.path, "utf-8"));
75397
+ const parsed = JSON.parse((0, import_fs17.readFileSync)(f.path, "utf-8"));
75045
75398
  const rec = parsed && typeof parsed === "object" ? parsed.record : null;
75046
75399
  record2 = rec && typeof rec === "object" ? rec : null;
75047
75400
  } catch {
@@ -75087,7 +75440,15 @@ ${cleanBody}`;
75087
75440
  } catch (e) {
75088
75441
  LOG2.warn("DiskRetention", `Ledger rotation cap sweep failed: ${e?.message || e}`);
75089
75442
  }
75090
- return { ledgerJsonl, dbBackups, sessionHostRuntimes, rotationEvicted, rotationEvictedBytes };
75443
+ let diskLevel = "ok";
75444
+ try {
75445
+ const status = checkDiskSpace(getConfigDir2());
75446
+ logDiskSpaceStatus(status, "disk retention sweep");
75447
+ if (status) diskLevel = status.level;
75448
+ } catch (e) {
75449
+ LOG2.warn("DiskRetention", `Disk space check failed: ${e?.message || e}`);
75450
+ }
75451
+ return { ledgerJsonl, dbBackups, sessionHostRuntimes, rotationEvicted, rotationEvictedBytes, diskLevel };
75091
75452
  }
75092
75453
  async function detectAndSignalOrphanWorktrees(mesh, now = Date.now()) {
75093
75454
  const nodes = Array.isArray(mesh.nodes) ? mesh.nodes : [];
@@ -75138,7 +75499,7 @@ ${cleanBody}`;
75138
75499
  }
75139
75500
  return signalled;
75140
75501
  }
75141
- var import_fs16;
75502
+ var import_fs17;
75142
75503
  var import_path14;
75143
75504
  var DAY_MS2;
75144
75505
  var LEDGER_JSONL_MAX_AGE_MS;
@@ -75148,13 +75509,14 @@ ${cleanBody}`;
75148
75509
  var init_mesh_disk_retention = __esm2({
75149
75510
  "src/mesh/mesh-disk-retention.ts"() {
75150
75511
  "use strict";
75151
- import_fs16 = require("fs");
75512
+ import_fs17 = require("fs");
75152
75513
  import_path14 = require("path");
75153
75514
  init_config();
75154
75515
  init_mesh_ledger();
75155
75516
  init_runtime_surface();
75156
75517
  init_git_worktree();
75157
75518
  init_logger();
75519
+ init_disk_space_preflight();
75158
75520
  DAY_MS2 = 24 * 60 * 60 * 1e3;
75159
75521
  LEDGER_JSONL_MAX_AGE_MS = 30 * DAY_MS2;
75160
75522
  SESSION_HOST_RUNTIME_MAX_AGE_MS = 14 * DAY_MS2;
@@ -76167,6 +76529,7 @@ ${cleanBody}`;
76167
76529
  const { meshId, taskId, sessionId, nodeId, providerType, synthKey, reason, detail } = args;
76168
76530
  updateDirectDispatchStatus(meshId, sessionId, "stale", taskId);
76169
76531
  deleteHoldState(synthKey, meshId);
76532
+ clearAckedHoldDispatchFailureStreak(synthKey);
76170
76533
  try {
76171
76534
  appendLedgerEntry(meshId, {
76172
76535
  kind: "acked_hold_terminalized",
@@ -76222,6 +76585,27 @@ ${cleanBody}`;
76222
76585
  };
76223
76586
  const synthKey = inFlightSynthKey(mesh.id, taskId);
76224
76587
  const isAcked = dispatch.status === "acked";
76588
+ if (isAcked) {
76589
+ const dispatchStreak = getAckedHoldDispatchFailureStreak(mesh.id, taskId);
76590
+ if (dispatchStreak && dispatchStreak.count >= ACKED_DEATH_CONSECUTIVE_DISPATCH_FAILURES) {
76591
+ LOG2.warn("MeshReconcile", `Acked-hold dispatch-failure death signal: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) failed dispatch ${dispatchStreak.count}x consecutively with a worker-absence reason ("${dispatchStreak.lastReason}") \u2014 the target adapter does not exist, so the worker cannot be holding this turn; marking the dispatch row STALE and deleting the synth hold (no completion is asserted \u2014 the transcript never showed one).`);
76592
+ terminalizeAckedHold({
76593
+ meshId: mesh.id,
76594
+ taskId,
76595
+ sessionId,
76596
+ nodeId,
76597
+ ...providerType ? { providerType } : {},
76598
+ synthKey,
76599
+ reason: "acked_dispatch_failure_death",
76600
+ detail: {
76601
+ consecutiveDispatchFailures: dispatchStreak.count,
76602
+ threshold: ACKED_DEATH_CONSECUTIVE_DISPATCH_FAILURES,
76603
+ lastReason: dispatchStreak.lastReason
76604
+ }
76605
+ });
76606
+ continue;
76607
+ }
76608
+ }
76225
76609
  if (isAcked) {
76226
76610
  const ceilingMs = resolveAckedHoldHardCeilingMs();
76227
76611
  const heldSinceMs = getHoldState(synthKey, mesh.id)?.heldSinceMs;
@@ -81175,6 +81559,69 @@ ${cleanBody}`;
81175
81559
  return void 0;
81176
81560
  }
81177
81561
  }
81562
+ function gitRefsResolvable(cwd, refs) {
81563
+ for (const ref of refs) {
81564
+ if (!ref) return false;
81565
+ try {
81566
+ (0, import_node_child_process11.execFileSync)(GIT4, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], { cwd, stdio: "ignore", windowsHide: true });
81567
+ } catch {
81568
+ return false;
81569
+ }
81570
+ }
81571
+ return true;
81572
+ }
81573
+ function probeGitAncestry(cwd, ancestor, descendant) {
81574
+ try {
81575
+ if (!fs51.existsSync(cwd)) return "undeterminable";
81576
+ } catch {
81577
+ return "undeterminable";
81578
+ }
81579
+ if (!gitRefsResolvable(cwd, [ancestor, descendant])) return "undeterminable";
81580
+ try {
81581
+ (0, import_node_child_process11.execFileSync)(GIT4, ["merge-base", "--is-ancestor", ancestor, descendant], { cwd, stdio: "ignore", windowsHide: true });
81582
+ return true;
81583
+ } catch (e) {
81584
+ return e?.status === 1 ? false : "undeterminable";
81585
+ }
81586
+ }
81587
+ function probeSubmoduleGitlinkReachability(input) {
81588
+ const { path: path60, baseCommit, branchCommit, probeRoot, baseRepoRoot } = input;
81589
+ const submoduleRepo = (0, import_path17.resolve)(probeRoot, path60);
81590
+ const baseSubmoduleRepo = (0, import_path17.resolve)(baseRepoRoot, path60);
81591
+ if (baseCommit) ensureSubmoduleCommitLocal(submoduleRepo, baseSubmoduleRepo, baseCommit);
81592
+ if (branchCommit) ensureSubmoduleCommitLocal(submoduleRepo, baseSubmoduleRepo, branchCommit);
81593
+ let fastForward;
81594
+ let reachableFromOriginMain;
81595
+ const undeterminable = [];
81596
+ if (branchCommit) {
81597
+ if (baseCommit) {
81598
+ const ff = probeGitAncestry(submoduleRepo, baseCommit, branchCommit);
81599
+ if (ff === "undeterminable") undeterminable.push("fastForward");
81600
+ else fastForward = ff;
81601
+ }
81602
+ const reach = probeGitAncestry(submoduleRepo, branchCommit, "refs/remotes/origin/main");
81603
+ if (reach === "undeterminable") undeterminable.push("reachableFromOriginMain");
81604
+ else reachableFromOriginMain = reach;
81605
+ }
81606
+ return {
81607
+ path: path60,
81608
+ baseCommit,
81609
+ branchCommit,
81610
+ fastForward,
81611
+ reachableFromOriginMain,
81612
+ ...undeterminable.length ? { undeterminable } : {},
81613
+ probedRepo: submoduleRepo
81614
+ };
81615
+ }
81616
+ function buildSubmoduleReachabilityUndeterminableWarning(nodeId, evidence) {
81617
+ if (!evidence.submoduleReachabilityUndeterminable) return void 0;
81618
+ const entries = (evidence.submoduleGitlinks || []).filter((g) => (g.undeterminable || []).includes("reachableFromOriginMain")).map((g) => `${g.path}@${(g.branchCommit || "?").slice(0, 12)} (probed: ${g.probedRepo || "unknown"})`).join(", ");
81619
+ return `[Refinery] Could NOT determine submodule gitlink reachability from submodule origin/main for node ${nodeId} \u2014 probe had NO ANSWER (missing commit object and/or missing refs/remotes/origin/main in the probed repo). "undeterminable", NOT "unpublished" and NOT "nothing to converge": ${entries}`;
81620
+ }
81621
+ function warnRefineSubmoduleUndeterminable(nodeId, evidence) {
81622
+ const message = buildSubmoduleReachabilityUndeterminableWarning(nodeId, evidence);
81623
+ if (message) LOG2.warn("Mesh", message);
81624
+ }
81178
81625
  function submoduleCommitPresent(submoduleRepoPath, commit) {
81179
81626
  if (!commit) return false;
81180
81627
  try {
@@ -81220,8 +81667,52 @@ ${cleanBody}`;
81220
81667
  }
81221
81668
  return false;
81222
81669
  }
81670
+ function probeSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) {
81671
+ if (!baseCommit || !branchCommit) return "undeterminable";
81672
+ if (baseCommit === branchCommit) return true;
81673
+ return probeGitAncestry(submoduleRepoPath, baseCommit, branchCommit);
81674
+ }
81675
+ function isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) {
81676
+ return probeSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) === true;
81677
+ }
81678
+ function buildGitlinkFastForwardUndeterminableWarning(label, gitlinks) {
81679
+ const entries = gitlinks.filter((entry) => entry.fastForwardUndeterminable);
81680
+ if (entries.length === 0) return void 0;
81681
+ const detail = entries.map((entry) => `${entry.path} (base=${(entry.baseCommit || "?").slice(0, 12)} branch=${(entry.branchCommit || "?").slice(0, 12)})`).join(", ");
81682
+ return `[Refinery] Could NOT determine submodule gitlink fast-forward for ${label} \u2014 ancestry probe had NO ANSWER (a gitlink commit is missing from the local submodule object store, or the submodule checkout is absent). "undeterminable", NOT "diverged": ${detail}`;
81683
+ }
81684
+ function warnGitlinkFastForwardUndeterminable(label, gitlinks) {
81685
+ const message = buildGitlinkFastForwardUndeterminableWarning(label, gitlinks);
81686
+ if (message) LOG2.warn("Mesh", message);
81687
+ }
81688
+ function readChangedPathKinds(repoRoot, fromRef, toRef) {
81689
+ try {
81690
+ const output = (0, import_node_child_process11.execFileSync)(GIT4, ["diff", "--raw", "--no-abbrev", fromRef, toRef], {
81691
+ cwd: repoRoot,
81692
+ encoding: "utf8",
81693
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
81694
+ });
81695
+ const result = [];
81696
+ const seen = /* @__PURE__ */ new Set();
81697
+ for (const line of output.split("\n")) {
81698
+ if (!line.trim()) continue;
81699
+ const metaAndPath = line.split(" ");
81700
+ const meta3 = metaAndPath[0] || "";
81701
+ const path60 = metaAndPath[metaAndPath.length - 1]?.trim();
81702
+ if (!path60 || seen.has(path60)) continue;
81703
+ seen.add(path60);
81704
+ const parts = meta3.split(/\s+/);
81705
+ const isGitlink = !!(parts[0]?.includes("160000") || parts[1]?.includes("160000"));
81706
+ result.push({ path: path60, isGitlink });
81707
+ }
81708
+ return result;
81709
+ } catch {
81710
+ return [];
81711
+ }
81712
+ }
81223
81713
  var fs51;
81224
81714
  var import_node_child_process11;
81715
+ var import_path17;
81225
81716
  var GIT4;
81226
81717
  var REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES;
81227
81718
  var init_mesh_refine_gitlink_utils = __esm2({
@@ -81229,6 +81720,8 @@ ${cleanBody}`;
81229
81720
  "use strict";
81230
81721
  fs51 = __toESM2(require("fs"));
81231
81722
  import_node_child_process11 = require("child_process");
81723
+ import_path17 = require("path");
81724
+ init_logger();
81232
81725
  init_resolve_executable();
81233
81726
  GIT4 = process.platform === "win32" ? resolveWin32Executable("git") : "git";
81234
81727
  REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
@@ -81302,7 +81795,16 @@ ${cleanBody}`;
81302
81795
  }
81303
81796
  return void 0;
81304
81797
  }
81305
- function convergeDivergedSubmoduleGitlinks(worktreeRoot, baseRepoRoot, baseHead, branchHead) {
81798
+ function isCommitReachableFromRemoteMain(submoduleRepoPath, commit, remoteMainRef) {
81799
+ if (!remoteMainRef) return false;
81800
+ try {
81801
+ (0, import_node_child_process12.execFileSync)(GIT4, ["merge-base", "--is-ancestor", commit, remoteMainRef], { cwd: submoduleRepoPath, stdio: "ignore" });
81802
+ return true;
81803
+ } catch {
81804
+ return false;
81805
+ }
81806
+ }
81807
+ function convergeDivergedSubmoduleGitlinks(worktreeRoot, baseRepoRoot, baseHead, branchHead, options = {}) {
81306
81808
  const changed = readChangedGitlinkPaths(worktreeRoot, baseHead, branchHead);
81307
81809
  if (changed.length === 0) {
81308
81810
  return { converged: false, reason: "no_changed_gitlinks", resolutions: [], gitlinks: [] };
@@ -81314,9 +81816,9 @@ ${cleanBody}`;
81314
81816
  for (const path60 of changed) {
81315
81817
  const baseCommit = readTreeObject(baseRepoRoot, baseHead, path60);
81316
81818
  const branchCommit = readTreeObject(worktreeRoot, branchHead, path60);
81317
- const submoduleRepoPath = (0, import_path17.resolve)(worktreeRoot, path60);
81819
+ const submoduleRepoPath = (0, import_path18.resolve)(worktreeRoot, path60);
81318
81820
  if (baseCommit) {
81319
- ensureSubmoduleCommitLocal(submoduleRepoPath, (0, import_path17.resolve)(baseRepoRoot, path60), baseCommit);
81821
+ ensureSubmoduleCommitLocal(submoduleRepoPath, (0, import_path18.resolve)(baseRepoRoot, path60), baseCommit);
81320
81822
  }
81321
81823
  const markUndeterminable = () => {
81322
81824
  const unavailable = [];
@@ -81354,6 +81856,30 @@ ${cleanBody}`;
81354
81856
  resolutions.push({ path: path60, baseCommit, branchCommit, rebasedCommit: publishedEquivalent });
81355
81857
  continue;
81356
81858
  }
81859
+ const branchAlreadyPublished = isCommitReachableFromRemoteMain(submoduleRepoPath, branchCommit, remoteMainRef);
81860
+ const willMintUnpublishedCommit = !branchAlreadyPublished;
81861
+ const wouldConflict = (() => {
81862
+ try {
81863
+ (0, import_node_child_process12.execFileSync)(GIT4, ["merge-tree", "--write-tree", baseCommit, branchCommit], {
81864
+ cwd: submoduleRepoPath,
81865
+ encoding: "utf8",
81866
+ stdio: ["ignore", "pipe", "pipe"]
81867
+ });
81868
+ return false;
81869
+ } catch {
81870
+ return true;
81871
+ }
81872
+ })();
81873
+ if (willMintUnpublishedCommit && !wouldConflict && options.allowAutoPublishSubmoduleMainCommits !== true) {
81874
+ gitlinks.push({
81875
+ path: path60,
81876
+ baseCommit,
81877
+ branchCommit,
81878
+ action: "publish_required_before_rebase",
81879
+ remoteMainRef
81880
+ });
81881
+ return { converged: false, reason: "submodule_publish_required", resolutions: [], gitlinks };
81882
+ }
81357
81883
  let rebasedCommit;
81358
81884
  try {
81359
81885
  (0, import_node_child_process12.execFileSync)(GIT4, ["checkout", "-q", "--detach", branchCommit], { cwd: submoduleRepoPath, stdio: ["ignore", "ignore", "pipe"] });
@@ -81393,7 +81919,14 @@ ${cleanBody}`;
81393
81919
  gitlinks.push({ path: path60, baseCommit, branchCommit, rebasedCommit, action: "rebase_dropped_branch_commits" });
81394
81920
  return { converged: false, reason: "rebase_dropped_branch_commits", resolutions: [], gitlinks };
81395
81921
  }
81396
- gitlinks.push({ path: path60, baseCommit, branchCommit, rebasedCommit, action: "rebased" });
81922
+ gitlinks.push({
81923
+ path: path60,
81924
+ baseCommit,
81925
+ branchCommit,
81926
+ rebasedCommit,
81927
+ action: "rebased",
81928
+ ...willMintUnpublishedCommit ? { mintedUnpublishedCommit: true, remoteMainRef } : {}
81929
+ });
81397
81930
  resolutions.push({ path: path60, baseCommit, branchCommit, rebasedCommit });
81398
81931
  }
81399
81932
  if (!sawDiverged) {
@@ -81402,6 +81935,31 @@ ${cleanBody}`;
81402
81935
  }
81403
81936
  return { converged: resolutions.length > 0, resolutions, gitlinks };
81404
81937
  }
81938
+ function describeSubmodulePublishRequired(nodeId, gitlinks) {
81939
+ const pending = gitlinks.filter((g) => g.action === "publish_required_before_rebase");
81940
+ return `[Refinery] Refusing to auto-converge diverged submodule gitlink(s) for node ${nodeId} \u2014 the rebase would create a NEW submodule commit that is not reachable from the submodule remote, and allowAutoPublishSubmoduleMainCommits is disabled, so the reachability gate would reject it. The branch was left untouched. NEXT STEP: publish the branch-side submodule commit(s) to submodule origin main, then rerun mesh_refine_node \u2014 do NOT rebase: ` + pending.map((g) => `${g.path} (branch=${(g.branchCommit || "?").slice(0, 12)} base=${(g.baseCommit || "?").slice(0, 12)} remote=${g.remoteMainRef || "unresolved"})`).join(", ");
81941
+ }
81942
+ function describeSubmoduleConvergeDecline(nodeId, reason, gitlinks) {
81943
+ if (reason === "submodule_publish_required") {
81944
+ return describeSubmodulePublishRequired(nodeId, gitlinks);
81945
+ }
81946
+ if (reason === "submodule_commit_unavailable") {
81947
+ return `[Refinery] Could NOT determine submodule gitlink divergence for node ${nodeId} \u2014 a gitlink commit is missing from the local submodule object store (auto-converge could not run): ` + gitlinks.filter((g) => g.action === "skipped_commit_unavailable").map((g) => `${g.path} (missing: ${(g.unavailable || []).join("+") || "unknown"}; base=${(g.baseCommit || "?").slice(0, 12)} branch=${(g.branchCommit || "?").slice(0, 12)})`).join(", ");
81948
+ }
81949
+ return void 0;
81950
+ }
81951
+ function buildSubmoduleConvergeDeclineDetails(reason, autoPublishEnabled) {
81952
+ if (reason !== "submodule_publish_required") return {};
81953
+ return {
81954
+ recommendedAction: SUBMODULE_PUBLISH_REQUIRED_RECOMMENDED_ACTION,
81955
+ autoPublishAllowed: autoPublishEnabled
81956
+ };
81957
+ }
81958
+ function describeMintedUnpublishedCommits(nodeId, gitlinks, autoPublishEnabled) {
81959
+ const minted = gitlinks.filter((g) => g.mintedUnpublishedCommit);
81960
+ if (minted.length === 0) return void 0;
81961
+ return `[Refinery] Node ${nodeId}: the convergence MINTED ${minted.length} new submodule commit(s) not reachable from the submodule remote (allowed because allowAutoPublishSubmoduleMainCommits=${autoPublishEnabled}); a publish is still required before merge: ` + minted.map((g) => `${g.path}\u2192${(g.rebasedCommit || "?").slice(0, 12)} (remote=${g.remoteMainRef || "unresolved"})`).join(", ");
81962
+ }
81405
81963
  function rootRebaseResolvingGitlinks(worktreeRoot, baseHead, resolutions) {
81406
81964
  const resolveByPath = new Map(resolutions.map((r) => [r.path, r.rebasedCommit]));
81407
81965
  const runRebase = (args) => {
@@ -81462,7 +82020,7 @@ ${cleanBody}`;
81462
82020
  for (const p of conflicts) {
81463
82021
  const commit = resolveByPath.get(p);
81464
82022
  try {
81465
- (0, import_node_child_process12.execFileSync)(GIT4, ["checkout", "-q", "--detach", commit], { cwd: (0, import_path17.resolve)(worktreeRoot, p), stdio: "ignore" });
82023
+ (0, import_node_child_process12.execFileSync)(GIT4, ["checkout", "-q", "--detach", commit], { cwd: (0, import_path18.resolve)(worktreeRoot, p), stdio: "ignore" });
81466
82024
  } catch {
81467
82025
  }
81468
82026
  try {
@@ -81481,15 +82039,17 @@ ${cleanBody}`;
81481
82039
  return { ok: true, branchHead };
81482
82040
  }
81483
82041
  var fs52;
81484
- var import_path17;
82042
+ var import_path18;
81485
82043
  var import_node_child_process12;
82044
+ var SUBMODULE_PUBLISH_REQUIRED_RECOMMENDED_ACTION;
81486
82045
  var init_mesh_refine_submodule_converge = __esm2({
81487
82046
  "src/mesh/mesh-refine-submodule-converge.ts"() {
81488
82047
  "use strict";
81489
82048
  fs52 = __toESM2(require("fs"));
81490
- import_path17 = require("path");
82049
+ import_path18 = require("path");
81491
82050
  import_node_child_process12 = require("child_process");
81492
82051
  init_mesh_refine_gitlink_utils();
82052
+ SUBMODULE_PUBLISH_REQUIRED_RECOMMENDED_ACTION = "Publish the branch-side submodule commit(s) to submodule origin main, then rerun mesh_refine_node. Do NOT retry the rebase: the branch was intentionally left unrewritten because a rebase here would mint a submodule commit the reachability gate must reject.";
81493
82053
  }
81494
82054
  });
81495
82055
  function truncateValidationOutput(value) {
@@ -81509,10 +82069,10 @@ ${tail}`;
81509
82069
  }
81510
82070
  function writeValidationFailureLog(workspace, index, candidate, streams, now = () => /* @__PURE__ */ new Date()) {
81511
82071
  try {
81512
- const dir = (0, import_path18.join)(workspace, REFINE_VALIDATION_LOG_DIR);
82072
+ const dir = (0, import_path19.join)(workspace, REFINE_VALIDATION_LOG_DIR);
81513
82073
  fs53.mkdirSync(dir, { recursive: true });
81514
82074
  const stamp = now().toISOString().replace(/[:.]/g, "-");
81515
- const file2 = (0, import_path18.join)(dir, `refine-${stamp}-${index}.log`);
82075
+ const file2 = (0, import_path19.join)(dir, `refine-${stamp}-${index}.log`);
81516
82076
  const asText = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
81517
82077
  const shown = candidate.displayCommand || [candidate.command, ...candidate.args || []].join(" ");
81518
82078
  fs53.writeFileSync(
@@ -81632,6 +82192,7 @@ ${mergeTreeErr?.stderr || ""}`;
81632
82192
  if (!isSubmoduleConflict) throw mergeTreeErr;
81633
82193
  const evaluation = evaluateGitlinkTrivialFastForward(repoRoot, baseHead, branchHead);
81634
82194
  if (!evaluation.trivial) {
82195
+ warnGitlinkFastForwardUndeterminable("patch-equivalence gate", evaluation.gitlinks);
81635
82196
  return {
81636
82197
  status: "failed",
81637
82198
  equivalent: false,
@@ -81700,9 +82261,15 @@ ${e?.stderr || ""}`
81700
82261
  };
81701
82262
  }
81702
82263
  }
82264
+ async function classifyAndWarnPatchEquivalenceFailure(nodeId, repoRoot, baseHead, branchHead, summary, options = {}) {
82265
+ const classification = await classifyPatchEquivalenceFailure(repoRoot, baseHead, branchHead, summary, options);
82266
+ warnRefineSubmoduleUndeterminable(nodeId, classification.evidence);
82267
+ return classification;
82268
+ }
81703
82269
  async function classifyPatchEquivalenceFailure(repoRoot, baseHead, branchHead, summary, options = {}) {
81704
82270
  const targetBaseRef = options.targetBaseRef || baseHead;
81705
82271
  const autoPublish = options.autoPublishSubmoduleMainCommits;
82272
+ const submoduleProbeRoot = options.worktreeRoot || repoRoot;
81706
82273
  const evidence = {
81707
82274
  baseHead,
81708
82275
  branchHead,
@@ -81719,14 +82286,6 @@ ${e?.stderr || ""}`
81719
82286
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
81720
82287
  windowsHide: true
81721
82288
  });
81722
- const gitOk = (args) => {
81723
- try {
81724
- git3(args);
81725
- return true;
81726
- } catch {
81727
- return false;
81728
- }
81729
- };
81730
82289
  let ahead = 0;
81731
82290
  let behind = 0;
81732
82291
  try {
@@ -81738,8 +82297,13 @@ ${e?.stderr || ""}`
81738
82297
  }
81739
82298
  evidence.ahead = ahead;
81740
82299
  evidence.behind = behind;
81741
- const baseIsAncestor = gitOk(["merge-base", "--is-ancestor", targetBaseRef, branchHead]);
81742
- evidence.baseDiverged = !baseIsAncestor;
82300
+ const baseAncestry = probeGitAncestry(repoRoot, targetBaseRef, branchHead);
82301
+ const baseIsAncestor = baseAncestry === true;
82302
+ if (baseAncestry === "undeterminable") {
82303
+ evidence.baseAncestryUndeterminable = true;
82304
+ } else {
82305
+ evidence.baseDiverged = !baseAncestry;
82306
+ }
81743
82307
  let diffStat = "";
81744
82308
  try {
81745
82309
  if (summary.mergedTree) {
@@ -81767,20 +82331,19 @@ ${e?.stderr || ""}`
81767
82331
  } catch {
81768
82332
  continue;
81769
82333
  }
81770
- const submoduleRepo = (0, import_path18.join)(repoRoot, p);
81771
- let fastForward;
81772
- let reachableFromOriginMain;
81773
- if (branchCommit) {
81774
- if (baseCommit) {
81775
- fastForward = execGitOk(submoduleRepo, ["merge-base", "--is-ancestor", baseCommit, branchCommit]);
81776
- }
81777
- reachableFromOriginMain = execGitOk(submoduleRepo, ["merge-base", "--is-ancestor", branchCommit, "refs/remotes/origin/main"]);
81778
- }
81779
- submoduleGitlinks.push({ path: p, baseCommit, branchCommit, fastForward, reachableFromOriginMain });
82334
+ submoduleGitlinks.push(probeSubmoduleGitlinkReachability({
82335
+ path: p,
82336
+ baseCommit,
82337
+ branchCommit,
82338
+ probeRoot: submoduleProbeRoot,
82339
+ baseRepoRoot: repoRoot
82340
+ }));
81780
82341
  }
81781
82342
  } catch {
81782
82343
  }
81783
82344
  if (submoduleGitlinks.length) evidence.submoduleGitlinks = submoduleGitlinks;
82345
+ const undeterminableGitlinks = submoduleGitlinks.filter((g) => (g.undeterminable || []).includes("reachableFromOriginMain"));
82346
+ if (undeterminableGitlinks.length > 0) evidence.submoduleReachabilityUndeterminable = true;
81784
82347
  const gitlinkFf = summary.gitlinkTrivialFastForward;
81785
82348
  const noResidualDiff = !evidence.diffStat && (!summary.actualPatchId || summary.actualPatchId === "");
81786
82349
  if (ahead === 0 && behind === 0 && noResidualDiff) {
@@ -81801,6 +82364,15 @@ ${e?.stderr || ""}`
81801
82364
  evidence
81802
82365
  };
81803
82366
  }
82367
+ if (undeterminableGitlinks.length > 0) {
82368
+ const refs = undeterminableGitlinks.map((g) => `${g.path}@${(g.branchCommit || "?").slice(0, 12)} (probed: ${g.probedRepo || "unknown"})`).join(", ");
82369
+ return {
82370
+ detailedReason: "submodule_reachability_undeterminable",
82371
+ detailedReasonDescription: `Could NOT determine whether submodule gitlink commit(s) are reachable from submodule origin/main \u2014 the probe had no answer (missing commit object and/or missing refs/remotes/origin/main in the probed repo): ${refs}. This is "undeterminable", NOT "unpublished".`,
82372
+ recommendedAction: "Make the probe answerable, then rerun mesh_refine_node: fetch the submodule remote in the probed checkout (`git -C <probedRepo> fetch origin main`) so both the gitlink commit object and refs/remotes/origin/main exist locally. Do NOT publish/push the submodule commit on the strength of this result \u2014 reachability was never established either way.",
82373
+ evidence
82374
+ };
82375
+ }
81804
82376
  const changedGitlinks = submoduleGitlinks.length > 0;
81805
82377
  const allGitlinksFf = changedGitlinks && submoduleGitlinks.every((g) => g.fastForward === true);
81806
82378
  const gateSawUnresolvedGitlinkFf = gitlinkFf?.resolved === false && Array.isArray(gitlinkFf.gitlinks) && gitlinkFf.gitlinks.some((g) => g.fastForward);
@@ -81812,6 +82384,14 @@ ${e?.stderr || ""}`
81812
82384
  evidence
81813
82385
  };
81814
82386
  }
82387
+ if (baseAncestry === "undeterminable") {
82388
+ return {
82389
+ detailedReason: "base_ancestry_undeterminable",
82390
+ detailedReasonDescription: `Could NOT determine whether HEAD descends ${targetBaseRef} \u2014 the ancestry probe had no answer (${targetBaseRef} and/or ${branchHead.slice(0, 12)} does not resolve in ${repoRoot}). This is "undeterminable", NOT "diverged": the ahead/behind counts above are unmeasured, not zero.`,
82391
+ recommendedAction: `Make the probe answerable before judging: fetch/verify that ${targetBaseRef} and the branch head both resolve in ${repoRoot} (e.g. git fetch origin, git rev-parse --verify), then retry mesh_refine_node. Do NOT rebase on the strength of this result \u2014 no divergence has been measured.`,
82392
+ evidence
82393
+ };
82394
+ }
81815
82395
  if (!baseIsAncestor) {
81816
82396
  return {
81817
82397
  detailedReason: "base_divergence",
@@ -81836,14 +82416,6 @@ ${e?.stderr || ""}`
81836
82416
  };
81837
82417
  }
81838
82418
  }
81839
- function execGitOk(cwd, args) {
81840
- try {
81841
- (0, import_node_child_process13.execFileSync)(GIT4, args, { cwd, encoding: "utf8", maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES, windowsHide: true });
81842
- return true;
81843
- } catch {
81844
- return false;
81845
- }
81846
- }
81847
82419
  async function checkWorktreeChangesPatchEquivalentInRef(repoRoot, ref, worktreeHead) {
81848
82420
  const startedAt = Date.now();
81849
82421
  try {
@@ -81865,6 +82437,7 @@ ${mergeTreeErr?.stderr || ""}`;
81865
82437
  if (!isSubmoduleConflict) throw mergeTreeErr;
81866
82438
  const evaluation = evaluateGitlinkTrivialFastForward(repoRoot, ref, worktreeHead);
81867
82439
  if (!evaluation.trivial) {
82440
+ warnGitlinkFastForwardUndeterminable(`containment check for ${ref}`, evaluation.gitlinks);
81868
82441
  return {
81869
82442
  contained: false,
81870
82443
  ref,
@@ -81994,50 +82567,12 @@ ${mergeTreeErr?.stderr || ""}`;
81994
82567
  }).trim();
81995
82568
  return out;
81996
82569
  }
81997
- function isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) {
81998
- if (!baseCommit || !branchCommit) return false;
81999
- if (baseCommit === branchCommit) return true;
82000
- try {
82001
- if (!fs53.existsSync(submoduleRepoPath)) return false;
82002
- (0, import_node_child_process13.execFileSync)(GIT4, ["cat-file", "-e", `${baseCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
82003
- (0, import_node_child_process13.execFileSync)(GIT4, ["cat-file", "-e", `${branchCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
82004
- (0, import_node_child_process13.execFileSync)(GIT4, ["merge-base", "--is-ancestor", baseCommit, branchCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
82005
- return true;
82006
- } catch {
82007
- return false;
82008
- }
82009
- }
82010
- function readChangedPathKinds(repoRoot, fromRef, toRef) {
82011
- try {
82012
- const output = (0, import_node_child_process13.execFileSync)(GIT4, ["diff", "--raw", "--no-abbrev", fromRef, toRef], {
82013
- cwd: repoRoot,
82014
- encoding: "utf8",
82015
- maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
82016
- });
82017
- const result = [];
82018
- const seen = /* @__PURE__ */ new Set();
82019
- for (const line of output.split("\n")) {
82020
- if (!line.trim()) continue;
82021
- const metaAndPath = line.split(" ");
82022
- const meta3 = metaAndPath[0] || "";
82023
- const path60 = metaAndPath[metaAndPath.length - 1]?.trim();
82024
- if (!path60 || seen.has(path60)) continue;
82025
- seen.add(path60);
82026
- const parts = meta3.split(/\s+/);
82027
- const isGitlink = !!(parts[0]?.includes("160000") || parts[1]?.includes("160000"));
82028
- result.push({ path: path60, isGitlink });
82029
- }
82030
- return result;
82031
- } catch {
82032
- return [];
82033
- }
82034
- }
82035
82570
  function collectFastForwardGitlinkPaths(repoRoot, baseHead, branchHead) {
82036
82571
  return readChangedGitlinkPaths(repoRoot, baseHead, branchHead).filter((path60) => {
82037
82572
  const baseCommit = readTreeObject(repoRoot, baseHead, path60);
82038
82573
  const branchCommit = readTreeObject(repoRoot, branchHead, path60);
82039
82574
  if (!baseCommit || !branchCommit) return false;
82040
- return isSubmoduleFastForward((0, import_path18.resolve)(repoRoot, path60), baseCommit, branchCommit);
82575
+ return isSubmoduleFastForward((0, import_path19.resolve)(repoRoot, path60), baseCommit, branchCommit);
82041
82576
  });
82042
82577
  }
82043
82578
  function collectTrivialFastForwardGitlinkResolutions(worktreeRoot, baseRepoRoot, baseHead, branchHead) {
@@ -82046,8 +82581,8 @@ ${mergeTreeErr?.stderr || ""}`;
82046
82581
  const baseCommit = readTreeObject(baseRepoRoot, baseHead, path60);
82047
82582
  const branchCommit = readTreeObject(worktreeRoot, branchHead, path60);
82048
82583
  if (!baseCommit || !branchCommit) continue;
82049
- const submoduleRepoPath = (0, import_path18.resolve)(worktreeRoot, path60);
82050
- ensureSubmoduleCommitLocal(submoduleRepoPath, (0, import_path18.resolve)(baseRepoRoot, path60), baseCommit);
82584
+ const submoduleRepoPath = (0, import_path19.resolve)(worktreeRoot, path60);
82585
+ ensureSubmoduleCommitLocal(submoduleRepoPath, (0, import_path19.resolve)(baseRepoRoot, path60), baseCommit);
82051
82586
  if (baseCommit === branchCommit) {
82052
82587
  continue;
82053
82588
  }
@@ -82063,20 +82598,28 @@ ${mergeTreeErr?.stderr || ""}`;
82063
82598
  const changedGitlinks = readChangedGitlinkPaths(repoRoot, baseHead, branchHead).map((path60) => {
82064
82599
  const baseCommit = readTreeObject(repoRoot, baseHead, path60);
82065
82600
  const branchCommit = readTreeObject(repoRoot, branchHead, path60);
82066
- const submoduleRepoPath = (0, import_path18.resolve)(repoRoot, path60);
82067
- const fastForward = !!baseCommit && !!branchCommit && isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit);
82068
- return { path: path60, baseCommit, branchCommit, fastForward };
82601
+ const submoduleRepoPath = (0, import_path19.resolve)(repoRoot, path60);
82602
+ const probe = !!baseCommit && !!branchCommit ? probeSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) : "undeterminable";
82603
+ return {
82604
+ path: path60,
82605
+ baseCommit,
82606
+ branchCommit,
82607
+ fastForward: probe === true,
82608
+ ...probe === "undeterminable" ? { fastForwardUndeterminable: true } : {}
82609
+ };
82069
82610
  });
82070
82611
  if (changedGitlinks.length === 0) {
82071
82612
  return { trivial: false, reason: "no_changed_gitlinks", gitlinks: changedGitlinks };
82072
82613
  }
82073
- const nonFastForward = changedGitlinks.filter((entry) => !entry.fastForward);
82074
- if (nonFastForward.length > 0) {
82075
- return {
82076
- trivial: false,
82077
- reason: `diverged_gitlinks:${nonFastForward.map((entry) => entry.path).join(",")}`,
82078
- gitlinks: changedGitlinks
82079
- };
82614
+ const undeterminable = changedGitlinks.filter((entry) => entry.fastForwardUndeterminable);
82615
+ const diverged = changedGitlinks.filter((entry) => !entry.fastForward && !entry.fastForwardUndeterminable);
82616
+ if (undeterminable.length > 0 || diverged.length > 0) {
82617
+ const parts = [];
82618
+ if (diverged.length > 0) parts.push(`diverged_gitlinks:${diverged.map((entry) => entry.path).join(",")}`);
82619
+ if (undeterminable.length > 0) {
82620
+ parts.push(`undeterminable_gitlinks:${undeterminable.map((entry) => entry.path).join(",")}`);
82621
+ }
82622
+ return { trivial: false, reason: parts.join(" "), gitlinks: changedGitlinks };
82080
82623
  }
82081
82624
  let mergeBase = "";
82082
82625
  try {
@@ -82118,7 +82661,7 @@ ${mergeTreeErr?.stderr || ""}`;
82118
82661
  if (!tree) return void 0;
82119
82662
  const updates = paths.map((path60) => `160000 commit ${placeholderCommit} ${path60}`).join("\n");
82120
82663
  if (!updates) return tree;
82121
- const tmpIndex = (0, import_path18.join)(resolveGitDir(repoRoot), `adhdev-refine-eq-${commitish.slice(0, 12)}.index`);
82664
+ const tmpIndex = (0, import_path19.join)(resolveGitDir(repoRoot), `adhdev-refine-eq-${commitish.slice(0, 12)}.index`);
82122
82665
  const env2 = { ...process.env, GIT_INDEX_FILE: tmpIndex };
82123
82666
  try {
82124
82667
  (0, import_node_child_process13.execFileSync)(GIT4, ["read-tree", tree], { cwd: repoRoot, env: env2, stdio: "ignore" });
@@ -82193,7 +82736,7 @@ ${mergeTreeErr?.stderr || ""}`;
82193
82736
  if (!contentTree) return void 0;
82194
82737
  const updates = branchGitlinks.map((entry) => `160000 commit ${entry.branchCommit} ${entry.path}`).join("\n");
82195
82738
  if (!updates) return contentTree;
82196
- const tmpIndex = (0, import_path18.join)(resolveGitDir(repoRoot), `adhdev-refine-ff-${baseHead.slice(0, 12)}-${branchHead.slice(0, 12)}.index`);
82739
+ const tmpIndex = (0, import_path19.join)(resolveGitDir(repoRoot), `adhdev-refine-ff-${baseHead.slice(0, 12)}-${branchHead.slice(0, 12)}.index`);
82197
82740
  const env2 = { ...process.env, GIT_INDEX_FILE: tmpIndex };
82198
82741
  try {
82199
82742
  (0, import_node_child_process13.execFileSync)(GIT4, ["read-tree", contentTree], { cwd: repoRoot, env: env2, stdio: "ignore" });
@@ -82350,7 +82893,7 @@ ${mergeTreeErr?.stderr || ""}`;
82350
82893
  return match ? { commit: match[1], path: match[2] } : null;
82351
82894
  }).filter((entry) => !!entry);
82352
82895
  for (const gitlink of gitlinks) {
82353
- const submodulePath = (0, import_path18.resolve)(repoRoot, gitlink.path);
82896
+ const submodulePath = (0, import_path19.resolve)(repoRoot, gitlink.path);
82354
82897
  const entry = {
82355
82898
  path: gitlink.path,
82356
82899
  commit: gitlink.commit,
@@ -82377,7 +82920,7 @@ ${mergeTreeErr?.stderr || ""}`;
82377
82920
  entry.localReachable = false;
82378
82921
  if (options.allowAutoPublishSubmoduleMainCommits === true && options.worktreeRoot) {
82379
82922
  try {
82380
- const imported = await importCommitFromWorktreeSubmodule(submodulePath, (0, import_path18.resolve)(options.worktreeRoot, gitlink.path), gitlink.commit);
82923
+ const imported = await importCommitFromWorktreeSubmodule(submodulePath, (0, import_path19.resolve)(options.worktreeRoot, gitlink.path), gitlink.commit);
82381
82924
  if (imported) {
82382
82925
  entry.localReachable = true;
82383
82926
  entry.importedFromWorktree = true;
@@ -82607,13 +83150,13 @@ ${mergeTreeErr?.stderr || ""}`;
82607
83150
  ...extras
82608
83151
  });
82609
83152
  const isPackageManagerValidation = (candidate) => {
82610
- const command = (0, import_path18.basename)(candidate.command).replace(/\.(?:cmd|exe)$/i, "");
83153
+ const command = (0, import_path19.basename)(candidate.command).replace(/\.(?:cmd|exe)$/i, "");
82611
83154
  return ["npm", "pnpm", "yarn", "bun"].includes(command) && candidate.args.some((arg) => arg === "run" || arg === "test" || arg === "exec");
82612
83155
  };
82613
83156
  const dependenciesLikelyMissing = (cwd) => {
82614
- if (!fs53.existsSync((0, import_path18.join)(cwd, "package.json"))) return false;
82615
- if (fs53.existsSync((0, import_path18.join)(cwd, "node_modules"))) return false;
82616
- return ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"].some((lock) => fs53.existsSync((0, import_path18.join)(cwd, lock)));
83157
+ if (!fs53.existsSync((0, import_path19.join)(cwd, "package.json"))) return false;
83158
+ if (fs53.existsSync((0, import_path19.join)(cwd, "node_modules"))) return false;
83159
+ return ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"].some((lock) => fs53.existsSync((0, import_path19.join)(cwd, lock)));
82617
83160
  };
82618
83161
  const needsNodeModules = (candidate, cwd) => isPackageManagerValidation(candidate) && dependenciesLikelyMissing(cwd);
82619
83162
  const isDaemonScopedCommand = (candidate) => {
@@ -82680,7 +83223,7 @@ ${mergeTreeErr?.stderr || ""}`;
82680
83223
  summary.bootstrap = { stage: "legacy" };
82681
83224
  for (const candidate of selection.bootstrapCommands) {
82682
83225
  const startedAt = Date.now();
82683
- const cwd = candidate.cwd ? (0, import_path18.resolve)(workspace, candidate.cwd) : workspace;
83226
+ const cwd = candidate.cwd ? (0, import_path19.resolve)(workspace, candidate.cwd) : workspace;
82684
83227
  const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
82685
83228
  const resolvedCommand = resolveWin32Executable(candidate.command);
82686
83229
  const spawn7 = buildWin32ExecFileSpawn(resolvedCommand, candidate.args);
@@ -82713,7 +83256,7 @@ ${mergeTreeErr?.stderr || ""}`;
82713
83256
  let missingDepsBlocked = false;
82714
83257
  for (const candidate of commandsToRun) {
82715
83258
  const startedAt = Date.now();
82716
- const cwd = candidate.cwd ? (0, import_path18.resolve)(workspace, candidate.cwd) : workspace;
83259
+ const cwd = candidate.cwd ? (0, import_path19.resolve)(workspace, candidate.cwd) : workspace;
82717
83260
  const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
82718
83261
  const bootstrapProvidedDependencies = summary.bootstrap?.stage === "cached" || summary.bootstrap?.stage === "ran" || summary.bootstrap?.stage === "legacy";
82719
83262
  if (!bootstrapProvidedDependencies && needsNodeModules(candidate, cwd)) {
@@ -82780,7 +83323,7 @@ ${mergeTreeErr?.stderr || ""}`;
82780
83323
  summary.status = "passed";
82781
83324
  return summary;
82782
83325
  }
82783
- var import_path18;
83326
+ var import_path19;
82784
83327
  var fs53;
82785
83328
  var import_node_child_process13;
82786
83329
  var REFINE_VALIDATION_TIMEOUT_MS;
@@ -82795,19 +83338,20 @@ ${mergeTreeErr?.stderr || ""}`;
82795
83338
  init_git_status();
82796
83339
  init_refine_config();
82797
83340
  init_worktree_bootstrap_config();
82798
- import_path18 = require("path");
83341
+ import_path19 = require("path");
82799
83342
  fs53 = __toESM2(require("fs"));
82800
83343
  import_node_child_process13 = require("child_process");
82801
83344
  init_resolve_executable();
82802
83345
  init_mesh_refine_worker_cap();
82803
83346
  init_mesh_refine_gitlink_utils();
82804
83347
  init_mesh_refine_submodule_converge();
83348
+ init_mesh_refine_gitlink_utils();
82805
83349
  REFINE_VALIDATION_TIMEOUT_MS = 12e4;
82806
83350
  REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
82807
83351
  REFINE_VALIDATION_SUMMARY_HEAD_CHARS = 2e3;
82808
83352
  REFINE_VALIDATION_SUMMARY_TAIL_CHARS = 6e3;
82809
83353
  REFINE_VALIDATION_SUMMARY_CHARS = REFINE_VALIDATION_SUMMARY_HEAD_CHARS + REFINE_VALIDATION_SUMMARY_TAIL_CHARS;
82810
- REFINE_VALIDATION_LOG_DIR = (0, import_path18.join)(".adhdev", "logs");
83354
+ REFINE_VALIDATION_LOG_DIR = (0, import_path19.join)(".adhdev", "logs");
82811
83355
  }
82812
83356
  });
82813
83357
  function findOpenRefineDispatchForNode(args) {
@@ -83791,7 +84335,14 @@ ${excerpt}` : "\n--- git output ---\n(none captured)");
83791
84335
  return { kind: "continue", ctx };
83792
84336
  }
83793
84337
  if (submoduleConflict) {
83794
- const converge = convergeDivergedSubmoduleGitlinks(node.workspace, repoRoot, baseHead, branchHead);
84338
+ const convergeAutoPublish = resolveRefineryAutoPublishSubmoduleMainCommits(ctx.mesh, node.workspace);
84339
+ const converge = convergeDivergedSubmoduleGitlinks(
84340
+ node.workspace,
84341
+ repoRoot,
84342
+ baseHead,
84343
+ branchHead,
84344
+ { allowAutoPublishSubmoduleMainCommits: convergeAutoPublish.enabled }
84345
+ );
83795
84346
  if (converge.converged) {
83796
84347
  gitlinkResolutions = converge.resolutions;
83797
84348
  recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "passed", syncStarted, {
@@ -83799,15 +84350,19 @@ ${excerpt}` : "\n--- git output ---\n(none captured)");
83799
84350
  gitlinks: converge.gitlinks
83800
84351
  });
83801
84352
  LOG2.info("Mesh", `[Refinery] Auto-converged diverged submodule gitlink(s) onto base for node ${node.id}: ` + converge.resolutions.map((r) => `${r.path}\u2192${r.rebasedCommit.slice(0, 12)}`).join(", "));
84353
+ const minted = describeMintedUnpublishedCommits(node.id, converge.gitlinks, convergeAutoPublish.enabled);
84354
+ if (minted) LOG2.warn("Mesh", minted);
83802
84355
  } else {
83803
84356
  recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "skipped", syncStarted, {
83804
84357
  reason: "submodule_conflict_defer_to_patch_equivalence",
83805
84358
  convergeReason: converge.reason,
83806
- gitlinks: converge.gitlinks
84359
+ gitlinks: converge.gitlinks,
84360
+ // ★Structural next step, so the coordinator is told to PUBLISH
84361
+ // rather than retry the rebase (retrying re-mints the orphan).
84362
+ ...buildSubmoduleConvergeDeclineDetails(converge.reason, convergeAutoPublish.enabled)
83807
84363
  });
83808
- if (converge.reason === "submodule_commit_unavailable") {
83809
- LOG2.warn("Mesh", `[Refinery] Could NOT determine submodule gitlink divergence for node ${node.id} \u2014 a gitlink commit is missing from the local submodule object store (auto-converge could not run): ` + converge.gitlinks.filter((g) => g.action === "skipped_commit_unavailable").map((g) => `${g.path} (missing: ${(g.unavailable || []).join("+") || "unknown"}; base=${(g.baseCommit || "?").slice(0, 12)} branch=${(g.branchCommit || "?").slice(0, 12)})`).join(", "));
83810
- }
84364
+ const declined = describeSubmoduleConvergeDecline(node.id, converge.reason, converge.gitlinks);
84365
+ if (declined) LOG2.warn("Mesh", declined);
83811
84366
  recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
83812
84367
  ahead: divergence.ahead,
83813
84368
  behind: divergence.behind,
@@ -83889,16 +84444,11 @@ ${excerpt}` : "\n--- git output ---\n(none captured)");
83889
84444
  error: submoduleHintPatchEquivalence.error,
83890
84445
  actionableHint: submoduleHintPatchEquivalence.actionableHint
83891
84446
  });
83892
- const classification = await classifyPatchEquivalenceFailure(
83893
- repoRoot,
83894
- baseHead,
83895
- ctx.branchHead,
83896
- submoduleHintPatchEquivalence,
83897
- {
83898
- targetBaseRef: baseHead,
83899
- autoPublishSubmoduleMainCommits: resolveRefineryAutoPublishSubmoduleMainCommits(ctx.mesh, node.workspace).enabled
83900
- }
83901
- );
84447
+ const classification = await classifyAndWarnPatchEquivalenceFailure(node.id, repoRoot, baseHead, ctx.branchHead, submoduleHintPatchEquivalence, {
84448
+ targetBaseRef: baseHead,
84449
+ worktreeRoot: node.workspace,
84450
+ autoPublishSubmoduleMainCommits: resolveRefineryAutoPublishSubmoduleMainCommits(ctx.mesh, node.workspace).enabled
84451
+ });
83902
84452
  return { kind: "terminal", result: {
83903
84453
  success: false,
83904
84454
  code: "patch_equivalence_failed",
@@ -84082,19 +84632,15 @@ ${tail}` : ""
84082
84632
  if (!patchEquivalence.equivalent) {
84083
84633
  const alreadyMergedViaOtherPath = !patchEquivalence.actualPatchId && !!patchEquivalence.expectedPatchId;
84084
84634
  if (!alreadyMergedViaOtherPath) {
84085
- const classification = await classifyPatchEquivalenceFailure(
84086
- repoRoot,
84087
- baseHead,
84088
- branchHead,
84089
- patchEquivalence,
84090
- {
84091
- targetBaseRef: baseHead,
84092
- autoPublishSubmoduleMainCommits: resolveRefineryAutoPublishSubmoduleMainCommits(mesh, node.workspace).enabled
84093
- }
84094
- );
84635
+ const classification = await classifyAndWarnPatchEquivalenceFailure(node.id, repoRoot, baseHead, branchHead, patchEquivalence, {
84636
+ targetBaseRef: baseHead,
84637
+ worktreeRoot: node.workspace,
84638
+ autoPublishSubmoduleMainCommits: resolveRefineryAutoPublishSubmoduleMainCommits(mesh, node.workspace).enabled
84639
+ });
84095
84640
  recordMeshRefineStage(refineStages, "patch_equivalence_classification", "failed", patchEquivalenceStarted, {
84096
84641
  detailedReason: classification.detailedReason,
84097
- recommendedAction: classification.recommendedAction
84642
+ recommendedAction: classification.recommendedAction,
84643
+ ...classification.evidence.submoduleReachabilityUndeterminable ? { submoduleReachabilityUndeterminable: true } : {}
84098
84644
  });
84099
84645
  return { kind: "terminal", result: {
84100
84646
  success: false,
@@ -85473,6 +86019,10 @@ ${e?.stderr || ""}`;
85473
86019
  DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS: () => DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS,
85474
86020
  DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS: () => DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS,
85475
86021
  DEV_SERVER_PORT: () => DEV_SERVER_PORT,
86022
+ DISK_CRITICAL_FREE_BYTES: () => DISK_CRITICAL_FREE_BYTES,
86023
+ DISK_CRITICAL_PERCENT_FREE: () => DISK_CRITICAL_PERCENT_FREE,
86024
+ DISK_WARNING_FREE_BYTES: () => DISK_WARNING_FREE_BYTES,
86025
+ DISK_WARNING_PERCENT_FREE: () => DISK_WARNING_PERCENT_FREE,
85476
86026
  DUPLICATE_MESH_DISPATCH_CODE: () => DUPLICATE_MESH_DISPATCH_CODE,
85477
86027
  DaemonAgentStreamManager: () => DaemonAgentStreamManager,
85478
86028
  DaemonCdpInitializer: () => DaemonCdpInitializer,
@@ -85497,8 +86047,11 @@ ${e?.stderr || ""}`;
85497
86047
  KNOWN_DIGEST_ALGORITHMS: () => KNOWN_DIGEST_ALGORITHMS,
85498
86048
  LEGACY_UNVERIFIED_ALGORITHM: () => LEGACY_UNVERIFIED_ALGORITHM,
85499
86049
  LOG: () => LOG2,
86050
+ LowDiskSpaceError: () => LowDiskSpaceError,
85500
86051
  MAGI_NEEDS_VERIFICATION_PREVIEW_CAP: () => MAGI_NEEDS_VERIFICATION_PREVIEW_CAP,
85501
86052
  MAGI_RAW_ANSWER_CAP: () => MAGI_RAW_ANSWER_CAP,
86053
+ MAX_CAPTURE_LOG_GENERATIONS: () => MAX_CAPTURE_LOG_GENERATIONS,
86054
+ MAX_CAPTURE_LOG_SIZE: () => MAX_CAPTURE_LOG_SIZE,
85502
86055
  MAX_LEDGER_SLICE_LIMIT: () => MAX_LEDGER_SLICE_LIMIT,
85503
86056
  MAX_SESSIONS_PER_MESH: () => MAX_SESSIONS_PER_MESH,
85504
86057
  MAX_SIZE_ROTATION_GENERATIONS: () => MAX_SIZE_ROTATION_GENERATIONS,
@@ -85628,9 +86181,11 @@ ${e?.stderr || ""}`;
85628
86181
  buildUserChatMessage: () => buildUserChatMessage,
85629
86182
  cancelTask: () => cancelTask,
85630
86183
  canonicalDaemonId: () => canonicalDaemonId,
86184
+ checkDiskSpace: () => checkDiskSpace,
85631
86185
  claimMeshGraphGate: () => claimMeshGraphGate,
85632
86186
  claimNextTask: () => claimNextTask,
85633
86187
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
86188
+ classifyDiskSpace: () => classifyDiskSpace,
85634
86189
  classifyDuplicateMeshDispatch: () => classifyDuplicateMeshDispatch,
85635
86190
  classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush2,
85636
86191
  classifyP2pRelayFailure: () => classifyP2pRelayFailure,
@@ -85682,6 +86237,7 @@ ${e?.stderr || ""}`;
85682
86237
  deriveMeshReviewInboxItems: () => deriveMeshReviewInboxItems,
85683
86238
  deriveWorkspaceBranchIdentity: () => deriveWorkspaceBranchIdentity,
85684
86239
  deriveWorkspaceOwnerTag: () => deriveWorkspaceOwnerTag,
86240
+ describeDiskSpace: () => describeDiskSpace,
85685
86241
  describeTaskDependencyState: () => describeTaskDependencyState,
85686
86242
  detectAllVersions: () => detectAllVersions,
85687
86243
  detectCLI: () => detectCLI,
@@ -85717,6 +86273,7 @@ ${e?.stderr || ""}`;
85717
86273
  flattenMessageParts: () => flattenMessageParts,
85718
86274
  foldUsageRecords: () => foldUsageRecords,
85719
86275
  forceRefreshQuota: () => forceRefreshQuota2,
86276
+ formatBytes: () => formatBytes,
85720
86277
  formatManifestValidationIssues: () => formatManifestValidationIssues,
85721
86278
  formatMeshTaskModeViolations: () => formatMeshTaskModeViolations,
85722
86279
  forwardAgentStreamsToIdeInstance: () => forwardAgentStreamsToIdeInstance2,
@@ -85834,6 +86391,7 @@ ${e?.stderr || ""}`;
85834
86391
  loadRepoSettings: () => loadRepoSettings,
85835
86392
  loadState: () => loadState,
85836
86393
  logCommand: () => logCommand,
86394
+ logDiskSpaceStatus: () => logDiskSpaceStatus,
85837
86395
  machineCoreFromDaemonId: () => machineCoreFromDaemonId,
85838
86396
  magiAutoLaunchedSessionCleanupDecision: () => magiAutoLaunchedSessionCleanupDecision,
85839
86397
  makeUsage: () => makeUsage,
@@ -85890,6 +86448,7 @@ ${e?.stderr || ""}`;
85890
86448
  partitionSessionHostDiagnosticsSessions: () => import_session_host_core32.partitionSessionHostDiagnosticsSessions,
85891
86449
  partitionSessionHostRecords: () => import_session_host_core32.partitionSessionHostRecords,
85892
86450
  planMeshOnboarding: () => planMeshOnboarding,
86451
+ preflightDiskSpace: () => preflightDiskSpace,
85893
86452
  prepareSessionChatTailUpdate: () => prepareSessionChatTailUpdate2,
85894
86453
  prepareSessionModalUpdate: () => prepareSessionModalUpdate2,
85895
86454
  presentationFromAttemptRow: () => presentationFromAttemptRow,
@@ -85911,6 +86470,7 @@ ${e?.stderr || ""}`;
85911
86470
  readChatHistory: () => readChatHistory,
85912
86471
  readClaudeCliSession: () => readSession,
85913
86472
  readCodexCliSession: () => readSession2,
86473
+ readDiskSpace: () => readDiskSpace,
85914
86474
  readHermesCliSession: () => readSession4,
85915
86475
  readLatestCodexRateLimits: () => readLatestCodexRateLimits,
85916
86476
  readLedgerEntries: () => readLedgerEntries,
@@ -85992,6 +86552,7 @@ ${e?.stderr || ""}`;
85992
86552
  resolveTurnAttemptRow: () => resolveTurnAttemptRow,
85993
86553
  resolveWorktreeBaseDir: () => resolveWorktreeBaseDir,
85994
86554
  resolveWorktreePath: () => resolveWorktreePath,
86555
+ rotateCaptureLogIfNeeded: () => rotateCaptureLogIfNeeded,
85995
86556
  runAsyncBatch: () => runAsyncBatch2,
85996
86557
  runGit: () => runGit,
85997
86558
  runMeshWorktreeBootstrap: () => runMeshWorktreeBootstrap,
@@ -89543,7 +90104,7 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
89543
90104
  init_state_store();
89544
90105
  var import_child_process5 = require("child_process");
89545
90106
  var import_util22 = require("util");
89546
- var import_fs17 = require("fs");
90107
+ var import_fs18 = require("fs");
89547
90108
  var import_os3 = require("os");
89548
90109
  var path24 = __toESM2(require("path"));
89549
90110
  var fs16 = __toESM2(require("fs"));
@@ -89620,7 +90181,7 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
89620
90181
  if (path24.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
89621
90182
  const candidate = trimmed.startsWith("~") ? path24.join((0, import_os3.homedir)(), trimmed.slice(1)) : trimmed;
89622
90183
  const resolved = path24.isAbsolute(candidate) ? candidate : path24.resolve(candidate);
89623
- return (0, import_fs17.existsSync)(resolved) ? resolved : null;
90184
+ return (0, import_fs18.existsSync)(resolved) ? resolved : null;
89624
90185
  }
89625
90186
  const isWin = (0, import_os3.platform)() === "win32";
89626
90187
  const paths = (process.env.PATH || "").split(isWin ? ";" : ":");
@@ -89630,8 +90191,8 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
89630
90191
  for (const ext of exes) {
89631
90192
  const fullPath = path24.join(p, trimmed + ext);
89632
90193
  try {
89633
- if ((0, import_fs17.existsSync)(fullPath)) {
89634
- const stat2 = (0, import_fs17.statSync)(fullPath);
90194
+ if ((0, import_fs18.existsSync)(fullPath)) {
90195
+ const stat2 = (0, import_fs18.statSync)(fullPath);
89635
90196
  if (stat2.isFile() && (isWin || stat2.mode & 73)) {
89636
90197
  return fullPath;
89637
90198
  }
@@ -89649,9 +90210,9 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
89649
90210
  if (normalized.includes("*")) {
89650
90211
  const username = home.split(/[\\/]/).pop() || "";
89651
90212
  const resolved = normalized.replace("*", username);
89652
- if ((0, import_fs17.existsSync)(resolved)) return resolved;
90213
+ if ((0, import_fs18.existsSync)(resolved)) return resolved;
89653
90214
  } else {
89654
- if ((0, import_fs17.existsSync)(normalized)) return normalized;
90215
+ if ((0, import_fs18.existsSync)(normalized)) return normalized;
89655
90216
  }
89656
90217
  }
89657
90218
  return null;
@@ -89665,7 +90226,7 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
89665
90226
  let resolvedCli = cliPath;
89666
90227
  if (!resolvedCli && appPath && os33 === "darwin") {
89667
90228
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
89668
- if ((0, import_fs17.existsSync)(bundledCli)) resolvedCli = bundledCli;
90229
+ if ((0, import_fs18.existsSync)(bundledCli)) resolvedCli = bundledCli;
89669
90230
  }
89670
90231
  if (!resolvedCli && appPath && os33 === "win32") {
89671
90232
  const { dirname: dirname24 } = await import("path");
@@ -89678,7 +90239,7 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
89678
90239
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
89679
90240
  ];
89680
90241
  for (const c of candidates) {
89681
- if ((0, import_fs17.existsSync)(c)) {
90242
+ if ((0, import_fs18.existsSync)(c)) {
89682
90243
  resolvedCli = c;
89683
90244
  break;
89684
90245
  }
@@ -101316,7 +101877,7 @@ ${marker}`,
101316
101877
  var os24 = __toESM2(require("os"));
101317
101878
  var path40 = __toESM2(require("path"));
101318
101879
  var crypto6 = __toESM2(require("crypto"));
101319
- var import_fs18 = require("fs");
101880
+ var import_fs19 = require("fs");
101320
101881
  var import_child_process11 = require("child_process");
101321
101882
  var import_chalk = __toESM2((init_source(), __toCommonJS(source_exports)));
101322
101883
  var fs322 = __toESM2(require("fs"));
@@ -112640,7 +113201,7 @@ ${rawInput}` : rawInput;
112640
113201
  const trimmed = command.trim();
112641
113202
  if (!trimmed) return false;
112642
113203
  if (isExplicitCommand(trimmed)) {
112643
- return (0, import_fs18.existsSync)(expandExecutable(trimmed));
113204
+ return (0, import_fs19.existsSync)(expandExecutable(trimmed));
112644
113205
  }
112645
113206
  try {
112646
113207
  (0, import_child_process11.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -112786,10 +113347,10 @@ ${rawInput}` : rawInput;
112786
113347
  }
112787
113348
  function ensureEmptyDelegatedMcpConfig(workspace) {
112788
113349
  const baseDir = path40.join(os24.tmpdir(), "adhdev-delegated-agent-empty-mcp");
112789
- (0, import_fs18.mkdirSync)(baseDir, { recursive: true });
113350
+ (0, import_fs19.mkdirSync)(baseDir, { recursive: true });
112790
113351
  const workspaceHash = shortHash(path40.resolve(workspace || os24.tmpdir()));
112791
113352
  const filePath = path40.join(baseDir, `${workspaceHash}.json`);
112792
- (0, import_fs18.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
113353
+ (0, import_fs19.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
112793
113354
  return filePath;
112794
113355
  }
112795
113356
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
@@ -120541,7 +121102,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
120541
121102
  normalizeRepoMeshDeclarativeConfig: normalizeRepoMeshDeclarativeConfig2,
120542
121103
  MESH_JSON_CONFIG_LOCATIONS: MESH_JSON_CONFIG_LOCATIONS2
120543
121104
  } = await Promise.resolve().then(() => (init_mesh_json_config(), mesh_json_config_exports));
120544
- const { mkdirSync: mkdirSync31, writeFileSync: writeFileSync31 } = await import("fs");
121105
+ const { mkdirSync: mkdirSync32, writeFileSync: writeFileSync31 } = await import("fs");
120545
121106
  const { dirname: dirname24, join: join68 } = await import("path");
120546
121107
  const scaffold = buildMeshJsonConfigScaffold2(mesh);
120547
121108
  const scaffoldJson = serializeMeshJsonConfigScaffold2(scaffold);
@@ -120582,7 +121143,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
120582
121143
  note: "Dry-run: nothing written. Re-run with write=true to persist to the repo (commit target). meshes.json is untouched."
120583
121144
  };
120584
121145
  }
120585
- mkdirSync31(dirname24(absolutePath), { recursive: true });
121146
+ mkdirSync32(dirname24(absolutePath), { recursive: true });
120586
121147
  writeFileSync31(absolutePath, `${scaffoldJson}
120587
121148
  `, "utf-8");
120588
121149
  return {
@@ -120652,7 +121213,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
120652
121213
  normalizeRepoMeshDeclarativeConfig: normalizeRepoMeshDeclarativeConfig2,
120653
121214
  MESH_JSON_CONFIG_LOCATIONS: MESH_JSON_CONFIG_LOCATIONS2
120654
121215
  } = await Promise.resolve().then(() => (init_mesh_json_config(), mesh_json_config_exports));
120655
- const { existsSync: existsSync70, readFileSync: readFileSync55, mkdirSync: mkdirSync31, writeFileSync: writeFileSync31 } = await import("fs");
121216
+ const { existsSync: existsSync70, readFileSync: readFileSync55, mkdirSync: mkdirSync32, writeFileSync: writeFileSync31 } = await import("fs");
120656
121217
  const { dirname: dirname24, join: join68 } = await import("path");
120657
121218
  const yaml6 = await Promise.resolve().then(() => (init_js_yaml(), js_yaml_exports));
120658
121219
  const relativePath = MESH_JSON_CONFIG_LOCATIONS2[0];
@@ -120717,7 +121278,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
120717
121278
  note: "Dry-run: nothing written. Re-run with write=true to persist. Only the providerDefaults zone is merged; other repo zones are preserved."
120718
121279
  };
120719
121280
  }
120720
- mkdirSync31(dirname24(absolutePath), { recursive: true });
121281
+ mkdirSync32(dirname24(absolutePath), { recursive: true });
120721
121282
  writeFileSync31(absolutePath, serialized, "utf-8");
120722
121283
  return {
120723
121284
  success: true,
@@ -121674,7 +122235,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
121674
122235
  error: applied.reason
121675
122236
  };
121676
122237
  }
121677
- ctx.inlineMeshCache.set(meshId, applied.mesh);
122238
+ ctx.getCachedInlineMesh(meshId, applied.mesh);
121678
122239
  ctx.invalidateAggregateMeshStatus(meshId);
121679
122240
  try {
121680
122241
  const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
@@ -121807,14 +122368,14 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
121807
122368
  let daemonId;
121808
122369
  if (nodeId) {
121809
122370
  try {
121810
- const [{ getMesh: getMesh2 }, { meshNodeIdMatches: meshNodeIdMatches2 }, { readMeshNodeDaemonId: readMeshNodeDaemonId2 }] = await Promise.all([
122371
+ const [{ getMesh: getMesh2 }, { meshNodeIdMatches: meshNodeIdMatches2 }, { readMeshNodeDaemonId: readMeshNodeDaemonId3 }] = await Promise.all([
121811
122372
  Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports)),
121812
122373
  Promise.resolve().then(() => (init_dist(), dist_exports)),
121813
122374
  Promise.resolve().then(() => (init_mesh_node_identity(), mesh_node_identity_exports))
121814
122375
  ]);
121815
122376
  const mesh = getMesh2(meshId);
121816
122377
  const node = mesh?.nodes?.find((n) => meshNodeIdMatches2(n, nodeId));
121817
- daemonId = node ? readMeshNodeDaemonId2(node) || void 0 : void 0;
122378
+ daemonId = node ? readMeshNodeDaemonId3(node) || void 0 : void 0;
121818
122379
  } catch {
121819
122380
  }
121820
122381
  }
@@ -123026,7 +123587,7 @@ ${ptyResult.output.slice(-2e3)}`);
123026
123587
  workspace
123027
123588
  };
123028
123589
  }
123029
- const { existsSync: existsSync70, readFileSync: readFileSync55, writeFileSync: writeFileSync31, copyFileSync: copyFileSync3, mkdirSync: mkdirSync31 } = await import("fs");
123590
+ const { existsSync: existsSync70, readFileSync: readFileSync55, writeFileSync: writeFileSync31, copyFileSync: copyFileSync3, mkdirSync: mkdirSync32 } = await import("fs");
123030
123591
  const { dirname: dirname24 } = await import("path");
123031
123592
  const mcpConfigPath = coordinatorSetup.configPath;
123032
123593
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -123064,7 +123625,7 @@ ${ptyResult.output.slice(-2e3)}`);
123064
123625
  ...mcpServerEnv ? { env: mcpServerEnv } : {}
123065
123626
  });
123066
123627
  try {
123067
- mkdirSync31(dirname24(mcpConfigPath), { recursive: true });
123628
+ mkdirSync32(dirname24(mcpConfigPath), { recursive: true });
123068
123629
  } catch (error48) {
123069
123630
  const message = `Could not prepare MCP config path for automatic setup: ${error48?.message || error48}`;
123070
123631
  LOG2.error("MeshCoordinator", message);
@@ -124092,7 +124653,7 @@ ${ptyResult.output.slice(-2e3)}`);
124092
124653
  init_router_refine();
124093
124654
  var fs54 = __toESM2(require("fs"));
124094
124655
  var import_node_os22 = require("os");
124095
- var import_path19 = require("path");
124656
+ var import_path20 = require("path");
124096
124657
  init_logger();
124097
124658
  init_git_locale();
124098
124659
  init_dist();
@@ -124104,7 +124665,7 @@ ${ptyResult.output.slice(-2e3)}`);
124104
124665
  function acceptableManagedWorktreePaths(resolveWorktreePath2, normalizePath2, repoRoot, meshName, branch, worktreeBaseDir) {
124105
124666
  const safeBranch = branch.replace(/[/\\:*?"<>|]/g, "-").replace(/^\.+|\.+$/g, "");
124106
124667
  const safeMeshName = meshName.replace(/[/\\:*?"<>|]/g, "-").replace(/^\.+|\.+$/g, "");
124107
- const legacyPath = (0, import_path19.join)((0, import_path19.dirname)(repoRoot), LEGACY_WORKTREE_DIR_NAME, safeMeshName, safeBranch);
124668
+ const legacyPath = (0, import_path20.join)((0, import_path20.dirname)(repoRoot), LEGACY_WORKTREE_DIR_NAME, safeMeshName, safeBranch);
124108
124669
  const candidates = [
124109
124670
  resolveWorktreePath2(repoRoot, meshName, branch, worktreeBaseDir),
124110
124671
  legacyPath
@@ -124131,8 +124692,8 @@ ${ptyResult.output.slice(-2e3)}`);
124131
124692
  function looksLikeCrossTrackWorktreePath(actualPath, normalizePath2) {
124132
124693
  const home = normalizePath2((0, import_node_os22.homedir)());
124133
124694
  return TRACK_CONFIG_DIR_NAMES.some((trackDirName) => {
124134
- const trackWorktreesDir = (0, import_path19.join)(home, trackDirName, "worktrees");
124135
- return actualPath === normalizePath2(trackWorktreesDir) || actualPath.startsWith(normalizePath2(trackWorktreesDir) + import_path19.sep);
124695
+ const trackWorktreesDir = (0, import_path20.join)(home, trackDirName, "worktrees");
124696
+ return actualPath === normalizePath2(trackWorktreesDir) || actualPath.startsWith(normalizePath2(trackWorktreesDir) + import_path20.sep);
124136
124697
  });
124137
124698
  }
124138
124699
  function unexpectedPathRecoveryHint(actualPath, normalizePath2, suffix) {
@@ -124242,7 +124803,7 @@ ${ptyResult.output.slice(-2e3)}`);
124242
124803
  }
124243
124804
  const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
124244
124805
  const normalizePath2 = (value) => {
124245
- const resolved = (0, import_path19.resolve)(value);
124806
+ const resolved = (0, import_path20.resolve)(value);
124246
124807
  try {
124247
124808
  return fs54.realpathSync(resolved);
124248
124809
  } catch {
@@ -124369,7 +124930,7 @@ ${ptyResult.output.slice(-2e3)}`);
124369
124930
  }
124370
124931
  const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
124371
124932
  const normalizePath2 = (value) => {
124372
- const resolved = (0, import_path19.resolve)(value);
124933
+ const resolved = (0, import_path20.resolve)(value);
124373
124934
  try {
124374
124935
  return fs54.realpathSync(resolved);
124375
124936
  } catch {
@@ -125037,7 +125598,7 @@ ${ptyResult.output.slice(-2e3)}`);
125037
125598
  init_logger();
125038
125599
  var yaml5 = __toESM2(require_js_yaml());
125039
125600
  var import_os5 = require("os");
125040
- var import_path20 = require("path");
125601
+ var import_path21 = require("path");
125041
125602
  var fs55 = __toESM2(require("fs"));
125042
125603
  var MESH_COORDINATOR_AUTO_IMPORT_FORMATS = ["claude_mcp_json", "hermes_config_yaml", "opencode_json"];
125043
125604
  function isSupportedMeshCoordinatorConfigFormat(format) {
@@ -125078,13 +125639,13 @@ ${ptyResult.output.slice(-2e3)}`);
125078
125639
  }
125079
125640
  function resolveHermesUserHome() {
125080
125641
  const explicitHome = process.env.HERMES_HOME?.trim();
125081
- return explicitHome || (0, import_path20.join)((0, import_os5.homedir)(), ".hermes");
125642
+ return explicitHome || (0, import_path21.join)((0, import_os5.homedir)(), ".hermes");
125082
125643
  }
125083
125644
  function loadHermesCoordinatorBaseConfig(targetConfigPath) {
125084
125645
  const sourceHome = resolveHermesUserHome();
125085
- const sourceConfigPath = (0, import_path20.join)(sourceHome, "config.yaml");
125646
+ const sourceConfigPath = (0, import_path21.join)(sourceHome, "config.yaml");
125086
125647
  if (!fs55.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
125087
- if ((0, import_path20.resolve)(sourceConfigPath) === (0, import_path20.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
125648
+ if ((0, import_path21.resolve)(sourceConfigPath) === (0, import_path21.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
125088
125649
  const parsed = parseMeshCoordinatorMcpConfig(fs55.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
125089
125650
  const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
125090
125651
  return { config: baseConfig, sourceHome, sourceConfigPath };
@@ -125118,10 +125679,10 @@ ${ptyResult.output.slice(-2e3)}`);
125118
125679
  return sanitized;
125119
125680
  }
125120
125681
  function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
125121
- if ((0, import_path20.resolve)(sourceHome) === (0, import_path20.resolve)(targetHome)) return;
125682
+ if ((0, import_path21.resolve)(sourceHome) === (0, import_path21.resolve)(targetHome)) return;
125122
125683
  for (const fileName of [".env", "auth.json"]) {
125123
- const sourcePath = (0, import_path20.join)(sourceHome, fileName);
125124
- const targetPath = (0, import_path20.join)(targetHome, fileName);
125684
+ const sourcePath = (0, import_path21.join)(sourceHome, fileName);
125685
+ const targetPath = (0, import_path21.join)(targetHome, fileName);
125125
125686
  if (!fs55.existsSync(sourcePath)) continue;
125126
125687
  try {
125127
125688
  fs55.copyFileSync(sourcePath, targetPath);
@@ -126258,6 +126819,7 @@ ${ptyResult.output.slice(-2e3)}`);
126258
126819
  init_track_identity();
126259
126820
  init_normalize();
126260
126821
  init_logger();
126822
+ init_disk_space_preflight();
126261
126823
  var UNICODE_SYMBOLS = {
126262
126824
  ok: "\u2713",
126263
126825
  fail: "\u2717",
@@ -133326,6 +133888,7 @@ data: ${JSON.stringify(msg.data)}
133326
133888
  var path59 = __toESM2(require("path"));
133327
133889
  var import_session_host_core14 = require_dist();
133328
133890
  init_logger();
133891
+ init_disk_space_preflight();
133329
133892
  init_runtime_defaults();
133330
133893
  function createManagedSessionHost2(options) {
133331
133894
  const appName = options.appName;
@@ -133429,6 +133992,7 @@ data: ${JSON.stringify(msg.data)}
133429
133992
  function spawnHost() {
133430
133993
  const entry = resolveEntry();
133431
133994
  verifyConptyPrebuildBeforeSpawn(entry);
133995
+ preflightDiskSpace(instance().configDir, "spawn the session host");
133432
133996
  const nodeExecutable = resolveSessionHostNode();
133433
133997
  let stdio = "ignore";
133434
133998
  let logFd = null;
@@ -133444,7 +134008,20 @@ data: ${JSON.stringify(msg.data)}
133444
134008
  windowsHide: true,
133445
134009
  env: buildEnv(process.env)
133446
134010
  });
133447
- child.unref();
134011
+ if (typeof child?.on === "function") {
134012
+ child.on("error", (error48) => {
134013
+ LOG2.error("SessionHost", `Failed to spawn session-host process: ${error48?.message || error48}`);
134014
+ });
134015
+ child.on("exit", (code, signal) => {
134016
+ const how = signal ? `signal ${signal}` : `code ${code}`;
134017
+ const detail = options.spawnStdio === "logfile" ? ` \u2014 see ${path59.join(instance().configDir, "logs", "session-host.log")} for its stderr` : "";
134018
+ LOG2.error(
134019
+ "SessionHost",
134020
+ `Session-host process (pid ${child.pid ?? "unknown"}) exited with ${how}${detail}`
134021
+ );
134022
+ });
134023
+ }
134024
+ if (typeof child?.unref === "function") child.unref();
133448
134025
  if (logFd !== null) {
133449
134026
  try {
133450
134027
  fs62.closeSync(logFd);
@@ -133895,8 +134472,8 @@ data: ${JSON.stringify(msg.data)}
133895
134472
  init_config_dir();
133896
134473
  init_mesh_events();
133897
134474
  init_mesh_reconcile_loop();
133898
- var import_fs19 = require("fs");
133899
- var import_path21 = require("path");
134475
+ var import_fs20 = require("fs");
134476
+ var import_path222 = require("path");
133900
134477
  var import_crypto18 = require("crypto");
133901
134478
  init_logger();
133902
134479
  init_mesh_ledger();
@@ -133964,31 +134541,31 @@ data: ${JSON.stringify(msg.data)}
133964
134541
  } catch {
133965
134542
  return result;
133966
134543
  }
133967
- if (!(0, import_fs19.existsSync)(dir)) return result;
134544
+ if (!(0, import_fs20.existsSync)(dir)) return result;
133968
134545
  let names;
133969
134546
  try {
133970
- names = (0, import_fs19.readdirSync)(dir);
134547
+ names = (0, import_fs20.readdirSync)(dir);
133971
134548
  } catch (e) {
133972
134549
  LOG2.warn("MeshEvents", `Pending-events migration: cannot read ledger dir: ${e?.message || e}`);
133973
134550
  return result;
133974
134551
  }
133975
134552
  for (const name of names) {
133976
134553
  if (!isPendingEventsFile(name)) continue;
133977
- const path60 = (0, import_path21.join)(dir, name);
134554
+ const path60 = (0, import_path222.join)(dir, name);
133978
134555
  result.filesScanned++;
133979
134556
  const claimed = `${path60}.migrating`;
133980
134557
  try {
133981
- (0, import_fs19.renameSync)(path60, claimed);
134558
+ (0, import_fs20.renameSync)(path60, claimed);
133982
134559
  } catch {
133983
134560
  continue;
133984
134561
  }
133985
134562
  let content;
133986
134563
  try {
133987
- content = (0, import_fs19.readFileSync)(claimed, "utf-8");
134564
+ content = (0, import_fs20.readFileSync)(claimed, "utf-8");
133988
134565
  } catch (e) {
133989
134566
  LOG2.warn("MeshEvents", `Pending-events migration: cannot read ${name}; leaving it for the next boot: ${e?.message || e}`);
133990
134567
  try {
133991
- (0, import_fs19.renameSync)(claimed, path60);
134568
+ (0, import_fs20.renameSync)(claimed, path60);
133992
134569
  } catch {
133993
134570
  }
133994
134571
  result.filesRetained++;
@@ -134003,14 +134580,14 @@ data: ${JSON.stringify(msg.data)}
134003
134580
  }
134004
134581
  if (!allImported) {
134005
134582
  try {
134006
- (0, import_fs19.renameSync)(claimed, path60);
134583
+ (0, import_fs20.renameSync)(claimed, path60);
134007
134584
  } catch {
134008
134585
  }
134009
134586
  result.filesRetained++;
134010
134587
  continue;
134011
134588
  }
134012
134589
  try {
134013
- (0, import_fs19.unlinkSync)(claimed);
134590
+ (0, import_fs20.unlinkSync)(claimed);
134014
134591
  result.filesRemoved++;
134015
134592
  } catch (e) {
134016
134593
  LOG2.warn("MeshEvents", `Pending-events migration: imported ${name} but could not unlink it: ${e?.message || e}`);
@@ -135429,14 +136006,77 @@ function createLineParser(onEnvelope) {
135429
136006
  }
135430
136007
  };
135431
136008
  }
136009
+ var establishedSockets = /* @__PURE__ */ new WeakSet();
135432
136010
  var SessionHostClient = class {
135433
136011
  endpoint;
135434
136012
  socket = null;
135435
136013
  requestWaiters = /* @__PURE__ */ new Map();
135436
136014
  eventListeners = /* @__PURE__ */ new Set();
136015
+ disconnectListeners = /* @__PURE__ */ new Set();
136016
+ /** Guards against emitting both an 'error'-path and a 'close'-path disconnect for one socket. */
136017
+ disconnectedSockets = /* @__PURE__ */ new WeakSet();
135437
136018
  constructor(options = {}) {
135438
136019
  this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || "adhdev");
135439
136020
  }
136021
+ /**
136022
+ * Fires when an established connection to the session host goes away.
136023
+ *
136024
+ * Before this existed the client registered only 'data' and 'error' handlers,
136025
+ * so a *clean* FIN — the host process exiting normally, or dying to an
136026
+ * uncaught exception that took the socket down without an error event on our
136027
+ * end — ran nothing at all: `this.socket` stayed non-null pointing at a dead
136028
+ * socket, in-flight waiters hung to their 30s timeout, and no caller was told.
136029
+ * That silence is what let a session-host death present downstream as an
136030
+ * unrelated application-level fault (a missing completion marker) instead of
136031
+ * "the host is gone".
136032
+ */
136033
+ onDisconnect(listener) {
136034
+ this.disconnectListeners.add(listener);
136035
+ return () => {
136036
+ this.disconnectListeners.delete(listener);
136037
+ };
136038
+ }
136039
+ handleDisconnect(socket, reason, error48) {
136040
+ if (this.disconnectedSockets.has(socket)) return;
136041
+ this.disconnectedSockets.add(socket);
136042
+ const wasEstablished = establishedSockets.has(socket);
136043
+ if (!wasEstablished) {
136044
+ if (this.socket === socket) this.socket = null;
136045
+ try {
136046
+ socket.destroy();
136047
+ } catch {
136048
+ }
136049
+ const failure2 = error48 || new Error(`Session host connect failed (${this.endpoint.path})`);
136050
+ for (const waiter of this.requestWaiters.values()) waiter.reject(failure2);
136051
+ this.requestWaiters.clear();
136052
+ return;
136053
+ }
136054
+ const pendingRequests = this.requestWaiters.size;
136055
+ const failure = error48 || new Error(`Session host connection ${reason} (${this.endpoint.path})`);
136056
+ for (const waiter of this.requestWaiters.values()) {
136057
+ waiter.reject(failure);
136058
+ }
136059
+ this.requestWaiters.clear();
136060
+ if (this.socket === socket) {
136061
+ this.socket = null;
136062
+ }
136063
+ try {
136064
+ socket.destroy();
136065
+ } catch {
136066
+ }
136067
+ const info = {
136068
+ reason,
136069
+ endpointPath: this.endpoint.path,
136070
+ pendingRequests,
136071
+ error: error48
136072
+ };
136073
+ for (const listener of this.disconnectListeners) {
136074
+ try {
136075
+ listener(info);
136076
+ } catch {
136077
+ }
136078
+ }
136079
+ }
135440
136080
  async connect() {
135441
136081
  if (this.socket && !this.socket.destroyed) return;
135442
136082
  if (this.socket) {
@@ -135462,20 +136102,19 @@ var SessionHostClient = class {
135462
136102
  }
135463
136103
  }));
135464
136104
  socket.on("error", (error48) => {
135465
- for (const waiter of this.requestWaiters.values()) {
135466
- waiter.reject(error48);
135467
- }
135468
- this.requestWaiters.clear();
135469
- if (this.socket === socket) {
135470
- this.socket = null;
135471
- }
135472
- try {
135473
- socket.destroy();
135474
- } catch {
135475
- }
136105
+ this.handleDisconnect(socket, "error", error48);
136106
+ });
136107
+ socket.on("close", () => {
136108
+ this.handleDisconnect(socket, "closed");
136109
+ });
136110
+ socket.on("end", () => {
136111
+ this.handleDisconnect(socket, "ended");
135476
136112
  });
135477
136113
  await new Promise((resolve3, reject) => {
135478
- socket.once("connect", () => resolve3());
136114
+ socket.once("connect", () => {
136115
+ establishedSockets.add(socket);
136116
+ resolve3();
136117
+ });
135479
136118
  socket.once("error", reject);
135480
136119
  });
135481
136120
  }
@@ -135517,6 +136156,7 @@ var SessionHostClient = class {
135517
136156
  if (!this.socket) return;
135518
136157
  const socket = this.socket;
135519
136158
  this.socket = null;
136159
+ this.disconnectedSockets.add(socket);
135520
136160
  for (const waiter of this.requestWaiters.values()) {
135521
136161
  waiter.reject(new Error("Session host client closed"));
135522
136162
  }