@adhdev/daemon-core 0.9.82-rc.421 → 0.9.82-rc.422

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
@@ -389,10 +389,10 @@ function readInjected(value) {
389
389
  }
390
390
  function getDaemonBuildInfo() {
391
391
  if (cached) return cached;
392
- const commit = readInjected(true ? "ae90493419784d563faac61e6caab2a6f05e0719" : void 0) ?? "unknown";
393
- const commitShort = readInjected(true ? "ae904934" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
394
- const version = readInjected(true ? "0.9.82-rc.421" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
395
- const builtAt = readInjected(true ? "2026-06-29T03:34:21.633Z" : void 0);
392
+ const commit = readInjected(true ? "308e099c65d2de2491f3db46913f9ced6aa3992c" : void 0) ?? "unknown";
393
+ const commitShort = readInjected(true ? "308e099c" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
394
+ const version = readInjected(true ? "0.9.82-rc.422" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
395
+ const builtAt = readInjected(true ? "2026-06-29T05:26:31.350Z" : void 0);
396
396
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
397
397
  return cached;
398
398
  }
@@ -3686,21 +3686,6 @@ var init_async_batch_writer = __esm({
3686
3686
  });
3687
3687
 
3688
3688
  // src/logging/logger.ts
3689
- var logger_exports = {};
3690
- __export(logger_exports, {
3691
- LOG: () => LOG,
3692
- LOG_DIR_PATH: () => LOG_DIR_PATH,
3693
- LOG_PATH: () => LOG_PATH,
3694
- daemonLog: () => daemonLog,
3695
- getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
3696
- getDaemonLogDir: () => getDaemonLogDir,
3697
- getLogBufferSize: () => getLogBufferSize,
3698
- getLogLevel: () => getLogLevel,
3699
- getLogPath: () => getLogPath,
3700
- getRecentLogs: () => getRecentLogs,
3701
- installGlobalInterceptor: () => installGlobalInterceptor,
3702
- setLogLevel: () => setLogLevel
3703
- });
3704
3689
  function setLogLevel(level) {
3705
3690
  currentLevel = level;
3706
3691
  daemonLog("Logger", `Log level set to: ${level}`, "info");
@@ -3772,9 +3757,6 @@ function getRecentLogs(count = 50, minLevel = "info") {
3772
3757
  const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
3773
3758
  return filtered.slice(-count);
3774
3759
  }
3775
- function getLogBufferSize() {
3776
- return ringBuffer.length;
3777
- }
3778
3760
  function ts() {
3779
3761
  return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
3780
3762
  }
@@ -3854,10 +3836,7 @@ function installGlobalInterceptor() {
3854
3836
  writeToFile(`Log file: ${currentLogFile}`);
3855
3837
  writeToFile(`Log level: ${currentLevel}`);
3856
3838
  }
3857
- function getLogPath() {
3858
- return currentLogFile;
3859
- }
3860
- var fs4, path9, os3, LEVEL_NUM, LEVEL_LABEL, currentLevel, ADHDEV_HOME, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
3839
+ var fs4, path9, os3, LEVEL_NUM, LEVEL_LABEL, currentLevel, ADHDEV_HOME, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH;
3861
3840
  var init_logger = __esm({
3862
3841
  "src/logging/logger.ts"() {
3863
3842
  "use strict";
@@ -3919,7 +3898,6 @@ var init_logger = __esm({
3919
3898
  };
3920
3899
  interceptorInstalled = false;
3921
3900
  LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
3922
- LOG_DIR_PATH = LOG_DIR;
3923
3901
  }
3924
3902
  });
3925
3903
 
@@ -12528,6 +12506,23 @@ function liveSessionCountForNode(components, meshId, nodeId) {
12528
12506
  return !isTerminalSessionStatus(status);
12529
12507
  }).length;
12530
12508
  }
12509
+ function nodeHasLiveSessionPendingClaim(components, meshId, nodeId) {
12510
+ const busySessionIds = new Set(
12511
+ getQueue(meshId, { status: ["assigned"] }).filter((task) => daemonIdsEquivalent(task.assignedNodeId, nodeId)).map((task) => readNonEmptyString2(task.assignedSessionId)).filter(Boolean)
12512
+ );
12513
+ return components.instanceManager.getByCategory("cli").some((inst) => {
12514
+ const state = inst.getState();
12515
+ const settings = state.settings || {};
12516
+ if (readNonEmptyString2(settings.meshNodeFor) !== meshId) return false;
12517
+ const instNodeId = readNonEmptyString2(settings.meshNodeId) || readNonEmptyString2(settings.nodeId);
12518
+ if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
12519
+ const status = readNonEmptyString2(state.status).toLowerCase();
12520
+ if (isTerminalSessionStatus(status)) return false;
12521
+ const sessionId = readNonEmptyString2(state.instanceId);
12522
+ if (sessionId && busySessionIds.has(sessionId)) return false;
12523
+ return true;
12524
+ });
12525
+ }
12531
12526
  function recordAutoLaunchEvent(meshId, args) {
12532
12527
  const dedupKey = `${meshId}:${args.taskId}`;
12533
12528
  const currentSig = `${args.phase}|${args.reason || ""}`;
@@ -12713,6 +12708,10 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
12713
12708
  sweepExpiredCooldowns();
12714
12709
  continue;
12715
12710
  }
12711
+ if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId)) {
12712
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_live_session_pending_claim", nodeId });
12713
+ continue;
12714
+ }
12716
12715
  if (!isTaskReadonly(task) && nodeHasActiveAssignment(meshId, nodeId)) {
12717
12716
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_active_assignment", nodeId });
12718
12717
  continue;
@@ -44986,8 +44985,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
44986
44985
  const meshContext = args?.meshContext;
44987
44986
  if (meshContext && typeof meshContext === "object" && typeof meshContext.meshId === "string" && meshContext.meshId) {
44988
44987
  const targetInstanceId = key2;
44988
+ let stampResult;
44989
44989
  try {
44990
- this.deps.getInstanceManager()?.attachMeshAssignmentToInstance(targetInstanceId, {
44990
+ stampResult = this.deps.getInstanceManager()?.attachMeshAssignmentToInstance(targetInstanceId, {
44991
44991
  meshId: meshContext.meshId,
44992
44992
  ...typeof meshContext.nodeId === "string" && meshContext.nodeId ? { nodeId: meshContext.nodeId } : {},
44993
44993
  ...typeof meshContext.taskId === "string" && meshContext.taskId ? { taskId: meshContext.taskId } : {},
@@ -44995,6 +44995,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
44995
44995
  });
44996
44996
  } catch {
44997
44997
  }
44998
+ if (stampResult && stampResult.stamped === false && stampResult.reason === "task_already_stamped_on_live_instance") {
44999
+ throw new Error(`Refusing duplicate mesh dispatch: task ${meshContext.taskId} is already being worked by a live session on this daemon`);
45000
+ }
44998
45001
  }
44999
45002
  const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
45000
45003
  const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
@@ -58602,25 +58605,54 @@ var ProviderInstanceManager = class {
58602
58605
  }
58603
58606
  /** Stamp a mesh assignment on a single instance (used by mesh_send_task
58604
58607
  * --direct so the worker's completion event has a coordinator routing
58605
- * marker in state.settings). Returns true if the instance existed and
58606
- * the stamp was applied. */
58608
+ * marker in state.settings). Returns `{ stamped: true }` when the stamp was
58609
+ * applied, or `{ stamped: false, reason }` when it was refused — the instance
58610
+ * was missing / has no attach method, or the DOUBLE-DISPATCH idempotence guard
58611
+ * fired (the same task is already running on another live session here). */
58607
58612
  attachMeshAssignmentToInstance(instanceId, assignment) {
58608
58613
  const inst = this.instances.get(instanceId);
58609
58614
  if (!inst || typeof inst.attachMeshAssignment !== "function") {
58610
- try {
58611
- const { LOG: LOG2 } = (init_logger(), __toCommonJS(logger_exports));
58612
- LOG2.warn?.("MeshDispatch", `attachMeshAssignment skipped: instance ${instanceId} ${inst ? "has no attach method" : "not found"}`);
58613
- } catch {
58615
+ LOG.warn("MeshDispatch", `attachMeshAssignment skipped: instance ${instanceId} ${inst ? "has no attach method" : "not found"}`);
58616
+ return { stamped: false, reason: inst ? "instance_has_no_attach_method" : "instance_not_found" };
58617
+ }
58618
+ if (assignment.taskId) {
58619
+ const conflict = this.findLiveWorkingTaskHolder(assignment.meshId, assignment.taskId, instanceId);
58620
+ if (conflict) {
58621
+ LOG.warn("MeshDispatch", `attachMeshAssignment refused: task ${assignment.taskId} (mesh ${assignment.meshId}) is already being worked by live session ${conflict} \u2014 skipping duplicate stamp on ${instanceId}`);
58622
+ return { stamped: false, reason: "task_already_stamped_on_live_instance" };
58614
58623
  }
58615
- return false;
58616
58624
  }
58617
58625
  inst.attachMeshAssignment(assignment);
58618
- try {
58619
- const { LOG: LOG2 } = (init_logger(), __toCommonJS(logger_exports));
58620
- LOG2.info?.("MeshDispatch", `stamped mesh assignment on ${instanceId}: mesh=${assignment.meshId} node=${assignment.nodeId || ""} task=${assignment.taskId || ""} coordinator=${assignment.coordinatorDaemonId || ""}`);
58621
- } catch {
58626
+ LOG.info("MeshDispatch", `stamped mesh assignment on ${instanceId}: mesh=${assignment.meshId} node=${assignment.nodeId || ""} task=${assignment.taskId || ""} coordinator=${assignment.coordinatorDaemonId || ""}`);
58627
+ return { stamped: true };
58628
+ }
58629
+ /**
58630
+ * DOUBLE-DISPATCH support: the id of another LIVE, actively-working instance that already
58631
+ * holds (meshId, taskId), or null. "Live working" = stamped with this exact mesh+task AND
58632
+ * currently mid-turn / booting toward it (generating / waiting on approval-or-choice /
58633
+ * starting) — NOT idle, stopped, or errored. A stale/dead/idle holder is deliberately
58634
+ * ignored so a legitimate re-dispatch (e.g. after a dispatch failure) is never blocked.
58635
+ * The instance being stamped (excludeInstanceId) is skipped so re-stamping the same
58636
+ * session stays idempotent. O(n) over instances — the count is small.
58637
+ */
58638
+ findLiveWorkingTaskHolder(meshId, taskId, excludeInstanceId) {
58639
+ const working = /* @__PURE__ */ new Set(["generating", "waiting_approval", "waiting_choice", "starting", "streaming", "working", "no_progress", "long_generating"]);
58640
+ for (const [id, inst] of this.instances) {
58641
+ if (id === excludeInstanceId) continue;
58642
+ let state;
58643
+ try {
58644
+ state = inst.getState();
58645
+ } catch {
58646
+ continue;
58647
+ }
58648
+ const settings = state.settings || {};
58649
+ if (settings.meshNodeFor !== meshId) continue;
58650
+ if (settings.meshActiveTaskId !== taskId) continue;
58651
+ const status = (typeof state.status === "string" ? state.status : "").toLowerCase();
58652
+ const chatStatus = (typeof state.activeChat?.status === "string" ? state.activeChat.status : "").toLowerCase();
58653
+ if (working.has(status) || working.has(chatStatus)) return id;
58622
58654
  }
58623
- return true;
58655
+ return null;
58624
58656
  }
58625
58657
  /** Clear a mesh assignment after the dispatched task reaches a terminal
58626
58658
  * state (generating_completed / stopped / failed). */