@adhdev/daemon-core 0.9.82-rc.47 → 0.9.82-rc.49

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
@@ -662,17 +662,91 @@ var init_config = __esm({
662
662
  }
663
663
  });
664
664
 
665
+ // src/mesh/mesh-host-ownership.ts
666
+ function readObject(value) {
667
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
668
+ }
669
+ function readString(value) {
670
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
671
+ }
672
+ function normalizeMeshDaemonRole(value) {
673
+ return value === "host" || value === "member" ? value : void 0;
674
+ }
675
+ function resolveMeshHostStatus(mesh) {
676
+ const meshRecord = readObject(mesh);
677
+ const raw = readObject(meshRecord?.meshHost);
678
+ const role = normalizeMeshDaemonRole(raw?.role) ?? "host";
679
+ const pairing = readObject(raw?.pairing);
680
+ const normalized = {
681
+ role,
682
+ canOwnCoordinator: role === "host",
683
+ canOwnQueue: role === "host",
684
+ defaulted: !raw
685
+ };
686
+ const hostDaemonId = readString(raw?.hostDaemonId);
687
+ const hostNodeId = readString(raw?.hostNodeId);
688
+ const hostAddress = readString(raw?.hostAddress);
689
+ if (hostDaemonId) normalized.hostDaemonId = hostDaemonId;
690
+ if (hostNodeId) normalized.hostNodeId = hostNodeId;
691
+ if (hostAddress) normalized.hostAddress = hostAddress;
692
+ if (pairing) {
693
+ const status = pairing.status === "pairing" || pairing.status === "paired" || pairing.status === "rejected" || pairing.status === "revoked" ? pairing.status : "not_configured";
694
+ normalized.pairing = {
695
+ status,
696
+ ...readString(pairing.tokenId) ? { tokenId: readString(pairing.tokenId) } : {},
697
+ ...readString(pairing.joinedAt) ? { joinedAt: readString(pairing.joinedAt) } : {},
698
+ ...readString(pairing.lastPairedAt) ? { lastPairedAt: readString(pairing.lastPairedAt) } : {},
699
+ ...readString(pairing.lastRejectedAt) ? { lastRejectedAt: readString(pairing.lastRejectedAt) } : {},
700
+ ...readString(pairing.expiresAt) ? { expiresAt: readString(pairing.expiresAt) } : {}
701
+ };
702
+ }
703
+ return normalized;
704
+ }
705
+ function isMeshHostOwner(mesh) {
706
+ return resolveMeshHostStatus(mesh).role === "host";
707
+ }
708
+ function buildMeshHostRequiredFailure(mesh, operation) {
709
+ const meshHost = resolveMeshHostStatus(mesh);
710
+ return {
711
+ success: false,
712
+ code: "mesh_host_required",
713
+ error: `Mesh Host daemon required for ${operation}; member daemons must pair with the host and cannot own coordinator/queue mutations.`,
714
+ meshHost
715
+ };
716
+ }
717
+ function requireMeshHostQueueOwner(opts) {
718
+ if (opts?.ownerRole === "member") {
719
+ throw new Error("Mesh Host daemon required to mutate mesh queue; member daemons must use the host-owned queue.");
720
+ }
721
+ }
722
+ function createDefaultMeshHostMetadata() {
723
+ return {
724
+ role: "host",
725
+ pairing: { status: "not_configured" }
726
+ };
727
+ }
728
+ var init_mesh_host_ownership = __esm({
729
+ "src/mesh/mesh-host-ownership.ts"() {
730
+ "use strict";
731
+ }
732
+ });
733
+
665
734
  // src/config/mesh-config.ts
666
735
  var mesh_config_exports = {};
667
736
  __export(mesh_config_exports, {
668
737
  addNode: () => addNode,
738
+ applyMeshHostJoinRequest: () => applyMeshHostJoinRequest,
739
+ configureMeshHostPairing: () => configureMeshHostPairing,
669
740
  createMesh: () => createMesh,
741
+ createMeshHostPairingToken: () => createMeshHostPairingToken,
670
742
  deleteMesh: () => deleteMesh,
671
743
  getMesh: () => getMesh,
672
744
  getMeshByRepo: () => getMeshByRepo,
673
745
  listMeshes: () => listMeshes,
746
+ markMeshHostPairingJoined: () => markMeshHostPairingJoined,
674
747
  normalizeRepoIdentity: () => normalizeRepoIdentity,
675
748
  removeNode: () => removeNode,
749
+ tokenIdForManualPairing: () => tokenIdForManualPairing,
676
750
  updateMesh: () => updateMesh,
677
751
  updateNode: () => updateNode
678
752
  });
