@adhdev/daemon-core 0.9.82-rc.342 → 0.9.82-rc.344

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
@@ -316,10 +316,10 @@ function readInjected(value) {
316
316
  }
317
317
  function getDaemonBuildInfo() {
318
318
  if (cached) return cached;
319
- const commit = readInjected(true ? "ff8ffda8c3d1a14f846ad4d785e4cab4d111a36b" : void 0) ?? "unknown";
320
- const commitShort = readInjected(true ? "ff8ffda8" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
- const version = readInjected(true ? "0.9.82-rc.342" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
- const builtAt = readInjected(true ? "2026-06-21T05:12:18.017Z" : void 0);
319
+ const commit = readInjected(true ? "d04317b7eab7aec6a5557cb1991af603c7e5143c" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "d04317b7" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.344" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-21T13:38:42.110Z" : void 0);
323
323
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
324
324
  return cached;
325
325
  }
@@ -2541,6 +2541,297 @@ Follow these recovery rules:
2541
2541
  }
2542
2542
  });
2543
2543
 
2544
+ // src/logging/async-batch-writer.ts
2545
+ var fs3, AsyncBatchWriter;
2546
+ var init_async_batch_writer = __esm({
2547
+ "src/logging/async-batch-writer.ts"() {
2548
+ "use strict";
2549
+ fs3 = __toESM(require("fs"));
2550
+ AsyncBatchWriter = class {
2551
+ // Maps filePath -> string buffer
2552
+ static buffers = /* @__PURE__ */ new Map();
2553
+ static writePromises = /* @__PURE__ */ new Map();
2554
+ static flushTimer = null;
2555
+ /**
2556
+ * Queues data to be written to a file asynchronously in a batch.
2557
+ */
2558
+ static write(filePath, data) {
2559
+ let buf = this.buffers.get(filePath);
2560
+ if (!buf) {
2561
+ buf = [];
2562
+ this.buffers.set(filePath, buf);
2563
+ }
2564
+ buf.push(data);
2565
+ if (!this.flushTimer) {
2566
+ this.flushTimer = setTimeout(() => {
2567
+ this.flushTimer = null;
2568
+ this.flushAll();
2569
+ }, 50);
2570
+ }
2571
+ }
2572
+ static async flushAll() {
2573
+ const entries = Array.from(this.buffers.entries());
2574
+ this.buffers.clear();
2575
+ for (const [filePath, buffer] of entries) {
2576
+ const dataToWrite = buffer.join("");
2577
+ const doWrite = async () => {
2578
+ try {
2579
+ const prevPromise = this.writePromises.get(filePath);
2580
+ if (prevPromise) await prevPromise;
2581
+ await fs3.promises.appendFile(filePath, dataToWrite, { encoding: "utf-8", mode: 384 });
2582
+ } catch {
2583
+ }
2584
+ };
2585
+ const writePromise = doWrite();
2586
+ this.writePromises.set(filePath, writePromise);
2587
+ writePromise.finally(() => {
2588
+ if (this.writePromises.get(filePath) === writePromise) {
2589
+ this.writePromises.delete(filePath);
2590
+ }
2591
+ });
2592
+ }
2593
+ }
2594
+ };
2595
+ }
2596
+ });
2597
+
2598
+ // src/logging/logger.ts
2599
+ var logger_exports = {};
2600
+ __export(logger_exports, {
2601
+ LOG: () => LOG,
2602
+ LOG_DIR_PATH: () => LOG_DIR_PATH,
2603
+ LOG_PATH: () => LOG_PATH,
2604
+ daemonLog: () => daemonLog,
2605
+ getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
2606
+ getDaemonLogDir: () => getDaemonLogDir,
2607
+ getLogBufferSize: () => getLogBufferSize,
2608
+ getLogLevel: () => getLogLevel,
2609
+ getLogPath: () => getLogPath,
2610
+ getRecentLogs: () => getRecentLogs,
2611
+ installGlobalInterceptor: () => installGlobalInterceptor,
2612
+ setLogLevel: () => setLogLevel
2613
+ });
2614
+ function setLogLevel(level) {
2615
+ currentLevel = level;
2616
+ daemonLog("Logger", `Log level set to: ${level}`, "info");
2617
+ }
2618
+ function getLogLevel() {
2619
+ return currentLevel;
2620
+ }
2621
+ function getDateStr() {
2622
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2623
+ }
2624
+ function getDaemonLogDir() {
2625
+ return LOG_DIR;
2626
+ }
2627
+ function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
2628
+ return path9.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
2629
+ }
2630
+ function checkDateRotation() {
2631
+ const today = getDateStr();
2632
+ if (today !== currentDate) {
2633
+ currentDate = today;
2634
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
2635
+ cleanOldLogs();
2636
+ }
2637
+ }
2638
+ function cleanOldLogs() {
2639
+ try {
2640
+ const files = fs4.readdirSync(LOG_DIR).filter((f) => f.startsWith("daemon-") && f.endsWith(".log"));
2641
+ const cutoff = /* @__PURE__ */ new Date();
2642
+ cutoff.setDate(cutoff.getDate() - MAX_LOG_DAYS);
2643
+ const cutoffStr = cutoff.toISOString().slice(0, 10);
2644
+ for (const file of files) {
2645
+ const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
2646
+ if (dateMatch && dateMatch[1] < cutoffStr) {
2647
+ try {
2648
+ fs4.unlinkSync(path9.join(LOG_DIR, file));
2649
+ } catch {
2650
+ }
2651
+ }
2652
+ }
2653
+ } catch {
2654
+ }
2655
+ }
2656
+ function rotateSizeIfNeeded() {
2657
+ try {
2658
+ const stat2 = fs4.statSync(currentLogFile);
2659
+ if (stat2.size > MAX_LOG_SIZE) {
2660
+ const backup = currentLogFile.replace(".log", ".1.log");
2661
+ try {
2662
+ fs4.unlinkSync(backup);
2663
+ } catch {
2664
+ }
2665
+ fs4.renameSync(currentLogFile, backup);
2666
+ }
2667
+ } catch {
2668
+ }
2669
+ }
2670
+ function writeToFile(line) {
2671
+ try {
2672
+ if (++writeCount % 1e3 === 0) {
2673
+ checkDateRotation();
2674
+ rotateSizeIfNeeded();
2675
+ }
2676
+ AsyncBatchWriter.write(currentLogFile, line + "\n");
2677
+ } catch {
2678
+ }
2679
+ }
2680
+ function getRecentLogs(count = 50, minLevel = "info") {
2681
+ const minNum = LEVEL_NUM[minLevel];
2682
+ const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
2683
+ return filtered.slice(-count);
2684
+ }
2685
+ function getLogBufferSize() {
2686
+ return ringBuffer.length;
2687
+ }
2688
+ function ts() {
2689
+ return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
2690
+ }
2691
+ function fullTs() {
2692
+ return (/* @__PURE__ */ new Date()).toISOString();
2693
+ }
2694
+ function daemonLog(category, msg, level = "info") {
2695
+ const shouldOutput = LEVEL_NUM[level] >= LEVEL_NUM[currentLevel];
2696
+ const label = LEVEL_LABEL[level];
2697
+ const line = `[${ts()}] [${label}] [${category}] ${msg}`;
2698
+ if (!shouldOutput) return;
2699
+ writeToFile(line);
2700
+ ringBuffer.push({ ts: Date.now(), level, category, message: msg });
2701
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
2702
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
2703
+ }
2704
+ origConsoleLog(line);
2705
+ }
2706
+ function installGlobalInterceptor() {
2707
+ if (interceptorInstalled) return;
2708
+ interceptorInstalled = true;
2709
+ const stripAnsi4 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
2710
+ const isDaemonLogLine = (msg) => /\[(DBG|INF|WRN|ERR)\]/.test(msg);
2711
+ console.log = (...args) => {
2712
+ origConsoleLog(...args);
2713
+ try {
2714
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
2715
+ const clean = stripAnsi4(msg);
2716
+ if (isDaemonLogLine(clean)) return;
2717
+ const line = clean.startsWith("[20") ? clean : `[${fullTs()}] ${clean}`;
2718
+ writeToFile(line);
2719
+ const catMatch = clean.match(/\[([^\]]+)\]/);
2720
+ ringBuffer.push({
2721
+ ts: Date.now(),
2722
+ level: "info",
2723
+ category: catMatch?.[1] || "System",
2724
+ message: clean
2725
+ });
2726
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
2727
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
2728
+ }
2729
+ } catch {
2730
+ }
2731
+ };
2732
+ console.error = (...args) => {
2733
+ origConsoleError(...args);
2734
+ try {
2735
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
2736
+ const clean = stripAnsi4(msg);
2737
+ if (isDaemonLogLine(clean)) return;
2738
+ const line = `[${fullTs()}] [ERROR] ${clean}`;
2739
+ writeToFile(line);
2740
+ ringBuffer.push({ ts: Date.now(), level: "error", category: "System", message: clean });
2741
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
2742
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
2743
+ }
2744
+ } catch {
2745
+ }
2746
+ };
2747
+ console.warn = (...args) => {
2748
+ origConsoleWarn(...args);
2749
+ try {
2750
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
2751
+ const clean = stripAnsi4(msg);
2752
+ if (isDaemonLogLine(clean)) return;
2753
+ const line = `[${fullTs()}] [WARN] ${clean}`;
2754
+ writeToFile(line);
2755
+ ringBuffer.push({ ts: Date.now(), level: "warn", category: "System", message: clean });
2756
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
2757
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
2758
+ }
2759
+ } catch {
2760
+ }
2761
+ };
2762
+ writeToFile(`
2763
+ === ADHDev Daemon started at ${fullTs()} ===`);
2764
+ writeToFile(`Log file: ${currentLogFile}`);
2765
+ writeToFile(`Log level: ${currentLevel}`);
2766
+ }
2767
+ function getLogPath() {
2768
+ return currentLogFile;
2769
+ }
2770
+ var fs4, path9, os3, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
2771
+ var init_logger = __esm({
2772
+ "src/logging/logger.ts"() {
2773
+ "use strict";
2774
+ fs4 = __toESM(require("fs"));
2775
+ path9 = __toESM(require("path"));
2776
+ os3 = __toESM(require("os"));
2777
+ init_async_batch_writer();
2778
+ LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
2779
+ LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
2780
+ currentLevel = "info";
2781
+ LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os3.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os3.homedir(), "Library", "Logs", "adhdev") : path9.join(os3.homedir(), ".local", "share", "adhdev", "logs");
2782
+ MAX_LOG_SIZE = 5 * 1024 * 1024;
2783
+ MAX_LOG_DAYS = 7;
2784
+ try {
2785
+ fs4.mkdirSync(LOG_DIR, { recursive: true });
2786
+ } catch {
2787
+ }
2788
+ currentDate = getDateStr();
2789
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
2790
+ cleanOldLogs();
2791
+ try {
2792
+ const oldLog = path9.join(LOG_DIR, "daemon.log");
2793
+ if (fs4.existsSync(oldLog)) {
2794
+ const stat2 = fs4.statSync(oldLog);
2795
+ const oldDate = stat2.mtime.toISOString().slice(0, 10);
2796
+ fs4.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
2797
+ }
2798
+ const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
2799
+ if (fs4.existsSync(oldLogBackup)) {
2800
+ fs4.unlinkSync(oldLogBackup);
2801
+ }
2802
+ } catch {
2803
+ }
2804
+ writeCount = 0;
2805
+ RING_BUFFER_SIZE = 200;
2806
+ ringBuffer = [];
2807
+ origConsoleLog = console.log.bind(console);
2808
+ origConsoleError = console.error.bind(console);
2809
+ origConsoleWarn = console.warn.bind(console);
2810
+ LOG = {
2811
+ debug: (category, msg) => daemonLog(category, msg, "debug"),
2812
+ info: (category, msg) => daemonLog(category, msg, "info"),
2813
+ warn: (category, msg) => daemonLog(category, msg, "warn"),
2814
+ error: (category, msg) => daemonLog(category, msg, "error"),
2815
+ /**
2816
+ * Create a scoped logger for a specific component.
2817
+ * Category is baked in so callers only pass the message.
2818
+ */
2819
+ forComponent(category) {
2820
+ return {
2821
+ debug: (msg) => daemonLog(category, msg, "debug"),
2822
+ info: (msg) => daemonLog(category, msg, "info"),
2823
+ warn: (msg) => daemonLog(category, msg, "warn"),
2824
+ error: (msg) => daemonLog(category, msg, "error"),
2825
+ asLogFn: (level = "info") => (msg) => daemonLog(category, msg, level)
2826
+ };
2827
+ }
2828
+ };
2829
+ interceptorInstalled = false;
2830
+ LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
2831
+ LOG_DIR_PATH = LOG_DIR;
2832
+ }
2833
+ });
2834
+
2544
2835
  // src/system/load-better-sqlite3.ts
