@adhdev/daemon-standalone 0.9.82-rc.343 → 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
@@ -30036,10 +30036,10 @@ var require_dist3 = __commonJS({
30036
30036
  }
30037
30037
  function getDaemonBuildInfo() {
30038
30038
  if (cached2) return cached2;
30039
- const commit = readInjected(true ? "9a4c1af9349a342fc63d8d42746747a6feda8f08" : void 0) ?? "unknown";
30040
- const commitShort = readInjected(true ? "9a4c1af9" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30041
- const version2 = readInjected(true ? "0.9.82-rc.343" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30042
- const builtAt = readInjected(true ? "2026-06-21T11:25:43.064Z" : void 0);
30039
+ const commit = readInjected(true ? "d04317b7eab7aec6a5557cb1991af603c7e5143c" : void 0) ?? "unknown";
30040
+ const commitShort = readInjected(true ? "d04317b7" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30041
+ const version2 = readInjected(true ? "0.9.82-rc.344" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30042
+ const builtAt = readInjected(true ? "2026-06-21T16:38:22.983Z" : void 0);
30043
30043
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
30044
30044
  return cached2;
30045
30045
  }
@@ -32279,6 +32279,314 @@ Follow these recovery rules:
32279
32279
  4. **Always record what happened**: After handling a failure, briefly note the outcome in your report to the user.`;
32280
32280
  }
32281
32281
  });
32282
+ var fs32;
32283
+ var AsyncBatchWriter;
32284
+ var init_async_batch_writer = __esm2({
32285
+ "src/logging/async-batch-writer.ts"() {
32286
+ "use strict";
32287
+ fs32 = __toESM2(require("fs"));
32288
+ AsyncBatchWriter = class {
32289
+ // Maps filePath -> string buffer
32290
+ static buffers = /* @__PURE__ */ new Map();
32291
+ static writePromises = /* @__PURE__ */ new Map();
32292
+ static flushTimer = null;
32293
+ /**
32294
+ * Queues data to be written to a file asynchronously in a batch.
32295
+ */
32296
+ static write(filePath, data) {
32297
+ let buf = this.buffers.get(filePath);
32298
+ if (!buf) {
32299
+ buf = [];
32300
+ this.buffers.set(filePath, buf);
32301
+ }
32302
+ buf.push(data);
32303
+ if (!this.flushTimer) {
32304
+ this.flushTimer = setTimeout(() => {
32305
+ this.flushTimer = null;
32306
+ this.flushAll();
32307
+ }, 50);
32308
+ }
32309
+ }
32310
+ static async flushAll() {
32311
+ const entries = Array.from(this.buffers.entries());
32312
+ this.buffers.clear();
32313
+ for (const [filePath, buffer] of entries) {
32314
+ const dataToWrite = buffer.join("");
32315
+ const doWrite = async () => {
32316
+ try {
32317
+ const prevPromise = this.writePromises.get(filePath);
32318
+ if (prevPromise) await prevPromise;
32319
+ await fs32.promises.appendFile(filePath, dataToWrite, { encoding: "utf-8", mode: 384 });
32320
+ } catch {
32321
+ }
32322
+ };
32323
+ const writePromise = doWrite();
32324
+ this.writePromises.set(filePath, writePromise);
32325
+ writePromise.finally(() => {
32326
+ if (this.writePromises.get(filePath) === writePromise) {
32327
+ this.writePromises.delete(filePath);
32328
+ }
32329
+ });
32330
+ }
32331
+ }
32332
+ };
32333
+ }
32334
+ });
32335
+ var logger_exports = {};
32336
+ __export2(logger_exports, {
32337
+ LOG: () => LOG2,
32338
+ LOG_DIR_PATH: () => LOG_DIR_PATH,
32339
+ LOG_PATH: () => LOG_PATH,
32340
+ daemonLog: () => daemonLog,
32341
+ getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
32342
+ getDaemonLogDir: () => getDaemonLogDir,
32343
+ getLogBufferSize: () => getLogBufferSize,
32344
+ getLogLevel: () => getLogLevel,
32345
+ getLogPath: () => getLogPath,
32346
+ getRecentLogs: () => getRecentLogs,
32347
+ installGlobalInterceptor: () => installGlobalInterceptor,
32348
+ setLogLevel: () => setLogLevel
32349
+ });
32350
+ function setLogLevel(level) {
32351
+ currentLevel = level;
32352
+ daemonLog("Logger", `Log level set to: ${level}`, "info");
32353
+ }
32354
+ function getLogLevel() {
32355
+ return currentLevel;
32356
+ }
32357
+ function getDateStr() {
32358
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
32359
+ }
32360
+ function getDaemonLogDir() {
32361
+ return LOG_DIR;
32362
+ }
32363
+ function getCurrentDaemonLogPath(date5 = /* @__PURE__ */ new Date()) {
32364
+ return path9.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
32365
+ }
32366
+ function checkDateRotation() {
32367
+ const today = getDateStr();
32368
+ if (today !== currentDate) {
32369
+ currentDate = today;
32370
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
32371
+ cleanOldLogs();
32372
+ }
32373
+ }
32374
+ function cleanOldLogs() {
32375
+ try {
32376
+ const files = fs4.readdirSync(LOG_DIR).filter((f) => f.startsWith("daemon-") && f.endsWith(".log"));
32377
+ const cutoff = /* @__PURE__ */ new Date();
32378
+ cutoff.setDate(cutoff.getDate() - MAX_LOG_DAYS);
32379
+ const cutoffStr = cutoff.toISOString().slice(0, 10);
32380
+ for (const file2 of files) {
32381
+ const dateMatch = file2.match(/daemon-(\d{4}-\d{2}-\d{2})/);
32382
+ if (dateMatch && dateMatch[1] < cutoffStr) {
32383
+ try {
32384
+ fs4.unlinkSync(path9.join(LOG_DIR, file2));
32385
+ } catch {
32386
+ }
32387
+ }
32388
+ }
32389
+ } catch {
32390
+ }
32391
+ }
32392
+ function rotateSizeIfNeeded() {
32393
+ try {
32394
+ const stat2 = fs4.statSync(currentLogFile);
32395
+ if (stat2.size > MAX_LOG_SIZE) {
32396
+ const backup = currentLogFile.replace(".log", ".1.log");
32397
+ try {
32398
+ fs4.unlinkSync(backup);
32399
+ } catch {
32400
+ }
32401
+ fs4.renameSync(currentLogFile, backup);
32402
+ }
32403
+ } catch {
32404
+ }
32405
+ }
32406
+ function writeToFile(line) {
32407
+ try {
32408
+ if (++writeCount % 1e3 === 0) {
32409
+ checkDateRotation();
32410
+ rotateSizeIfNeeded();
32411
+ }
32412
+ AsyncBatchWriter.write(currentLogFile, line + "\n");
32413
+ } catch {
32414
+ }
32415
+ }
32416
+ function getRecentLogs(count = 50, minLevel = "info") {
32417
+ const minNum = LEVEL_NUM[minLevel];
32418
+ const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
32419
+ return filtered.slice(-count);
32420
+ }
32421
+ function getLogBufferSize() {
32422
+ return ringBuffer.length;
32423
+ }
32424
+ function ts() {
32425
+ return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
32426
+ }
32427
+ function fullTs() {
32428
+ return (/* @__PURE__ */ new Date()).toISOString();
32429
+ }
32430
+ function daemonLog(category, msg, level = "info") {
32431
+ const shouldOutput = LEVEL_NUM[level] >= LEVEL_NUM[currentLevel];
32432
+ const label = LEVEL_LABEL[level];
32433
+ const line = `[${ts()}] [${label}] [${category}] ${msg}`;
32434
+ if (!shouldOutput) return;
32435
+ writeToFile(line);
32436
+ ringBuffer.push({ ts: Date.now(), level, category, message: msg });
32437
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
32438
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
32439
+ }
32440
+ origConsoleLog(line);
32441
+ }
32442
+ function installGlobalInterceptor() {
32443
+ if (interceptorInstalled) return;
32444
+ interceptorInstalled = true;
32445
+ const stripAnsi4 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
32446
+ const isDaemonLogLine = (msg) => /\[(DBG|INF|WRN|ERR)\]/.test(msg);
32447
+ console.log = (...args) => {
32448
+ origConsoleLog(...args);
32449
+ try {
32450
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
32451
+ const clean = stripAnsi4(msg);
32452
+ if (isDaemonLogLine(clean)) return;
32453
+ const line = clean.startsWith("[20") ? clean : `[${fullTs()}] ${clean}`;
32454
+ writeToFile(line);
32455
+ const catMatch = clean.match(/\[([^\]]+)\]/);
32456
+ ringBuffer.push({
32457
+ ts: Date.now(),
32458
+ level: "info",
32459
+ category: catMatch?.[1] || "System",
32460
+ message: clean
32461
+ });
32462
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
32463
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
32464
+ }
32465
+ } catch {
32466
+ }
32467
+ };
32468
+ console.error = (...args) => {
32469
+ origConsoleError(...args);
32470
+ try {
32471
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
32472
+ const clean = stripAnsi4(msg);
32473
+ if (isDaemonLogLine(clean)) return;
32474
+ const line = `[${fullTs()}] [ERROR] ${clean}`;
32475
+ writeToFile(line);
32476
+ ringBuffer.push({ ts: Date.now(), level: "error", category: "System", message: clean });
32477
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
32478
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
32479
+ }
32480
+ } catch {
32481
+ }
32482
+ };
32483
+ console.warn = (...args) => {
32484
+ origConsoleWarn(...args);
32485
+ try {
32486
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
32487
+ const clean = stripAnsi4(msg);
32488
+ if (isDaemonLogLine(clean)) return;
32489
+ const line = `[${fullTs()}] [WARN] ${clean}`;
32490
+ writeToFile(line);
32491
+ ringBuffer.push({ ts: Date.now(), level: "warn", category: "System", message: clean });
32492
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
32493
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
32494
+ }
32495
+ } catch {
32496
+ }
32497
+ };
32498
+ writeToFile(`
32499
+ === ADHDev Daemon started at ${fullTs()} ===`);
32500
+ writeToFile(`Log file: ${currentLogFile}`);
32501
+ writeToFile(`Log level: ${currentLevel}`);
32502
+ }
32503
+ function getLogPath() {
32504
+ return currentLogFile;
32505
+ }
32506
+ var fs4;
32507
+ var path9;
32508
+ var os32;
32509
+ var LEVEL_NUM;
32510
+ var LEVEL_LABEL;
32511
+ var currentLevel;
32512
+ var LOG_DIR;
32513
+ var MAX_LOG_SIZE;
32514
+ var MAX_LOG_DAYS;
32515
+ var currentDate;
32516
+ var currentLogFile;
32517
+ var writeCount;
32518
+ var RING_BUFFER_SIZE;
32519
+ var ringBuffer;
32520
+ var origConsoleLog;
32521
+ var origConsoleError;
32522
+ var origConsoleWarn;
32523
+ var LOG2;
32524
+ var interceptorInstalled;
32525
+ var LOG_PATH;
32526
+ var LOG_DIR_PATH;
32527
+ var init_logger = __esm2({
32528
+ "src/logging/logger.ts"() {
32529
+ "use strict";
32530
+ fs4 = __toESM2(require("fs"));
32531
+ path9 = __toESM2(require("path"));
32532
+ os32 = __toESM2(require("os"));
32533
+ init_async_batch_writer();
32534
+ LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
32535
+ LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
32536
+ currentLevel = "info";
32537
+ LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os32.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os32.homedir(), "Library", "Logs", "adhdev") : path9.join(os32.homedir(), ".local", "share", "adhdev", "logs");
32538
+ MAX_LOG_SIZE = 5 * 1024 * 1024;
32539
+ MAX_LOG_DAYS = 7;
32540
+ try {
32541
+ fs4.mkdirSync(LOG_DIR, { recursive: true });
32542
+ } catch {
32543
+ }
32544
+ currentDate = getDateStr();
32545
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
32546
+ cleanOldLogs();
32547
+ try {
32548
+ const oldLog = path9.join(LOG_DIR, "daemon.log");
32549
+ if (fs4.existsSync(oldLog)) {
32550
+ const stat2 = fs4.statSync(oldLog);
32551
+ const oldDate = stat2.mtime.toISOString().slice(0, 10);
32552
+ fs4.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
32553
+ }
32554
+ const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
32555
+ if (fs4.existsSync(oldLogBackup)) {
32556
+ fs4.unlinkSync(oldLogBackup);
32557
+ }
32558
+ } catch {
32559
+ }
32560
+ writeCount = 0;
32561
+ RING_BUFFER_SIZE = 200;
32562
+ ringBuffer = [];
32563
+ origConsoleLog = console.log.bind(console);
32564
+ origConsoleError = console.error.bind(console);
32565
+ origConsoleWarn = console.warn.bind(console);
32566
+ LOG2 = {
32567
+ debug: (category, msg) => daemonLog(category, msg, "debug"),
32568
+ info: (category, msg) => daemonLog(category, msg, "info"),
32569
+ warn: (category, msg) => daemonLog(category, msg, "warn"),
32570
+ error: (category, msg) => daemonLog(category, msg, "error"),
32571
+ /**
32572
+ * Create a scoped logger for a specific component.
32573
+ * Category is baked in so callers only pass the message.
32574
+ */
32575
+ forComponent(category) {
32576
+ return {
32577
+ debug: (msg) => daemonLog(category, msg, "debug"),
32578
+ info: (msg) => daemonLog(category, msg, "info"),
32579
+ warn: (msg) => daemonLog(category, msg, "warn"),
32580
+ error: (msg) => daemonLog(category, msg, "error"),
32581
+ asLogFn: (level = "info") => (msg) => daemonLog(category, msg, level)
32582
+ };
32583
+ }
32584
+ };
32585
+ interceptorInstalled = false;
32586
+ LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
32587
+ LOG_DIR_PATH = LOG_DIR;
32588
+ }
32589
+ });
32282
32590
  function loadBetterSqlite3() {
32283
32591
  if (cached22) return cached22;
32284
32592
  const errors = [];
@@ -33012,314 +33320,6 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
33012
33320
  ledgerImportDone = /* @__PURE__ */ new Set();
33013
33321
  }
33014
33322
  });
33015
- var fs32;
33016
- var AsyncBatchWriter;
33017
- var init_async_batch_writer = __esm2({
33018
- "src/logging/async-batch-writer.ts"() {
33019
- "use strict";
33020
- fs32 = __toESM2(require("fs"));
33021
- AsyncBatchWriter = class {
33022
- // Maps filePath -> string buffer
33023
- static buffers = /* @__PURE__ */ new Map();
33024
- static writePromises = /* @__PURE__ */ new Map();
33025
- static flushTimer = null;
33026
- /**
33027
- * Queues data to be written to a file asynchronously in a batch.
33028
- */
33029
- static write(filePath, data) {
33030
- let buf = this.buffers.get(filePath);
33031
- if (!buf) {
33032
- buf = [];
33033
- this.buffers.set(filePath, buf);
33034
- }
33035
- buf.push(data);
33036
- if (!this.flushTimer) {
33037
- this.flushTimer = setTimeout(() => {
33038
- this.flushTimer = null;
33039
- this.flushAll();
33040
- }, 50);
33041
- }
33042
- }
33043
- static async flushAll() {
33044
- const entries = Array.from(this.buffers.entries());
33045
- this.buffers.clear();
33046
- for (const [filePath, buffer] of entries) {
33047
- const dataToWrite = buffer.join("");
33048
- const doWrite = async () => {
33049
- try {
33050
- const prevPromise = this.writePromises.get(filePath);
33051
- if (prevPromise) await prevPromise;
33052
- await fs32.promises.appendFile(filePath, dataToWrite, { encoding: "utf-8", mode: 384 });
33053
- } catch {
33054
- }
33055
- };
33056
- const writePromise = doWrite();
33057
- this.writePromises.set(filePath, writePromise);
33058
- writePromise.finally(() => {
33059
- if (this.writePromises.get(filePath) === writePromise) {
33060
- this.writePromises.delete(filePath);
33061
- }
33062
- });
33063
- }
33064
- }
33065
- };
33066
- }
33067
- });
33068
- var logger_exports = {};
33069
- __export2(logger_exports, {
33070
- LOG: () => LOG2,
33071
- LOG_DIR_PATH: () => LOG_DIR_PATH,
33072
- LOG_PATH: () => LOG_PATH,
33073
- daemonLog: () => daemonLog,
33074
- getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
33075
- getDaemonLogDir: () => getDaemonLogDir,
33076
- getLogBufferSize: () => getLogBufferSize,
33077
- getLogLevel: () => getLogLevel,
33078
- getLogPath: () => getLogPath,
33079
- getRecentLogs: () => getRecentLogs,
33080
- installGlobalInterceptor: () => installGlobalInterceptor,
33081
- setLogLevel: () => setLogLevel
33082
- });
33083
- function setLogLevel(level) {
33084
- currentLevel = level;
33085
- daemonLog("Logger", `Log level set to: ${level}`, "info");
33086
- }
33087
- function getLogLevel() {
33088
- return currentLevel;
33089
- }
33090
- function getDateStr() {
33091
- return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
33092
- }
33093
- function getDaemonLogDir() {
33094
- return LOG_DIR;
33095
- }
33096
- function getCurrentDaemonLogPath(date5 = /* @__PURE__ */ new Date()) {
33097
- return path9.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
33098
- }
33099
- function checkDateRotation() {
33100
- const today = getDateStr();
33101
- if (today !== currentDate) {
33102
- currentDate = today;
33103
- currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
33104
- cleanOldLogs();
33105
- }
33106
- }
33107
- function cleanOldLogs() {
33108
- try {
33109
- const files = fs4.readdirSync(LOG_DIR).filter((f) => f.startsWith("daemon-") && f.endsWith(".log"));
33110
- const cutoff = /* @__PURE__ */ new Date();
33111
- cutoff.setDate(cutoff.getDate() - MAX_LOG_DAYS);
33112
- const cutoffStr = cutoff.toISOString().slice(0, 10);
33113
- for (const file2 of files) {
33114
- const dateMatch = file2.match(/daemon-(\d{4}-\d{2}-\d{2})/);
33115
- if (dateMatch && dateMatch[1] < cutoffStr) {
33116
- try {
33117
- fs4.unlinkSync(path9.join(LOG_DIR, file2));
33118
- } catch {
33119
- }
33120
- }
33121
- }
33122
- } catch {
33123
- }
33124
- }
33125
- function rotateSizeIfNeeded() {
33126
- try {
33127
- const stat2 = fs4.statSync(currentLogFile);
33128
- if (stat2.size > MAX_LOG_SIZE) {
33129
- const backup = currentLogFile.replace(".log", ".1.log");
33130
- try {
33131
- fs4.unlinkSync(backup);
33132
- } catch {
33133
- }
33134
- fs4.renameSync(currentLogFile, backup);
33135
- }
33136
- } catch {
33137
- }
33138
- }
33139
- function writeToFile(line) {
33140
- try {
33141
- if (++writeCount % 1e3 === 0) {
33142
- checkDateRotation();
33143
- rotateSizeIfNeeded();
33144
- }
33145
- AsyncBatchWriter.write(currentLogFile, line + "\n");
33146
- } catch {
33147
- }
33148
- }
33149
- function getRecentLogs(count = 50, minLevel = "info") {
33150
- const minNum = LEVEL_NUM[minLevel];
33151
- const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
33152
- return filtered.slice(-count);
33153
- }
33154
- function getLogBufferSize() {
33155
- return ringBuffer.length;
33156
- }
33157
- function ts() {
33158
- return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
33159
- }
33160
- function fullTs() {
33161
- return (/* @__PURE__ */ new Date()).toISOString();
33162
- }
33163
- function daemonLog(category, msg, level = "info") {
33164
- const shouldOutput = LEVEL_NUM[level] >= LEVEL_NUM[currentLevel];
33165
- const label = LEVEL_LABEL[level];
33166
- const line = `[${ts()}] [${label}] [${category}] ${msg}`;
33167
- if (!shouldOutput) return;
33168
- writeToFile(line);
33169
- ringBuffer.push({ ts: Date.now(), level, category, message: msg });
33170
- if (ringBuffer.length > RING_BUFFER_SIZE) {
33171
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
33172
- }
33173
- origConsoleLog(line);
33174
- }
33175
- function installGlobalInterceptor() {
33176
- if (interceptorInstalled) return;
33177
- interceptorInstalled = true;
33178
- const stripAnsi4 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
33179
- const isDaemonLogLine = (msg) => /\[(DBG|INF|WRN|ERR)\]/.test(msg);
33180
- console.log = (...args) => {
33181
- origConsoleLog(...args);
33182
- try {
33183
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
33184
- const clean = stripAnsi4(msg);
33185
- if (isDaemonLogLine(clean)) return;
33186
- const line = clean.startsWith("[20") ? clean : `[${fullTs()}] ${clean}`;
33187
- writeToFile(line);
33188
- const catMatch = clean.match(/\[([^\]]+)\]/);
33189
- ringBuffer.push({
33190
- ts: Date.now(),
33191
- level: "info",
33192
- category: catMatch?.[1] || "System",
33193
- message: clean
33194
- });
33195
- if (ringBuffer.length > RING_BUFFER_SIZE) {
33196
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
33197
- }
33198
- } catch {
33199
- }
33200
- };
33201
- console.error = (...args) => {
33202
- origConsoleError(...args);
33203
- try {
33204
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
33205
- const clean = stripAnsi4(msg);
33206
- if (isDaemonLogLine(clean)) return;
33207
- const line = `[${fullTs()}] [ERROR] ${clean}`;
33208
- writeToFile(line);
33209
- ringBuffer.push({ ts: Date.now(), level: "error", category: "System", message: clean });
33210
- if (ringBuffer.length > RING_BUFFER_SIZE) {
33211
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
33212
- }
33213
- } catch {
33214
- }
33215
- };
33216
- console.warn = (...args) => {
33217
- origConsoleWarn(...args);
33218
- try {
33219
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
33220
- const clean = stripAnsi4(msg);
33221
- if (isDaemonLogLine(clean)) return;
33222
- const line = `[${fullTs()}] [WARN] ${clean}`;
33223
- writeToFile(line);
33224
- ringBuffer.push({ ts: Date.now(), level: "warn", category: "System", message: clean });
33225
- if (ringBuffer.length > RING_BUFFER_SIZE) {
33226
- ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
33227
- }
33228
- } catch {
33229
- }
33230
- };
33231
- writeToFile(`
33232
- === ADHDev Daemon started at ${fullTs()} ===`);
33233
- writeToFile(`Log file: ${currentLogFile}`);
33234
- writeToFile(`Log level: ${currentLevel}`);
33235
- }
33236
- function getLogPath() {
33237
- return currentLogFile;
33238
- }
33239
- var fs4;
33240
- var path9;
33241
- var os32;
33242
- var LEVEL_NUM;
33243
- var LEVEL_LABEL;
33244
- var currentLevel;
33245
- var LOG_DIR;
33246
- var MAX_LOG_SIZE;
33247
- var MAX_LOG_DAYS;
33248
- var currentDate;
33249
- var currentLogFile;
33250
- var writeCount;
33251
- var RING_BUFFER_SIZE;
33252
- var ringBuffer;
33253
- var origConsoleLog;
33254
- var origConsoleError;
33255
- var origConsoleWarn;
33256
- var LOG2;
33257
- var interceptorInstalled;
33258
- var LOG_PATH;
33259
- var LOG_DIR_PATH;
33260
- var init_logger = __esm2({
33261
- "src/logging/logger.ts"() {
33262
- "use strict";
33263
- fs4 = __toESM2(require("fs"));
33264
- path9 = __toESM2(require("path"));
33265
- os32 = __toESM2(require("os"));
33266
- init_async_batch_writer();
33267
- LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
33268
- LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
33269
- currentLevel = "info";
33270
- LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os32.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os32.homedir(), "Library", "Logs", "adhdev") : path9.join(os32.homedir(), ".local", "share", "adhdev", "logs");
33271
- MAX_LOG_SIZE = 5 * 1024 * 1024;
33272
- MAX_LOG_DAYS = 7;
33273
- try {
33274
- fs4.mkdirSync(LOG_DIR, { recursive: true });
33275
- } catch {
33276
- }
33277
- currentDate = getDateStr();
33278
- currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
33279
- cleanOldLogs();
33280
- try {
33281
- const oldLog = path9.join(LOG_DIR, "daemon.log");
33282
- if (fs4.existsSync(oldLog)) {
33283
- const stat2 = fs4.statSync(oldLog);
33284
- const oldDate = stat2.mtime.toISOString().slice(0, 10);
33285
- fs4.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
33286
- }
33287
- const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
33288
- if (fs4.existsSync(oldLogBackup)) {
33289
- fs4.unlinkSync(oldLogBackup);
33290
- }
33291
- } catch {
33292
- }
33293
- writeCount = 0;
33294
- RING_BUFFER_SIZE = 200;
33295
- ringBuffer = [];
33296
- origConsoleLog = console.log.bind(console);
33297
- origConsoleError = console.error.bind(console);
33298
- origConsoleWarn = console.warn.bind(console);
33299
- LOG2 = {
33300
- debug: (category, msg) => daemonLog(category, msg, "debug"),
33301
- info: (category, msg) => daemonLog(category, msg, "info"),
33302
- warn: (category, msg) => daemonLog(category, msg, "warn"),
33303
- error: (category, msg) => daemonLog(category, msg, "error"),
33304
- /**
33305
- * Create a scoped logger for a specific component.
33306
- * Category is baked in so callers only pass the message.
33307
- */
33308
- forComponent(category) {
33309
- return {
33310
- debug: (msg) => daemonLog(category, msg, "debug"),
33311
- info: (msg) => daemonLog(category, msg, "info"),
33312
- warn: (msg) => daemonLog(category, msg, "warn"),
33313
- error: (msg) => daemonLog(category, msg, "error"),
33314
- asLogFn: (level = "info") => (msg) => daemonLog(category, msg, level)
33315
- };
33316
- }
33317
- };
33318
- interceptorInstalled = false;
33319
- LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
33320
- LOG_DIR_PATH = LOG_DIR;
33321
- }
33322
- });
33323
33323
  var mesh_work_queue_exports = {};
33324
33324
  __export2(mesh_work_queue_exports, {
33325
33325
  ACTIVE_MESH_QUEUE_STATUSES: () => ACTIVE_MESH_QUEUE_STATUSES,
@@ -33635,6 +33635,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
33635
33635
  requiredTags: resolvedRequiredTags,
33636
33636
  ...dependsOn.length > 0 ? { dependsOn } : {},
33637
33637
  ...typeof opts?.missionId === "string" && opts.missionId.trim() ? { missionId: opts.missionId.trim() } : {},
33638
+ ...typeof opts?.sourceCoordinatorSessionId === "string" && opts.sourceCoordinatorSessionId.trim() ? { sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId.trim() } : {},
33638
33639
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
33639
33640
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
33640
33641
  };
@@ -33992,22 +33993,33 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
33992
33993
  (0, import_fs5.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
33993
33994
  }
33994
33995
  }
33995
- } catch {
33996
+ } catch (err) {
33997
+ if (!loggedMigrationFailure) {
33998
+ loggedMigrationFailure = true;
33999
+ LOG2.warn(
34000
+ "MeshRuntimeStore",
34001
+ `Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
34002
+ );
34003
+ }
34004
+ return (0, import_fs5.existsSync)(nextPath) ? nextPath : legacyPath;
33996
34005
  }
33997
34006
  return nextPath;
33998
34007
  }
33999
34008
  var import_fs5;
34000
34009
  var import_path5;
34001
34010
  var DatabaseCtor;
34011
+ var loggedMigrationFailure;
34002
34012
  var MeshRuntimeStore;
34003
34013
  var init_mesh_runtime_store = __esm2({
34004
34014
  "src/mesh/mesh-runtime-store.ts"() {
34005
34015
  "use strict";
34006
34016
  import_fs5 = require("fs");
34007
34017
  import_path5 = require("path");
34018
+ init_logger();
34008
34019
  init_load_better_sqlite3();
34009
34020
  init_mesh_ledger();
34010
34021
  init_mesh_work_queue();
34022
+ loggedMigrationFailure = false;
34011
34023
  MeshRuntimeStore = class _MeshRuntimeStore {
34012
34024
  static instance;
34013
34025
  db;
@@ -34029,9 +34041,21 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
34029
34041
  this.db.pragma("busy_timeout = 5000");
34030
34042
  this.migrate();
34031
34043
  }
34044
+ static loggedGetInstanceFailure = false;
34032
34045
  static getInstance() {
34033
34046
  if (!this.instance) {
34034
- this.instance = new _MeshRuntimeStore(meshRuntimeStorePath());
34047
+ try {
34048
+ this.instance = new _MeshRuntimeStore(meshRuntimeStorePath());
34049
+ } catch (err) {
34050
+ if (!_MeshRuntimeStore.loggedGetInstanceFailure) {
34051
+ _MeshRuntimeStore.loggedGetInstanceFailure = true;
34052
+ LOG2.warn(
34053
+ "MeshRuntimeStore",
34054
+ `getInstance failed; callers will degrade to JSONL-only: ${err?.message || err}`
34055
+ );
34056
+ }
34057
+ throw err;
34058
+ }
34035
34059
  }
34036
34060
  return this.instance;
34037
34061
  }
@@ -37622,7 +37646,11 @@ ${rendered}`, "utf-8");
37622
37646
  if (coordinatorDaemonId && !readNonEmptyString2(settings.meshCoordinatorDaemonId)) {
37623
37647
  stamp.meshCoordinatorDaemonId = coordinatorDaemonId;
37624
37648
  }
37625
- if ((meshId || nodeId || coordinatorDaemonId) && settings.launchedByCoordinator !== true) {
37649
+ const coordinatorSessionId = readNonEmptyString2(meshContext.coordinatorSessionId);
37650
+ if (coordinatorSessionId && !readNonEmptyString2(settings.meshCoordinatorSessionId)) {
37651
+ stamp.meshCoordinatorSessionId = coordinatorSessionId;
37652
+ }
37653
+ if ((meshId || nodeId || coordinatorDaemonId || coordinatorSessionId) && settings.launchedByCoordinator !== true) {
37626
37654
  stamp.launchedByCoordinator = true;
37627
37655
  }
37628
37656
  return Object.keys(stamp).length > 0 ? stamp : void 0;
@@ -37639,10 +37667,13 @@ ${rendered}`, "utf-8");
37639
37667
  function readWorkerResultMetadata(event) {
37640
37668
  return readRecord4(event.workerResult) || readRecord4(event.meshWorkerResult) || readRecord4(event.structuredResult);
37641
37669
  }
37642
- function resolveMeshSurfacedSessionPreview(metadataEvent) {
37670
+ function readMeshCompletionSummary(metadataEvent) {
37643
37671
  const workerResult = readWorkerResultMetadata(metadataEvent);
37644
37672
  const resultRecord = readRecord4(metadataEvent.result);
37645
- const summaryText = readNonEmptyString2(metadataEvent.finalSummary) || readNonEmptyString2(workerResult?.summary) || readNonEmptyString2(workerResult?.finalSummary) || readNonEmptyString2(resultRecord?.summary) || readNonEmptyString2(resultRecord?.finalSummary);
37673
+ return readNonEmptyString2(metadataEvent.finalSummary) || readNonEmptyString2(workerResult?.summary) || readNonEmptyString2(workerResult?.finalSummary) || readNonEmptyString2(resultRecord?.summary) || readNonEmptyString2(resultRecord?.finalSummary);
37674
+ }
37675
+ function resolveMeshSurfacedSessionPreview(metadataEvent) {
37676
+ const summaryText = readMeshCompletionSummary(metadataEvent);
37646
37677
  if (!summaryText) return void 0;
37647
37678
  const truncationSuffix = "...[truncated]";
37648
37679
  const preview = summaryText.length > MESH_SURFACED_PREVIEW_MAX_CHARS ? `${summaryText.slice(0, MESH_SURFACED_PREVIEW_MAX_CHARS - truncationSuffix.length)}${truncationSuffix}` : summaryText;
@@ -37680,7 +37711,18 @@ ${rendered}`, "utf-8");
37680
37711
  if (args.metadataEvent.source === "no_progress_reconciliation") {
37681
37712
  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.`;
37682
37713
  }
37683
- 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.";
37714
+ const reviewRecommended = args.metadataEvent.reviewRecommended === true;
37715
+ const completionSummary = readMeshCompletionSummary(args.metadataEvent);
37716
+ if (completionSummary) {
37717
+ const truncationSuffix = "\n\u2026[truncated \u2014 call mesh_read_chat once for the full transcript]";
37718
+ const surfaced = completionSummary.length > MESH_COMPLETION_SURFACE_MAX_CHARS ? `${completionSummary.slice(0, MESH_COMPLETION_SURFACE_MAX_CHARS - truncationSuffix.length)}${truncationSuffix}` : completionSummary;
37719
+ const verifyNote = reviewRecommended ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done." : "";
37720
+ 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}
37721
+
37722
+ --- ${args.nodeLabel} final summary ---
37723
+ ${surfaced}`;
37724
+ }
37725
+ 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.";
37684
37726
  return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
37685
37727
  }
37686
37728
  if (args.event === "agent:waiting_approval") {
@@ -37789,10 +37831,12 @@ Next step: ${nextStep}`;
37789
37831
  return "";
37790
37832
  }
37791
37833
  var MESH_SURFACED_PREVIEW_MAX_CHARS;
37834
+ var MESH_COMPLETION_SURFACE_MAX_CHARS;
37792
37835
  var init_mesh_events_utils = __esm2({
37793
37836
  "src/mesh/mesh-events-utils.ts"() {
37794
37837
  "use strict";
37795
37838
  MESH_SURFACED_PREVIEW_MAX_CHARS = 512;
37839
+ MESH_COMPLETION_SURFACE_MAX_CHARS = 4e3;
37796
37840
  }
37797
37841
  });
37798
37842
  function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
@@ -37948,6 +37992,37 @@ Next step: ${nextStep}`;
37948
37992
  if ((0, import_fs10.statSync)(path422).size <= MAX_PENDING_EVENTS_BYTES) return;
37949
37993
  const lines = (0, import_fs10.readFileSync)(path422, "utf-8").split("\n").filter(Boolean);
37950
37994
  if (lines.length <= MAX_PENDING_EVENTS_KEEP) return;
37995
+ const dropped = lines.slice(0, lines.length - MAX_PENDING_EVENTS_KEEP);
37996
+ for (const line of dropped) {
37997
+ let event;
37998
+ try {
37999
+ event = JSON.parse(line);
38000
+ } catch {
38001
+ continue;
38002
+ }
38003
+ if (!event || !event.meshId) continue;
38004
+ const finalSummary = readMeshCompletionSummary(event.metadataEvent || {});
38005
+ if (!readNonEmptyString2(event.coordinatorMessage) && !finalSummary) continue;
38006
+ try {
38007
+ appendLedgerEntry(event.meshId, {
38008
+ kind: "event_held",
38009
+ ...event.nodeId ? { nodeId: event.nodeId } : {},
38010
+ payload: {
38011
+ event: event.event,
38012
+ reason: "pending_trim_dropped",
38013
+ recoverable: true,
38014
+ nodeLabel: event.nodeLabel,
38015
+ ...event.workspace ? { workspace: event.workspace } : {},
38016
+ targetCoordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
38017
+ queuedAt: event.queuedAt,
38018
+ ...finalSummary ? { finalSummary } : {}
38019
+ }
38020
+ });
38021
+ LOG2.warn("MeshEvents", `Pending-events trim dropping undelivered ${event.event} for mesh ${event.meshId} \u2014 recorded to ledger (recoverable)`);
38022
+ } catch (e) {
38023
+ LOG2.warn("MeshEvents", `Failed to ledger-record trim-dropped ${event.event} for mesh ${event.meshId}: ${e?.message || e}`);
38024
+ }
38025
+ }
37951
38026
  (0, import_fs10.writeFileSync)(path422, lines.slice(-MAX_PENDING_EVENTS_KEEP).join("\n") + "\n", "utf-8");
37952
38027
  } catch {
37953
38028
  }
@@ -38077,7 +38152,8 @@ Next step: ${nextStep}`;
38077
38152
  if (event) pushUnique(event);
38078
38153
  }
38079
38154
  }
38080
- } catch {
38155
+ } catch (e) {
38156
+ LOG2.warn("MeshEvents", `SQLite pending-event drain failed for mesh ${meshId}; JSONL fallback only: ${e?.message || e}`);
38081
38157
  }
