@adhdev/daemon-core 0.9.82-rc.343 → 0.9.82-rc.345
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/commands/router.d.ts +20 -4
- package/dist/index.js +599 -324
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +619 -344
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-pending.d.ts +10 -0
- package/dist/mesh/mesh-events-utils.d.ts +10 -0
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/mesh/mesh-runtime-store.d.ts +1 -0
- package/dist/mesh/mesh-work-queue.d.ts +10 -0
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/dist/providers/provider-instance-manager.d.ts +1 -0
- package/dist/providers/provider-instance.d.ts +2 -0
- package/package.json +2 -2
- package/src/commands/cli-manager.ts +18 -0
- package/src/commands/router.ts +86 -8
- package/src/mesh/mesh-events-coordinator.ts +38 -2
- package/src/mesh/mesh-events-pending.ts +54 -4
- package/src/mesh/mesh-events-utils.ts +61 -9
- package/src/mesh/mesh-ledger.ts +1 -0
- package/src/mesh/mesh-reconcile-loop.ts +170 -3
- package/src/mesh/mesh-runtime-store.ts +38 -4
- package/src/mesh/mesh-work-queue.ts +13 -0
- package/src/providers/cli-provider-instance.ts +4 -1
- package/src/providers/provider-instance-manager.ts +1 -1
- package/src/providers/provider-instance.ts +1 -1
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 ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
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.345" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-21T13:55:50.824Z" : 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
|
-
|
|
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
|
-
|
|
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
|
|
7893
|
+
function readMeshCompletionSummary(metadataEvent) {
|
|
7867
7894
|
const workerResult = readWorkerResultMetadata(metadataEvent);
|
|
7868
7895
|
const resultRecord = readRecord4(metadataEvent.result);
|
|
7869
|
-
|
|
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
|
|
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,
|
|
@@ -10431,6 +10515,9 @@ function injectMeshSystemMessage(components, args) {
|
|
|
10431
10515
|
const workerCoordinatorDaemonId = readNonEmptyString2(
|
|
10432
10516
|
sourceSession?.getState()?.settings?.meshCoordinatorDaemonId
|
|
10433
10517
|
);
|
|
10518
|
+
const workerCoordinatorSessionId = readNonEmptyString2(
|
|
10519
|
+
sourceSession?.getState()?.settings?.meshCoordinatorSessionId
|
|
10520
|
+
) || readNonEmptyString2(args.metadataEvent.meshCoordinatorSessionId);
|
|
10434
10521
|
if (components.onMeshCoordinatorEventForwarded) {
|
|
10435
10522
|
try {
|
|
10436
10523
|
const surfacedPreview = resolveMeshSurfacedSessionPreview(args.metadataEvent);
|
|
@@ -10811,7 +10898,12 @@ function injectMeshSystemMessage(components, args) {
|
|
|
10811
10898
|
workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName),
|
|
10812
10899
|
metadataEvent: {
|
|
10813
10900
|
...args.metadataEvent,
|
|
10814
|
-
...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 } : {}
|
|
10815
10907
|
},
|
|
10816
10908
|
// Silent lifecycle events (agent:ready / agent:generating_started) carry no
|
|
10817
10909
|
// coordinator message; they are queued only so the coordinator re-runs the
|
|
@@ -10819,10 +10911,13 @@ function injectMeshSystemMessage(components, args) {
|
|
|
10819
10911
|
// entries without a coordinatorMessage, so a live CLI coordinator is not spammed.
|
|
10820
10912
|
...messageText ? { coordinatorMessage: messageText } : {},
|
|
10821
10913
|
queuedAt: Date.now(),
|
|
10822
|
-
...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 } : {}
|
|
10823
10918
|
};
|
|
10824
10919
|
if (queuePendingMeshCoordinatorEvent(pendingEvent)) {
|
|
10825
|
-
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}` : ""})`);
|
|
10826
10921
|
}
|
|
10827
10922
|
return { success: true, forwarded: 0 };
|
|
10828
10923
|
}
|
|
@@ -10847,6 +10942,13 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
10847
10942
|
targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
|
|
10848
10943
|
providerType: readNonEmptyString2(payload.providerType),
|
|
10849
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),
|
|
10850
10952
|
// Carry the session identity fields the worker provider event emits so the
|
|
10851
10953
|
// coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
|
|
10852
10954
|
// settings. Without these the remote-relay hop reconstructs metadataEvent with
|
|
@@ -11743,7 +11845,8 @@ function findLiveCoordinators(components) {
|
|
|
11743
11845
|
if (!meshId) continue;
|
|
11744
11846
|
const status = readNonEmptyString2(state.status).toLowerCase();
|
|
11745
11847
|
const modalParked = status === "waiting_choice" || status === "waiting_approval";
|
|
11746
|
-
|
|
11848
|
+
const sessionId = readNonEmptyString2(state.instanceId);
|
|
11849
|
+
out.push({ meshId, instance: inst, sessionId, idle: status === "idle", modalParked });
|
|
11747
11850
|
}
|
|
11748
11851
|
return out;
|
|
11749
11852
|
}
|
|
@@ -11755,6 +11858,44 @@ function injectPendingIntoCoordinator(coordinator, pending) {
|
|
|
11755
11858
|
...force ? { force: true } : {}
|
|
11756
11859
|
});
|
|
11757
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
|
+
}
|
|
11758
11899
|
async function runMeshReconcileTick(components) {
|
|
11759
11900
|
const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
11760
11901
|
const drainDaemonIds = resolveCoordinatorDaemonIds(components);
|
|
@@ -11839,6 +11980,21 @@ async function runMeshReconcileTick(components) {
|
|
|
11839
11980
|
if (targetCoordinators.length === 0) {
|
|
11840
11981
|
if (modalParkedCoordinators.length > 0) {
|
|
11841
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
|
+
}
|
|
11842
11998
|
}
|
|
11843
11999
|
continue;
|
|
11844
12000
|
}
|
|
@@ -11863,12 +12019,55 @@ async function runMeshReconcileTick(components) {
|
|
|
11863
12019
|
const mode = forceOnly ? "force-drain \u2192 generating" : "inject \u2192 idle";
|
|
11864
12020
|
LOG.info("MeshReconcile", `Reconcile ${mode}: ${pendingEvents.length} pending event(s) \u2192 ${targetCoordinators.length} coordinator(s) for mesh ${meshId}`);
|
|
11865
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
|
+
}
|
|
11866
12032
|
for (const c of targetCoordinators) {
|
|
11867
12033
|
injectPendingIntoCoordinator(c.instance, pending);
|
|
11868
12034
|
}
|
|
11869
12035
|
}
|
|
11870
12036
|
}
|
|
11871
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
|
+
}
|
|
11872
12071
|
async function retryUnresolvedDelegateForwards(components) {
|
|
11873
12072
|
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
11874
12073
|
if (!dispatchMeshCommand) return;
|
|
@@ -12086,6 +12285,11 @@ function buildForwardPayloadFromPending(event) {
|
|
|
12086
12285
|
meshId: readNonEmptyString2(event?.meshId),
|
|
12087
12286
|
nodeId: readNonEmptyString2(event?.nodeId) || readNonEmptyString2(metadata.meshNodeId),
|
|
12088
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) } : {},
|
|
12089
12293
|
...metadata
|
|
12090
12294
|
};
|
|
12091
12295
|
}
|
|
@@ -12108,7 +12312,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
12108
12312
|
}
|
|
12109
12313
|
};
|
|
12110
12314
|
}
|
|
12111
|
-
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;
|
|
12112
12316
|
var init_mesh_reconcile_loop = __esm({
|
|
12113
12317
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
12114
12318
|
"use strict";
|
|
@@ -12116,6 +12320,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
12116
12320
|
init_mesh_config();
|
|
12117
12321
|
init_logger();
|
|
12118
12322
|
init_mesh_events_pending();
|
|
12323
|
+
init_mesh_ledger();
|
|
12119
12324
|
init_mesh_runtime_store();
|
|
12120
12325
|
init_mesh_events_coordinator();
|
|
12121
12326
|
init_mesh_unresolved_forward_outbox();
|
|
@@ -12127,6 +12332,8 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
12127
12332
|
init_chat_message_normalization();
|
|
12128
12333
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
12129
12334
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
12335
|
+
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
12336
|
+
STRICT_SESSION_MATCH_TTL_MS = 6e4;
|
|
12130
12337
|
}
|
|
12131
12338
|
});
|
|
12132
12339
|
|
|
@@ -35059,7 +35266,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35059
35266
|
meshNodeFor: assignment.meshId,
|
|
35060
35267
|
...assignment.nodeId ? { meshNodeId: assignment.nodeId } : {},
|
|
35061
35268
|
...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
|
|
35062
|
-
...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 } : {}
|
|
35063
35273
|
};
|
|
35064
35274
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
35065
35275
|
}
|
|
@@ -38043,6 +38253,12 @@ Enable and detect this provider from the Machine Providers page before starting
|
|
|
38043
38253
|
);
|
|
38044
38254
|
}
|
|
38045
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
|
+
}
|
|
38046
38262
|
const sessionRegistry = this.deps.getSessionRegistry?.() || null;
|
|
38047
38263
|
if (provider && provider.category === "acp") {
|
|
38048
38264
|
const instanceManager2 = this.deps.getInstanceManager();
|
|
@@ -44457,6 +44673,40 @@ function readCachedInlineMeshActiveSessions(node) {
|
|
|
44457
44673
|
const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
|
|
44458
44674
|
return sessionId ? [sessionId] : [];
|
|
44459
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
|
+
}
|
|
44460
44710
|
function resolveMeshNodeAttribution(node) {
|
|
44461
44711
|
const record = readObjectRecord(node);
|
|
44462
44712
|
return {
|
|
@@ -46335,25 +46585,47 @@ var DaemonCommandRouter = class {
|
|
|
46335
46585
|
* controlbar commands do not — so the controlbar buttons appear to do nothing.
|
|
46336
46586
|
*
|
|
46337
46587
|
* Mirror the existing node-level remote-forward pattern (fast_forward_mesh_node etc.):
|
|
46338
|
-
* scan the
|
|
46339
|
-
*
|
|
46340
|
-
*
|
|
46341
|
-
*
|
|
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.
|
|
46342
46601
|
*/
|
|
46343
46602
|
resolveRemoteMeshSessionOwnerDaemonId(sessionId) {
|
|
46344
46603
|
const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
46345
46604
|
if (!trimmed) return void 0;
|
|
46346
46605
|
const selfDaemonId = this.deps.statusInstanceId;
|
|
46347
|
-
for (const node of this.
|
|
46348
|
-
|
|
46349
|
-
if (!nodeSessions.includes(trimmed)) continue;
|
|
46606
|
+
for (const node of this.collectMeshSessionOwnerCandidateNodes()) {
|
|
46607
|
+
if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
|
|
46350
46608
|
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
46351
|
-
if (!nodeDaemonId)
|
|
46609
|
+
if (!nodeDaemonId) continue;
|
|
46352
46610
|
if (selfDaemonId && nodeDaemonId === selfDaemonId) return void 0;
|
|
46353
46611
|
return nodeDaemonId;
|
|
46354
46612
|
}
|
|
46355
46613
|
return void 0;
|
|
46356
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
|
+
}
|
|
46357
46629
|
getCachedInlineMesh(meshId, inlineMesh) {
|
|
46358
46630
|
if (inlineMesh && typeof inlineMesh === "object") {
|
|
46359
46631
|
return this.warmInlineMeshCache(meshId, inlineMesh);
|
|
@@ -48373,7 +48645,10 @@ ${hintLines.join("\n")}` : "",
|
|
|
48373
48645
|
{
|
|
48374
48646
|
meshId: dispatchMeshContext.meshId,
|
|
48375
48647
|
nodeId: dispatchMeshContext.nodeId,
|
|
48376
|
-
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
|
|
48377
48652
|
}
|
|
48378
48653
|
);
|
|
48379
48654
|
if (stamp) inst.updateSettings(stamp);
|