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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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 ? "ff8ffda8c3d1a14f846ad4d785e4cab4d111a36b" : void 0) ?? "unknown";
30040
- const commitShort = readInjected(true ? "ff8ffda8" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30041
- const version2 = readInjected(true ? "0.9.82-rc.342" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30042
- const builtAt = readInjected(true ? "2026-06-21T05:13:50.125Z" : 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,
@@ -39569,7 +39655,16 @@ Next step: ${nextStep}`;
39569
39655
  }).catch((e) => {
39570
39656
  LOG2.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
39571
39657
  updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
39572
- updateTaskStatus(meshId, task.id, "failed");
39658
+ updateTaskStatus(meshId, task.id, "pending");
39659
+ try {
39660
+ appendLedgerEntry(meshId, {
39661
+ kind: "dispatch_failed",
39662
+ nodeId,
39663
+ sessionId,
39664
+ payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
39665
+ });
39666
+ } catch {
39667
+ }
39573
39668
  });
39574
39669
  return true;
39575
39670
  }
@@ -40198,6 +40293,9 @@ Next step: ${nextStep}`;
40198
40293
  const workerCoordinatorDaemonId = readNonEmptyString2(
40199
40294
  sourceSession?.getState()?.settings?.meshCoordinatorDaemonId
40200
40295
  );
40296
+ const workerCoordinatorSessionId = readNonEmptyString2(
40297
+ sourceSession?.getState()?.settings?.meshCoordinatorSessionId
40298
+ ) || readNonEmptyString2(args.metadataEvent.meshCoordinatorSessionId);
40201
40299
  if (components.onMeshCoordinatorEventForwarded) {
40202
40300
  try {
40203
40301
  const surfacedPreview = resolveMeshSurfacedSessionPreview(args.metadataEvent);
@@ -40578,7 +40676,12 @@ Next step: ${nextStep}`;
40578
40676
  workspace: readNonEmptyString2(args.metadataEvent.workspace) || readNonEmptyString2(args.metadataEvent.workspaceName),
40579
40677
  metadataEvent: {
40580
40678
  ...args.metadataEvent,
40581
- ...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 } : {}
40582
40685
  },
40583
40686
  // Silent lifecycle events (agent:ready / agent:generating_started) carry no
40584
40687
  // coordinator message; they are queued only so the coordinator re-runs the
@@ -40586,10 +40689,13 @@ Next step: ${nextStep}`;
40586
40689
  // entries without a coordinatorMessage, so a live CLI coordinator is not spammed.
40587
40690
  ...messageText ? { coordinatorMessage: messageText } : {},
40588
40691
  queuedAt: Date.now(),
40589
- ...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 } : {}
40590
40696
  };
40591
40697
  if (queuePendingMeshCoordinatorEvent(pendingEvent)) {
40592
- 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}` : ""})`);
40593
40699
  }
40594
40700
  return { success: true, forwarded: 0 };
40595
40701
  }
@@ -40614,6 +40720,13 @@ Next step: ${nextStep}`;
40614
40720
  targetSessionId: readNonEmptyString2(payload.targetSessionId) || readNonEmptyString2(payload.sessionId) || readNonEmptyString2(payload.instanceId),
40615
40721
  providerType: readNonEmptyString2(payload.providerType),
40616
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),
40617
40730
  // Carry the session identity fields the worker provider event emits so the
40618
40731
  // coordinator's mirror (updateMeshOwnedSession) gets a real workspace/title/
40619
40732
  // settings. Without these the remote-relay hop reconstructs metadataEvent with
@@ -41532,7 +41645,8 @@ Next step: ${nextStep}`;
41532
41645
  if (!meshId) continue;
41533
41646
  const status = readNonEmptyString2(state.status).toLowerCase();
41534
41647
  const modalParked = status === "waiting_choice" || status === "waiting_approval";
41535
- 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 });
41536
41650
  }
41537
41651
  return out;