38082
38158
  const paths = primaryDaemonId ? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)] : [getPendingEventsPath(meshId)];
38083
38159
  for (const path422 of paths) {
@@ -39494,6 +39570,8 @@ Next step: ${nextStep}`;
39494
39570
  if (node?.daemonId && components.dispatchMeshCommand) {
39495
39571
  const isLocalNode = components.cliManager.adapters.has(sessionId);
39496
39572
  if (!isLocalNode) {
39573
+ const localDaemonIdForDispatch = readNonEmptyString2(loadConfig2().machineId) || void 0;
39574
+ const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
39497
39575
  const delivery2 = createSessionDelivery({
39498
39576
  meshId,
39499
39577
  nodeId,
@@ -39502,9 +39580,10 @@ Next step: ${nextStep}`;
39502
39580
  taskId: task.id,
39503
39581
  kind: "task",
39504
39582
  message: task.message,
39505
- status: "delivering"
39583
+ status: "delivering",
39584
+ ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
39585
+ ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
39506
39586
  });
39507
- const localDaemonIdForDispatch = readNonEmptyString2(loadConfig2().machineId) || void 0;
39508
39587
  components.dispatchMeshCommand(node.daemonId, "agent_command", {
39509
39588
  targetSessionId: sessionId,
39510
39589
  cliType: providerType,
@@ -39514,7 +39593,8 @@ Next step: ${nextStep}`;
39514
39593
  meshId,
39515
39594
  nodeId,
39516
39595
  taskId: task.id,
39517
- ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {}
39596
+ ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
39597
+ ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
39518
39598
  }
39519
39599
  }).then(() => {
39520
39600
  updateSessionDeliveryStatus(delivery2.id, "delivered");
@@ -39539,12 +39619,16 @@ Next step: ${nextStep}`;
39539
39619
  const inst = components.instanceManager.getInstance(sessionId);
39540
39620
  if (inst && typeof inst.updateSettings === "function") {
39541
39621
  const localDaemonId = readNonEmptyString2(loadConfig2().machineId);
39622
+ const localSourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId);
39542
39623
  inst.updateSettings({
39543
39624
  meshNodeFor: meshId,
39544
39625
  meshNodeId: nodeId,
39545
39626
  launchedByCoordinator: true,
39546
39627
  autoApprove: resolveDelegatedWorkerAutoApprove(mesh?.policy, node?.policy),
39547
- ...localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {}
39628
+ ...localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {},
39629
+ // (3) Stamp the originating coordinator session for session-anchored routing
39630
+ // of this co-located worker's completion. Absent → daemon-level fallback.
39631
+ ...localSourceCoordinatorSessionId ? { meshCoordinatorSessionId: localSourceCoordinatorSessionId } : {}
39548
39632
  });
39549
39633
  }
39550
39634
  } catch {
@@ -39557,7 +39641,9 @@ Next step: ${nextStep}`;
39557
39641
  taskId: task.id,
39558
39642
  kind: "task",
39559
39643
  message: task.message,
39560
- status: "delivering"
39644
+ status: "delivering",
39645
+ ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
39646
+ ...readNonEmptyString2(loadConfig2().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig2().machineId) } : {}
39561
39647
  });
39562
39648
  components.cliManager.handleCliCommand("agent_command", {
39563
39649
  targetSessionId: sessionId,
@@ -40207,6 +40293,9 @@ Next step: ${nextStep}`;
40207
40293
  const workerCoordinatorDaemonId = readNonEmptyString2(
40208
40294
  sourceSession?.getState()?.settings?.meshCoordinatorDaemonId
40209
40295
  );
40296
+ const workerCoordinatorSessionId = readNonEmptyString2(
40297
+ sourceSession?.getState()?.settings?.meshCoordinatorSessionId
40298
+ ) || readNonEmptyString2(args.metadataEvent.meshCoordinatorSessionId);
40210
40299
  if (components.onMeshCoordinatorEventForwarded) {
40211
40300
  try {
40212
40301
  const surfacedPreview = resolveMeshSurfacedSessionPreview(args.metadataEvent);
@@ -40587,7 +40676,12 @@ Next step: ${nextStep}`;
40587
40676
  workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName),
40588
40677
  metadataEvent: {
40589
40678
  ...args.metadataEvent,
40590
- ...recoveryContext ? { recoveryContext } : {}
40679
+ ...recoveryContext ? { recoveryContext } : {},
40680
+ // Stash the coordinator session id INSIDE metadataEvent too, so it survives the
40681
+ // P2P relay serialization (buildForwardPayloadFromPending spreads metadata; the
40682
+ // handleMeshForwardEvent whitelist reads it back) — a top-level field alone would
40683
+ // be dropped when the event crosses a machine boundary.
40684
+ ...workerCoordinatorSessionId ? { meshCoordinatorSessionId: workerCoordinatorSessionId } : {}
40591
40685
  },
40592
40686
  // Silent lifecycle events (agent:ready / agent:generating_started) carry no
40593
40687
  // coordinator message; they are queued only so the coordinator re-runs the
@@ -40595,10 +40689,13 @@ Next step: ${nextStep}`;
40595
40689
  // entries without a coordinatorMessage, so a live CLI coordinator is not spammed.
40596
40690
  ...messageText ? { coordinatorMessage: messageText } : {},
40597
40691
  queuedAt: Date.now(),
40598
- ...workerCoordinatorDaemonId ? { targetCoordinatorDaemonId: workerCoordinatorDaemonId } : {}
40692
+ ...workerCoordinatorDaemonId ? { targetCoordinatorDaemonId: workerCoordinatorDaemonId } : {},
40693
+ // Top-level session anchor for the local PHASE 2 strict-match on the coordinator
40694
+ // daemon. Absent → daemon-level broadcast (legacy / single-coordinator path).
40695
+ ...workerCoordinatorSessionId ? { targetCoordinatorSessionId: workerCoordinatorSessionId } : {}
40599
40696
  };
40600
40697
  if (queuePendingMeshCoordinatorEvent(pendingEvent)) {
40601
- LOG2.info("MeshEvents", `Queued ${args.event} for coordinator (mesh ${args.meshId}${workerCoordinatorDaemonId ? `, coordinator daemon ${workerCoordinatorDaemonId}` : ""})`);
40698
+ LOG2.info("MeshEvents", `Queued ${args.event} for coordinator (mesh ${args.meshId}${workerCoordinatorDaemonId ? `, coordinator daemon ${workerCoordinatorDaemonId}` : ""}${workerCoordinatorSessionId ? `, coordinator session ${workerCoordinatorSessionId}` : ""})`);
40602
40699
  }
40603
40700
  return { success: true, forwarded: 0 };
40604
40701
  }
@@ -40623,6 +40720,13 @@ Next step: ${nextStep}`;
40623
40720
  targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
40624
40721
  providerType: readNonEmptyString2(payload.providerType),
40625
40722
  providerSessionId: readNonEmptyString2(payload.providerSessionId),
40723
+ // Preserve the originating coordinator SESSION id across the machine boundary so
40724
+ // the completion routes back to the exact coordinator session (multi-coordinator).
40725
+ // buildForwardPayloadFromPending spreads the worker event's metadata, so the id
40726
+ // arrives as payload.meshCoordinatorSessionId; the top-level targetCoordinatorSessionId
40727
+ // is also accepted as a fallback. injectMeshSystemMessage re-derives the routing
40728
+ // anchors from this. Absent → daemon-level fallback (version-skew safe).
40729
+ meshCoordinatorSessionId: readNonEmptyString2(payload.meshCoordinatorSessionId) || readNonEmptyString2(payload.targetCoordinatorSessionId),
40626
40730
  // Carry the session identity fields the worker provider event emits so the
40627
40731
  // coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
40628
40732
  // settings. Without these the remote-relay hop reconstructs metadataEvent with
@@ -41541,7 +41645,8 @@ Next step: ${nextStep}`;
41541
41645
  if (!meshId) continue;
41542
41646
  const status = readNonEmptyString2(state.status).toLowerCase();
41543
41647
  const modalParked = status === "waiting_choice" || status === "waiting_approval";
41544
- out.push({ meshId, instance: inst, idle: status === "idle", modalParked });
41648
+ const sessionId = readNonEmptyString2(state.instanceId);
41649
+ out.push({ meshId, instance: inst, sessionId, idle: status === "idle", modalParked });
41545
41650
  }