2545
2836
  function loadBetterSqlite3() {
2546
2837
  if (cached2) return cached2;
@@ -3260,297 +3551,6 @@ var init_mesh_ledger = __esm({
3260
3551
  }
3261
3552
  });
3262
3553
 
3263
- // src/logging/async-batch-writer.ts
3264
- var fs3, AsyncBatchWriter;
3265
- var init_async_batch_writer = __esm({
3266
- "src/logging/async-batch-writer.ts"() {
3267
- "use strict";
3268
- fs3 = __toESM(require("fs"));
3269
- AsyncBatchWriter = class {
3270
- // Maps filePath -> string buffer
3271
- static buffers = /* @__PURE__ */ new Map();
3272
- static writePromises = /* @__PURE__ */ new Map();
3273
- static flushTimer = null;
3274
- /**
3275
- * Queues data to be written to a file asynchronously in a batch.
3276
- */
3277
- static write(filePath, data) {
3278
- let buf = this.buffers.get(filePath);
3279
- if (!buf) {
3280
- buf = [];
3281
- this.buffers.set(filePath, buf);
3282
- }
3283
- buf.push(data);
3284
- if (!this.flushTimer) {
3285
- this.flushTimer = setTimeout(() => {
3286
- this.flushTimer = null;
3287
- this.flushAll();
3288
- }, 50);
3289
- }
3290
- }
3291
- static async flushAll() {
3292
- const entries = Array.from(this.buffers.entries());
3293
- this.buffers.clear();
3294
- for (const [filePath, buffer] of entries) {
3295
- const dataToWrite = buffer.join("");
3296
- const doWrite = async () => {
3297
- try {
3298
- const prevPromise = this.writePromises.get(filePath);
3299
- if (prevPromise) await prevPromise;
3300
- await fs3.promises.appendFile(filePath, dataToWrite, { encoding: "utf-8", mode: 384 });
3301
- } catch {
3302
- }
3303
- };
3304
- const writePromise = doWrite();
3305
- this.writePromises.set(filePath, writePromise);
3306
- writePromise.finally(() => {
3307
- if (this.writePromises.get(filePath) === writePromise) {
3308
- this.writePromises.delete(filePath);
3309
- }
3310
- });
3311
- }
3312
- }
3313
- };
3314
- }
3315
- });
3316
-
3317
- // src/logging/logger.ts
3318
- var logger_exports = {};
3319
- __export(logger_exports, {
3320
- LOG: () => LOG,
3321
- LOG_DIR_PATH: () => LOG_DIR_PATH,
3322
- LOG_PATH: () => LOG_PATH,
3323
- daemonLog: () => daemonLog,
3324
- getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
3325
- getDaemonLogDir: () => getDaemonLogDir,
3326
- getLogBufferSize: () => getLogBufferSize,
3327
- getLogLevel: () => getLogLevel,
3328
- getLogPath: () => getLogPath,
3329
- getRecentLogs: () => getRecentLogs,
3330
- installGlobalInterceptor: () => installGlobalInterceptor,
3331
- setLogLevel: () => setLogLevel
3332
- });
3333
- function setLogLevel(level) {
3334
- currentLevel = level;
3335
- daemonLog("Logger", `Log level set to: ${level}`, "info");
3336
- }
3337
- function getLogLevel() {
3338
- return currentLevel;
3339
- }
3340
- function getDateStr() {
3341
- return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3342
- }
3343
- function getDaemonLogDir() {
3344
- return LOG_DIR;
3345
- }
3346
- function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
3347
- return path9.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
3348
- }
3349
- function checkDateRotation() {
3350
- const today = getDateStr();
3351
- if (today !== currentDate) {
3352
- currentDate = today;
3353
- currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
3354
- cleanOldLogs();
3355
- }
3356
- }
3357
- function cleanOldLogs() {
3358
- try {
3359
- const files = fs4.readdirSync(LOG_DIR).filter((f) => f.startsWith("daemon-") && f.endsWith(".log"));
3360
- const cutoff = /* @__PURE__ */ new Date();
3361
- cutoff.setDate(cutoff.getDate() - MAX_LOG_DAYS);
3362
- const cutoffStr = cutoff.toISOString().slice(0, 10);
3363
- for (const file of files) {
3364
- const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
3365
- if (dateMatch && dateMatch[1] < cutoffStr) {
3366
- try {
3367
- fs4.unlinkSync(path9.join(LOG_DIR, file));
3368
- } catch {
3369
- }
3370
- }
3371
- }
3372
- } catch {
3373
- }
3374
- }
3375
- function rotateSizeIfNeeded() {
3376
- try {
3377
- const stat2 = fs4.statSync(currentLogFile);
3378
- if (stat2.size > MAX_LOG_SIZE) {
3379
- const backup = currentLogFile.replace(".log", ".1.log");
3380
- try {
3381
- fs4.unlinkSync(backup);
3382
- } catch {
3383
- }
3384
- fs4.renameSync(currentLogFile, backup);
3385
- }
3386
- } catch {
3387
- }
3388
- }
3389
- function writeToFile(line) {
3390
- try {
3391
- if (++writeCount % 1e3 === 0) {
3392
- checkDateRotation();
3393
- rotateSizeIfNeeded();
3394
- }
3395
- AsyncBatchWriter.write(currentLogFile, line + "\n");
3396
- } catch {
3397
- }
3398
- }
3399
- function getRecentLogs(count = 50, minLevel = "info") {
3400
- const minNum = LEVEL_NUM[minLevel];
3401
- const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
3402
- return filtered.slice(-count);
3403
- }
3404
- function getLogBufferSize() {
3405
- return ringBuffer.length;
3406
- }
3407
- function ts() {
3408
- return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
3409
- }
3410
- function fullTs() {
3411
- return (/* @__PURE__ */ new Date()).toISOString();
3412
- }
3413
- function daemonLog(category, msg, level = "info") {
3414
- const shouldOutput = LEVEL_NUM[level] >= LEVEL_NUM[currentLevel];
3415
- const label = LEVEL_LABEL[level];
3416
- const line = `[${ts()}] [${label}] [${category}] ${msg}`;
3417
- if (!shouldOutput) return;
3418
- writeToFile(line);
3419
- ringBuffer.push({ ts: Date.now(), level, category, message: msg });
3420
- if (ringBuffer.length > RING_BUFFER_SIZE) {
3421
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
3422
- }
3423
- origConsoleLog(line);
3424
- }
3425
- function installGlobalInterceptor() {
3426
- if (interceptorInstalled) return;
3427
- interceptorInstalled = true;
3428
- const stripAnsi4 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
3429
- const isDaemonLogLine = (msg) => /\[(DBG|INF|WRN|ERR)\]/.test(msg);
3430
- console.log = (...args) => {
3431
- origConsoleLog(...args);
3432
- try {
3433
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
3434
- const clean = stripAnsi4(msg);
3435
- if (isDaemonLogLine(clean)) return;
3436
- const line = clean.startsWith("[20") ? clean : `[${fullTs()}] ${clean}`;
3437
- writeToFile(line);
3438
- const catMatch = clean.match(/\[([^\]]+)\]/);
3439
- ringBuffer.push({
3440
- ts: Date.now(),
3441
- level: "info",
3442
- category: catMatch?.[1] || "System",
3443
- message: clean
3444
- });
3445
- if (ringBuffer.length > RING_BUFFER_SIZE) {
3446
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
3447
- }
3448
- } catch {
3449
- }
3450
- };
3451
- console.error = (...args) => {
3452
- origConsoleError(...args);
3453
- try {
3454
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
3455
- const clean = stripAnsi4(msg);
3456
- if (isDaemonLogLine(clean)) return;
3457
- const line = `[${fullTs()}] [ERROR] ${clean}`;
3458
- writeToFile(line);
3459
- ringBuffer.push({ ts: Date.now(), level: "error", category: "System", message: clean });
3460
- if (ringBuffer.length > RING_BUFFER_SIZE) {
3461
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
3462
- }
3463
- } catch {
3464
- }
3465
- };
3466
- console.warn = (...args) => {
3467
- origConsoleWarn(...args);
3468
- try {
3469
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
3470
- const clean = stripAnsi4(msg);
3471
- if (isDaemonLogLine(clean)) return;
3472
- const line = `[${fullTs()}] [WARN] ${clean}`;
3473
- writeToFile(line);
3474
- ringBuffer.push({ ts: Date.now(), level: "warn", category: "System", message: clean });
3475
- if (ringBuffer.length > RING_BUFFER_SIZE) {
3476
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
3477
- }
3478
- } catch {
3479
- }
3480
- };
3481
- writeToFile(`
3482
- === ADHDev Daemon started at ${fullTs()} ===`);
3483
- writeToFile(`Log file: ${currentLogFile}`);
3484
- writeToFile(`Log level: ${currentLevel}`);
3485
- }
3486
- function getLogPath() {
3487
- return currentLogFile;
3488
- }
3489
- var fs4, path9, os3, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
3490
- var init_logger = __esm({
3491
- "src/logging/logger.ts"() {
3492
- "use strict";
3493
- fs4 = __toESM(require("fs"));
3494
- path9 = __toESM(require("path"));
3495
- os3 = __toESM(require("os"));
3496
- init_async_batch_writer();
3497
- LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
3498
- LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
3499
- currentLevel = "info";
3500
- LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os3.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os3.homedir(), "Library", "Logs", "adhdev") : path9.join(os3.homedir(), ".local", "share", "adhdev", "logs");
3501
- MAX_LOG_SIZE = 5 * 1024 * 1024;
3502
- MAX_LOG_DAYS = 7;
3503
- try {
3504
- fs4.mkdirSync(LOG_DIR, { recursive: true });
3505
- } catch {
3506
- }
3507
- currentDate = getDateStr();
3508
- currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
3509
- cleanOldLogs();
3510
- try {
3511
- const oldLog = path9.join(LOG_DIR, "daemon.log");
3512
- if (fs4.existsSync(oldLog)) {
3513
- const stat2 = fs4.statSync(oldLog);
3514
- const oldDate = stat2.mtime.toISOString().slice(0, 10);
3515
- fs4.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
3516
- }
3517
- const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
3518
- if (fs4.existsSync(oldLogBackup)) {
3519
- fs4.unlinkSync(oldLogBackup);
3520
- }
3521
- } catch {
3522
- }
3523
- writeCount = 0;
3524
- RING_BUFFER_SIZE = 200;
3525
- ringBuffer = [];
3526
- origConsoleLog = console.log.bind(console);
3527
- origConsoleError = console.error.bind(console);
3528
- origConsoleWarn = console.warn.bind(console);
3529
- LOG = {
3530
- debug: (category, msg) => daemonLog(category, msg, "debug"),
3531
- info: (category, msg) => daemonLog(category, msg, "info"),
3532
- warn: (category, msg) => daemonLog(category, msg, "warn"),
3533
- error: (category, msg) => daemonLog(category, msg, "error"),
3534
- /**
3535
- * Create a scoped logger for a specific component.
3536
- * Category is baked in so callers only pass the message.
3537
- */
3538
- forComponent(category) {
3539
- return {
3540
- debug: (msg) => daemonLog(category, msg, "debug"),
3541
- info: (msg) => daemonLog(category, msg, "info"),
3542
- warn: (msg) => daemonLog(category, msg, "warn"),
3543
- error: (msg) => daemonLog(category, msg, "error"),
3544
- asLogFn: (level = "info") => (msg) => daemonLog(category, msg, level)
3545
- };
3546
- }
3547
- };
3548
- interceptorInstalled = false;
3549
- LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
3550
- LOG_DIR_PATH = LOG_DIR;
3551
- }
3552
- });
3553
-
3554
3554
  // src/mesh/mesh-work-queue.ts