41538
41652
  }
@@ -41544,6 +41658,44 @@ Next step: ${nextStep}`;
41544
41658
  ...force ? { force: true } : {}
41545
41659
  });
41546
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
+ }
41547
41699
  async function runMeshReconcileTick(components) {
41548
41700
  const localDaemonId = readNonEmptyString2(loadConfig2().machineId) || void 0;
41549
41701
  const drainDaemonIds = resolveCoordinatorDaemonIds(components);
@@ -41628,6 +41780,21 @@ Next step: ${nextStep}`;
41628
41780
  if (targetCoordinators.length === 0) {
41629
41781
  if (modalParkedCoordinators.length > 0) {
41630
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
+ }
41631
41798
  }
41632
41799
  continue;
41633
41800
  }
@@ -41652,12 +41819,55 @@ Next step: ${nextStep}`;
41652
41819
  const mode = forceOnly ? "force-drain \u2192 generating" : "inject \u2192 idle";
41653
41820
  LOG2.info("MeshReconcile", `Reconcile ${mode}: ${pendingEvents.length} pending event(s) \u2192 ${targetCoordinators.length} coordinator(s) for mesh ${meshId}`);
41654
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
+ }
41655
41832
  for (const c of targetCoordinators) {
41656
41833
  injectPendingIntoCoordinator(c.instance, pending);
41657
41834
  }
41658
41835
  }
41659
41836
  }
41660
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
+ }
41661
41871
  async function retryUnresolvedDelegateForwards(components) {
41662
41872
  const dispatchMeshCommand = components.dispatchMeshCommand;
41663
41873
  if (!dispatchMeshCommand) return;
@@ -41875,6 +42085,11 @@ Next step: ${nextStep}`;
41875
42085
  meshId: readNonEmptyString2(event?.meshId),
41876
42086
  nodeId: readNonEmptyString2(event?.nodeId) || readNonEmptyString2(metadata.meshNodeId),
41877
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) } : {},
41878
42093
  ...metadata
41879
42094
  };
41880
42095
  }
@@ -41899,6 +42114,8 @@ Next step: ${nextStep}`;
41899
42114
  }
41900
42115
  var DEFAULT_RECONCILE_INTERVAL_MS;
41901
42116
  var DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
42117
+ var heldEventLedgerRecorded;
42118
+ var STRICT_SESSION_MATCH_TTL_MS;
41902
42119
  var init_mesh_reconcile_loop = __esm2({
41903
42120
  "src/mesh/mesh-reconcile-loop.ts"() {
41904
42121
  "use strict";
@@ -41906,6 +42123,7 @@ Next step: ${nextStep}`;
41906
42123
  init_mesh_config();
41907
42124
  init_logger();
41908
42125
  init_mesh_events_pending();
42126
+ init_mesh_ledger();
41909
42127
  init_mesh_runtime_store();
41910
42128
  init_mesh_events_coordinator();
41911
42129
  init_mesh_unresolved_forward_outbox();