41546
41651
  return out;
41547
41652
  }
@@ -41553,6 +41658,44 @@ Next step: ${nextStep}`;
41553
41658
  ...force ? { force: true } : {}
41554
41659
  });
41555
41660
  }
41661
+ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldForCoordinatorCount) {
41662
+ let pending;
41663
+ try {
41664
+ pending = getPendingMeshCoordinatorEvents(meshId, drainDaemonIds.length > 0 ? drainDaemonIds : void 0);
41665
+ } catch {
41666
+ return;
41667
+ }
41668
+ for (const event of pending) {
41669
+ if (!shouldForceInjectMeshEvent(event.event)) continue;
41670
+ const fingerprint = buildPendingEventFingerprint(event);
41671
+ const key = `${meshId}::${fingerprint || `${event.event}::${event.nodeId || ""}::${event.queuedAt}`}`;
41672
+ if (heldEventLedgerRecorded.has(key)) continue;
41673
+ heldEventLedgerRecorded.add(key);
41674
+ const finalSummary = readMeshCompletionSummary(event.metadataEvent);
41675
+ try {
41676
+ appendLedgerEntry(meshId, {
41677
+ kind: "event_held",
41678
+ ...event.nodeId ? { nodeId: event.nodeId } : {},
41679
+ payload: {
41680
+ event: event.event,
41681
+ reason,
41682
+ recoverable: true,
41683
+ heldForCoordinators: heldForCoordinatorCount,
41684
+ nodeLabel: event.nodeLabel,
41685
+ ...event.workspace ? { workspace: event.workspace } : {},
41686
+ targetCoordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
41687
+ queuedAt: event.queuedAt,
41688
+ ...fingerprint ? { fingerprint } : {},
41689
+ ...finalSummary ? { finalSummary } : {}
41690
+ }
41691
+ });
41692
+ LOG2.info("MeshReconcile", `Ledger-recorded held ${event.event} for mesh ${meshId} (reason ${reason}) \u2014 recoverable from ledger`);
41693
+ } catch (e) {
41694
+ heldEventLedgerRecorded.delete(key);
41695
+ LOG2.warn("MeshReconcile", `Failed to ledger-record held ${event.event} for mesh ${meshId}: ${e?.message || e}`);
41696
+ }
41697
+ }
41698
+ }
41556
41699
  async function runMeshReconcileTick(components) {
41557
41700
  const localDaemonId = readNonEmptyString2(loadConfig2().machineId) || void 0;
41558
41701
  const drainDaemonIds = resolveCoordinatorDaemonIds(components);
@@ -41637,6 +41780,21 @@ Next step: ${nextStep}`;
41637
41780
  if (targetCoordinators.length === 0) {
41638
41781
  if (modalParkedCoordinators.length > 0) {
41639
41782
  LOG2.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)`);
41783
+ let hasPending = true;
41784
+ if (store) {
41785
+ try {
41786
+ hasPending = store.pendingEventCount(meshId) > 0;
41787
+ } catch {
41788
+ }
41789
+ }
41790
+ if (hasPending) {
41791
+ recordHeldTerminalEventsToLedger(
41792
+ meshId,
41793
+ drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId ? [localDaemonId] : [],
41794
+ "modal_parked",
41795
+ modalParkedCoordinators.length
41796
+ );
41797
+ }
41640
41798
  }
41641
41799
  continue;
41642
41800
  }
@@ -41661,12 +41819,55 @@ Next step: ${nextStep}`;
41661
41819
  const mode = forceOnly ? "force-drain \u2192 generating" : "inject \u2192 idle";
41662
41820
  LOG2.info("MeshReconcile", `Reconcile ${mode}: ${pendingEvents.length} pending event(s) \u2192 ${targetCoordinators.length} coordinator(s) for mesh ${meshId}`);
41663
41821
  for (const pending of pendingEvents) {
41822
+ const wantSession = readNonEmptyString2(pending.targetCoordinatorSessionId);
41823
+ if (wantSession) {
41824
+ const matched = targetCoordinators.filter((c) => c.sessionId === wantSession);
41825
+ if (matched.length === 0) {
41826
+ holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId);
41827
+ continue;
41828
+ }
41829
+ for (const c of matched) injectPendingIntoCoordinator(c.instance, pending);
41830
+ continue;
41831
+ }
41664
41832
  for (const c of targetCoordinators) {
41665
41833
  injectPendingIntoCoordinator(c.instance, pending);
41666
41834
  }
41667
41835
  }