@@ -748,6 +822,7 @@ function createMesh(opts) {
748
822
  defaultBranch: opts.defaultBranch,
749
823
  policy: mergeMeshPolicy(void 0, opts.policy),
750
824
  coordinator: opts.coordinator || {},
825
+ meshHost: opts.meshHost || createDefaultMeshHostMetadata(),
751
826
  nodes: [],
752
827
  createdAt: now,
753
828
  updatedAt: now
@@ -764,6 +839,7 @@ function updateMesh(meshId, opts) {
764
839
  if (opts.defaultBranch !== void 0) mesh.defaultBranch = opts.defaultBranch;
765
840
  if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
766
841
  if (opts.coordinator) mesh.coordinator = opts.coordinator;
842
+ if (opts.meshHost) mesh.meshHost = opts.meshHost;
767
843
  mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
768
844
  saveMeshConfig(config);
769
845
  return mesh;
@@ -776,6 +852,186 @@ function deleteMesh(meshId) {
776
852
  saveMeshConfig(config);
777
853
  return true;
778
854
  }
855
+ function normalizeManualHostAddress(hostAddress) {
856
+ const normalized = hostAddress.trim().replace(/\/+$/, "");
857
+ if (!normalized) throw new Error("hostAddress required");
858
+ let parsed;
859
+ try {
860
+ parsed = new URL(normalized);
861
+ } catch {
862
+ throw new Error("hostAddress must be a valid http(s) or ws(s) URL");
863
+ }
864
+ if (!["http:", "https:", "ws:", "wss:"].includes(parsed.protocol)) {
865
+ throw new Error("hostAddress must use http, https, ws, or wss");
866
+ }
867
+ return normalized;
868
+ }
869
+ function tokenIdForManualPairing(token) {
870
+ return `tok_${(0, import_crypto3.createHash)("sha256").update(token).digest("hex").slice(0, 16)}`;
871
+ }
872
+ function normalizeTokenExpiry(value) {
873
+ if (typeof value !== "string" || !value.trim()) return void 0;
874
+ const date = new Date(value);
875
+ if (Number.isNaN(date.getTime())) throw new Error("expiresAt must be a valid ISO date");
876
+ return date.toISOString();
877
+ }
878
+ function assertPairingTokenValid(pairing, rawToken, nowIso) {
879
+ const token = rawToken.trim();
880
+ if (!token) return { ok: false, reason: "token required" };
881
+ const presentedTokenId = tokenIdForManualPairing(token);
882
+ const expectedTokenId = pairing?.tokenId;
883
+ if (!expectedTokenId || pairing?.status === "not_configured" || pairing?.status === "revoked") {
884
+ return { ok: false, reason: "host pairing token is not configured", presentedTokenId };
885
+ }
886
+ if (pairing.expiresAt && new Date(pairing.expiresAt).getTime() <= new Date(nowIso).getTime()) {
887
+ return { ok: false, reason: "host pairing token expired", expectedTokenId, presentedTokenId };
888
+ }
889
+ if (presentedTokenId !== expectedTokenId) {
890
+ return { ok: false, reason: "invalid pairing token", expectedTokenId, presentedTokenId };
891
+ }
892
+ return { ok: true, tokenId: presentedTokenId };
893
+ }
894
+ function configureMeshHostPairing(meshId, opts) {
895
+ const hostAddress = normalizeManualHostAddress(opts.hostAddress);
896
+ const token = opts.token.trim();
897
+ if (!token) throw new Error("token required");
898
+ const config = loadMeshConfig();
899
+ const mesh = config.meshes.find((m) => m.id === meshId);
900
+ if (!mesh) return void 0;
901
+ const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
902
+ const previous = mesh.meshHost || createDefaultMeshHostMetadata();
903
+ const meshHost = {
904
+ ...previous,
905
+ role: "member",
906
+ hostAddress,
907
+ pairing: {
908
+ status: "pairing",
909
+ tokenId: tokenIdForManualPairing(token),
910
+ lastPairedAt: now
911
+ }
912
+ };
913
+ mesh.meshHost = meshHost;
914
+ mesh.updatedAt = now;
915
+ saveMeshConfig(config);
916
+ return { mesh, meshHost, hostAddress };
917
+ }
918
+ function createMeshHostPairingToken(meshId, opts = {}) {
919
+ const config = loadMeshConfig();
920
+ const mesh = config.meshes.find((m) => m.id === meshId);
921
+ if (!mesh) return void 0;
922
+ const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
923
+ const token = (opts.token || `mhj_${(0, import_crypto3.randomBytes)(24).toString("base64url")}`).trim();
924
+ if (!token) throw new Error("token required");
925
+ const tokenId = tokenIdForManualPairing(token);
926
+ const expiresAt = normalizeTokenExpiry(opts.expiresAt);
927
+ const previous = mesh.meshHost || createDefaultMeshHostMetadata();
928
+ if (previous.role === "member") {
929
+ throw new Error("Mesh Host daemon required to create host pairing tokens; member daemons cannot mint host join tokens.");
930
+ }
931
+ const meshHost = {
932
+ ...previous,
933
+ role: "host",
934
+ pairing: {
935
+ status: "pairing",
936
+ tokenId,
937
+ lastPairedAt: now,
938
+ ...expiresAt ? { expiresAt } : {}
939
+ }
940
+ };
941
+ mesh.meshHost = meshHost;
942
+ mesh.updatedAt = now;
943
+ saveMeshConfig(config);
944
+ return { mesh, meshHost, token, tokenId, ...expiresAt ? { expiresAt } : {} };
945
+ }
946
+ function applyMeshHostJoinRequest(meshId, opts) {
947
+ const config = loadMeshConfig();
948
+ const mesh = config.meshes.find((m) => m.id === meshId);
949
+ if (!mesh) return void 0;
950
+ const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
951
+ const previous = mesh.meshHost || createDefaultMeshHostMetadata();
952
+ if (previous.role === "member") {
953
+ return { accepted: false, mesh, meshHost: previous, reason: "Mesh Host daemon required to accept join requests" };
954
+ }
955
+ const meshHost = { ...previous, role: "host" };
956
+ const validation = assertPairingTokenValid(meshHost.pairing, opts.token, now);
957
+ if (!validation.ok) {
958
+ mesh.meshHost = {
959
+ ...meshHost,
960
+ pairing: {
961
+ ...meshHost.pairing || { status: "not_configured" },
962
+ status: "rejected",
963
+ lastRejectedAt: now
964
+ }
965
+ };
966
+ mesh.updatedAt = now;
967
+ saveMeshConfig(config);
968
+ return { accepted: false, mesh, meshHost: mesh.meshHost, tokenId: validation.presentedTokenId, reason: validation.reason };
969
+ }
970
+ const workspace = opts.memberNode.workspace.trim();
971
+ if (!workspace) throw new Error("memberNode.workspace required");
972
+ const memberId = opts.memberNode.id?.trim();
973
+ let node = mesh.nodes.find((n) => memberId && n.id === memberId || n.workspace === workspace);
974
+ if (node) {
975
+ node.workspace = workspace;
976
+ node.repoRoot = opts.memberNode.repoRoot;
977
+ node.daemonId = opts.memberNode.daemonId;
978
+ node.machineId = opts.memberNode.machineId;
979
+ node.userOverrides = opts.memberNode.userOverrides || node.userOverrides || {};
980
+ node.policy = { ...node.policy || {}, ...opts.memberNode.policy || {} };
981
+ node.role = "member";
982
+ } else {
983
+ if (mesh.nodes.length >= 10) throw new Error("Maximum 10 nodes per mesh");
984
+ node = {
985
+ id: memberId || `node_${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`,
986
+ workspace,
987
+ repoRoot: opts.memberNode.repoRoot,
988
+ daemonId: opts.memberNode.daemonId,
989
+ machineId: opts.memberNode.machineId,
990
+ userOverrides: opts.memberNode.userOverrides || {},
991
+ policy: opts.memberNode.policy || {},
992
+ role: "member"
993
+ };
994
+ mesh.nodes.push(node);
995
+ }
996
+ mesh.meshHost = {
997
+ ...meshHost,
998
+ pairing: {
999
+ ...meshHost.pairing || {},
1000
+ status: "paired",
1001
+ tokenId: validation.tokenId,
1002
+ joinedAt: now,
1003
+ lastPairedAt: meshHost.pairing?.lastPairedAt || now,
1004
+ ...meshHost.pairing?.expiresAt ? { expiresAt: meshHost.pairing.expiresAt } : {}
1005
+ }
1006
+ };
1007
+ mesh.updatedAt = now;
1008
+ saveMeshConfig(config);
1009
+ return { accepted: true, mesh, meshHost: mesh.meshHost, node, tokenId: validation.tokenId };
1010
+ }
1011
+ function markMeshHostPairingJoined(meshId, opts) {
1012
+ const config = loadMeshConfig();
1013
+ const mesh = config.meshes.find((m) => m.id === meshId);
1014
+ if (!mesh) return void 0;
1015
+ const now = opts.joinedAt || (/* @__PURE__ */ new Date()).toISOString();
1016
+ const previous = mesh.meshHost || createDefaultMeshHostMetadata();
1017
+ const tokenId = opts.tokenId || (opts.token ? tokenIdForManualPairing(opts.token) : previous.pairing?.tokenId);
1018
+ mesh.meshHost = {
1019
+ ...previous,
1020
+ role: "member",
1021
+ ...opts.hostDaemonId ? { hostDaemonId: opts.hostDaemonId } : {},
1022
+ ...opts.hostNodeId ? { hostNodeId: opts.hostNodeId } : {},
1023
+ pairing: {
1024
+ ...previous.pairing || {},
1025
+ status: "paired",
1026
+ ...tokenId ? { tokenId } : {},
1027
+ joinedAt: now,
1028
+ lastPairedAt: previous.pairing?.lastPairedAt || now
1029
+ }
1030
+ };
1031
+ mesh.updatedAt = now;
1032
+ saveMeshConfig(config);
1033
+ return { mesh, meshHost: mesh.meshHost };
1034
+ }
779
1035
  function addNode(meshId, opts) {
780
1036
  const config = loadMeshConfig();
781
1037
  const mesh = config.meshes.find((m) => m.id === meshId);
@@ -796,7 +1052,8 @@ function addNode(meshId, opts) {
796
1052
  policy: opts.policy || {},
797
1053
  isLocalWorktree: opts.isLocalWorktree,
798
1054
  worktreeBranch: opts.worktreeBranch,
799
- clonedFromNodeId: opts.clonedFromNodeId
1055
+ clonedFromNodeId: opts.clonedFromNodeId,
1056
+ role: opts.role
800
1057
  };
801
1058
  mesh.nodes.push(node);
802
1059
  mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -835,6 +1092,7 @@ var init_mesh_config = __esm({
835
1092
  import_crypto3 = require("crypto");
836
1093
  init_config();
837
1094
  init_repo_mesh_types();
1095
+ init_mesh_host_ownership();
838
1096
  SESSION_CLEANUP_MODES = /* @__PURE__ */ new Set(["preserve", "stop", "delete_stopped", "stop_and_delete"]);
839
1097
  SPAWNED_SESSION_VISIBILITY_MODES = /* @__PURE__ */ new Set(["visible", "hidden"]);
840
1098
  }
@@ -1018,19 +1276,19 @@ function isIntentionalCleanupStopEntry(entry) {
1018
1276
  return payload.intentional === true && (payload.reason === "operator_cleanup" || payload.intentionalStopReason === "operator_cleanup" || payload.source === "mesh_cleanup_sessions" || payload.source === "mesh_remove_node");
1019
1277
  }
1020
1278
  function getLedgerDir() {
1021
- const dir = (0, import_path3.join)(getConfigDir(), LEDGER_DIR_NAME);
1022
- if (!(0, import_fs3.existsSync)(dir)) {
1023
- (0, import_fs3.mkdirSync)(dir, { recursive: true, mode: 448 });
1279
+ const dir = (0, import_path4.join)(getConfigDir(), LEDGER_DIR_NAME);
1280
+ if (!(0, import_fs4.existsSync)(dir)) {
1281
+ (0, import_fs4.mkdirSync)(dir, { recursive: true, mode: 448 });
1024
1282
  }
1025
1283
  return dir;
1026
1284
  }
1027
1285
  function getLedgerPath(meshId) {
1028
1286
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
1029
- return (0, import_path3.join)(getLedgerDir(), `${safe}.jsonl`);
1287
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.jsonl`);
1030
1288
  }
1031
1289
  function getRotatedPath(meshId, index) {
1032
1290
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
1033
- return (0, import_path3.join)(getLedgerDir(), `${safe}.${index}.jsonl`);
1291
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.${index}.jsonl`);
1034
1292
  }
1035
1293
  function buildTaskCompletionEvidence(opts) {
1036
1294
  const providerSessionId = opts.providerSessionId?.trim() || void 0;
@@ -1071,9 +1329,9 @@ function appendLedgerEntry(meshId, partial) {
1071
1329
  ...partial
1072
1330
  };
1073
1331
  const filePath = getLedgerPath(meshId);
1074
- if ((0, import_fs3.existsSync)(filePath)) {
1332
+ if ((0, import_fs4.existsSync)(filePath)) {
1075
1333
  try {
1076
- const stat2 = (0, import_fs3.statSync)(filePath);
1334
+ const stat2 = (0, import_fs4.statSync)(filePath);
1077
1335
  if (stat2.size >= MAX_FILE_SIZE_BYTES) {
1078
1336
  rotateLedgerFile(meshId, filePath);
1079
1337
  }
@@ -1082,7 +1340,7 @@ function appendLedgerEntry(meshId, partial) {
1082
1340
  }
1083
1341
  try {
1084
1342
  const line = JSON.stringify(entry) + "\n";
1085
- (0, import_fs3.appendFileSync)(filePath, line, { encoding: "utf-8", mode: 384 });
1343
+ (0, import_fs4.appendFileSync)(filePath, line, { encoding: "utf-8", mode: 384 });
1086
1344
  meshLedgerEvents.emit("append", meshId, entry);
1087
1345
  return entry;
1088
1346
  } catch (e) {
@@ -1127,7 +1385,7 @@ function appendRemoteLedgerEntries(meshId, entries) {
1127
1385
  }
1128
1386
  try {
1129
1387
  const lines = validEntries.map((e) => JSON.stringify(e)).join("\n") + "\n";
1130
- (0, import_fs3.appendFileSync)(ledgerPath, lines, { encoding: "utf-8", mode: 384 });
1388
+ (0, import_fs4.appendFileSync)(ledgerPath, lines, { encoding: "utf-8", mode: 384 });
1131
1389
  for (const entry of validEntries) {
1132
1390
  meshLedgerEvents.emit("append", meshId, entry);
1133
1391
  }
@@ -1138,10 +1396,10 @@ function appendRemoteLedgerEntries(meshId, entries) {
1138
1396
  }
1139
1397
  function readLedgerEntries(meshId, opts) {
1140
1398
  const filePath = getLedgerPath(meshId);
1141
- if (!(0, import_fs3.existsSync)(filePath)) return [];
1399
+ if (!(0, import_fs4.existsSync)(filePath)) return [];
1142
1400
  let content;
1143
1401
  try {
1144
- content = (0, import_fs3.readFileSync)(filePath, "utf-8");
1402
+ content = (0, import_fs4.readFileSync)(filePath, "utf-8");
1145
1403
  } catch {
1146
1404
  return [];
1147
1405
  }
@@ -1313,22 +1571,22 @@ function getSessionRecoveryContext(meshId, opts) {
1313
1571
  }
1314
1572
  function rotateLedgerFile(meshId, currentPath) {
1315
1573
  let index = 1;
1316
- while ((0, import_fs3.existsSync)(getRotatedPath(meshId, index))) {
1574
+ while ((0, import_fs4.existsSync)(getRotatedPath(meshId, index))) {
1317
1575
  index++;
1318
1576
  if (index > 10) break;
1319
1577
  }
1320
1578
  if (index > 10) index = 10;
1321
1579
  try {
1322
- (0, import_fs3.renameSync)(currentPath, getRotatedPath(meshId, index));
1580
+ (0, import_fs4.renameSync)(currentPath, getRotatedPath(meshId, index));
1323
1581
  } catch {
1324
1582
  }
1325
1583
  }
1326
- var import_fs3, import_path3, import_crypto4, import_events, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, DEFAULT_LEDGER_SLICE_LIMIT, MAX_LEDGER_SLICE_LIMIT, meshLedgerEvents;
1584
+ var import_fs4, import_path4, import_crypto4, import_events, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, DEFAULT_LEDGER_SLICE_LIMIT, MAX_LEDGER_SLICE_LIMIT, meshLedgerEvents;
1327
1585
  var init_mesh_ledger = __esm({
1328
1586
  "src/mesh/mesh-ledger.ts"() {
1329
1587
  "use strict";
1330
- import_fs3 = require("fs");
1331
- import_path3 = require("path");
1588
+ import_fs4 = require("fs");
1589
+ import_path4 = require("path");
1332
1590
  import_crypto4 = require("crypto");
1333
1591
  init_config();
1334
1592
  import_events = require("events");
@@ -1358,18 +1616,18 @@ __export(mesh_work_queue_exports, {
1358
1616
  });
1359
1617
  function getQueuePath(meshId) {
1360
1618
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
1361
- return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.json`);
1619
+ return (0, import_path5.join)(getLedgerDir(), `${safe}.queue.json`);
1362
1620
  }
1363
1621
  function getLockPath(meshId) {
1364
1622
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
1365
- return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.lock`);
1623
+ return (0, import_path5.join)(getLedgerDir(), `${safe}.queue.lock`);
1366
1624
  }
1367
1625
  function withQueueLock(meshId, fn) {
1368
1626
  const lockPath = getLockPath(meshId);
1369
1627
  let fd = -1;
1370
1628
  for (let i = 0; i < 10; i++) {
1371
1629
  try {
1372
- fd = (0, import_fs4.openSync)(lockPath, "wx");
1630
+ fd = (0, import_fs5.openSync)(lockPath, "wx");
1373
1631
  break;
1374
1632
  } catch {
1375
1633
  const deadline = Date.now() + 30;
@@ -1381,20 +1639,20 @@ function withQueueLock(meshId, fn) {
1381
1639
  return fn();
1382
1640
  } finally {
1383
1641
  if (fd !== -1) try {
1384
- (0, import_fs4.closeSync)(fd);
1642
+ (0, import_fs5.closeSync)(fd);
1385
1643
  } catch {
1386
1644
  }
1387
1645
  try {
1388
- (0, import_fs4.unlinkSync)(lockPath);
1646
+ (0, import_fs5.unlinkSync)(lockPath);
1389
1647
  } catch {
1390
1648
  }
1391
1649
  }
1392
1650
  }
1393
1651
  function readQueue(meshId) {
1394
1652
  const path28 = getQueuePath(meshId);
1395
- if (!(0, import_fs4.existsSync)(path28)) return [];
1653
+ if (!(0, import_fs5.existsSync)(path28)) return [];
1396
1654
  try {
1397
- const content = (0, import_fs4.readFileSync)(path28, "utf-8");
1655
+ const content = (0, import_fs5.readFileSync)(path28, "utf-8");
1398
1656
  return JSON.parse(content);
1399
1657
  } catch {
1400
1658
  return [];
@@ -1402,9 +1660,10 @@ function readQueue(meshId) {
1402
1660
  }
1403
1661
  function writeQueue(meshId, queue) {
1404
1662
  const path28 = getQueuePath(meshId);
1405
- (0, import_fs4.writeFileSync)(path28, JSON.stringify(queue, null, 2), "utf-8");
1663
+ (0, import_fs5.writeFileSync)(path28, JSON.stringify(queue, null, 2), "utf-8");
1406
1664
  }
1407
1665
  function enqueueTask(meshId, message, opts) {
1666
+ requireMeshHostQueueOwner(opts);
1408
1667
  return withQueueLock(meshId, () => {
1409
1668
  const queue = readQueue(meshId);
1410
1669
  const entry = {
@@ -1453,7 +1712,8 @@ function claimNextTask(meshId, nodeId, sessionId) {
1453
1712
  return entry;
1454
1713
  });
1455
1714
  }
1456
- function updateTaskStatus(meshId, taskId, status) {
1715
+ function updateTaskStatus(meshId, taskId, status, opts) {
1716
+ requireMeshHostQueueOwner(opts);
1457
1717
  return withQueueLock(meshId, () => {
1458
1718
  const queue = readQueue(meshId);
1459
1719
  const idx = queue.findIndex((q) => q.id === taskId);
@@ -1477,6 +1737,7 @@ function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
1477
1737
  });
1478
1738
  }
1479
1739
  function cancelTask(meshId, taskId, opts) {
1740
+ requireMeshHostQueueOwner(opts);
1480
1741
  return withQueueLock(meshId, () => {
1481
1742
  const queue = readQueue(meshId);
1482
1743
  const idx = queue.findIndex((q) => q.id === taskId);
@@ -1491,6 +1752,7 @@ function cancelTask(meshId, taskId, opts) {
1491
1752
  });
1492
1753
  }
1493
1754
  function requeueTask(meshId, taskId, opts) {
1755
+ requireMeshHostQueueOwner(opts);
1494
1756
  return withQueueLock(meshId, () => {
1495
1757
  const queue = readQueue(meshId);
1496
1758
  const idx = queue.findIndex((q) => q.id === taskId);
@@ -1570,14 +1832,15 @@ function getMeshQueueStats(meshId) {
1570
1832
  }))
1571
1833
  };
1572
1834
  }
1573
- var import_fs4, import_path4, import_crypto5, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES;
1835
+ var import_fs5, import_path5, import_crypto5, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES;
1574
1836
  var init_mesh_work_queue = __esm({
1575
1837
  "src/mesh/mesh-work-queue.ts"() {
1576
1838
  "use strict";
1577
- import_fs4 = require("fs");
1578
- import_path4 = require("path");
1839
+ import_fs5 = require("fs");
1840
+ import_path5 = require("path");
1579
1841
  import_crypto5 = require("crypto");
1580
1842
  init_mesh_ledger();
1843
+ init_mesh_host_ownership();
1581
1844
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
1582
1845
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
1583
1846
  }
@@ -1607,7 +1870,7 @@ function resolveCommandPath(command) {
1607
1870
  if (isExplicitCommandPath(trimmed)) {
1608
1871
  const expanded = expandHome(trimmed);
1609
1872
  const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
1610
- return (0, import_fs5.existsSync)(candidate) ? candidate : null;
1873
+ return (0, import_fs6.existsSync)(candidate) ? candidate : null;
1611
1874
  }
1612
1875
  return null;
1613
1876
  }
@@ -1707,14 +1970,14 @@ async function detectCLI(cliId, providerLoader, options) {
1707
1970
  const all = await detectCLIs(providerLoader, options);
1708
1971
  return all.find((c) => c.id === resolvedId && c.installed) || null;
1709
1972
  }
1710
- var import_child_process, os2, path8, import_fs5;
1973
+ var import_child_process, os2, path8, import_fs6;
1711
1974
  var init_cli_detector = __esm({
1712
1975
  "src/detection/cli-detector.ts"() {
1713
1976
  "use strict";
1714
1977
  import_child_process = require("child_process");
1715
1978
  os2 = __toESM(require("os"));
1716
1979
  path8 = __toESM(require("path"));
1717
- import_fs5 = require("fs");
1980
+ import_fs6 = require("fs");
1718
1981
  }
1719
1982
  });
1720
1983
 
@@ -1952,11 +2215,11 @@ function sweepExpiredRemoteIdleSessions() {
1952
2215
  }
1953
2216
  function getPendingEventsPath(meshId) {
1954
2217
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
1955
- return (0, import_path5.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
2218
+ return (0, import_path6.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
1956
2219
  }
1957
2220
  function queuePendingMeshCoordinatorEvent(event) {
1958
2221
  try {
1959
- (0, import_fs6.appendFileSync)(getPendingEventsPath(event.meshId), JSON.stringify(event) + "\n", "utf-8");
2222
+ (0, import_fs7.appendFileSync)(getPendingEventsPath(event.meshId), JSON.stringify(event) + "\n", "utf-8");
1960
2223
  return true;
1961
2224
  } catch (e) {
1962
2225
  LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
@@ -1966,11 +2229,11 @@ function queuePendingMeshCoordinatorEvent(event) {
1966
2229
  function drainPendingMeshCoordinatorEvents(meshId) {
1967
2230
  if (!meshId) return [];
1968
2231
  const path28 = getPendingEventsPath(meshId);
1969
- if (!(0, import_fs6.existsSync)(path28)) return [];
2232
+ if (!(0, import_fs7.existsSync)(path28)) return [];
1970
2233
  try {
1971
- const raw = (0, import_fs6.readFileSync)(path28, "utf-8");
2234
+ const raw = (0, import_fs7.readFileSync)(path28, "utf-8");
1972
2235
  try {
1973
- (0, import_fs6.unlinkSync)(path28);
2236
+ (0, import_fs7.unlinkSync)(path28);
1974
2237
  } catch {
1975
2238
  }
1976
2239
  return raw.split("\n").filter(Boolean).flatMap((line) => {
@@ -1987,9 +2250,9 @@ function drainPendingMeshCoordinatorEvents(meshId) {
1987
2250
  function getPendingMeshCoordinatorEvents(meshId) {
1988
2251
  if (!meshId) return [];
1989
2252
  const path28 = getPendingEventsPath(meshId);
1990
- if (!(0, import_fs6.existsSync)(path28)) return [];
2253
+ if (!(0, import_fs7.existsSync)(path28)) return [];
1991
2254
  try {
1992
- const raw = (0, import_fs6.readFileSync)(path28, "utf-8");
2255
+ const raw = (0, import_fs7.readFileSync)(path28, "utf-8");
1993
2256
  return raw.split("\n").filter(Boolean).flatMap((line) => {
1994
2257
  try {
1995
2258
  return [JSON.parse(line)];
@@ -2004,8 +2267,8 @@ function getPendingMeshCoordinatorEvents(meshId) {
2004
2267
  function clearPendingMeshCoordinatorEvents(meshId) {
2005
2268
  if (!meshId) return;
2006
2269
  const path28 = getPendingEventsPath(meshId);
2007
- if ((0, import_fs6.existsSync)(path28)) try {
2008
- (0, import_fs6.unlinkSync)(path28);
2270
+ if ((0, import_fs7.existsSync)(path28)) try {
2271
+ (0, import_fs7.unlinkSync)(path28);
2009
2272
  } catch {
2010
2273
  }
2011
2274
  }
@@ -2727,12 +2990,12 @@ function setupMeshEventForwarding(components) {
2727
2990
  });
2728
2991
  });
2729
2992
  }
2730
- var import_fs6, import_path5, REMOTE_IDLE_SESSION_TTL_MS, remoteIdleSessions, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, recentCompletionFingerprints, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
2993
+ var import_fs7, import_path6, REMOTE_IDLE_SESSION_TTL_MS, remoteIdleSessions, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, recentCompletionFingerprints, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
2731
2994
  var init_mesh_events = __esm({
2732
2995
  "src/mesh/mesh-events.ts"() {
2733
2996
  "use strict";
2734
- import_fs6 = require("fs");
2735
- import_path5 = require("path");
2997
+ import_fs7 = require("fs");
2998
+ import_path6 = require("path");
2736
2999
  init_config();
2737
3000
  init_mesh_config();
2738
3001
  init_cli_detector();
@@ -5881,6 +6144,8 @@ __export(index_exports, {
5881
6144
  InMemoryGitSnapshotStore: () => InMemoryGitSnapshotStore,
5882
6145
  LOG: () => LOG,
5883
6146
  MAX_LEDGER_SLICE_LIMIT: () => MAX_LEDGER_SLICE_LIMIT,
6147
+ MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
6148
+ MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
5884
6149
  MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
5885
6150
  MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
5886
6151
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
@@ -5903,6 +6168,7 @@ __export(index_exports, {
5903
6168
  buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
5904
6169
  buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
5905
6170
  buildMachineInfo: () => buildMachineInfo,
6171
+ buildMeshHostRequiredFailure: () => buildMeshHostRequiredFailure,
5906
6172
  buildMeshLedgerReconciliationEvidence: () => buildMeshLedgerReconciliationEvidence,
5907
6173
  buildMeshLedgerReplicaEvidence: () => buildMeshLedgerReplicaEvidence,
5908
6174
  buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
@@ -5928,6 +6194,7 @@ __export(index_exports, {
5928
6194
  connectCdpManager: () => connectCdpManager,
5929
6195
  createDebugTraceStore: () => createDebugTraceStore,
5930
6196
  createDefaultGitCommandServices: () => createDefaultGitCommandServices,
6197
+ createDefaultMeshHostMetadata: () => createDefaultMeshHostMetadata,
5931
6198
  createGitCompactSummary: () => createGitCompactSummary,
5932
6199
  createGitSnapshotStore: () => createGitSnapshotStore,
5933
6200
  createGitWorkspaceMonitor: () => createGitWorkspaceMonitor,
@@ -5991,6 +6258,7 @@ __export(index_exports, {
5991
6258
  isInternalChatMessage: () => isInternalChatMessage,
5992
6259
  isManagedStatusWaiting: () => isManagedStatusWaiting,
5993
6260
  isManagedStatusWorking: () => isManagedStatusWorking,
6261
+ isMeshHostOwner: () => isMeshHostOwner,
5994
6262
  isP2pRelayTransportFailure: () => isP2pRelayTransportFailure,
5995
6263
  isPathInside: () => isPathInside,
5996
6264
  isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
@@ -6004,6 +6272,7 @@ __export(index_exports, {
6004
6272
  listMeshes: () => listMeshes,
6005
6273
  listWorktrees: () => listWorktrees,
6006
6274
  loadConfig: () => loadConfig,
6275
+ loadMeshRefineConfig: () => loadMeshRefineConfig,
6007
6276
  loadState: () => loadState,
6008
6277
  logCommand: () => logCommand,
6009
6278
  markSetupComplete: () => markSetupComplete,
@@ -6017,6 +6286,7 @@ __export(index_exports, {
6017
6286
  normalizeGitWorkspaceSubscriptionParams: () => normalizeGitWorkspaceSubscriptionParams,
6018
6287
  normalizeInputEnvelope: () => normalizeInputEnvelope,
6019
6288
  normalizeManagedStatus: () => normalizeManagedStatus,
6289
+ normalizeMeshDaemonRole: () => normalizeMeshDaemonRole,
6020
6290
  normalizeMessageParts: () => normalizeMessageParts,
6021
6291
  normalizeRepoIdentity: () => normalizeRepoIdentity,
6022
6292
  normalizeSessionModalFields: () => normalizeSessionModalFields,
@@ -6037,6 +6307,7 @@ __export(index_exports, {
6037
6307
  removeNode: () => removeNode,
6038
6308
  removeWorktree: () => removeWorktree,
6039
6309
  requeueTask: () => requeueTask,
6310
+ requireMeshHostQueueOwner: () => requireMeshHostQueueOwner,
6040
6311
  resetConfig: () => resetConfig,
6041
6312
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
6042
6313
  resetState: () => resetState,
@@ -6044,6 +6315,8 @@ __export(index_exports, {
6044
6315
  resolveCurrentGlobalInstallSurface: () => resolveCurrentGlobalInstallSurface,
6045
6316
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
6046
6317
  resolveGitRepository: () => resolveGitRepository,
6318
+ resolveMeshHostStatus: () => resolveMeshHostStatus,
6319
+ resolveMeshRefineValidationPlan: () => resolveMeshRefineValidationPlan,
6047
6320
  resolveSessionHostAppName: () => resolveSessionHostAppName,
6048
6321
  resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution,
6049
6322
  resolveWorktreePath: () => resolveWorktreePath,
@@ -6059,6 +6332,7 @@ __export(index_exports, {
6059
6332
  shutdownDaemonComponents: () => shutdownDaemonComponents,
6060
6333
  spawnDetachedDaemonUpgradeHelper: () => spawnDetachedDaemonUpgradeHelper,
6061
6334
  startDaemonDevSupport: () => startDaemonDevSupport,
6335
+ suggestMeshRefineConfig: () => suggestMeshRefineConfig,
6062
6336
  summarizeGitStatus: () => summarizeGitStatus,
6063
6337
  syncMeshes: () => syncMeshes,
6064
6338
  triggerMeshQueue: () => triggerMeshQueue,
@@ -6067,7 +6341,8 @@ __export(index_exports, {
6067
6341
  updateNode: () => updateNode,
6068
6342
  updateSessionTaskStatus: () => updateSessionTaskStatus,
6069
6343
  updateTaskStatus: () => updateTaskStatus,
6070
- upsertSavedProviderSession: () => upsertSavedProviderSession
6344
+ upsertSavedProviderSession: () => upsertSavedProviderSession,
6345
+ validateMeshRefineConfig: () => validateMeshRefineConfig
6071
6346
  });
6072
6347
  module.exports = __toCommonJS(index_exports);
6073
6348
  init_repo_mesh_types();
@@ -7799,6 +8074,238 @@ function getSavedProviderSessions(state, filters) {
7799
8074
  init_mesh_config();
7800
8075
  init_coordinator_prompt();
7801
8076
 
8077
+ // src/mesh/refine-config.ts
8078
+ var import_fs3 = require("fs");
8079
+ var import_path3 = require("path");
8080
+ var yaml = __toESM(require("js-yaml"));
8081
+ var MESH_REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
8082
+ var MESH_REFINE_CONFIG_LOCATIONS = [
8083
+ ".adhdev/refine.json",
8084
+ ".adhdev/refine.yaml",
8085
+ ".adhdev/refine.yml",
8086
+ ".adhdev/repo-mesh-refine.json",
8087
+ ".adhdev/repo-mesh-refine.yaml",
8088
+ ".adhdev/repo-mesh-refine.yml",
8089
+ "repo-mesh.refine.json",
8090
+ "repo-mesh.refine.yaml",
8091
+ "repo-mesh.refine.yml"
8092
+ ];
8093
+ var MESH_REFINE_CONFIG_SCHEMA = {
8094
+ $schema: "https://json-schema.org/draft/2020-12/schema",
8095
+ title: "ADHDev Repo Mesh Refinery Config",
8096
+ type: "object",
8097
+ additionalProperties: false,
8098
+ required: ["version"],
8099
+ properties: {
8100
+ version: { const: 1 },
8101
+ validation: {
8102
+ type: "object",
8103
+ additionalProperties: false,
8104
+ properties: {
8105
+ required: { type: "boolean", default: true },
8106
+ commands: {
8107
+ type: "array",
8108
+ minItems: 1,
8109
+ maxItems: 8,
8110
+ items: {
8111
+ type: "object",
8112
+ additionalProperties: false,
8113
+ required: ["command"],
8114
+ properties: {
8115
+ command: { type: "string", minLength: 1 },
8116
+ args: { type: "array", items: { type: "string" } },
8117
+ category: { enum: [...MESH_REFINE_VALIDATION_CATEGORIES, "custom"] },
8118
+ cwd: { type: "string" },
8119
+ timeoutMs: { type: "number", minimum: 1e3, maximum: 6e5 },
8120
+ env: { type: "object", additionalProperties: { type: "string" } }
8121
+ }
8122
+ }
8123
+ }
8124
+ }
8125
+ }
8126
+ }
8127
+ };
8128
+ function isRecord(value) {
8129
+ return !!value && typeof value === "object" && !Array.isArray(value);
8130
+ }
8131
+ function tokenizeCommandString(command) {
8132
+ const trimmed = command.trim();
8133
+ if (!trimmed) return null;
8134
+ if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
8135
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
8136
+ if (!tokens.length) return null;
8137
+ if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
8138
+ return tokens;
8139
+ }
8140
+ function validateCategory(value) {
8141
+ return typeof value === "string" && [...MESH_REFINE_VALIDATION_CATEGORIES, "custom"].includes(value) ? value : "custom";
8142
+ }
8143
+ function normalizeCommandConfig(entry, source) {
8144
+ if (!isRecord(entry) || typeof entry.command !== "string") {
8145
+ return { rejected: { source, reason: "validation command must be an object with a command string" } };
8146
+ }
8147
+ const commandText = entry.command.trim();
8148
+ const explicitArgs = Array.isArray(entry.args) ? entry.args : void 0;
8149
+ if (explicitArgs && !explicitArgs.every((arg) => typeof arg === "string")) {
8150
+ return { rejected: { source, command: commandText, reason: "args must be an array of strings" } };
8151
+ }
8152
+ let command = commandText;
8153
+ let args = explicitArgs ? [...explicitArgs] : [];
8154
+ if (!explicitArgs) {
8155
+ const tokens = tokenizeCommandString(commandText);
8156
+ if (!tokens) return { rejected: { source, command: commandText, reason: "unsafe command string is not allowlisted" } };
8157
+ command = tokens[0];
8158
+ args = tokens.slice(1);
8159
+ } else if (!tokenizeCommandString(command)) {
8160
+ return { rejected: { source, command: commandText, reason: "unsafe executable name is not allowlisted" } };
8161
+ }
8162
+ if (args.some((arg) => /[\n\r\0]/.test(arg))) {
8163
+ return { rejected: { source, command: commandText, reason: "args cannot contain control characters" } };
8164
+ }
8165
+ if (entry.cwd !== void 0 && typeof entry.cwd !== "string") {
8166
+ return { rejected: { source, command: commandText, reason: "cwd must be a string when provided" } };
8167
+ }
8168
+ if (entry.timeoutMs !== void 0 && (typeof entry.timeoutMs !== "number" || !Number.isFinite(entry.timeoutMs) || entry.timeoutMs < 1e3 || entry.timeoutMs > 6e5)) {
8169
+ return { rejected: { source, command: commandText, reason: "timeoutMs must be between 1000 and 600000" } };
8170
+ }
8171
+ if (entry.env !== void 0 && (!isRecord(entry.env) || !Object.values(entry.env).every((value) => typeof value === "string"))) {
8172
+ return { rejected: { source, command: commandText, reason: "env must be an object of string values" } };
8173
+ }
8174
+ return {
8175
+ command: {
8176
+ command,
8177
+ args,
8178
+ displayCommand: [command, ...args].join(" "),
8179
+ category: validateCategory(entry.category),
8180
+ source,
8181
+ ...typeof entry.cwd === "string" && entry.cwd.trim() ? { cwd: entry.cwd.trim() } : {},
8182
+ ...typeof entry.timeoutMs === "number" ? { timeoutMs: entry.timeoutMs } : {},
8183
+ ...isRecord(entry.env) ? { env: entry.env } : {}
8184
+ }
8185
+ };
8186
+ }
8187
+ function validateMeshRefineConfig(config, source = "inline") {
8188
+ const errors = [];
8189
+ const commands = [];
8190
+ const rejectedCommands = [];
8191
+ if (!isRecord(config)) return { valid: false, errors: ["config must be an object"], commands, rejectedCommands };
8192
+ if (config.version !== 1) errors.push("version must be 1");
8193
+ const validation = config.validation;
8194
+ if (validation !== void 0 && !isRecord(validation)) errors.push("validation must be an object");
8195
+ const rawCommands = isRecord(validation) ? validation.commands : void 0;
8196
+ if (rawCommands !== void 0 && !Array.isArray(rawCommands)) errors.push("validation.commands must be an array");
8197
+ if (Array.isArray(rawCommands)) {
8198
+ rawCommands.forEach((entry, index) => {
8199
+ const normalized = normalizeCommandConfig(entry, `${source}:validation.commands[${index}]`);
8200
+ if (normalized.command) commands.push(normalized.command);
8201
+ if (normalized.rejected) rejectedCommands.push(normalized.rejected);
8202
+ });
8203
+ }
8204
+ if (rejectedCommands.length) errors.push("one or more validation commands are invalid");
8205
+ return { valid: errors.length === 0, errors, commands, rejectedCommands };
8206
+ }
8207
+ function parseConfigText(path28, text) {
8208
+ if (/\.json$/i.test(path28)) return JSON.parse(text);
8209
+ return yaml.load(text);
8210
+ }
8211
+ function loadMeshRefineConfig(mesh, workspace) {
8212
+ const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
8213
+ const inline = mesh?.refineConfig || policy.refineConfig || policy.refine;
8214
+ if (inline !== void 0) {
8215
+ const validation = validateMeshRefineConfig(inline, "mesh.policy.refineConfig");
8216
+ if (!validation.valid) return { source: "mesh.policy.refineConfig", sourceType: "invalid", error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
8217
+ return { config: inline, source: "mesh.policy.refineConfig", sourceType: "mesh_policy" };
8218
+ }
8219
+ for (const relative3 of MESH_REFINE_CONFIG_LOCATIONS) {
8220
+ const configPath = (0, import_path3.join)(workspace, relative3);
8221
+ if (!(0, import_fs3.existsSync)(configPath)) continue;
8222
+ try {
8223
+ const parsed = parseConfigText(configPath, (0, import_fs3.readFileSync)(configPath, "utf-8"));
8224
+ const validation = validateMeshRefineConfig(parsed, relative3);
8225
+ if (!validation.valid) return { source: relative3, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
8226
+ return { config: parsed, source: relative3, sourceType: "repo_file", path: configPath };
8227
+ } catch (error) {
8228
+ return { source: relative3, sourceType: "invalid", path: configPath, error: error?.message || String(error) };
8229
+ }
8230
+ }
8231
+ return {
8232
+ source: "unavailable",
8233
+ sourceType: "unavailable",
8234
+ error: `No repo mesh/refine config found. Checked: ${MESH_REFINE_CONFIG_LOCATIONS.join(", ")}`
8235
+ };
8236
+ }
8237
+ function readPackageScripts(workspace) {
8238
+ try {
8239
+ const parsed = JSON.parse((0, import_fs3.readFileSync)((0, import_path3.join)(workspace, "package.json"), "utf-8"));
8240
+ return isRecord(parsed?.scripts) ? parsed.scripts : {};
8241
+ } catch {
8242
+ return {};
8243
+ }
8244
+ }
8245
+ function collectProjectContextSuggestions(mesh) {
8246
+ const commands = mesh?.projectContext?.commands;
8247
+ if (!isRecord(commands)) return [];
8248
+ const suggestions = [];
8249
+ for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
8250
+ const entries = Array.isArray(commands[category]) ? commands[category] : [];
8251
+ for (const entry of entries) {
8252
+ if (isRecord(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
8253
+ }
8254
+ }
8255
+ return suggestions;
8256
+ }
8257
+ function collectPackageScriptSuggestions(workspace) {
8258
+ const scripts = readPackageScripts(workspace);
8259
+ const suggestions = [];
8260
+ for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
8261
+ for (const scriptName of Object.keys(scripts)) {
8262
+ if (scriptName === category || scriptName.startsWith(`${category}:`)) {
8263
+ suggestions.push({ command: "npm", args: ["run", scriptName], category });
8264
+ }
8265
+ }
8266
+ }
8267
+ return suggestions;
8268
+ }
8269
+ function suggestMeshRefineConfig(mesh, workspace) {
8270
+ const seen = /* @__PURE__ */ new Set();
8271
+ const suggestions = [];
8272
+ for (const entry of [...collectProjectContextSuggestions(mesh), ...collectPackageScriptSuggestions(workspace)]) {
8273
+ const key = `${entry.command} ${(entry.args || []).join(" ")}`.trim();
8274
+ if (seen.has(key)) continue;
8275
+ seen.add(key);
8276
+ suggestions.push(entry);
8277
+ }
8278
+ return {
8279
+ suggestions,
8280
+ suggestedConfig: suggestions.length ? { version: 1, validation: { required: true, commands: suggestions.slice(0, 4) } } : void 0
8281
+ };
8282
+ }
8283
+ function resolveMeshRefineValidationPlan(mesh, workspace) {
8284
+ const loaded = loadMeshRefineConfig(mesh, workspace);
8285
+ const suggestion = suggestMeshRefineConfig(mesh, workspace);
8286
+ if (!loaded.config) {
8287
+ return {
8288
+ source: loaded.source,
8289
+ sourceType: loaded.sourceType,
8290
+ commands: [],
8291
+ rejectedCommands: loaded.error ? [{ source: loaded.source, reason: loaded.error }] : [],
8292
+ suggestions: suggestion.suggestions,
8293
+ suggestedConfig: suggestion.suggestedConfig,
8294
+ unavailableReason: loaded.error || "validation_unavailable: repo mesh/refine config missing"
8295
+ };
8296
+ }
8297
+ const validation = validateMeshRefineConfig(loaded.config, loaded.source);
8298
+ return {
8299
+ source: loaded.path || loaded.source,
8300
+ sourceType: loaded.sourceType,
8301
+ commands: validation.commands,
8302
+ rejectedCommands: validation.rejectedCommands,
8303
+ suggestions: suggestion.suggestions,
8304
+ suggestedConfig: suggestion.suggestedConfig,
8305
+ unavailableReason: validation.commands.length ? void 0 : "validation_unavailable: repo mesh/refine config has no validation.commands"
8306
+ };
8307
+ }
8308
+
7802
8309
  // src/mesh/mesh-sync.ts
7803
8310
  init_mesh_config();
7804
8311
  async function syncMeshes(transport) {
@@ -7918,6 +8425,7 @@ function buildMeshLedgerReconciliationEvidence(meshId, replicas) {
7918
8425
 
7919
8426
  // src/index.ts
7920
8427
  init_mesh_work_queue();
8428
+ init_mesh_host_ownership();
7921
8429
  init_mesh_events();
7922
8430
 
7923
8431
  // src/mesh/p2p-relay-failure.ts
@@ -8031,8 +8539,8 @@ var P2pRelayFailureError = class extends Error {
8031
8539
  };
8032
8540
 
8033
8541
  // src/config/state-store.ts
8034
- var import_fs7 = require("fs");
8035
- var import_path6 = require("path");
8542
+ var import_fs8 = require("fs");
8543
+ var import_path7 = require("path");
8036
8544
  init_config();
8037
8545
  var DEFAULT_STATE = {
8038
8546
  recentActivity: [],
@@ -8046,7 +8554,7 @@ function isPlainObject2(value) {
8046
8554
  return !!value && typeof value === "object" && !Array.isArray(value);
8047
8555
  }
8048
8556
  function getStatePath() {
8049
- return (0, import_path6.join)(getConfigDir(), "state.json");
8557
+ return (0, import_path7.join)(getConfigDir(), "state.json");
8050
8558
  }
8051
8559
  function normalizeState(raw) {
8052
8560
  const parsed = isPlainObject2(raw) ? raw : {};
@@ -8082,11 +8590,11 @@ function normalizeState(raw) {
8082
8590
  }
8083
8591
  function loadState() {
8084
8592
  const statePath = getStatePath();
8085
- if (!(0, import_fs7.existsSync)(statePath)) {
8593
+ if (!(0, import_fs8.existsSync)(statePath)) {
8086
8594
  return { ...DEFAULT_STATE };
8087
8595
  }
8088
8596
  try {
8089
- const raw = (0, import_fs7.readFileSync)(statePath, "utf-8");
8597
+ const raw = (0, import_fs8.readFileSync)(statePath, "utf-8");
8090
8598
  return normalizeState(JSON.parse(raw));
8091
8599
  } catch {
8092
8600
  return { ...DEFAULT_STATE };
@@ -8095,7 +8603,7 @@ function loadState() {
8095
8603
  function saveState(state) {
8096
8604
  const statePath = getStatePath();
8097
8605
  const normalized = normalizeState(state);
8098
- (0, import_fs7.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
8606
+ (0, import_fs8.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
8099
8607
  }
8100
8608
  function resetState() {
8101
8609
  saveState({ ...DEFAULT_STATE });
@@ -8103,7 +8611,7 @@ function resetState() {
8103
8611
 
8104
8612
  // src/detection/ide-detector.ts
8105
8613
  var import_child_process2 = require("child_process");
8106
- var import_fs8 = require("fs");
8614
+ var import_fs9 = require("fs");
8107
8615
  var import_os2 = require("os");
8108
8616
  var path10 = __toESM(require("path"));
8109
8617
  var BUILTIN_IDE_DEFINITIONS = [];
@@ -8127,7 +8635,7 @@ function findCliCommand(command) {
8127
8635
  if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
8128
8636
  const candidate = trimmed.startsWith("~") ? path10.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
8129
8637
  const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
8130
- return (0, import_fs8.existsSync)(resolved) ? resolved : null;
8638
+ return (0, import_fs9.existsSync)(resolved) ? resolved : null;
8131
8639
  }
8132
8640
  try {
8133
8641
  const result = (0, import_child_process2.execSync)(
@@ -8158,9 +8666,9 @@ function checkPathExists(paths) {
8158
8666
  if (normalized.includes("*")) {
8159
8667
  const username = home.split(/[\\/]/).pop() || "";
8160
8668
  const resolved = normalized.replace("*", username);
8161
- if ((0, import_fs8.existsSync)(resolved)) return resolved;
8669
+ if ((0, import_fs9.existsSync)(resolved)) return resolved;
8162
8670
  } else {
8163
- if ((0, import_fs8.existsSync)(normalized)) return normalized;
8671
+ if ((0, import_fs9.existsSync)(normalized)) return normalized;
8164
8672
  }
8165
8673
  }
8166
8674
  return null;
@@ -8174,7 +8682,7 @@ async function detectIDEs(providerLoader) {
8174
8682
  let resolvedCli = cliPath;
8175
8683
  if (!resolvedCli && appPath && os22 === "darwin") {
8176
8684
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
8177
- if ((0, import_fs8.existsSync)(bundledCli)) resolvedCli = bundledCli;
8685
+ if ((0, import_fs9.existsSync)(bundledCli)) resolvedCli = bundledCli;
8178
8686
  }
8179
8687
  if (!resolvedCli && appPath && os22 === "win32") {
8180
8688
  const { dirname: dirname9 } = await import("path");
@@ -8187,7 +8695,7 @@ async function detectIDEs(providerLoader) {
8187
8695
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
8188
8696
  ];
8189
8697
  for (const c of candidates) {
8190
- if ((0, import_fs8.existsSync)(c)) {
8698
+ if ((0, import_fs9.existsSync)(c)) {
8191
8699
  resolvedCli = c;
8192
8700
  break;
8193
8701
  }
@@ -17340,7 +17848,7 @@ var DaemonCommandHandler = class {
17340
17848
  var os13 = __toESM(require("os"));
17341
17849
  var path18 = __toESM(require("path"));
17342
17850
  var crypto4 = __toESM(require("crypto"));
17343
- var import_fs9 = require("fs");
17851
+ var import_fs10 = require("fs");
17344
17852
  var import_child_process6 = require("child_process");
17345
17853
  var import_chalk = __toESM(require("chalk"));
17346
17854
  init_provider_cli_adapter();
@@ -19812,7 +20320,7 @@ function commandExists(command) {
19812
20320
  const trimmed = command.trim();
19813
20321
  if (!trimmed) return false;
19814
20322
  if (isExplicitCommand(trimmed)) {
19815
- return (0, import_fs9.existsSync)(expandExecutable(trimmed));
20323
+ return (0, import_fs10.existsSync)(expandExecutable(trimmed));
19816
20324
  }
19817
20325
  try {
19818
20326
  (0, import_child_process6.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -19841,10 +20349,10 @@ function hasCliArg(args, flag) {
19841
20349
  }
19842
20350
  function ensureEmptyDelegatedMcpConfig(workspace) {
19843
20351
  const baseDir = path18.join(os13.tmpdir(), "adhdev-delegated-agent-empty-mcp");
19844
- (0, import_fs9.mkdirSync)(baseDir, { recursive: true });
20352
+ (0, import_fs10.mkdirSync)(baseDir, { recursive: true });
19845
20353
  const workspaceHash = crypto4.createHash("sha256").update(path18.resolve(workspace || os13.tmpdir())).digest("hex").slice(0, 16);
19846
20354
  const filePath = path18.join(baseDir, `${workspaceHash}.json`);
19847
- (0, import_fs9.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
20355
+ (0, import_fs10.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
19848
20356
  return filePath;
19849
20357
  }
19850
20358
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
@@ -23075,7 +23583,7 @@ function getRecentCommands(count = 50) {
23075
23583
  cleanOldFiles();
23076
23584
 
23077
23585
  // src/commands/router.ts
23078
- var yaml = __toESM(require("js-yaml"));
23586
+ var yaml2 = __toESM(require("js-yaml"));
23079
23587
  init_logger();
23080
23588
 
23081
23589
  // src/commands/mesh-coordinator.ts
@@ -23369,6 +23877,7 @@ function normalizeExistingPath(filePath) {
23369
23877
 
23370
23878
  // src/commands/router.ts
23371
23879
  init_mesh_events();
23880
+ init_mesh_host_ownership();
23372
23881
 
23373
23882
  // src/status/snapshot.ts
23374
23883
  var os18 = __toESM(require("os"));
@@ -24023,7 +24532,7 @@ async function maybeRunDaemonUpgradeHelperFromEnv() {
24023
24532
 
24024
24533
  // src/commands/router.ts
24025
24534
  var import_os3 = require("os");
24026
- var import_path7 = require("path");
24535
+ var import_path8 = require("path");
24027
24536
  var fs10 = __toESM(require("fs"));
24028
24537
  var CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
24029
24538
  var CHANNEL_SERVER_URL = {
@@ -24664,11 +25173,9 @@ async function resolveProviderTypeFromPriority(args) {
24664
25173
  }
24665
25174
  return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
24666
25175
  }
24667
- var REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
24668
25176
  var REFINE_VALIDATION_TIMEOUT_MS = 12e4;
24669
25177
  var REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
24670
25178
  var REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
24671
- var REFINE_VALIDATION_MAX_COMMANDS = 4;
24672
25179
  var REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
24673
25180
  function truncateValidationOutput(value) {
24674
25181
  const text = typeof value === "string" ? value : value == null ? "" : String(value);
@@ -24752,141 +25259,30 @@ async function runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead)
24752
25259
  };
24753
25260
  }
24754
25261
  }
24755
- function readPackageScripts(workspace) {
24756
- try {
24757
- const packageJsonPath = (0, import_path7.join)(workspace, "package.json");
24758
- const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
24759
- return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
24760
- } catch {
24761
- return {};
24762
- }
24763
- }
24764
- function tokenizeValidationCommand(command) {
24765
- const trimmed = command.trim();
24766
- if (!trimmed) return null;
24767
- if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
24768
- const tokens = trimmed.split(/\s+/).filter(Boolean);
24769
- if (!tokens.length) return null;
24770
- if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
24771
- return tokens;
24772
- }
24773
- function scriptMatchesValidationCategory(scriptName, category) {
24774
- return scriptName === category || scriptName.startsWith(`${category}:`);
24775
- }
24776
- function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
24777
- const tokens = tokenizeValidationCommand(rawCommand);
24778
- if (!tokens) {
24779
- return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
24780
- }
24781
- const [binary, second, third, ...rest] = tokens;
24782
- let scriptName = "";
24783
- let command = binary;
24784
- let args = [];
24785
- if ((binary === "npm" || binary === "pnpm" || binary === "bun") && second === "run" && third) {
24786
- scriptName = third;
24787
- args = ["run", scriptName, ...rest];
24788
- } else if (binary === "npm" && second === "test" && !third) {
24789
- scriptName = "test";
24790
- args = ["test"];
24791
- } else if (binary === "yarn" && second === "run" && third) {
24792
- scriptName = third;
24793
- args = ["run", scriptName, ...rest];
24794
- } else if (binary === "yarn" && second && !third) {
24795
- scriptName = second;
24796
- args = [scriptName];
24797
- } else {
24798
- return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
24799
- }
24800
- if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
24801
- return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
24802
- }
24803
- if (!scriptMatchesValidationCategory(scriptName, category)) {
24804
- return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
24805
- }
24806
- return {
24807
- command: {
24808
- command,
24809
- args,
24810
- displayCommand: [command, ...args].join(" "),
24811
- category,
24812
- source
24813
- }
24814
- };
24815
- }
24816
- function collectProjectContextValidationCandidates(mesh) {
24817
- const commands = mesh?.projectContext?.commands;
24818
- if (!commands || typeof commands !== "object" || Array.isArray(commands)) return [];
24819
- const candidates = [];
24820
- for (const category of REFINE_VALIDATION_CATEGORIES) {
24821
- const entries = Array.isArray(commands[category]) ? commands[category] : [];
24822
- for (const entry of entries) {
24823
- if (typeof entry?.command !== "string") continue;
24824
- candidates.push({
24825
- command: entry.command,
24826
- category,
24827
- source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
24828
- confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
24829
- });
24830
- }
24831
- }
24832
- return candidates.sort((a, b) => {
24833
- const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
24834
- return rank(a.confidence) - rank(b.confidence);
24835
- });
24836
- }
24837
- function collectPolicyValidationCandidates(mesh) {
24838
- const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
24839
- const configured = Array.isArray(policy.validationCommands) ? policy.validationCommands : Array.isArray(policy.validationGate?.commands) ? policy.validationGate.commands : [];
24840
- return configured.map((entry) => typeof entry === "string" ? { command: entry, category: "", source: "mesh.policy.validationCommands" } : entry).filter((entry) => entry && typeof entry.command === "string").map((entry) => {
24841
- const commandText = entry.command.trim();
24842
- const category = REFINE_VALIDATION_CATEGORIES.find((cat) => commandText.includes(` ${cat}`)) ?? "";
24843
- return { command: commandText, category, source: "mesh.policy.validationCommands" };
24844
- }).filter((entry) => !!entry.category);
24845
- }
24846
- function selectMeshRefineValidationCommands(mesh, workspace) {
24847
- const scripts = readPackageScripts(workspace);
24848
- const rejectedCommands = [];
24849
- const selected = [];
24850
- const seen = /* @__PURE__ */ new Set();
24851
- const candidates = [
24852
- ...collectPolicyValidationCandidates(mesh),
24853
- ...collectProjectContextValidationCandidates(mesh)
24854
- ];
24855
- for (const candidate of candidates) {
24856
- const parsed = parsePackageManagerValidationCommand(candidate.command, candidate.category, scripts, candidate.source);
24857
- if (parsed.rejected) {
24858
- rejectedCommands.push(parsed.rejected);
24859
- continue;
24860
- }
24861
- if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
24862
- selected.push(parsed.command);
24863
- seen.add(parsed.command.displayCommand);
24864
- if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
24865
- }
24866
- if (!selected.length && candidates.length === 0) {
24867
- for (const category of REFINE_VALIDATION_CATEGORIES) {
24868
- if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
24869
- const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
24870
- if (fallback.command && !seen.has(fallback.command.displayCommand)) {
24871
- selected.push(fallback.command);
24872
- seen.add(fallback.command.displayCommand);
24873
- } else if (fallback.rejected) {
24874
- rejectedCommands.push(fallback.rejected);
24875
- }
24876
- if (selected.length >= 2) break;
24877
- }
24878
- }
25262
+ function buildMeshRefineValidationPlan(mesh, workspace) {
25263
+ const plan = resolveMeshRefineValidationPlan(mesh, workspace);
24879
25264
  return {
24880
- commands: selected,
24881
- rejectedCommands,
24882
- source: selected.some((command) => command.source === "mesh.policy.validationCommands") ? "mesh_policy" : selected.some((command) => command.source !== "package.json:scripts") ? "project_context" : selected.length ? "package_json_scripts" : "unavailable"
25265
+ source: plan.source,
25266
+ sourceType: plan.sourceType,
25267
+ commands: plan.commands.map((command) => ({
25268
+ displayCommand: command.displayCommand,
25269
+ category: command.category,
25270
+ source: command.source,
25271
+ cwd: command.cwd,
25272
+ timeoutMs: command.timeoutMs
25273
+ })),
25274
+ unavailableReason: plan.unavailableReason,
25275
+ rejectedCommands: plan.rejectedCommands,
25276
+ suggestions: plan.suggestions,
25277
+ suggestedConfig: plan.suggestedConfig,
25278
+ note: plan.sourceType === "unavailable" ? "No validation command will be executed until a repo mesh/refine config is provided. Heuristics are suggestions only." : "Validation commands are resolved from repo mesh/refine config; heuristics are suggestions only."
24883
25279
  };
24884
25280
  }
24885
25281
  async function runMeshRefineValidationGate(mesh, workspace) {
24886
25282
  const { execFile: execFile3 } = await import("child_process");
24887
25283
  const { promisify: promisify3 } = await import("util");
24888
25284
  const execFileAsync3 = promisify3(execFile3);
24889
- const selection = selectMeshRefineValidationCommands(mesh, workspace);
25285
+ const selection = resolveMeshRefineValidationPlan(mesh, workspace);
24890
25286
  const summary = {
24891
25287
  status: "skipped",
24892
25288
  required: true,
@@ -24894,21 +25290,27 @@ async function runMeshRefineValidationGate(mesh, workspace) {
24894
25290
  rejectedCommands: selection.rejectedCommands,
24895
25291
  skippedReason: void 0,
24896
25292
  timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
24897
- outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
25293
+ outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
25294
+ configSource: selection.source,
25295
+ configSourceType: selection.sourceType,
25296
+ suggestions: selection.suggestions,
25297
+ suggestedConfig: selection.suggestedConfig
24898
25298
  };
24899
25299
  if (!selection.commands.length) {
24900
- summary.skippedReason = "validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available";
25300
+ summary.skippedReason = selection.unavailableReason || "validation_unavailable: repo mesh/refine config did not provide executable validation.commands";
24901
25301
  return summary;
24902
25302
  }
24903
25303
  for (const candidate of selection.commands) {
24904
25304
  const startedAt = Date.now();
25305
+ const cwd = candidate.cwd ? (0, import_path8.resolve)(workspace, candidate.cwd) : workspace;
25306
+ const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
24905
25307
  try {
24906
25308
  const result = await execFileAsync3(candidate.command, candidate.args, {
24907
- cwd: workspace,
25309
+ cwd,
24908
25310
  encoding: "utf8",
24909
- timeout: REFINE_VALIDATION_TIMEOUT_MS,
25311
+ timeout,
24910
25312
  maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
24911
- env: { ...process.env, CI: process.env.CI || "1" }
25313
+ env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} }
24912
25314
  });
24913
25315
  summary.commandsRun.push({
24914
25316
  command: candidate.command,
@@ -24916,6 +25318,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
24916
25318
  displayCommand: candidate.displayCommand,
24917
25319
  category: candidate.category,
24918
25320
  source: candidate.source,
25321
+ cwd,
24919
25322
  passed: true,
24920
25323
  exitCode: 0,
24921
25324
  durationMs: Date.now() - startedAt,
@@ -24929,6 +25332,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
24929
25332
  displayCommand: candidate.displayCommand,
24930
25333
  category: candidate.category,
24931
25334
  source: candidate.source,
25335
+ cwd,
24932
25336
  passed: false,
24933
25337
  exitCode: typeof error?.code === "number" ? error.code : null,
24934
25338
  signal: typeof error?.signal === "string" ? error.signal : null,
@@ -24945,7 +25349,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
24945
25349
  return summary;
24946
25350
  }
24947
25351
  function loadYamlModule() {
24948
- return yaml;
25352
+ return yaml2;
24949
25353
  }
24950
25354
  function getMcpServersKey(format) {
24951
25355
  return format === "hermes_config_yaml" ? "mcp_servers" : "mcpServers";
@@ -24962,13 +25366,13 @@ function serializeMeshCoordinatorMcpConfig(config, format) {
24962
25366
  }
24963
25367
  function resolveHermesUserHome() {
24964
25368
  const explicitHome = process.env.HERMES_HOME?.trim();
24965
- return explicitHome || (0, import_path7.join)((0, import_os3.homedir)(), ".hermes");
25369
+ return explicitHome || (0, import_path8.join)((0, import_os3.homedir)(), ".hermes");
24966
25370
  }
24967
25371
  function loadHermesCoordinatorBaseConfig(targetConfigPath) {
24968
25372
  const sourceHome = resolveHermesUserHome();
24969
- const sourceConfigPath = (0, import_path7.join)(sourceHome, "config.yaml");
25373
+ const sourceConfigPath = (0, import_path8.join)(sourceHome, "config.yaml");
24970
25374
  if (!fs10.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
24971
- if ((0, import_path7.resolve)(sourceConfigPath) === (0, import_path7.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
25375
+ if ((0, import_path8.resolve)(sourceConfigPath) === (0, import_path8.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
24972
25376
  const parsed = parseMeshCoordinatorMcpConfig(fs10.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
24973
25377
  const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
24974
25378
  return { config: baseConfig, sourceHome, sourceConfigPath };
@@ -25002,10 +25406,10 @@ function stripHermesCoordinatorTempModelProviderOverrides(config) {
25002
25406
  return sanitized;
25003
25407
  }
25004
25408
  function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
25005
- if ((0, import_path7.resolve)(sourceHome) === (0, import_path7.resolve)(targetHome)) return;
25409
+ if ((0, import_path8.resolve)(sourceHome) === (0, import_path8.resolve)(targetHome)) return;
25006
25410
  for (const fileName of [".env", "auth.json"]) {
25007
- const sourcePath = (0, import_path7.join)(sourceHome, fileName);
25008
- const targetPath = (0, import_path7.join)(targetHome, fileName);
25411
+ const sourcePath = (0, import_path8.join)(sourceHome, fileName);
25412
+ const targetPath = (0, import_path8.join)(targetHome, fileName);
25009
25413
  if (!fs10.existsSync(sourcePath)) continue;
25010
25414
  try {
25011
25415
  fs10.copyFileSync(sourcePath, targetPath);
@@ -25103,6 +25507,34 @@ function summarizeSessionHostPruneResult(result) {
25103
25507
  keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
25104
25508
  };
25105
25509
  }
25510
+ function normalizeStandaloneHostCommandUrl(hostAddress) {
25511
+ const raw = hostAddress.trim();
25512
+ if (!raw) throw new Error("hostAddress required");
25513
+ const url = new URL(raw.replace(/^ws:/, "http:").replace(/^wss:/, "https:"));
25514
+ url.pathname = "/api/v1/command";
25515
+ url.search = "";
25516
+ url.hash = "";
25517
+ return url.toString();
25518
+ }
25519
+ function buildMemberJoinNode(mesh, args, fallbackDaemonId) {
25520
+ const requestedNodeId = typeof args?.memberNodeId === "string" ? args.memberNodeId.trim() : "";
25521
+ const explicit = args?.memberNode && typeof args.memberNode === "object" && !Array.isArray(args.memberNode) ? args.memberNode : null;
25522
+ const configured = Array.isArray(mesh?.nodes) ? requestedNodeId ? mesh.nodes.find((node) => node?.id === requestedNodeId || node?.nodeId === requestedNodeId) : mesh.nodes[0] : null;
25523
+ const source = explicit || configured;
25524
+ const workspace = typeof source?.workspace === "string" && source.workspace.trim() ? source.workspace.trim() : typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
25525
+ if (!workspace) return null;
25526
+ const nodeId = typeof source?.id === "string" && source.id.trim() ? source.id.trim() : typeof source?.nodeId === "string" && source.nodeId.trim() ? source.nodeId.trim() : void 0;
25527
+ return {
25528
+ ...nodeId ? { id: nodeId } : {},
25529
+ workspace,
25530
+ ...typeof source?.repoRoot === "string" && source.repoRoot.trim() ? { repoRoot: source.repoRoot.trim() } : {},
25531
+ ...typeof source?.daemonId === "string" && source.daemonId.trim() ? { daemonId: source.daemonId.trim() } : fallbackDaemonId ? { daemonId: fallbackDaemonId } : {},
25532
+ ...typeof source?.machineId === "string" && source.machineId.trim() ? { machineId: source.machineId.trim() } : {},
25533
+ userOverrides: source?.userOverrides && typeof source.userOverrides === "object" && !Array.isArray(source.userOverrides) ? source.userOverrides : {},
25534
+ policy: source?.policy && typeof source.policy === "object" && !Array.isArray(source.policy) ? source.policy : {},
25535
+ role: "member"
25536
+ };
25537
+ }
25106
25538
  var DaemonCommandRouter = class {
25107
25539
  deps;
25108
25540
  /** In-memory cache for cloud-originating meshes passed via inlineMesh.
@@ -25276,6 +25708,16 @@ var DaemonCommandRouter = class {
25276
25708
  invalidateAggregateMeshStatus(meshId) {
25277
25709
  this.aggregateMeshStatusCache.delete(meshId);
25278
25710
  }
25711
+ async requireMeshHostMutationOwner(meshId, inlineMesh, operation) {
25712
+ const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
25713
+ const mesh = meshRecord?.mesh;
25714
+ if (!mesh) return { success: false, error: "Mesh not found" };
25715
+ const meshHost = resolveMeshHostStatus(mesh);
25716
+ if (!meshHost.canOwnCoordinator || !meshHost.canOwnQueue) {
25717
+ return { ...buildMeshHostRequiredFailure(mesh, operation), success: false, meshId };
25718
+ }
25719
+ return null;
25720
+ }
25279
25721
  updateInlineMeshNode(meshId, mesh, node) {
25280
25722
  if (!mesh || !Array.isArray(mesh.nodes) || !node?.id) return;
25281
25723
  const idx = mesh.nodes.findIndex((entry) => entry?.id === node.id || entry?.nodeId === node.id);
@@ -25341,7 +25783,7 @@ var DaemonCommandRouter = class {
25341
25783
  }
25342
25784
  const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
25343
25785
  const normalizePath = (value) => {
25344
- const resolved = (0, import_path7.resolve)(value);
25786
+ const resolved = (0, import_path8.resolve)(value);
25345
25787
  try {
25346
25788
  return fs10.realpathSync(resolved);
25347
25789
  } catch {
@@ -26314,7 +26756,8 @@ var DaemonCommandRouter = class {
26314
26756
  if (!name) return { success: false, error: "name required" };
26315
26757
  try {
26316
26758
  const { createMesh: createMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
26317
- const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy });
26759
+ const meshHost = args?.meshHost && typeof args.meshHost === "object" && !Array.isArray(args.meshHost) ? args.meshHost : void 0;
26760
+ const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy, meshHost });
26318
26761
  return { success: true, mesh };
26319
26762
  } catch (e) {
26320
26763
  return { success: false, error: e.message };
@@ -26330,6 +26773,7 @@ var DaemonCommandRouter = class {
26330
26773
  if (typeof args?.defaultBranch === "string") patch.defaultBranch = args.defaultBranch;
26331
26774
  if (args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy)) patch.policy = args.policy;
26332
26775
  if (args?.coordinator && typeof args.coordinator === "object" && !Array.isArray(args.coordinator)) patch.coordinator = args.coordinator;
26776
+ if (args?.meshHost && typeof args.meshHost === "object" && !Array.isArray(args.meshHost)) patch.meshHost = args.meshHost;
26333
26777
  if (!Object.keys(patch).length) return { success: false, error: "No updates provided" };
26334
26778
  const mesh = updateMesh2(meshId, patch);
26335
26779
  if (!mesh) return { success: false, error: "Mesh not found" };
@@ -26340,6 +26784,215 @@ var DaemonCommandRouter = class {
26340
26784
  return { success: false, error: e.message };
26341
26785
  }
26342
26786
  }
26787
+ case "get_mesh_host_pairing": {
26788
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26789
+ if (!meshId) return { success: false, error: "meshId required" };
26790
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
26791
+ const mesh = meshRecord?.mesh;
26792
+ if (!mesh) return { success: false, error: "Mesh not found" };
26793
+ const meshHost = resolveMeshHostStatus(mesh);
26794
+ const pairingStatus = meshHost.pairing?.status || "not_configured";
26795
+ return {
26796
+ success: true,
26797
+ code: pairingStatus === "not_configured" ? "mesh_host_pairing_not_configured" : "mesh_host_pairing_pending",
26798
+ meshId,
26799
+ hostAddress: meshHost.hostAddress,
26800
+ meshHost,
26801
+ manualPairing: {
26802
+ status: pairingStatus,
26803
+ joinImplemented: true,
26804
+ protocol: "standalone_command_direct_v1",
26805
+ description: "Standalone manual pairing can save address/token metadata, apply a host join over direct standalone command HTTP or injected mesh command dispatch, and check persisted status. P2P signaling remains outside this slice."
26806
+ }
26807
+ };
26808
+ }
26809
+ case "configure_mesh_host_pairing": {
26810
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26811
+ const hostAddress = typeof args?.hostAddress === "string" ? args.hostAddress.trim() : "";
26812
+ const token = typeof args?.token === "string" ? args.token.trim() : "";
26813
+ if (!meshId) return { success: false, error: "meshId required" };
26814
+ if (!hostAddress || !token) return { success: false, error: "hostAddress and token required" };
26815
+ try {
26816
+ const { configureMeshHostPairing: configureMeshHostPairing2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
26817
+ const configured = configureMeshHostPairing2(meshId, { hostAddress, token });
26818
+ if (!configured) return { success: false, error: "Mesh not found" };
26819
+ this.inlineMeshCache.set(meshId, configured.mesh);
26820
+ const meshHost = resolveMeshHostStatus(configured.mesh);
26821
+ return {
26822
+ success: true,
26823
+ code: "mesh_host_pairing_pending",
26824
+ meshId,
26825
+ hostAddress: configured.hostAddress,
26826
+ meshHost,
26827
+ manualPairing: {
26828
+ status: meshHost.pairing?.status || "pairing",
26829
+ joinImplemented: true,
26830
+ protocol: "standalone_command_direct_v1",
26831
+ description: "Manual Mesh Host pairing config was saved locally. Use join_mesh_host_pairing to apply it to the host. Raw token was not persisted."
26832
+ }
26833
+ };
26834
+ } catch (e) {
26835
+ return { success: false, code: "mesh_host_pairing_invalid", meshId, hostAddress, error: e.message };
26836
+ }
26837
+ }
26838
+ case "create_mesh_host_pairing_token": {
26839
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26840
+ if (!meshId) return { success: false, error: "meshId required" };
26841
+ try {
26842
+ const { createMeshHostPairingToken: createMeshHostPairingToken2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
26843
+ const created = createMeshHostPairingToken2(meshId, {
26844
+ token: typeof args?.token === "string" ? args.token : void 0,
26845
+ expiresAt: typeof args?.expiresAt === "string" ? args.expiresAt : void 0
26846
+ });
26847
+ if (!created) return { success: false, error: "Mesh not found" };
26848
+ this.inlineMeshCache.set(meshId, created.mesh);
26849
+ this.invalidateAggregateMeshStatus(meshId);
26850
+ return {
26851
+ success: true,
26852
+ code: "mesh_host_pairing_token_created",
26853
+ meshId,
26854
+ token: created.token,
26855
+ tokenId: created.tokenId,
26856
+ expiresAt: created.expiresAt,
26857
+ meshHost: resolveMeshHostStatus(created.mesh),
26858
+ warning: "Raw token is returned once and is not persisted; share it with member daemons over a trusted channel."
26859
+ };
26860
+ } catch (e) {
26861
+ return { success: false, code: "mesh_host_pairing_token_invalid", meshId, error: e.message };
26862
+ }
26863
+ }
26864
+ case "apply_mesh_host_join": {
26865
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26866
+ const token = typeof args?.token === "string" ? args.token.trim() : "";
26867
+ const memberNode = args?.memberNode && typeof args.memberNode === "object" && !Array.isArray(args.memberNode) ? args.memberNode : null;
26868
+ if (!meshId) return { success: false, error: "meshId required" };
26869
+ if (!token || !memberNode) return { success: false, error: "token and memberNode required" };
26870
+ try {
26871
+ const { applyMeshHostJoinRequest: applyMeshHostJoinRequest2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
26872
+ const applied = applyMeshHostJoinRequest2(meshId, {
26873
+ token,
26874
+ memberNode,
26875
+ memberMeshId: typeof args?.memberMeshId === "string" ? args.memberMeshId : void 0
26876
+ });
26877
+ if (!applied) return { success: false, error: "Mesh not found" };
26878
+ if (!applied.accepted) {
26879
+ return {
26880
+ success: false,
26881
+ code: "mesh_host_join_rejected",
26882
+ meshId,
26883
+ tokenId: applied.tokenId,
26884
+ meshHost: applied.meshHost ? resolveMeshHostStatus({ meshHost: applied.meshHost }) : void 0,
26885
+ error: applied.reason
26886
+ };
26887
+ }
26888
+ this.inlineMeshCache.set(meshId, applied.mesh);
26889
+ this.invalidateAggregateMeshStatus(meshId);
26890
+ try {
26891
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
26892
+ appendLedgerEntry2(meshId, {
26893
+ kind: "node_joined",
26894
+ nodeId: applied.node.id,
26895
+ payload: { role: "member", tokenId: applied.tokenId, workspace: applied.node.workspace }
26896
+ });
26897
+ } catch {
26898
+ }
26899
+ return {
26900
+ success: true,
26901
+ code: "mesh_host_join_accepted",
26902
+ meshId,
26903
+ node: applied.node,
26904
+ tokenId: applied.tokenId,
26905
+ meshHost: resolveMeshHostStatus(applied.mesh)
26906
+ };
26907
+ } catch (e) {
26908
+ return { success: false, code: "mesh_host_join_failed", meshId, error: e.message };
26909
+ }
26910
+ }
26911
+ case "join_mesh_host_pairing": {
26912
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26913
+ const token = typeof args?.token === "string" ? args.token.trim() : "";
26914
+ if (!meshId) return { success: false, error: "meshId required" };
26915
+ if (!token) return { success: false, error: "token required because raw pairing tokens are not persisted" };
26916
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
26917
+ const mesh = meshRecord?.mesh;
26918
+ if (!mesh) return { success: false, error: "Mesh not found" };
26919
+ const meshHost = resolveMeshHostStatus(mesh);
26920
+ if (meshHost.role !== "member") {
26921
+ return { success: false, code: "mesh_host_join_not_member", meshId, meshHost, error: "join_mesh_host_pairing must run from a member daemon configured with a Mesh Host address/token." };
26922
+ }
26923
+ try {
26924
+ const { tokenIdForManualPairing: tokenIdForManualPairing2, markMeshHostPairingJoined: markMeshHostPairingJoined2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
26925
+ const tokenId = tokenIdForManualPairing2(token);
26926
+ if (meshHost.pairing?.tokenId && meshHost.pairing.tokenId !== tokenId) {
26927
+ return { success: false, code: "mesh_host_join_rejected", meshId, tokenId, meshHost, error: "invalid pairing token" };
26928
+ }
26929
+ const memberNode = buildMemberJoinNode(mesh, args, this.deps.statusInstanceId);
26930
+ if (!memberNode) return { success: false, error: "member node metadata unavailable" };
26931
+ const hostMeshId = typeof args?.hostMeshId === "string" && args.hostMeshId.trim() ? args.hostMeshId.trim() : meshId;
26932
+ const hostDaemonId = typeof args?.hostDaemonId === "string" && args.hostDaemonId.trim() ? args.hostDaemonId.trim() : meshHost.hostDaemonId;
26933
+ let hostResult;
26934
+ let transport;
26935
+ if (hostDaemonId && this.deps.dispatchMeshCommand) {
26936
+ transport = "mesh_command_dispatch";
26937
+ hostResult = await this.deps.dispatchMeshCommand(hostDaemonId, "apply_mesh_host_join", {
26938
+ meshId: hostMeshId,
26939
+ token,
26940
+ memberMeshId: meshId,
26941
+ memberNode
26942
+ });
26943
+ } else if (meshHost.hostAddress) {
26944
+ transport = "standalone_http_command";
26945
+ const commandUrl = normalizeStandaloneHostCommandUrl(meshHost.hostAddress);
26946
+ const response = await fetch(commandUrl, {
26947
+ method: "POST",
26948
+ headers: { "Content-Type": "application/json" },
26949
+ body: JSON.stringify({ type: "apply_mesh_host_join", payload: { meshId: hostMeshId, token, memberMeshId: meshId, memberNode } })
26950
+ });
26951
+ hostResult = await response.json().catch(() => ({ success: false, error: `Host returned HTTP ${response.status}` }));
26952
+ if (!response.ok && hostResult?.success !== false) hostResult = { success: false, error: `Host returned HTTP ${response.status}` };
26953
+ } else {
26954
+ return {
26955
+ success: false,
26956
+ code: "mesh_host_join_transport_unavailable",
26957
+ meshId,
26958
+ meshHost,
26959
+ error: "No hostDaemonId dispatch path or hostAddress HTTP command path is available. P2P signaling join is not implemented in this slice."
26960
+ };
26961
+ }
26962
+ if (!hostResult?.success) {
26963
+ return { success: false, code: hostResult?.code || "mesh_host_join_rejected", meshId, meshHost, transport, error: hostResult?.error || "Mesh Host rejected join request", hostResult };
26964
+ }
26965
+ const joined = meshRecord.inline ? null : markMeshHostPairingJoined2(meshId, {
26966
+ tokenId: hostResult.tokenId || tokenId,
26967
+ hostDaemonId: hostResult.meshHost?.hostDaemonId || hostDaemonId,
26968
+ hostNodeId: hostResult.meshHost?.hostNodeId,
26969
+ joinedAt: hostResult.meshHost?.pairing?.joinedAt
26970
+ });
26971
+ if (joined) {
26972
+ this.inlineMeshCache.set(meshId, joined.mesh);
26973
+ this.invalidateAggregateMeshStatus(meshId);
26974
+ }
26975
+ return {
26976
+ success: true,
26977
+ code: "mesh_host_join_applied",
26978
+ meshId,
26979
+ hostMeshId,
26980
+ transport,
26981
+ node: hostResult.node,
26982
+ tokenId: hostResult.tokenId || tokenId,
26983
+ meshHost: joined ? resolveMeshHostStatus(joined.mesh) : { ...meshHost, pairing: { ...meshHost.pairing || {}, status: "paired", tokenId: hostResult.tokenId || tokenId } },
26984
+ hostResult,
26985
+ manualPairing: {
26986
+ status: "paired",
26987
+ joinImplemented: true,
26988
+ protocol: "standalone_command_direct_v1",
26989
+ description: "Mesh Host accepted the join and local member pairing status was marked paired. P2P runtime signaling remains outside this slice."
26990
+ }
26991
+ };
26992
+ } catch (e) {
26993
+ return { success: false, code: "mesh_host_join_failed", meshId, meshHost, error: e.message };
26994
+ }
26995
+ }
26343
26996
  case "delete_mesh": {
26344
26997
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26345
26998
  if (!meshId) return { success: false, error: "meshId required" };
@@ -26422,6 +27075,8 @@ var DaemonCommandRouter = class {
26422
27075
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26423
27076
  const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
26424
27077
  if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
27078
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue cancellation");
27079
+ if (ownerFailure) return ownerFailure;
26425
27080
  try {
26426
27081
  const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
26427
27082
  const reason = typeof args?.reason === "string" ? args.reason : void 0;
@@ -26436,6 +27091,8 @@ var DaemonCommandRouter = class {
26436
27091
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26437
27092
  const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
26438
27093
  if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
27094
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue requeue");
27095
+ if (ownerFailure) return ownerFailure;
26439
27096
  try {
26440
27097
  const { requeueTask: requeueTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
26441
27098
  const task = requeueTask2(meshId, taskId, {
@@ -26456,6 +27113,8 @@ var DaemonCommandRouter = class {
26456
27113
  const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
26457
27114
  if (!meshId) return { success: false, error: "meshId required" };
26458
27115
  if (!workspace) return { success: false, error: "workspace required" };
27116
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node addition");
27117
+ if (ownerFailure) return ownerFailure;
26459
27118
  try {
26460
27119
  const { addNode: addNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
26461
27120
  const providerPriority = Array.isArray(args?.providerPriority) ? args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
@@ -26464,7 +27123,8 @@ var DaemonCommandRouter = class {
26464
27123
  ...readOnly ? { readOnly: true } : {},
26465
27124
  ...providerPriority.length ? { providerPriority } : {}
26466
27125
  };
26467
- const node = addNode3(meshId, { workspace, ...policy ? { policy } : {} });
27126
+ const role = normalizeMeshDaemonRole(args?.role);
27127
+ const node = addNode3(meshId, { workspace, ...policy ? { policy } : {}, ...role ? { role } : {} });
26468
27128
  if (!node) return { success: false, error: "Mesh not found" };
26469
27129
  return { success: true, node };
26470
27130
  } catch (e) {
@@ -26475,6 +27135,8 @@ var DaemonCommandRouter = class {
26475
27135
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26476
27136
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
26477
27137
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
27138
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node update");
27139
+ if (ownerFailure) return ownerFailure;
26478
27140
  try {
26479
27141
  const { updateNode: updateNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
26480
27142
  const policy = args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy) ? { ...args.policy } : {};
@@ -26498,6 +27160,8 @@ var DaemonCommandRouter = class {
26498
27160
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26499
27161
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
26500
27162
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
27163
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node removal");
27164
+ if (ownerFailure) return ownerFailure;
26501
27165
  try {
26502
27166
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
26503
27167
  const mesh = meshRecord?.mesh;
@@ -26520,6 +27184,49 @@ var DaemonCommandRouter = class {
26520
27184
  return { success: false, error: e.message };
26521
27185
  }
26522
27186
  }
27187
+ case "get_mesh_refine_config_schema": {
27188
+ return {
27189
+ success: true,
27190
+ schema: MESH_REFINE_CONFIG_SCHEMA,
27191
+ locations: MESH_REFINE_CONFIG_LOCATIONS,
27192
+ sourceOfTruth: "repo mesh/refine config",
27193
+ heuristicRole: "suggestions_only_not_execution_path"
27194
+ };
27195
+ }
27196
+ case "validate_mesh_refine_config": {
27197
+ const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
27198
+ const mesh = args?.inlineMesh || {};
27199
+ const loaded = args?.config !== void 0 ? { config: args.config, source: "inline", sourceType: "mesh_policy" } : loadMeshRefineConfig(mesh, workspace);
27200
+ const validation = loaded.config ? validateMeshRefineConfig(loaded.config, loaded.source) : { valid: false, errors: [loaded.error || "repo mesh/refine config unavailable"], commands: [], rejectedCommands: [] };
27201
+ return { success: validation.valid, ...loaded, ...validation };
27202
+ }
27203
+ case "suggest_mesh_refine_config": {
27204
+ const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
27205
+ const mesh = args?.inlineMesh || {};
27206
+ return {
27207
+ success: true,
27208
+ ...suggestMeshRefineConfig(mesh, workspace),
27209
+ note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
27210
+ };
27211
+ }
27212
+ case "plan_mesh_refine_node": {
27213
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
27214
+ const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
27215
+ if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
27216
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
27217
+ const mesh = meshRecord?.mesh;
27218
+ const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
27219
+ if (!node?.workspace) return { success: false, error: `Node '${nodeId}' workspace not found` };
27220
+ return {
27221
+ success: true,
27222
+ dryRun: true,
27223
+ nodeId,
27224
+ workspace: node.workspace,
27225
+ validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
27226
+ mergeWillRun: false,
27227
+ cleanupWillRun: false
27228
+ };
27229
+ }
26523
27230
  case "refine_mesh_node": {
26524
27231
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26525
27232
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
@@ -26805,6 +27512,8 @@ var DaemonCommandRouter = class {
26805
27512
  if (!meshId) return { success: false, error: "meshId required" };
26806
27513
  if (!sourceNodeId) return { success: false, error: "sourceNodeId required" };
26807
27514
  if (!branch) return { success: false, error: "branch required" };
27515
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "worktree clone");
27516
+ if (ownerFailure) return ownerFailure;
26808
27517
  try {
26809
27518
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
26810
27519
  const mesh = meshRecord?.mesh;
@@ -26886,6 +27595,8 @@ var DaemonCommandRouter = class {
26886
27595
  case "trigger_mesh_queue": {
26887
27596
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
26888
27597
  if (!meshId) return { success: false, error: "meshId required" };
27598
+ const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue trigger");
27599
+ if (ownerFailure) return ownerFailure;
26889
27600
  try {
26890
27601
  const { triggerMeshQueue: triggerMeshQueue2 } = await Promise.resolve().then(() => (init_mesh_events(), mesh_events_exports));
26891
27602
  if (meshId) {
@@ -26912,6 +27623,15 @@ var DaemonCommandRouter = class {
26912
27623
  mesh = getMesh3(meshId);
26913
27624
  }
26914
27625
  if (!mesh) return { success: false, error: "Mesh not found" };
27626
+ const meshHost = resolveMeshHostStatus(mesh);
27627
+ if (!meshHost.canOwnCoordinator) {
27628
+ return {
27629
+ success: false,
27630
+ ...buildMeshHostRequiredFailure(mesh, "coordinator launch"),
27631
+ meshId,
27632
+ cliType
27633
+ };
27634
+ }
26915
27635
  if (!Array.isArray(mesh.nodes) || mesh.nodes.length === 0) return { success: false, error: "No nodes in mesh" };
26916
27636
  const requestedCoordinatorNodeId = typeof args?.coordinatorNodeId === "string" ? args.coordinatorNodeId.trim() : "";
26917
27637
  const preferredCoordinatorNodeId = requestedCoordinatorNodeId || (typeof mesh.coordinator?.preferredNodeId === "string" ? mesh.coordinator.preferredNodeId.trim() : "");
@@ -27094,7 +27814,7 @@ ${block}`);
27094
27814
  workspace
27095
27815
  };
27096
27816
  }
27097
- const { existsSync: existsSync26, readFileSync: readFileSync18, writeFileSync: writeFileSync15, copyFileSync: copyFileSync4, mkdirSync: mkdirSync17 } = await import("fs");
27817
+ const { existsSync: existsSync27, readFileSync: readFileSync19, writeFileSync: writeFileSync15, copyFileSync: copyFileSync4, mkdirSync: mkdirSync17 } = await import("fs");
27098
27818
  const { dirname: dirname9 } = await import("path");
27099
27819
  const mcpConfigPath = coordinatorSetup.configPath;
27100
27820
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -27137,14 +27857,14 @@ ${block}`);
27137
27857
  if (hermesManualFallback) return returnManualFallback(message);
27138
27858
  return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
27139
27859
  }
27140
- const hadExistingMcpConfig = existsSync26(mcpConfigPath);
27860
+ const hadExistingMcpConfig = existsSync27(mcpConfigPath);
27141
27861
  let existingMcpConfig = hermesBaseConfig?.config || {};
27142
27862
  if (hermesBaseConfig) {
27143
27863
  copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname9(mcpConfigPath));
27144
27864
  }
27145
27865
  if (hadExistingMcpConfig) {
27146
27866
  try {
27147
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync18(mcpConfigPath, "utf-8"), configFormat);
27867
+ const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync19(mcpConfigPath, "utf-8"), configFormat);
27148
27868
  const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
27149
27869
  existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
27150
27870
  copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
@@ -27234,6 +27954,7 @@ ${block}`);
27234
27954
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
27235
27955
  const mesh = meshRecord?.mesh;
27236
27956
  if (!mesh) return { success: false, error: "Mesh not found" };
27957
+ const meshHost = resolveMeshHostStatus(mesh);
27237
27958
  const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
27238
27959
  const hadAggregateCache = this.aggregateMeshStatusCache.has(meshId);
27239
27960
  if (!refreshRequested) {
@@ -27329,6 +28050,7 @@ ${block}`);
27329
28050
  repoRoot: node.repoRoot,
27330
28051
  isLocalWorktree: node.isLocalWorktree,
27331
28052
  worktreeBranch: node.worktreeBranch,
28053
+ role: normalizeMeshDaemonRole(node.role) || (meshHost.hostNodeId && nodeId === meshHost.hostNodeId ? "host" : void 0),
27332
28054
  daemonId,
27333
28055
  machineId: node.machineId,
27334
28056
  machineStatus: node.machineStatus,
@@ -27507,9 +28229,17 @@ ${block}`);
27507
28229
  repoIdentity: mesh.repoIdentity,
27508
28230
  defaultBranch: mesh.defaultBranch,
27509
28231
  refreshedAt,
28232
+ meshHost,
27510
28233
  sourceOfTruth: {
27511
28234
  membership: meshRecord?.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord?.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
27512
28235
  coordinatorOwnsLiveTruth: directTruthSatisfied,
28236
+ meshHost: {
28237
+ owner: "mesh_host_daemon",
28238
+ localRole: meshHost.role,
28239
+ hostDaemonId: meshHost.hostDaemonId,
28240
+ hostNodeId: meshHost.hostNodeId,
28241
+ hostAddress: meshHost.hostAddress
28242
+ },
27513
28243
  ...requireDirectPeerTruth ? {
27514
28244
  currentStatus: directTruthSatisfied ? "live_git_and_session_probes" : "direct_peer_truth_unavailable",
27515
28245
  directPeerTruth: {
@@ -35593,6 +36323,8 @@ async function shutdownDaemonComponents(components) {
35593
36323
  InMemoryGitSnapshotStore,
35594
36324
  LOG,
35595
36325
  MAX_LEDGER_SLICE_LIMIT,
36326
+ MESH_REFINE_CONFIG_LOCATIONS,
36327
+ MESH_REFINE_CONFIG_SCHEMA,
35596
36328
  MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
35597
36329
  MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
35598
36330
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
@@ -35615,6 +36347,7 @@ async function shutdownDaemonComponents(components) {
35615
36347
  buildChatTailDeliverySignature,
35616
36348
  buildCoordinatorSystemPrompt,
35617
36349
  buildMachineInfo,
36350
+ buildMeshHostRequiredFailure,
35618
36351
  buildMeshLedgerReconciliationEvidence,
35619
36352
  buildMeshLedgerReplicaEvidence,
35620
36353
  buildP2pRelayFailurePayload,
@@ -35640,6 +36373,7 @@ async function shutdownDaemonComponents(components) {
35640
36373
  connectCdpManager,
35641
36374
  createDebugTraceStore,
35642
36375
  createDefaultGitCommandServices,
36376
+ createDefaultMeshHostMetadata,
35643
36377
  createGitCompactSummary,
35644
36378
  createGitSnapshotStore,
35645
36379
  createGitWorkspaceMonitor,
@@ -35703,6 +36437,7 @@ async function shutdownDaemonComponents(components) {
35703
36437
  isInternalChatMessage,
35704
36438
  isManagedStatusWaiting,
35705
36439
  isManagedStatusWorking,
36440
+ isMeshHostOwner,
35706
36441
  isP2pRelayTransportFailure,
35707
36442
  isPathInside,
35708
36443
  isSessionHostLiveRuntime,
@@ -35716,6 +36451,7 @@ async function shutdownDaemonComponents(components) {
35716
36451
  listMeshes,
35717
36452
  listWorktrees,
35718
36453
  loadConfig,
36454
+ loadMeshRefineConfig,
35719
36455
  loadState,
35720
36456
  logCommand,
35721
36457
  markSetupComplete,
@@ -35729,6 +36465,7 @@ async function shutdownDaemonComponents(components) {
35729
36465
  normalizeGitWorkspaceSubscriptionParams,
35730
36466
  normalizeInputEnvelope,
35731
36467
  normalizeManagedStatus,
36468
+ normalizeMeshDaemonRole,
35732
36469
  normalizeMessageParts,
35733
36470
  normalizeRepoIdentity,
35734
36471
  normalizeSessionModalFields,
@@ -35749,6 +36486,7 @@ async function shutdownDaemonComponents(components) {
35749
36486
  removeNode,
35750
36487
  removeWorktree,
35751
36488
  requeueTask,
36489
+ requireMeshHostQueueOwner,
35752
36490
  resetConfig,
35753
36491
  resetDebugRuntimeConfig,
35754
36492
  resetState,
@@ -35756,6 +36494,8 @@ async function shutdownDaemonComponents(components) {
35756
36494
  resolveCurrentGlobalInstallSurface,
35757
36495
  resolveDebugRuntimeConfig,
35758
36496
  resolveGitRepository,
36497
+ resolveMeshHostStatus,
36498
+ resolveMeshRefineValidationPlan,
35759
36499
  resolveSessionHostAppName,
35760
36500
  resolveSessionHostAppNameResolution,
35761
36501
  resolveWorktreePath,
@@ -35771,6 +36511,7 @@ async function shutdownDaemonComponents(components) {
35771
36511
  shutdownDaemonComponents,
35772
36512
  spawnDetachedDaemonUpgradeHelper,
35773
36513
  startDaemonDevSupport,
36514
+ suggestMeshRefineConfig,
35774
36515
  summarizeGitStatus,
35775
36516
  syncMeshes,
35776
36517
  triggerMeshQueue,
@@ -35779,6 +36520,7 @@ async function shutdownDaemonComponents(components) {
35779
36520
  updateNode,
35780
36521
  updateSessionTaskStatus,
35781
36522
  updateTaskStatus,
35782
- upsertSavedProviderSession
36523
+ upsertSavedProviderSession,
36524
+ validateMeshRefineConfig
35783
36525
  });
35784
36526
  //# sourceMappingURL=index.js.map