@@ -41917,6 +42135,8 @@ Next step: ${nextStep}`;
41917
42135
  init_chat_message_normalization();
41918
42136
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
41919
42137
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
42138
+ heldEventLedgerRecorded = /* @__PURE__ */ new Set();
42139
+ STRICT_SESSION_MATCH_TTL_MS = 6e4;
41920
42140
  }
41921
42141
  });
41922
42142
  var mesh_events_exports = {};
@@ -61621,7 +61841,8 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61621
61841
  return n;
61622
61842
  }
61623
61843
  var SUBMIT_DELAY_FLOOR_MS = 200;
61624
- var WIN32_SUBMIT_REPEAT_GAP_MS = 300;
61844
+ var WIN32_SUBMIT_RESEND_GAP_MS = 350;
61845
+ var WIN32_SUBMIT_MAX_RESENDS = 14;
61625
61846
  function resolveSubmitDelayMs(specBeforeSubmit, text) {
61626
61847
  const lines = countNewlines(text);
61627
61848
  const linesBonus = Math.min(800, lines * 80);
@@ -61673,6 +61894,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61673
61894
  * after a re-prime we don't re-inject until the screen changes (which
61674
61895
  * resets the stall reference) or another full stall window lapses. */
61675
61896
  lastRefocusAt = 0;
61897
+ /** Timer driving the win32 verification-based submit resend loop (see
61898
+ * WIN32_SUBMIT_* and scheduleWin32Submit). Re-arms itself until the FSM
61899
+ * leaves idle (submitted) or the resend budget is spent. */
61900
+ win32SubmitTimer = null;
61676
61901
  currentEval = null;
61677
61902
  stateHistory = [];
61678
61903
  prevStateAt = 0;
@@ -61789,6 +62014,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61789
62014
  clearTimeout(this.stallTimer);
61790
62015
  this.stallTimer = null;
61791
62016
  }
62017
+ if (this.win32SubmitTimer) {
62018
+ clearTimeout(this.win32SubmitTimer);
62019
+ this.win32SubmitTimer = null;
62020
+ }
61792
62021
  this.specWatcher?.close();
61793
62022
  this.adapter.kill();
61794
62023
  }
@@ -62201,13 +62430,8 @@ ${formatManifestValidationIssues2(validation.issues)}`,
62201
62430
  const perChar = sm.delay_ms_per_char ?? 0;
62202
62431
  const beforeSubmit = resolveSubmitDelayMs(sm.delay_ms_before_submit, text);
62203
62432
  if (process.platform === "win32") {
62204
- const submitTwice = () => {
62205
- this.adapter.send_keys(sm.submit_key);
62206
- setTimeout(() => this.adapter.send_keys(sm.submit_key), WIN32_SUBMIT_REPEAT_GAP_MS);
62207
- };
62208
62433
  this.adapter.send_keys(text);
62209
- if (beforeSubmit > 0) setTimeout(submitTwice, beforeSubmit);
62210
- else submitTwice();
62434
+ this.scheduleWin32Submit(sm.submit_key, beforeSubmit);
62211
62435
  return;
62212
62436
  }