41668
41836
  }
41669
41837
  }
41838
+ function holdOrExpireStrictUnmatchedEvent(pending, wantSession, meshId) {
41839
+ const queuedAt = typeof pending.queuedAt === "number" ? pending.queuedAt : Date.now();
41840
+ if (Date.now() - queuedAt <= STRICT_SESSION_MATCH_TTL_MS) {
41841
+ try {
41842
+ queuePendingMeshCoordinatorEvent(pending);
41843
+ LOG2.info("MeshReconcile", `Strict route hold: coordinator session ${wantSession} not live on mesh ${meshId} \u2014 re-queued (${pending.event})`);
41844
+ } catch (e) {
41845
+ LOG2.warn("MeshReconcile", `Strict route re-queue failed for ${pending.event} on mesh ${meshId}: ${e?.message || e}`);
41846
+ }
41847
+ return;
41848
+ }
41849
+ const finalSummary = readMeshCompletionSummary(pending.metadataEvent || {});
41850
+ try {
41851
+ appendLedgerEntry(meshId, {
41852
+ kind: "event_held",
41853
+ ...pending.nodeId ? { nodeId: pending.nodeId } : {},
41854
+ payload: {
41855
+ event: pending.event,
41856
+ reason: "strict_route_expired",
41857
+ recoverable: true,
41858
+ targetCoordinatorSessionId: wantSession,
41859
+ targetCoordinatorDaemonId: pending.targetCoordinatorDaemonId ?? null,
41860
+ nodeLabel: pending.nodeLabel,
41861
+ ...pending.workspace ? { workspace: pending.workspace } : {},
41862
+ queuedAt,
41863
+ ...finalSummary ? { finalSummary } : {}
41864
+ }
41865
+ });
41866
+ LOG2.warn("MeshReconcile", `Strict route expire: coordinator session ${wantSession} never returned for mesh ${meshId} \u2014 recorded to ledger (recoverable), dropped (${pending.event})`);
41867
+ } catch (e) {
41868
+ LOG2.warn("MeshReconcile", `Failed to ledger-expire strict-unmatched ${pending.event} for mesh ${meshId}: ${e?.message || e}`);
41869
+ }
41870
+ }
41670
41871
  async function retryUnresolvedDelegateForwards(components) {
41671
41872
  const dispatchMeshCommand = components.dispatchMeshCommand;
41672
41873
  if (!dispatchMeshCommand) return;
@@ -41884,6 +42085,11 @@ Next step: ${nextStep}`;
41884
42085
  meshId: readNonEmptyString2(event?.meshId),
41885
42086
  nodeId: readNonEmptyString2(event?.nodeId) || readNonEmptyString2(metadata.meshNodeId),
41886
42087
  workspace: readNonEmptyString2(event?.workspace) || readNonEmptyString2(metadata.workspace),
42088
+ // Preserve the originating coordinator session id across the relay. It is normally
42089
+ // carried inside metadataEvent.meshCoordinatorSessionId (spread below), but pass the
42090
+ // top-level field through explicitly too so the handleMeshForwardEvent whitelist
42091
+ // recovers it regardless of which carrier the producing daemon used.
42092
+ ...readNonEmptyString2(event?.targetCoordinatorSessionId) ? { targetCoordinatorSessionId: readNonEmptyString2(event.targetCoordinatorSessionId) } : {},
41887
42093
  ...metadata
41888
42094
  };
41889
42095
  }
@@ -41908,6 +42114,8 @@ Next step: ${nextStep}`;
41908
42114
  }