3555
3555
  var mesh_work_queue_exports = {};
3556
3556
  __export(mesh_work_queue_exports, {
@@ -3867,6 +3867,7 @@ function enqueueTask(meshId, message, opts) {
3867
3867
  requiredTags: resolvedRequiredTags,
3868
3868
  ...dependsOn.length > 0 ? { dependsOn } : {},
3869
3869
  ...typeof opts?.missionId === "string" && opts.missionId.trim() ? { missionId: opts.missionId.trim() } : {},
3870
+ ...typeof opts?.sourceCoordinatorSessionId === "string" && opts.sourceCoordinatorSessionId.trim() ? { sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId.trim() } : {},
3870
3871
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3871
3872
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3872
3873
  };
@@ -4217,19 +4218,29 @@ function meshRuntimeStorePath() {
4217
4218
  (0, import_fs5.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
4218
4219
  }
4219
4220
  }
4220
- } catch {
4221
+ } catch (err) {
4222
+ if (!loggedMigrationFailure) {
4223
+ loggedMigrationFailure = true;
4224
+ LOG.warn(
4225
+ "MeshRuntimeStore",
4226
+ `Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
4227
+ );
4228
+ }
4229
+ return (0, import_fs5.existsSync)(nextPath) ? nextPath : legacyPath;
4221
4230
  }
4222
4231
  return nextPath;
4223
4232
  }
4224
- var import_fs5, import_path5, DatabaseCtor, MeshRuntimeStore;
4233
+ var import_fs5, import_path5, DatabaseCtor, loggedMigrationFailure, MeshRuntimeStore;
4225
4234
  var init_mesh_runtime_store = __esm({
4226
4235
  "src/mesh/mesh-runtime-store.ts"() {
4227
4236
  "use strict";
4228
4237
  import_fs5 = require("fs");
4229
4238
  import_path5 = require("path");
4239
+ init_logger();
4230
4240
  init_load_better_sqlite3();
4231
4241
  init_mesh_ledger();
4232
4242
  init_mesh_work_queue();
4243
+ loggedMigrationFailure = false;
4233
4244
  MeshRuntimeStore = class _MeshRuntimeStore {
4234
4245
  static instance;
4235
4246
  db;
@@ -4251,9 +4262,21 @@ var init_mesh_runtime_store = __esm({
4251
4262
  this.db.pragma("busy_timeout = 5000");
4252
4263
  this.migrate();
4253
4264
  }
4265
+ static loggedGetInstanceFailure = false;
4254
4266
  static getInstance() {
4255
4267
  if (!this.instance) {
4256
- this.instance = new _MeshRuntimeStore(meshRuntimeStorePath());
4268
+ try {
4269
+ this.instance = new _MeshRuntimeStore(meshRuntimeStorePath());
4270
+ } catch (err) {
4271
+ if (!_MeshRuntimeStore.loggedGetInstanceFailure) {
4272
+ _MeshRuntimeStore.loggedGetInstanceFailure = true;
4273
+ LOG.warn(
4274
+ "MeshRuntimeStore",
4275
+ `getInstance failed; callers will degrade to JSONL-only: ${err?.message || err}`
4276
+ );
4277
+ }
4278
+ throw err;
4279
+ }
4257
4280
  }
4258
4281
  return this.instance;
4259
4282
  }
@@ -7846,7 +7869,11 @@ function buildMeshWorkerRelayStamp(currentSettings, meshContext) {
7846
7869
  if (coordinatorDaemonId && !readNonEmptyString2(settings.meshCoordinatorDaemonId)) {
7847
7870
  stamp.meshCoordinatorDaemonId = coordinatorDaemonId;
7848
7871
  }
7849
- if ((meshId || nodeId || coordinatorDaemonId) && settings.launchedByCoordinator !== true) {
7872
+ const coordinatorSessionId = readNonEmptyString2(meshContext.coordinatorSessionId);
7873
+ if (coordinatorSessionId && !readNonEmptyString2(settings.meshCoordinatorSessionId)) {
7874
+ stamp.meshCoordinatorSessionId = coordinatorSessionId;
7875
+ }
7876
+ if ((meshId || nodeId || coordinatorDaemonId || coordinatorSessionId) && settings.launchedByCoordinator !== true) {
7850
7877
  stamp.launchedByCoordinator = true;
7851
7878
  }
7852
7879
  return Object.keys(stamp).length > 0 ? stamp : void 0;
@@ -7863,10 +7890,13 @@ function readRefineJobId(event) {
7863
7890
  function readWorkerResultMetadata(event) {
7864
7891
  return readRecord4(event.workerResult) || readRecord4(event.meshWorkerResult) || readRecord4(event.structuredResult);
7865
7892
  }
7866
- function resolveMeshSurfacedSessionPreview(metadataEvent) {
7893
+ function readMeshCompletionSummary(metadataEvent) {
7867
7894
  const workerResult = readWorkerResultMetadata(metadataEvent);
7868
7895
  const resultRecord = readRecord4(metadataEvent.result);
7869
- const summaryText = readNonEmptyString2(metadataEvent.finalSummary) || readNonEmptyString2(workerResult?.summary) || readNonEmptyString2(workerResult?.finalSummary) || readNonEmptyString2(resultRecord?.summary) || readNonEmptyString2(resultRecord?.finalSummary);
7896
+ return readNonEmptyString2(metadataEvent.finalSummary) || readNonEmptyString2(workerResult?.summary) || readNonEmptyString2(workerResult?.finalSummary) || readNonEmptyString2(resultRecord?.summary) || readNonEmptyString2(resultRecord?.finalSummary);
7897
+ }
7898
+ function resolveMeshSurfacedSessionPreview(metadataEvent) {
7899
+ const summaryText = readMeshCompletionSummary(metadataEvent);
7870
7900
  if (!summaryText) return void 0;
7871
7901
  const truncationSuffix = "...[truncated]";
7872
7902
  const preview = summaryText.length > MESH_SURFACED_PREVIEW_MAX_CHARS ? `${summaryText.slice(0, MESH_SURFACED_PREVIEW_MAX_CHARS - truncationSuffix.length)}${truncationSuffix}` : summaryText;
@@ -7904,7 +7934,18 @@ function buildMeshSystemMessage(args) {
7904
7934
  if (args.metadataEvent.source === "no_progress_reconciliation") {
7905
7935
  return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The no-progress monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
7906
7936
  }
7907
- const reviewNote = args.metadataEvent.reviewRecommended === true ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly." : " Use mesh_read_chat once to review its final progress, but do not poll repeatedly.";
7937
+ const reviewRecommended = args.metadataEvent.reviewRecommended === true;
7938
+ const completionSummary = readMeshCompletionSummary(args.metadataEvent);
7939
+ if (completionSummary) {
7940
+ const truncationSuffix = "\n\u2026[truncated \u2014 call mesh_read_chat once for the full transcript]";
7941
+ const surfaced = completionSummary.length > MESH_COMPLETION_SURFACE_MAX_CHARS ? `${completionSummary.slice(0, MESH_COMPLETION_SURFACE_MAX_CHARS - truncationSuffix.length)}${truncationSuffix}` : completionSummary;
7942
+ const verifyNote = reviewRecommended ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done." : "";
7943
+ return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. Its final summary is included below \u2014 read it directly and only call mesh_read_chat if you need the full transcript.${verifyNote}
7944
+
7945
+ --- ${args.nodeLabel} final summary ---
7946
+ ${surfaced}`;
7947
+ }
7948
+ const reviewNote = reviewRecommended ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly." : " Use mesh_read_chat once to review its final progress, but do not poll repeatedly.";
7908
7949
  return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
7909
7950
  }
7910
7951
  if (args.event === "agent:waiting_approval") {
@@ -8012,11 +8053,12 @@ Next step: ${nextStep}`;
8012
8053
  }
8013
8054
  return "";
8014
8055
  }
8015
- var MESH_SURFACED_PREVIEW_MAX_CHARS;
8056
+ var MESH_SURFACED_PREVIEW_MAX_CHARS, MESH_COMPLETION_SURFACE_MAX_CHARS;
8016
8057
  var init_mesh_events_utils = __esm({
8017
8058
  "src/mesh/mesh-events-utils.ts"() {
8018
8059
  "use strict";
8019
8060
  MESH_SURFACED_PREVIEW_MAX_CHARS = 512;
8061
+ MESH_COMPLETION_SURFACE_MAX_CHARS = 4e3;
8020
8062
  }
8021
8063
  });
8022
8064
 
@@ -8174,6 +8216,37 @@ function trimPendingEventsIfNeeded(path42) {
8174
8216
  if ((0, import_fs10.statSync)(path42).size <= MAX_PENDING_EVENTS_BYTES) return;
8175
8217
  const lines = (0, import_fs10.readFileSync)(path42, "utf-8").split("\n").filter(Boolean);
8176
8218
  if (lines.length <= MAX_PENDING_EVENTS_KEEP) return;
8219
+ const dropped = lines.slice(0, lines.length - MAX_PENDING_EVENTS_KEEP);
8220
+ for (const line of dropped) {
8221
+ let event;
8222
+ try {
8223
+ event = JSON.parse(line);
8224
+ } catch {
8225
+ continue;
8226
+ }
8227
+ if (!event || !event.meshId) continue;
8228
+ const finalSummary = readMeshCompletionSummary(event.metadataEvent || {});
8229
+ if (!readNonEmptyString2(event.coordinatorMessage) && !finalSummary) continue;
8230
+ try {
8231
+ appendLedgerEntry(event.meshId, {
8232
+ kind: "event_held",
8233
+ ...event.nodeId ? { nodeId: event.nodeId } : {},
8234
+ payload: {
8235
+ event: event.event,
8236
+ reason: "pending_trim_dropped",
8237
+ recoverable: true,
8238
+ nodeLabel: event.nodeLabel,
8239
+ ...event.workspace ? { workspace: event.workspace } : {},
8240
+ targetCoordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
8241
+ queuedAt: event.queuedAt,
8242
+ ...finalSummary ? { finalSummary } : {}
8243
+ }
8244
+ });
8245
+ LOG.warn("MeshEvents", `Pending-events trim dropping undelivered ${event.event} for mesh ${event.meshId} \u2014 recorded to ledger (recoverable)`);
8246
+ } catch (e) {
8247
+ LOG.warn("MeshEvents", `Failed to ledger-record trim-dropped ${event.event} for mesh ${event.meshId}: ${e?.message || e}`);
8248
+ }
8249
+ }
8177
8250
  (0, import_fs10.writeFileSync)(path42, lines.slice(-MAX_PENDING_EVENTS_KEEP).join("\n") + "\n", "utf-8");
8178
8251
  } catch {
8179
8252
  }
@@ -8303,7 +8376,8 @@ function drainPendingMeshCoordinatorEvents(meshId, coordinatorDaemonId, opts) {
8303
8376
  if (event) pushUnique(event);
8304
8377
  }
8305
8378
  }
8306
- } catch {
8379
+ } catch (e) {
8380
+ LOG.warn("MeshEvents", `SQLite pending-event drain failed for mesh ${meshId}; JSONL fallback only: ${e?.message || e}`);
8307
8381
  }
8308
8382
  const paths = primaryDaemonId ? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
8309
8383
  for (const path42 of paths) {
@@ -9718,6 +9792,8 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
9718
9792
  if (node?.daemonId && components.dispatchMeshCommand) {
9719
9793
  const isLocalNode = components.cliManager.adapters.has(sessionId);
9720
9794
  if (!isLocalNode) {
9795
+ const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
9796
+ const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
9721
9797
  const delivery2 = createSessionDelivery({
9722
9798
  meshId,
9723
9799
  nodeId,
@@ -9726,9 +9802,10 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
9726
9802
  taskId: task.id,
9727
9803
  kind: "task",
9728
9804
  message: task.message,
9729
- status: "delivering"
9805
+ status: "delivering",
9806
+ ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
9807
+ ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
9730
9808
  });
9731
- const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
9732
9809
  components.dispatchMeshCommand(node.daemonId, "agent_command", {
9733
9810
  targetSessionId: sessionId,
9734
9811
  cliType: providerType,
@@ -9738,7 +9815,8 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
9738
9815
  meshId,
9739
9816
  nodeId,
9740
9817
  taskId: task.id,
9741
- ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {}
9818
+ ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
9819
+ ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
9742
9820
  }
9743
9821
  }).then(() => {
9744
9822
  updateSessionDeliveryStatus(delivery2.id, "delivered");
@@ -9763,12 +9841,16 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
9763
9841
  const inst = components.instanceManager.getInstance(sessionId);
9764
9842
  if (inst && typeof inst.updateSettings === "function") {
9765
9843
  const localDaemonId = readNonEmptyString2(loadConfig().machineId);
9844
+ const localSourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId);
9766
9845
  inst.updateSettings({
9767
9846
  meshNodeFor: meshId,
9768
9847
  meshNodeId: nodeId,
9769
9848
  launchedByCoordinator: true,
9770
9849
  autoApprove: resolveDelegatedWorkerAutoApprove(mesh?.policy, node?.policy),
9771
- ...localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {}
9850
+ ...localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {},
9851
+ // (3) Stamp the originating coordinator session for session-anchored routing
9852
+ // of this co-located worker's completion. Absent → daemon-level fallback.
9853
+ ...localSourceCoordinatorSessionId ? { meshCoordinatorSessionId: localSourceCoordinatorSessionId } : {}
9772
9854
  });
9773
9855
  }
9774
9856
  } catch {
@@ -9781,7 +9863,9 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
9781
9863
  taskId: task.id,
9782
9864
  kind: "task",
9783
9865
  message: task.message,
9784
- status: "delivering"
9866
+ status: "delivering",
9867
+ ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
9868
+ ...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
9785
9869
  });
9786
9870
  components.cliManager.handleCliCommand("agent_command", {
9787
9871
  targetSessionId: sessionId,
@@ -9793,7 +9877,16 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
9793
9877
  }).catch((e) => {
9794
9878
  LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
9795
9879
  updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
9796
- updateTaskStatus(meshId, task.id, "failed");
9880
+ updateTaskStatus(meshId, task.id, "pending");
9881
+ try {
9882
+ appendLedgerEntry(meshId, {
9883
+ kind: "dispatch_failed",
9884
+ nodeId,
9885
+ sessionId,
9886
+ payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
9887
+ });
9888
+ } catch {
9889
+ }
9797
9890
  });
9798
9891
  return true;
9799
9892
  }
@@ -10422,6 +10515,9 @@ function injectMeshSystemMessage(components, args) {
10422
10515
  const workerCoordinatorDaemonId = readNonEmptyString2(
10423
10516
  sourceSession?.getState()?.settings?.meshCoordinatorDaemonId
10424
10517
  );
10518
+ const workerCoordinatorSessionId = readNonEmptyString2(
10519
+ sourceSession?.getState()?.settings?.meshCoordinatorSessionId
10520
+ ) || readNonEmptyString2(args.metadataEvent.meshCoordinatorSessionId);
10425
10521
  if (components.onMeshCoordinatorEventForwarded) {
10426
10522
  try {
10427
10523
  const surfacedPreview = resolveMeshSurfacedSessionPreview(args.metadataEvent);
@@ -10802,7 +10898,12 @@ function injectMeshSystemMessage(components, args) {
10802
10898
  workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName),
10803
10899
  metadataEvent: {
10804
10900
  ...args.metadataEvent,
10805
- ...recoveryContext ? { recoveryContext } : {}
10901
+ ...recoveryContext ? { recoveryContext } : {},
10902
+ // Stash the coordinator session id INSIDE metadataEvent too, so it survives the
10903
+ // P2P relay serialization (buildForwardPayloadFromPending spreads metadata; the
10904
+ // handleMeshForwardEvent whitelist reads it back) — a top-level field alone would
10905
+ // be dropped when the event crosses a machine boundary.
10906
+ ...workerCoordinatorSessionId ? { meshCoordinatorSessionId: workerCoordinatorSessionId } : {}
10806
10907
  },
10807
10908
  // Silent lifecycle events (agent:ready / agent:generating_started) carry no
10808
10909
  // coordinator message; they are queued only so the coordinator re-runs the
@@ -10810,10 +10911,13 @@ function injectMeshSystemMessage(components, args) {
10810
10911
  // entries without a coordinatorMessage, so a live CLI coordinator is not spammed.
10811
10912
  ...messageText ? { coordinatorMessage: messageText } : {},
10812
10913
  queuedAt: Date.now(),
10813
- ...workerCoordinatorDaemonId ? { targetCoordinatorDaemonId: workerCoordinatorDaemonId } : {}
10914
+ ...workerCoordinatorDaemonId ? { targetCoordinatorDaemonId: workerCoordinatorDaemonId } : {},
10915
+ // Top-level session anchor for the local PHASE 2 strict-match on the coordinator
10916
+ // daemon. Absent → daemon-level broadcast (legacy / single-coordinator path).
10917
+ ...workerCoordinatorSessionId ? { targetCoordinatorSessionId: workerCoordinatorSessionId } : {}
10814
10918
  };
10815
10919
  if (queuePendingMeshCoordinatorEvent(pendingEvent)) {
10816
- LOG.info("MeshEvents", `Queued ${args.event} for coordinator (mesh ${args.meshId}${workerCoordinatorDaemonId ? `, coordinator daemon ${workerCoordinatorDaemonId}` : ""})`);
10920
+ LOG.info("MeshEvents", `Queued ${args.event} for coordinator (mesh ${args.meshId}${workerCoordinatorDaemonId ? `, coordinator daemon ${workerCoordinatorDaemonId}` : ""}${workerCoordinatorSessionId ? `, coordinator session ${workerCoordinatorSessionId}` : ""})`);
10817
10921
  }
10818
10922
  return { success: true, forwarded: 0 };
10819
10923
  }
@@ -10838,6 +10942,13 @@ function handleMeshForwardEvent(components, payload) {
10838
10942
  targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
10839
10943
  providerType: readNonEmptyString2(payload.providerType),
10840
10944
  providerSessionId: readNonEmptyString2(payload.providerSessionId),
10945
+ // Preserve the originating coordinator SESSION id across the machine boundary so
10946
+ // the completion routes back to the exact coordinator session (multi-coordinator).
10947
+ // buildForwardPayloadFromPending spreads the worker event's metadata, so the id
10948
+ // arrives as payload.meshCoordinatorSessionId; the top-level targetCoordinatorSessionId
10949
+ // is also accepted as a fallback. injectMeshSystemMessage re-derives the routing
10950
+ // anchors from this. Absent → daemon-level fallback (version-skew safe).
10951
+ meshCoordinatorSessionId: readNonEmptyString2(payload.meshCoordinatorSessionId) || readNonEmptyString2(payload.targetCoordinatorSessionId),
10841
10952
  // Carry the session identity fields the worker provider event emits so the
10842
10953
  // coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
10843
10954
  // settings. Without these the remote-relay hop reconstructs metadataEvent with
@@ -11734,7 +11845,8 @@ function findLiveCoordinators(components) {
11734
11845
  if (!meshId) continue;
11735
11846
  const status = readNonEmptyString2(state.status).toLowerCase();
11736
11847
  const modalParked = status === "waiting_choice" || status === "waiting_approval";
11737
- out.push({ meshId, instance: inst, idle: status === "idle", modalParked });
11848
+ const sessionId = readNonEmptyString2(state.instanceId);
11849
+ out.push({ meshId, instance: inst, sessionId, idle: status === "idle", modalParked });
11738
11850
  }
11739
11851
  return out;
11740
11852
  }
@@ -11746,6 +11858,44 @@ function injectPendingIntoCoordinator(coordinator, pending) {
11746
11858
  ...force ? { force: true } : {}
11747
11859
  });
11748
11860
  }
11861
+ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldForCoordinatorCount) {
11862
+ let pending;
11863
+ try {
11864
+ pending = getPendingMeshCoordinatorEvents(meshId, drainDaemonIds.length > 0 ? drainDaemonIds : void 0);
11865
+ } catch {
11866
+ return;
11867
+ }
11868
+ for (const event of pending) {
11869
+ if (!shouldForceInjectMeshEvent(event.event)) continue;
11870
+ const fingerprint = buildPendingEventFingerprint(event);
11871
+ const key = `${meshId}::${fingerprint || `${event.event}::${event.nodeId || ""}::${event.queuedAt}`}`;
11872
+ if (heldEventLedgerRecorded.has(key)) continue;
11873
+ heldEventLedgerRecorded.add(key);
11874
+ const finalSummary = readMeshCompletionSummary(event.metadataEvent);
11875
+ try {
11876
+ appendLedgerEntry(meshId, {
11877
+ kind: "event_held",
11878
+ ...event.nodeId ? { nodeId: event.nodeId } : {},
11879
+ payload: {
11880
+ event: event.event,
11881
+ reason,
11882
+ recoverable: true,
11883
+ heldForCoordinators: heldForCoordinatorCount,
11884
+ nodeLabel: event.nodeLabel,
11885
+ ...event.workspace ? { workspace: event.workspace } : {},
11886
+ targetCoordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
11887
+ queuedAt: event.queuedAt,
11888
+ ...fingerprint ? { fingerprint } : {},
11889
+ ...finalSummary ? { finalSummary } : {}
11890
+ }
11891
+ });
11892
+ LOG.info("MeshReconcile", `Ledger-recorded held ${event.event} for mesh ${meshId} (reason ${reason}) \u2014 recoverable from ledger`);
11893
+ } catch (e) {
11894
+ heldEventLedgerRecorded.delete(key);
11895
+ LOG.warn("MeshReconcile", `Failed to ledger-record held ${event.event} for mesh ${meshId}: ${e?.message || e}`);
11896
+ }
11897
+ }
11898
+ }
11749
11899
  async function runMeshReconcileTick(components) {
11750
11900
  const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
11751
11901
  const drainDaemonIds = resolveCoordinatorDaemonIds(components);
@@ -11830,6 +11980,21 @@ async function runMeshReconcileTick(components) {
11830
11980
  if (targetCoordinators.length === 0) {
11831
11981
  if (modalParkedCoordinators.length > 0) {
11832
11982
  LOG.info("MeshReconcile", `Reconcile skip \u2192 modal-parked: holding pending event(s) for mesh ${meshId} (${modalParkedCoordinators.length} coordinator(s) awaiting a modal answer; events left queued)`);
11983
+ let hasPending = true;
11984
+ if (store) {
11985
+ try {
11986
+ hasPending = store.pendingEventCount(meshId) > 0;
11987
+ } catch {
11988
+ }
11989
+ }
11990
+ if (hasPending) {
11991
+ recordHeldTerminalEventsToLedger(
11992
+ meshId,
11993
+ drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId ? [localDaemonId] : [],
11994
+ "modal_parked",
11995
+ modalParkedCoordinators.length
11996
+ );
11997
+ }
11833
11998
  }
11834
11999
  continue;
11835
12000
  }
@@ -11854,12 +12019,55 @@ async function runMeshReconcileTick(components) {
11854
12019
  const mode = forceOnly ? "force-drain \u2192 generating" : "inject \u2192 idle";
11855
12020
  LOG.info("MeshReconcile", `Reconcile ${mode}: ${pendingEvents.length} pending event(s) \u2192 ${targetCoordinators.length} coordinator(s) for mesh ${meshId}`);
11856
12021
  for (const pending of pendingEvents) {
12022
+ const wantSession = readNonEmptyString2(pending.targetCoordinatorSessionId);
12023
+ if (wantSession) {
12024
+ const matched = targetCoordinators.filter((c) => c.sessionId === wantSession);
12025
+ if (matched.length === 0) {
12026
+ holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId);
12027
+ continue;
12028
+ }
12029
+ for (const c of matched) injectPendingIntoCoordinator(c.instance, pending);
12030
+ continue;
12031
+ }
11857
12032
  for (const c of targetCoordinators) {
11858
12033
  injectPendingIntoCoordinator(c.instance, pending);
11859
12034
  }
11860
12035
  }
11861
12036
  }
11862
12037
  }
12038
+ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
12039
+ const queuedAt = typeof pending.queuedAt === "number" ? pending.queuedAt : Date.now();
12040
+ if (Date.now() - queuedAt <= STRICT_SESSION_MATCH_TTL_MS) {
12041
+ try {
12042
+ queuePendingMeshCoordinatorEvent(pending);
12043
+ LOG.info("MeshReconcile", `Strict route hold: coordinator session ${wantSession} not live on mesh ${meshId} \u2014 re-queued (${pending.event})`);
12044
+ } catch (e) {
12045
+ LOG.warn("MeshReconcile", `Strict route re-queue failed for ${pending.event} on mesh ${meshId}: ${e?.message || e}`);
12046
+ }
12047
+ return;
12048
+ }
12049
+ const finalSummary = readMeshCompletionSummary(pending.metadataEvent || {});
12050
+ try {
12051
+ appendLedgerEntry(meshId, {
12052
+ kind: "event_held",
12053
+ ...pending.nodeId ? { nodeId: pending.nodeId } : {},
12054
+ payload: {
12055
+ event: pending.event,
12056
+ reason: "strict_route_expired",
12057
+ recoverable: true,
12058
+ targetCoordinatorSessionId: wantSession,
12059
+ targetCoordinatorDaemonId: pending.targetCoordinatorDaemonId ?? null,
12060
+ nodeLabel: pending.nodeLabel,
12061
+ ...pending.workspace ? { workspace: pending.workspace } : {},
12062
+ queuedAt,
12063
+ ...finalSummary ? { finalSummary } : {}
12064
+ }
12065
+ });
12066
+ LOG.warn("MeshReconcile", `Strict route expire: coordinator session ${wantSession} never returned for mesh ${meshId} \u2014 recorded to ledger (recoverable), dropped (${pending.event})`);
12067
+ } catch (e) {
12068
+ LOG.warn("MeshReconcile", `Failed to ledger-expire strict-unmatched ${pending.event} for mesh ${meshId}: ${e?.message || e}`);
12069
+ }
12070
+ }
11863
12071
  async function retryUnresolvedDelegateForwards(components) {
11864
12072
  const dispatchMeshCommand = components.dispatchMeshCommand;
11865
12073
  if (!dispatchMeshCommand) return;
@@ -12077,6 +12285,11 @@ function buildForwardPayloadFromPending(event) {
12077
12285
  meshId: readNonEmptyString2(event?.meshId),
12078
12286
  nodeId: readNonEmptyString2(event?.nodeId) || readNonEmptyString2(metadata.meshNodeId),
12079
12287
  workspace: readNonEmptyString2(event?.workspace) || readNonEmptyString2(metadata.workspace),
12288
+ // Preserve the originating coordinator session id across the relay. It is normally
12289
+ // carried inside metadataEvent.meshCoordinatorSessionId (spread below), but pass the
12290
+ // top-level field through explicitly too so the handleMeshForwardEvent whitelist
12291
+ // recovers it regardless of which carrier the producing daemon used.
12292
+ ...readNonEmptyString2(event?.targetCoordinatorSessionId) ? { targetCoordinatorSessionId: readNonEmptyString2(event.targetCoordinatorSessionId) } : {},
12080
12293
  ...metadata
12081
12294
  };
12082
12295
  }
@@ -12099,7 +12312,7 @@ function setupMeshReconcileLoop(components) {
12099
12312
  }
12100
12313
  };
12101
12314
  }
12102
- var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
12315
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, STRICT_SESSION_MATCH_TTL_MS;
12103
12316
  var init_mesh_reconcile_loop = __esm({
12104
12317
  "src/mesh/mesh-reconcile-loop.ts"() {
12105
12318
  "use strict";
@@ -12107,6 +12320,7 @@ var init_mesh_reconcile_loop = __esm({
12107
12320
  init_mesh_config();
12108
12321
  init_logger();
12109
12322
  init_mesh_events_pending();
12323
+ init_mesh_ledger();
12110
12324
  init_mesh_runtime_store();
12111
12325
  init_mesh_events_coordinator();
12112
12326
  init_mesh_unresolved_forward_outbox();
@@ -12118,6 +12332,8 @@ var init_mesh_reconcile_loop = __esm({
12118
12332
  init_chat_message_normalization();
12119
12333
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
12120
12334
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
12335
+ heldEventLedgerRecorded = /* @__PURE__ */ new Set();
12336
+ STRICT_SESSION_MATCH_TTL_MS = 6e4;
12121
12337
  }
12122
12338
  });
12123
12339
 
@@ -31983,7 +32199,8 @@ function countNewlines(s) {
31983
32199
  return n;
31984
32200
  }
31985
32201
  var SUBMIT_DELAY_FLOOR_MS = 200;
31986
- var WIN32_SUBMIT_REPEAT_GAP_MS = 300;
32202
+ var WIN32_SUBMIT_RESEND_GAP_MS = 350;
32203
+ var WIN32_SUBMIT_MAX_RESENDS = 14;
31987
32204
  function resolveSubmitDelayMs(specBeforeSubmit, text) {
31988
32205
  const lines = countNewlines(text);
31989
32206
  const linesBonus = Math.min(800, lines * 80);
@@ -32035,6 +32252,10 @@ var FsmDriver = class {
32035
32252
  * after a re-prime we don't re-inject until the screen changes (which
32036
32253
  * resets the stall reference) or another full stall window lapses. */
32037
32254
  lastRefocusAt = 0;
32255
+ /** Timer driving the win32 verification-based submit resend loop (see
32256
+ * WIN32_SUBMIT_* and scheduleWin32Submit). Re-arms itself until the FSM
32257
+ * leaves idle (submitted) or the resend budget is spent. */
32258
+ win32SubmitTimer = null;
32038
32259
  currentEval = null;
32039
32260
  stateHistory = [];
32040
32261
  prevStateAt = 0;
@@ -32151,6 +32372,10 @@ var FsmDriver = class {
32151
32372
  clearTimeout(this.stallTimer);
32152
32373
  this.stallTimer = null;
32153
32374
  }
32375
+ if (this.win32SubmitTimer) {
32376
+ clearTimeout(this.win32SubmitTimer);
32377
+ this.win32SubmitTimer = null;
32378
+ }
32154
32379
  this.specWatcher?.close();
32155
32380
  this.adapter.kill();
32156
32381
  }
@@ -32563,13 +32788,8 @@ var FsmDriver = class {
32563
32788
  const perChar = sm.delay_ms_per_char ?? 0;
32564
32789
  const beforeSubmit = resolveSubmitDelayMs(sm.delay_ms_before_submit, text);
32565
32790
  if (process.platform === "win32") {
32566
- const submitTwice = () => {
32567
- this.adapter.send_keys(sm.submit_key);
32568
- setTimeout(() => this.adapter.send_keys(sm.submit_key), WIN32_SUBMIT_REPEAT_GAP_MS);
32569
- };
32570
32791
  this.adapter.send_keys(text);
32571
- if (beforeSubmit > 0) setTimeout(submitTwice, beforeSubmit);
32572
- else submitTwice();
32792
+ this.scheduleWin32Submit(sm.submit_key, beforeSubmit);
32573
32793
  return;
32574
32794
  }
32575
32795
  if (perChar === 0) {
@@ -32589,6 +32809,40 @@ var FsmDriver = class {
32589
32809
  i += 1;
32590
32810
  }, perChar);
32591
32811
  }
32812
+ /** The agent's current coarse status, derived from the FSM node we're in. */
32813
+ currentStatus() {
32814
+ const st = stateById(this.spec, this.currentStateId);
32815
+ return st ? statusForState(st) : "idle";
32816
+ }
32817
+ /**
32818
+ * win32 verification-based submit. Sends the submit key, waits a gap, and if
32819
+ * the FSM is still 'idle' (the prompt did not submit — the CR was absorbed as
32820
+ * a multiline-paste newline) resends, up to WIN32_SUBMIT_MAX_RESENDS. The
32821
+ * first CR always fires (so a stale/edge status never suppresses the submit);
32822
+ * subsequent resends are gated on still being idle, and stop the instant the
32823
+ * agent leaves idle (submitted → generating / approval). This converges the
32824
+ * nondeterministic multiline window without spamming Enter into the next turn.
32825
+ */
32826
+ scheduleWin32Submit(submitKey, initialDelayMs) {
32827
+ if (this.win32SubmitTimer) {
32828
+ clearTimeout(this.win32SubmitTimer);
32829
+ this.win32SubmitTimer = null;
32830
+ }
32831
+ const fire = (attempt) => {
32832
+ this.win32SubmitTimer = null;
32833
+ this.adapter.send_keys(submitKey);
32834
+ if (attempt + 1 >= WIN32_SUBMIT_MAX_RESENDS) return;
32835
+ this.win32SubmitTimer = setTimeout(() => {
32836
+ if (this.currentStatus() !== "idle") {
32837
+ this.win32SubmitTimer = null;
32838
+ return;
32839
+ }
32840
+ fire(attempt + 1);
32841
+ }, WIN32_SUBMIT_RESEND_GAP_MS);
32842
+ };
32843
+ if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(() => fire(0), initialDelayMs);
32844
+ else fire(0);
32845
+ }
32592
32846
  handleClickControl(controlId, payload) {
32593
32847
  const ctl = (this.spec.control_bar ?? []).find((c) => c.id === controlId);
32594
32848
  if (!ctl) return;
@@ -35012,7 +35266,10 @@ var CliProviderInstance = class _CliProviderInstance {
35012
35266
  meshNodeFor: assignment.meshId,
35013
35267
  ...assignment.nodeId ? { meshNodeId: assignment.nodeId } : {},
35014
35268
  ...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
35015
- ...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {}
35269
+ ...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {},
35270
+ // Session-level routing anchor: the originating coordinator session, so this
35271
+ // worker's completion events route back to the exact session that dispatched it.
35272
+ ...assignment.coordinatorSessionId ? { meshCoordinatorSessionId: assignment.coordinatorSessionId } : {}
35016
35273
  };
35017
35274
  this.adapter.updateRuntimeSettings?.(this.settings);
35018
35275
  }
@@ -37996,6 +38253,12 @@ Enable and detect this provider from the Machine Providers page before starting
37996
38253
  );
37997
38254
  }
37998
38255
  const key = crypto5.randomUUID();
38256
+ {
38257
+ const coordinatorMeshId = options?.settingsOverride?.meshCoordinatorFor;
38258
+ if (typeof coordinatorMeshId === "string" && coordinatorMeshId.trim()) {
38259
+ options = { ...options, extraEnv: { ...options?.extraEnv || {}, ADHDEV_COORDINATOR_SESSION_ID: key } };
38260
+ }
38261
+ }
37999
38262
  const sessionRegistry = this.deps.getSessionRegistry?.() || null;
38000
38263
  if (provider && provider.category === "acp") {
38001
38264
  const instanceManager2 = this.deps.getInstanceManager();
@@ -44410,6 +44673,40 @@ function readCachedInlineMeshActiveSessions(node) {
44410
44673
  const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
44411
44674
  return sessionId ? [sessionId] : [];
44412
44675
  }
44676
+ function collectMeshNodeHostedSessionIds(node) {
44677
+ const ids = /* @__PURE__ */ new Set();
44678
+ for (const id of readCachedInlineMeshActiveSessions(node)) ids.add(id);
44679
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
44680
+ for (const value of [
44681
+ node?.activeSessions,
44682
+ node?.active_sessions,
44683
+ node?.activeSessionDetails,
44684
+ node?.active_session_details,
44685
+ node?.sessions,
44686
+ node?.sessionDetails,
44687
+ node?.session_details,
44688
+ readObjectRecord(node?.lastProbe).sessions,
44689
+ readObjectRecord(node?.last_probe).sessions,
44690
+ cachedStatus.activeSessions,
44691
+ cachedStatus.active_sessions,
44692
+ cachedStatus.activeSessionDetails,
44693
+ cachedStatus.active_session_details,
44694
+ cachedStatus.sessions
44695
+ ]) {
44696
+ if (!Array.isArray(value)) continue;
44697
+ for (const item of value) {
44698
+ if (typeof item === "string") {
44699
+ const id2 = readStringValue(item);
44700
+ if (id2) ids.add(id2);
44701
+ continue;
44702
+ }
44703
+ const record = readObjectRecord(item);
44704
+ const id = readStringValue(record.id, record.sessionId, record.session_id, record.runtimeSessionId, record.instanceId);
44705
+ if (id) ids.add(id);
44706
+ }
44707
+ }
44708
+ return ids;
44709
+ }
44413
44710
  function resolveMeshNodeAttribution(node) {
44414
44711
  const record = readObjectRecord(node);
44415
44712
  return {
@@ -46288,25 +46585,47 @@ var DaemonCommandRouter = class {
46288
46585
  * controlbar commands do not — so the controlbar buttons appear to do nothing.
46289
46586
  *
46290
46587
  * Mirror the existing node-level remote-forward pattern (fast_forward_mesh_node etc.):
46291
- * scan the cached inline-mesh nodes for the one whose active session matches the
46292
- * targetSessionId, and return its daemonId when that daemonId is a remote daemon (i.e.
46293
- * not this coordinator's own statusInstanceId). Returns undefined for a locally-hosted
46294
- * session (no forward — execute locally as before) or when ownership can't be resolved.
46588
+ * scan the candidate mesh nodes for the one hosting the targetSessionId, and return its
46589
+ * daemonId when that daemonId is a remote daemon (i.e. not this coordinator's own
46590
+ * statusInstanceId). Returns undefined for a locally-hosted session (no forward — execute
46591
+ * locally as before) or when ownership can't be resolved.
46592
+ *
46593
+ * The candidate set spans BOTH the cached inline-mesh nodes and the live aggregate
46594
+ * mesh-status snapshots. The inline cache reliably carries only each node's single primary
46595
+ * session (cachedStatus.activeSession), so a worker hosting more than one session exposes its
46596
+ * non-primary sessions only on the aggregate snapshot nodes (status.activeSessions /
46597
+ * activeSessionDetails, built from live session records). collectMeshNodeHostedSessionIds does
46598
+ * the wider plural-shape scan so a controlbar/modal command targeting a non-primary remote
46599
+ * session still resolves its owner — the singular readCachedInlineMeshActiveSessions semantics
46600
+ * other consumers depend on stay untouched.
46295
46601
  */
46296
46602
  resolveRemoteMeshSessionOwnerDaemonId(sessionId) {
46297
46603
  const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
46298
46604
  if (!trimmed) return void 0;
46299
46605
  const selfDaemonId = this.deps.statusInstanceId;
46300
- for (const node of this.getCachedInlineMeshNodes()) {
46301
- const nodeSessions = readCachedInlineMeshActiveSessions(node);
46302
- if (!nodeSessions.includes(trimmed)) continue;
46606
+ for (const node of this.collectMeshSessionOwnerCandidateNodes()) {
46607
+ if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
46303
46608
  const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
46304
- if (!nodeDaemonId) return void 0;
46609
+ if (!nodeDaemonId) continue;
46305
46610
  if (selfDaemonId && nodeDaemonId === selfDaemonId) return void 0;
46306
46611
  return nodeDaemonId;
46307
46612
  }
46308
46613
  return void 0;
46309
46614
  }
46615
+ /**
46616
+ * Candidate nodes for remote-session owner resolution: the cached inline-mesh nodes (which
46617
+ * carry each node's primary session) plus the nodes from every cached aggregate mesh-status
46618
+ * snapshot (which carry each node's full live session list). getCachedInlineMeshNodes()
46619
+ * returns a fresh array, so appending the aggregate nodes never mutates cached state.
46620
+ */
46621
+ collectMeshSessionOwnerCandidateNodes() {
46622
+ const nodes = this.getCachedInlineMeshNodes();
46623
+ for (const cached3 of this.aggregateMeshStatusCache.values()) {
46624
+ const snapshotNodes = cached3?.snapshot?.nodes;
46625
+ if (Array.isArray(snapshotNodes)) nodes.push(...snapshotNodes);
46626
+ }
46627
+ return nodes;
46628
+ }
46310
46629
  getCachedInlineMesh(meshId, inlineMesh) {
46311
46630
  if (inlineMesh && typeof inlineMesh === "object") {
46312
46631
  return this.warmInlineMeshCache(meshId, inlineMesh);
@@ -48326,7 +48645,10 @@ ${hintLines.join("\n")}` : "",
48326
48645
  {
48327
48646
  meshId: dispatchMeshContext.meshId,
48328
48647
  nodeId: dispatchMeshContext.nodeId,
48329
- coordinatorDaemonId: dispatchMeshContext.coordinatorDaemonId
48648
+ coordinatorDaemonId: dispatchMeshContext.coordinatorDaemonId,
48649
+ // Session-level anchor: preserved across the P2P dispatch to a
48650
+ // remote worker so its completion echoes back to the right session.
48651
+ coordinatorSessionId: dispatchMeshContext.coordinatorSessionId
48330
48652
  }
48331
48653
  );
48332
48654
  if (stamp) inst.updateSettings(stamp);