62213
62437
  if (perChar === 0) {
@@ -62227,6 +62451,40 @@ ${formatManifestValidationIssues2(validation.issues)}`,
62227
62451
  i += 1;
62228
62452
  }, perChar);
62229
62453
  }
62454
+ /** The agent's current coarse status, derived from the FSM node we're in. */
62455
+ currentStatus() {
62456
+ const st = stateById(this.spec, this.currentStateId);
62457
+ return st ? statusForState(st) : "idle";
62458
+ }
62459
+ /**
62460
+ * win32 verification-based submit. Sends the submit key, waits a gap, and if
62461
+ * the FSM is still 'idle' (the prompt did not submit — the CR was absorbed as
62462
+ * a multiline-paste newline) resends, up to WIN32_SUBMIT_MAX_RESENDS. The
62463
+ * first CR always fires (so a stale/edge status never suppresses the submit);
62464
+ * subsequent resends are gated on still being idle, and stop the instant the
62465
+ * agent leaves idle (submitted → generating / approval). This converges the
62466
+ * nondeterministic multiline window without spamming Enter into the next turn.
62467
+ */
62468
+ scheduleWin32Submit(submitKey, initialDelayMs) {
62469
+ if (this.win32SubmitTimer) {
62470
+ clearTimeout(this.win32SubmitTimer);
62471
+ this.win32SubmitTimer = null;
62472
+ }
62473
+ const fire = (attempt) => {
62474
+ this.win32SubmitTimer = null;
62475
+ this.adapter.send_keys(submitKey);
62476
+ if (attempt + 1 >= WIN32_SUBMIT_MAX_RESENDS) return;
62477
+ this.win32SubmitTimer = setTimeout(() => {
62478
+ if (this.currentStatus() !== "idle") {
62479
+ this.win32SubmitTimer = null;
62480
+ return;
62481
+ }
62482
+ fire(attempt + 1);
62483
+ }, WIN32_SUBMIT_RESEND_GAP_MS);
62484
+ };
62485
+ if (initialDelayMs > 0) this.win32SubmitTimer = setTimeout(() => fire(0), initialDelayMs);
62486
+ else fire(0);
62487
+ }
62230
62488
  handleClickControl(controlId, payload) {
62231
62489
  const ctl = (this.spec.control_bar ?? []).find((c) => c.id === controlId);
62232
62490
  if (!ctl) return;
@@ -64634,7 +64892,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
64634
64892
  meshNodeFor: assignment.meshId,
64635
64893
  ...assignment.nodeId ? { meshNodeId: assignment.nodeId } : {},
64636
64894
  ...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
64637
- ...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 } : {}
64638
64899
  };
64639
64900
  this.adapter.updateRuntimeSettings?.(this.settings);
64640
64901
  }
@@ -67610,6 +67871,12 @@ Enable and detect this provider from the Machine Providers page before starting
67610
67871
  );
67611
67872
  }
67612
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
+ }
67613
67880
  const sessionRegistry = this.deps.getSessionRegistry?.() || null;
67614
67881
  if (provider && provider.category === "acp") {
67615
67882
  const instanceManager2 = this.deps.getInstanceManager();
@@ -73970,6 +74237,40 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
73970
74237
  const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
73971
74238
  return sessionId ? [sessionId] : [];
73972
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
+ }
73973
74274
  function resolveMeshNodeAttribution(node) {
73974
74275
  const record2 = readObjectRecord(node);
73975
74276
  return {
@@ -75848,25 +76149,47 @@ ${mergeTreeErr?.stderr || ""}`;
75848
76149
  * controlbar commands do not — so the controlbar buttons appear to do nothing.
75849
76150
  *
75850
76151
  * Mirror the existing node-level remote-forward pattern (fast_forward_mesh_node etc.):
75851
- * scan the cached inline-mesh nodes for the one whose active session matches the
75852
- * targetSessionId, and return its daemonId when that daemonId is a remote daemon (i.e.
75853
- * not this coordinator's own statusInstanceId). Returns undefined for a locally-hosted
75854
- * 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.
75855
76165
  */
75856
76166
  resolveRemoteMeshSessionOwnerDaemonId(sessionId) {
75857
76167
  const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
75858
76168
  if (!trimmed) return void 0;
75859
76169
  const selfDaemonId = this.deps.statusInstanceId;
75860
- for (const node of this.getCachedInlineMeshNodes()) {
75861
- const nodeSessions = readCachedInlineMeshActiveSessions(node);
75862
- if (!nodeSessions.includes(trimmed)) continue;
76170
+ for (const node of this.collectMeshSessionOwnerCandidateNodes()) {
76171
+ if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
75863
76172
  const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
75864
- if (!nodeDaemonId) return void 0;
76173
+ if (!nodeDaemonId) continue;
75865
76174
  if (selfDaemonId && nodeDaemonId === selfDaemonId) return void 0;
75866
76175
  return nodeDaemonId;
75867
76176
  }
75868
76177
  return void 0;
75869
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
+ }
75870
76193
  getCachedInlineMesh(meshId, inlineMesh) {
75871
76194
  if (inlineMesh && typeof inlineMesh === "object") {
75872
76195
  return this.warmInlineMeshCache(meshId, inlineMesh);
@@ -77886,7 +78209,10 @@ ${hintLines.join("\n")}` : "",
77886
78209
  {
77887
78210
  meshId: dispatchMeshContext.meshId,
77888
78211
  nodeId: dispatchMeshContext.nodeId,
77889
- 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
77890
78216
  }
77891
78217
  );
77892
78218
  if (stamp) inst.updateSettings(stamp);