41909
42115
  var DEFAULT_RECONCILE_INTERVAL_MS;
41910
42116
  var DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
42117
+ var heldEventLedgerRecorded;
42118
+ var STRICT_SESSION_MATCH_TTL_MS;
41911
42119
  var init_mesh_reconcile_loop = __esm2({
41912
42120
  "src/mesh/mesh-reconcile-loop.ts"() {
41913
42121
  "use strict";
@@ -41915,6 +42123,7 @@ Next step: ${nextStep}`;
41915
42123
  init_mesh_config();
41916
42124
  init_logger();
41917
42125
  init_mesh_events_pending();
42126
+ init_mesh_ledger();
41918
42127
  init_mesh_runtime_store();
41919
42128
  init_mesh_events_coordinator();
41920
42129
  init_mesh_unresolved_forward_outbox();
@@ -41926,6 +42135,8 @@ Next step: ${nextStep}`;
41926
42135
  init_chat_message_normalization();
41927
42136
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
41928
42137
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
42138
+ heldEventLedgerRecorded = /* @__PURE__ */ new Set();
42139
+ STRICT_SESSION_MATCH_TTL_MS = 6e4;
41929
42140
  }
41930
42141
  });
41931
42142
  var mesh_events_exports = {};
@@ -64681,7 +64892,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
64681
64892
  meshNodeFor: assignment.meshId,
