@adhdev/daemon-standalone 0.9.82-rc.371 → 0.9.82-rc.373

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-standalone",
3
- "version": "0.9.82-rc.371",
3
+ "version": "0.9.82-rc.373",
4
4
  "description": "ADHDev standalone daemon — embedded HTTP/WS server for local dashboard",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1210,6 +1210,7 @@ function summarizeCompactSubmodules(submodules) {
1210
1210
  ...outOfSync.length > 0 ? { outOfSyncPaths: outOfSync } : {}
1211
1211
  };
1212
1212
  }
1213
+ var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness"];
1213
1214
  function compactMeshStatusNode(entry) {
1214
1215
  if (!entry || typeof entry !== "object") return entry;
1215
1216
  const next = { ...entry };
@@ -1248,8 +1249,9 @@ function compactMeshStatusNode(entry) {
1248
1249
  };
1249
1250
  }
1250
1251
  delete next.capabilityTagsByProvider;
1252
+ const elideSkip = /* @__PURE__ */ new Set(["git", "machine", "branchConvergence", "staleDaemonBuild", "sessions", ...MESH_COMPACT_PRESERVED_MARKER_FIELDS]);
1251
1253
  for (const k of Object.keys(next)) {
1252
- if (k === "git" || k === "machine" || k === "branchConvergence" || k === "staleDaemonBuild" || k === "sessions" || k === "dataFreshness") continue;
1254
+ if (elideSkip.has(k)) continue;
1253
1255
  next[k] = elideLargeNestedValue(k, next[k]);
1254
1256
  }
1255
1257
  return next;
@@ -1289,6 +1291,10 @@ function minimalCompactNode(entry) {
1289
1291
  reason: entry.branchConvergence.reason,
1290
1292
  branch: entry.branchConvergence.branch
1291
1293
  } : void 0;
1294
+ const preservedMarkers = {};
1295
+ for (const field of MESH_COMPACT_PRESERVED_MARKER_FIELDS) {
1296
+ if (entry[field] !== void 0) preservedMarkers[field] = entry[field];
1297
+ }
1292
1298
  return {
1293
1299
  nodeId: entry.nodeId,
1294
1300
  workspace: entry.workspace,
@@ -1303,10 +1309,7 @@ function minimalCompactNode(entry) {
1303
1309
  ...entry.launchBlockedReason !== void 0 ? { launchBlockedReason: entry.launchBlockedReason } : {},
1304
1310
  ...bc ? { branchConvergence: bc } : {},
1305
1311
  ...entry.sessionSummary ? { sessionSummary: entry.sessionSummary } : {},
1306
- // The freshness/reachability marker is exactly the signal a coordinator needs
1307
- // on a QUIET node — is this idle peer live, cached, or unreachable? — and it is
1308
- // tiny (6 scalar fields), so keep it even on the minimal stub.
1309
- ...entry.dataFreshness !== void 0 ? { dataFreshness: entry.dataFreshness } : {},
1312
+ ...preservedMarkers,
1310
1313
  folded: true
1311
1314
  };
1312
1315
  }
@@ -2867,22 +2870,12 @@ async function meshStatus(ctx, args = {}) {
2867
2870
  noFallbackReason: failure.noFallbackReason
2868
2871
  });
2869
2872
  }
2870
- const freshnessDaemonId = readNodeDaemonId(node);
2871
- const freshnessStatus = {
2873
+ entry.dataFreshness = (0, import_daemon_core.buildMeshNodeProbeFreshness)({
2872
2874
  git: entry.git,
2873
- connection: { state: liveTruthProbed ? "connected" : "disconnected" }
2874
- };
2875
- if (liveTruthProbed) freshnessStatus[import_daemon_core.MESH_NODE_LIVE_TRUTH_MARKER] = true;
2876
- entry.dataFreshness = (0, import_daemon_core.buildMeshNodeDataFreshness)({
2877
- status: freshnessStatus,
2878
- node,
2879
- isSelfNode: entry.machine?.sameMachine === true,
2880
- daemonId: freshnessDaemonId,
2881
2875
  liveTruthProbed,
2882
- // A probe that threw against a configured (daemonId-bearing) peer is an
2883
- // unreachable peer; an unconfigured node (no daemonId) falls through to
2884
- // the classifier's `unconfigured` branch instead of being mislabeled.
2885
- directTruthUnavailable: !liveTruthProbed && !!freshnessDaemonId
2876
+ isSelfNode: entry.machine?.sameMachine === true,
2877
+ daemonId: readNodeDaemonId(node),
2878
+ node
2886
2879
  });
2887
2880
  const recoveryContext = (0, import_daemon_core.getSessionRecoveryContext)(mesh.id, { nodeId: node.id });
2888
2881
  if (recoveryContext.consecutiveNodeFailures > 0) {
@@ -3754,6 +3747,19 @@ async function meshSendTask(ctx, args) {
3754
3747
  if (node.policy?.readOnly) {
3755
3748
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
3756
3749
  }
3750
+ if (taskMode === "convergence" && node.isLocalWorktree === true) {
3751
+ return JSON.stringify({
3752
+ success: false,
3753
+ recoverable: true,
3754
+ code: "mesh_convergence_target_is_worktree",
3755
+ reason: "mesh_convergence_target_is_worktree",
3756
+ nodeId: args.node_id,
3757
+ sessionId: args.session_id,
3758
+ taskMode,
3759
+ error: `Node '${args.node_id}' is a worktree clone; a convergence task is base-only (it merges/pushes onto base). Dispatching it to a worktree session risks a multi-worktree push/deploy race.`,
3760
+ nextAction: `Dispatch the convergence task to the base node for this mesh, or run the deterministic fast-forward convergence path (mesh_fast_forward_node / mesh_refine_node) instead of mesh_send_task.`
3761
+ });
3762
+ }
3757
3763
  let explicitTargetSession;
3758
3764
  if (args.session_id && isWorkerTaskMode(taskMode)) {
3759
3765
  try {