64682
64893
  ...assignment.nodeId ? { meshNodeId: assignment.nodeId } : {},
64683
64894
  ...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
64684
- ...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {}
64895
+ ...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {},
64896
+ // Session-level routing anchor: the originating coordinator session, so this
64897
+ // worker's completion events route back to the exact session that dispatched it.
64898
+ ...assignment.coordinatorSessionId ? { meshCoordinatorSessionId: assignment.coordinatorSessionId } : {}
64685
64899
  };
64686
64900
  this.adapter.updateRuntimeSettings?.(this.settings);
64687
64901
  }
@@ -67657,6 +67871,12 @@ Enable and detect this provider from the Machine Providers page before starting
67657
67871
  );
67658
67872
  }
67659
67873
  const key = crypto5.randomUUID();
67874
+ {
67875
+ const coordinatorMeshId = options?.settingsOverride?.meshCoordinatorFor;
67876
+ if (typeof coordinatorMeshId === "string" && coordinatorMeshId.trim()) {
67877
+ options = { ...options, extraEnv: { ...options?.extraEnv || {}, ADHDEV_COORDINATOR_SESSION_ID: key } };
67878
+ }
67879
+ }
67660
67880
  const sessionRegistry = this.deps.getSessionRegistry?.() || null;
67661
67881
  if (provider && provider.category === "acp") {
67662
67882
  const instanceManager2 = this.deps.getInstanceManager();
@@ -74017,6 +74237,40 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
74017
74237
  const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
74018
74238
  return sessionId ? [sessionId] : [];
74019
74239
  }
74240
+ function collectMeshNodeHostedSessionIds(node) {
74241
+ const ids = /* @__PURE__ */ new Set();
74242
+ for (const id of readCachedInlineMeshActiveSessions(node)) ids.add(id);
74243
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
74244
+ for (const value of [
74245
+ node?.activeSessions,
74246
+ node?.active_sessions,
74247
+ node?.activeSessionDetails,
74248
+ node?.active_session_details,
74249
+ node?.sessions,
74250
+ node?.sessionDetails,
74251
+ node?.session_details,
74252
+ readObjectRecord(node?.lastProbe).sessions,
74253
+ readObjectRecord(node?.last_probe).sessions,
74254
+ cachedStatus.activeSessions,
74255
+ cachedStatus.active_sessions,
74256
+ cachedStatus.activeSessionDetails,
74257
+ cachedStatus.active_session_details,
74258
+ cachedStatus.sessions
74259
+ ]) {
74260
+ if (!Array.isArray(value)) continue;
74261
+ for (const item of value) {
74262
+ if (typeof item === "string") {
74263
+ const id2 = readStringValue(item);
74264
+ if (id2) ids.add(id2);
74265
+ continue;
74266
+ }
74267
+ const record2 = readObjectRecord(item);
74268
+ const id = readStringValue(record2.id, record2.sessionId, record2.session_id, record2.runtimeSessionId, record2.instanceId);
74269
+ if (id) ids.add(id);
74270
+ }
74271
+ }
74272
+ return ids;
74273
+ }
74020
74274
  function resolveMeshNodeAttribution(node) {
74021
74275
  const record2 = readObjectRecord(node);
74022
74276
  return {
@@ -75895,25 +76149,47 @@ ${mergeTreeErr?.stderr || ""}`;
75895
76149
  * controlbar commands do not — so the controlbar buttons appear to do nothing.
75896
76150
  *
75897
76151
  * Mirror the existing node-level remote-forward pattern (fast_forward_mesh_node etc.):
75898
- * scan the cached inline-mesh nodes for the one whose active session matches the
75899
- * targetSessionId, and return its daemonId when that daemonId is a remote daemon (i.e.
75900
- * not this coordinator's own statusInstanceId). Returns undefined for a locally-hosted
75901
- * session (no forward — execute locally as before) or when ownership can't be resolved.
76152
+ * scan the candidate mesh nodes for the one hosting the targetSessionId, and return its
76153
+ * daemonId when that daemonId is a remote daemon (i.e. not this coordinator's own
76154
+ * statusInstanceId). Returns undefined for a locally-hosted session (no forward — execute
76155
+ * locally as before) or when ownership can't be resolved.
76156
+ *
76157
+ * The candidate set spans BOTH the cached inline-mesh nodes and the live aggregate
76158
+ * mesh-status snapshots. The inline cache reliably carries only each node's single primary
76159
+ * session (cachedStatus.activeSession), so a worker hosting more than one session exposes its
76160
+ * non-primary sessions only on the aggregate snapshot nodes (status.activeSessions /
76161
+ * activeSessionDetails, built from live session records). collectMeshNodeHostedSessionIds does
76162
+ * the wider plural-shape scan so a controlbar/modal command targeting a non-primary remote
76163
+ * session still resolves its owner — the singular readCachedInlineMeshActiveSessions semantics
76164
+ * other consumers depend on stay untouched.
75902
76165
  */
75903
76166
  resolveRemoteMeshSessionOwnerDaemonId(sessionId) {
75904
76167
  const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
75905
76168
  if (!trimmed) return void 0;
75906
76169
  const selfDaemonId = this.deps.statusInstanceId;
75907
- for (const node of this.getCachedInlineMeshNodes()) {
75908
- const nodeSessions = readCachedInlineMeshActiveSessions(node);
75909
- if (!nodeSessions.includes(trimmed)) continue;
76170
+ for (const node of this.collectMeshSessionOwnerCandidateNodes()) {
76171
+ if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
75910
76172
  const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
75911
- if (!nodeDaemonId) return void 0;
76173
+ if (!nodeDaemonId) continue;
75912
76174
  if (selfDaemonId && nodeDaemonId === selfDaemonId) return void 0;
75913
76175
  return nodeDaemonId;
75914
76176
  }
75915
76177
  return void 0;
75916
76178
  }
76179
+ /**
76180
+ * Candidate nodes for remote-session owner resolution: the cached inline-mesh nodes (which
76181
+ * carry each node's primary session) plus the nodes from every cached aggregate mesh-status
76182
+ * snapshot (which carry each node's full live session list). getCachedInlineMeshNodes()
76183
+ * returns a fresh array, so appending the aggregate nodes never mutates cached state.
76184
+ */
76185
+ collectMeshSessionOwnerCandidateNodes() {
76186
+ const nodes = this.getCachedInlineMeshNodes();
76187
+ for (const cached3 of this.aggregateMeshStatusCache.values()) {
76188
+ const snapshotNodes = cached3?.snapshot?.nodes;
76189
+ if (Array.isArray(snapshotNodes)) nodes.push(...snapshotNodes);
76190
+ }
76191
+ return nodes;
76192
+ }
75917
76193
  getCachedInlineMesh(meshId, inlineMesh) {
75918
76194
  if (inlineMesh && typeof inlineMesh === "object") {
75919
76195
  return this.warmInlineMeshCache(meshId, inlineMesh);
@@ -77933,7 +78209,10 @@ ${hintLines.join("\n")}` : "",
77933
78209
  {
77934
78210
  meshId: dispatchMeshContext.meshId,
77935
78211
  nodeId: dispatchMeshContext.nodeId,
77936
- coordinatorDaemonId: dispatchMeshContext.coordinatorDaemonId
78212
+ coordinatorDaemonId: dispatchMeshContext.coordinatorDaemonId,
78213
+ // Session-level anchor: preserved across the P2P dispatch to a
78214
+ // remote worker so its completion echoes back to the right session.
78215
+ coordinatorSessionId: dispatchMeshContext.coordinatorSessionId
77937
78216
  }
77938
78217
  );
77939
78218
  if (stamp) inst.updateSettings(stamp);