@adhdev/daemon-standalone 1.0.29-rc.1 → 1.0.29-rc.10

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
@@ -33311,10 +33311,10 @@ var require_dist3 = __commonJS({
33311
33311
  }
33312
33312
  function getDaemonBuildInfo() {
33313
33313
  if (cached2) return cached2;
33314
- const commit = readInjected(true ? "97193d744d40d248ccdc0eb70e8170f4cccdf8a6" : void 0) ?? "unknown";
33315
- const commitShort = readInjected(true ? "97193d74" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
33316
- const version2 = readInjected(true ? "1.0.29-rc.1" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
33317
- const builtAt = readInjected(true ? "2026-07-31T22:51:04.932Z" : void 0);
33314
+ const commit = readInjected(true ? "8f47a188527a03de6f207dbc0c6d844f72dcfcc5" : void 0) ?? "unknown";
33315
+ const commitShort = readInjected(true ? "8f47a188" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
33316
+ const version2 = readInjected(true ? "1.0.29-rc.10" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
33317
+ const builtAt = readInjected(true ? "2026-08-02T07:59:04.126Z" : void 0);
33318
33318
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
33319
33319
  return cached2;
33320
33320
  }
@@ -36155,20 +36155,40 @@ ${error48.message || ""}`;
36155
36155
  let hostDaemonId = readString3(raw?.hostDaemonId);
36156
36156
  let hostNodeId = readString3(raw?.hostNodeId);
36157
36157
  const hostAddress = readString3(raw?.hostAddress);
36158
+ let hostSynthesized = false;
36159
+ const nodes = Array.isArray(meshRecord?.nodes) ? meshRecord.nodes : [];
36160
+ const nodeDaemonIdOf = (node) => {
36161
+ const record2 = readObject(node);
36162
+ return readString3(record2?.daemonId) ?? readString3(record2?.daemon_id);
36163
+ };
36164
+ if (role === "host" && !hostDaemonId) {
36165
+ const declaredHostNode = nodes.find((n) => normalizeMeshDaemonRole(readObject(n)?.role) === "host");
36166
+ if (declaredHostNode) {
36167
+ const declaredDaemonId = nodeDaemonIdOf(declaredHostNode);
36168
+ if (declaredDaemonId) {
36169
+ hostDaemonId = declaredDaemonId;
36170
+ if (!hostNodeId) hostNodeId = readString3(readObject(declaredHostNode)?.id);
36171
+ }
36172
+ }
36173
+ }
36158
36174
  const localDaemonId = readString3(opts?.localDaemonId);
36159
- if (role === "host" && !hostDaemonId && localDaemonId) {
36175
+ const hasForeignDaemonNode = localDaemonId ? nodes.some((n) => {
36176
+ const nodeDaemonId = nodeDaemonIdOf(n);
36177
+ return nodeDaemonId ? !daemonIdsEquivalent(nodeDaemonId, localDaemonId) : false;
36178
+ }) : false;
36179
+ if (role === "host" && !hostDaemonId && !hostNodeId && localDaemonId && !hasForeignDaemonNode) {
36160
36180
  hostDaemonId = localDaemonId;
36161
- if (!hostNodeId && Array.isArray(meshRecord?.nodes)) {
36162
- const selfNode = meshRecord.nodes.find((n) => {
36163
- const nodeDaemonId = readString3(readObject(n)?.daemonId);
36164
- return nodeDaemonId ? daemonIdsEquivalent(nodeDaemonId, localDaemonId) : false;
36165
- });
36166
- const selfNodeId = readString3(readObject(selfNode)?.id);
36167
- if (selfNodeId) hostNodeId = selfNodeId;
36168
- }
36181
+ hostSynthesized = true;
36182
+ const selfNode = nodes.find((n) => {
36183
+ const nodeDaemonId = nodeDaemonIdOf(n);
36184
+ return nodeDaemonId ? daemonIdsEquivalent(nodeDaemonId, localDaemonId) : false;
36185
+ });
36186
+ const selfNodeId = readString3(readObject(selfNode)?.id);
36187
+ if (selfNodeId) hostNodeId = selfNodeId;
36169
36188
  }
36170
36189
  if (hostDaemonId) normalized.hostDaemonId = hostDaemonId;
36171
36190
  if (hostNodeId) normalized.hostNodeId = hostNodeId;
36191
+ if (hostSynthesized) normalized.hostSynthesized = true;
36172
36192
  if (hostAddress) normalized.hostAddress = hostAddress;
36173
36193
  if (pairing) {
36174
36194
  const status = pairing.status === "pairing" || pairing.status === "paired" || pairing.status === "rejected" || pairing.status === "revoked" ? pairing.status : "not_configured";
@@ -36235,8 +36255,10 @@ ${error48.message || ""}`;
36235
36255
  normalizeRepoIdentity: () => normalizeRepoIdentity,
36236
36256
  removeMagiKindPanel: () => removeMagiKindPanel,
36237
36257
  removeNode: () => removeNode,
36258
+ resolveScopedMeshId: () => resolveScopedMeshId,
36238
36259
  setDifficultyBrains: () => setDifficultyBrains,
36239
36260
  setMagiKindPanel: () => setMagiKindPanel,
36261
+ setMeshHostPin: () => setMeshHostPin,
36240
36262
  tokenIdForManualPairing: () => tokenIdForManualPairing,
36241
36263
  updateMesh: () => updateMesh,
36242
36264
  updateNode: () => updateNode
@@ -36265,15 +36287,41 @@ ${error48.message || ""}`;
36265
36287
  }
36266
36288
  function migrateLoadedMeshConfig(config2) {
36267
36289
  let changed = false;
36290
+ if (foldLegacyTopLevelMeshSetting(config2, "magiKindPanels", "mesh_magi_kind_panel_set({ meshId, task_kind, slots })")) changed = true;
36291
+ if (foldLegacyTopLevelMeshSetting(config2, "difficultyBrains", "difficulty_brains_set({ meshId, difficultyBrains })")) changed = true;
36268
36292
  for (const mesh of config2.meshes) {
36269
36293
  if (!mesh || !Array.isArray(mesh.nodes)) continue;
36294
+ const brains = normalizeDifficultyBrainMap(mesh.difficultyBrains);
36295
+ const ownerBrains = Object.keys(brains).length > 0 ? brains : { ...DEFAULT_DIFFICULTY_BRAINS };
36270
36296
  for (const node of mesh.nodes) {
36271
- if (migrateProviderRolesToSlots(node?.policy)) changed = true;
36297
+ if (migrateProviderRolesToSlots(node?.policy, ownerBrains)) changed = true;
36272
36298
  }
36273
36299
  }
36274
36300
  return changed;
36275
36301
  }
36276
- function migrateProviderRolesToSlots(policy) {
36302
+ function foldLegacyTopLevelMeshSetting(config2, key2, rebindHint) {
36303
+ const root = config2;
36304
+ const legacy = root[key2];
36305
+ if (!legacy || typeof legacy !== "object" || Array.isArray(legacy)) {
36306
+ if (key2 in root) {
36307
+ delete root[key2];
36308
+ return true;
36309
+ }
36310
+ return false;
36311
+ }
36312
+ delete root[key2];
36313
+ const entryKeys = Object.keys(legacy);
36314
+ if (config2.meshes.length === 1 && entryKeys.length > 0) {
36315
+ const mesh = config2.meshes[0];
36316
+ mesh[key2] = { ...legacy, ...mesh[key2] ?? {} };
36317
+ } else if (entryKeys.length > 0) {
36318
+ console.warn(
36319
+ `[mesh-config] Dropped legacy top-level ${key2} (keys: ${entryKeys.join(", ")}) \u2014 ${config2.meshes.length} meshes are configured, so the owning mesh cannot be determined. Re-apply it per mesh with ${rebindHint}.`
36320
+ );
36321
+ }
36322
+ return true;
36323
+ }
36324
+ function migrateProviderRolesToSlots(policy, ownerDifficultyBrains) {
36277
36325
  if (!policy || typeof policy !== "object" || Array.isArray(policy)) return false;
36278
36326
  const p = policy;
36279
36327
  const rawRoles = p.providerRoles;
@@ -36296,12 +36344,7 @@ ${error48.message || ""}`;
36296
36344
  }
36297
36345
  p.slots = explicitSlots;
36298
36346
  } else if (roleCap.size) {
36299
- let difficultyBrains;
36300
- try {
36301
- difficultyBrains = getDifficultyBrains();
36302
- } catch {
36303
- difficultyBrains = void 0;
36304
- }
36347
+ const difficultyBrains = ownerDifficultyBrains;
36305
36348
  const priority = Array.isArray(p.providerPriority) ? p.providerPriority.map((t) => typeof t === "string" ? t.trim() : "").filter(Boolean) : [];
36306
36349
  const derived = deriveSlotsFromLegacy({ providerPriority: priority, difficultyBrains });
36307
36350
  const slots = derived.length ? derived : [...roleCap.values()].map((r) => ({ provider: r.provider }));
@@ -36382,7 +36425,11 @@ ${error48.message || ""}`;
36382
36425
  defaultBranch: opts.defaultBranch,
36383
36426
  policy: mergeMeshPolicy(void 0, opts.policy),
36384
36427
  coordinator: opts.coordinator || {},
36385
- meshHost: opts.meshHost || createDefaultMeshHostMetadata(),
36428
+ meshHost: opts.meshHost || (() => {
36429
+ const base = createDefaultMeshHostMetadata();
36430
+ const creatingDaemonId = typeof opts.hostDaemonId === "string" ? opts.hostDaemonId.trim() : "";
36431
+ return creatingDaemonId ? { ...base, hostDaemonId: creatingDaemonId } : base;
36432
+ })(),
36386
36433
  nodes: [],
36387
36434
  createdAt: now,
36388
36435
  updatedAt: now
@@ -36568,6 +36615,86 @@ ${error48.message || ""}`;
36568
36615
  saveMeshConfig(config2);
36569
36616
  return { accepted: true, mesh, meshHost: mesh.meshHost, node, tokenId: validation.tokenId };
36570
36617
  }
36618
+ function setMeshHostPin(meshId, opts) {
36619
+ const config2 = loadMeshConfig();
36620
+ const mesh = config2.meshes.find((m) => m.id === meshId);
36621
+ if (!mesh) return void 0;
36622
+ const hostDaemonId = typeof opts.hostDaemonId === "string" ? opts.hostDaemonId.trim() : "";
36623
+ const hostNodeId = typeof opts.hostNodeId === "string" ? opts.hostNodeId.trim() : "";
36624
+ const previous = mesh.meshHost || createDefaultMeshHostMetadata();
36625
+ if (!hostDaemonId && !hostNodeId) {
36626
+ return {
36627
+ mesh,
36628
+ meshHost: previous,
36629
+ applied: false,
36630
+ reason: "invalid_host_daemon_id",
36631
+ ...previous.hostDaemonId ? { hostDaemonId: previous.hostDaemonId } : {},
36632
+ ...previous.hostNodeId ? { hostNodeId: previous.hostNodeId } : {}
36633
+ };
36634
+ }
36635
+ if (previous.role === "member") {
36636
+ return {
36637
+ mesh,
36638
+ meshHost: previous,
36639
+ applied: false,
36640
+ reason: "not_host_role",
36641
+ ...previous.hostDaemonId ? { hostDaemonId: previous.hostDaemonId } : {},
36642
+ ...previous.hostNodeId ? { hostNodeId: previous.hostNodeId } : {}
36643
+ };
36644
+ }
36645
+ const existingDaemonId = typeof previous.hostDaemonId === "string" ? previous.hostDaemonId.trim() : "";
36646
+ if (existingDaemonId && !opts.force) {
36647
+ const sameHost = hostDaemonId ? daemonIdsEquivalent(existingDaemonId, hostDaemonId) : true;
36648
+ if (!sameHost) {
36649
+ return {
36650
+ mesh,
36651
+ meshHost: previous,
36652
+ applied: false,
36653
+ reason: "host_already_pinned",
36654
+ hostDaemonId: existingDaemonId,
36655
+ ...previous.hostNodeId ? { hostNodeId: previous.hostNodeId } : {}
36656
+ };
36657
+ }
36658
+ const existingNodeId = typeof previous.hostNodeId === "string" ? previous.hostNodeId.trim() : "";
36659
+ if (!hostNodeId || hostNodeId === existingNodeId) {
36660
+ return {
36661
+ mesh,
36662
+ meshHost: previous,
36663
+ applied: false,
36664
+ reason: "already_pinned_same",
36665
+ hostDaemonId: existingDaemonId,
36666
+ ...existingNodeId ? { hostNodeId: existingNodeId } : {}
36667
+ };
36668
+ }
36669
+ }
36670
+ const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
36671
+ const effectiveDaemonId = hostDaemonId || existingDaemonId;
36672
+ mesh.meshHost = {
36673
+ ...previous,
36674
+ role: "host",
36675
+ ...effectiveDaemonId ? { hostDaemonId: effectiveDaemonId } : {},
36676
+ ...hostNodeId ? { hostNodeId } : previous.hostNodeId ? { hostNodeId: previous.hostNodeId } : {},
36677
+ ...opts.hostAddress?.trim() ? { hostAddress: opts.hostAddress.trim() } : {}
36678
+ };
36679
+ const hostNode = hostNodeId ? mesh.nodes.find((n) => n.id === hostNodeId) : effectiveDaemonId ? mesh.nodes.find((n) => n.daemonId && daemonIdsEquivalent(n.daemonId, effectiveDaemonId)) : void 0;
36680
+ if (hostNode) {
36681
+ for (const node of mesh.nodes) {
36682
+ if (node.role === "host" && node !== hostNode) node.role = void 0;
36683
+ }
36684
+ hostNode.role = "host";
36685
+ if (!mesh.meshHost.hostNodeId) mesh.meshHost.hostNodeId = hostNode.id;
36686
+ }
36687
+ mesh.updatedAt = now;
36688
+ saveMeshConfig(config2);
36689
+ return {
36690
+ mesh,
36691
+ meshHost: mesh.meshHost,
36692
+ applied: true,
36693
+ reason: "pinned",
36694
+ ...mesh.meshHost.hostDaemonId ? { hostDaemonId: mesh.meshHost.hostDaemonId } : {},
36695
+ ...mesh.meshHost.hostNodeId ? { hostNodeId: mesh.meshHost.hostNodeId } : {}
36696
+ };
36697
+ }
36571
36698
  function markMeshHostPairingJoined(meshId, opts) {
36572
36699
  const config2 = loadMeshConfig();
36573
36700
  const mesh = config2.meshes.find((m) => m.id === meshId);
@@ -36628,6 +36755,12 @@ ${error48.message || ""}`;
36628
36755
  worktreeBootstrap: opts.worktreeBootstrap,
36629
36756
  role: opts.role
36630
36757
  };
36758
+ const pinnedHostDaemonId = typeof mesh.meshHost?.hostDaemonId === "string" ? mesh.meshHost.hostDaemonId.trim() : "";
36759
+ const anchorMissing = !(typeof mesh.meshHost?.hostNodeId === "string" && mesh.meshHost.hostNodeId.trim());
36760
+ if (!node.role && anchorMissing && pinnedHostDaemonId && mesh.meshHost?.role !== "member" && node.daemonId && daemonIdsEquivalent(node.daemonId, pinnedHostDaemonId) && !mesh.nodes.some((n) => n.role === "host")) {
36761
+ node.role = "host";
36762
+ mesh.meshHost = { ...mesh.meshHost || createDefaultMeshHostMetadata(), hostNodeId: node.id };
36763
+ }
36631
36764
  mesh.nodes.push(node);
36632
36765
  mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
36633
36766
  saveMeshConfig(config2);
@@ -36640,6 +36773,7 @@ ${error48.message || ""}`;
36640
36773
  const idx = mesh.nodes.findIndex((n) => n.id === nodeId);
36641
36774
  if (idx === -1) return false;
36642
36775
  mesh.nodes.splice(idx, 1);
36776
+ pruneMagiKindPanelsForRemovedNode(mesh, nodeId);
36643
36777
  mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
36644
36778
  saveMeshConfig(config2);
36645
36779
  return true;
@@ -36696,7 +36830,8 @@ ${error48.message || ""}`;
36696
36830
  }
36697
36831
  return s2;
36698
36832
  }
36699
- function normalizeMagiSlots(slots) {
36833
+ function normalizeMagiSlots(slots, knownNodeIds) {
36834
+ const allowed = knownNodeIds ? new Set(knownNodeIds) : void 0;
36700
36835
  if (!Array.isArray(slots) || slots.length === 0) {
36701
36836
  throw new Error("invalid_magi_kind_panel: slots must be a non-empty array");
36702
36837
  }
@@ -36713,6 +36848,11 @@ ${error48.message || ""}`;
36713
36848
  throw new Error(`invalid_magi_kind_panel: slot[${idx}].provider is required`);
36714
36849
  }
36715
36850
  const nodeId = typeof s2.nodeId === "string" && s2.nodeId.trim() ? s2.nodeId.trim() : void 0;
36851
+ if (nodeId && allowed && !allowed.has(nodeId)) {
36852
+ throw new Error(
36853
+ `invalid_magi_kind_panel: slot[${idx}].nodeId '${nodeId}' is not a node of this mesh (known: ${[...allowed].join(", ") || "(none)"}). Pin a node from this mesh, or omit nodeId to let the fan-out pick any node offering the provider.`
36854
+ );
36855
+ }
36716
36856
  const model = typeof s2.model === "string" && s2.model.trim() ? s2.model.trim() : void 0;
36717
36857
  const capabilityTags = normalizeCapabilityTags(s2.capabilityTags);
36718
36858
  const n = normalizeReplicaCount(s2.n);
@@ -36725,32 +36865,51 @@ ${error48.message || ""}`;
36725
36865
  };
36726
36866
  });
36727
36867
  }
36728
- function listMagiKindPanels() {
36729
- return loadMeshConfig().magiKindPanels ?? {};
36868
+ function resolveScopedMeshId(config2) {
36869
+ const meshes = (config2 ?? loadMeshConfig({ persistMigrations: false })).meshes;
36870
+ return meshes.length === 1 ? meshes[0].id : void 0;
36871
+ }
36872
+ function resolveScopedMesh(config2, meshId) {
36873
+ const id = meshId?.trim() || resolveScopedMeshId(config2);
36874
+ if (!id) return void 0;
36875
+ return config2.meshes.find((m) => m.id === id);
36730
36876
  }
36731
- function listMagiKindPanelsReadOnly() {
36732
- return loadMeshConfig({ persistMigrations: false }).magiKindPanels ?? {};
36877
+ function listMagiKindPanels(meshId) {
36878
+ const config2 = loadMeshConfig();
36879
+ return resolveScopedMesh(config2, meshId)?.magiKindPanels ?? {};
36880
+ }
36881
+ function listMagiKindPanelsReadOnly(meshId) {
36882
+ const config2 = loadMeshConfig({ persistMigrations: false });
36883
+ return resolveScopedMesh(config2, meshId)?.magiKindPanels ?? {};
36733
36884
  }
36734
- function getMagiKindPanel(kind) {
36885
+ function getMagiKindPanel(kind, meshId) {
36735
36886
  let key2;
36736
36887
  try {
36737
36888
  key2 = normalizeMagiTaskKindKey(kind);
36738
36889
  } catch {
36739
36890
  return void 0;
36740
36891
  }
36741
- return loadMeshConfig().magiKindPanels?.[key2];
36892
+ const config2 = loadMeshConfig();
36893
+ return resolveScopedMesh(config2, meshId)?.magiKindPanels?.[key2];
36742
36894
  }
36743
- function setMagiKindPanel(kind, slots) {
36895
+ function setMagiKindPanel(kind, slots, meshId) {
36744
36896
  const key2 = normalizeMagiTaskKindKey(kind);
36745
- const normalized = normalizeMagiSlots(slots);
36746
36897
  const stored = loadMeshConfig();
36747
- const map3 = stored.magiKindPanels ?? {};
36898
+ const mesh = resolveScopedMesh(stored, meshId);
36899
+ if (!mesh) {
36900
+ throw new Error(
36901
+ meshId?.trim() ? `invalid_magi_kind_panel: mesh '${meshId.trim()}' not found` : `magi_kind_panel_mesh_ambiguous: this machine hosts ${stored.meshes.length} meshes, so a MAGI kind-panel write must name its mesh explicitly (meshId). Panels are per mesh.`
36902
+ );
36903
+ }
36904
+ const normalized = normalizeMagiSlots(slots, mesh.nodes.map((n) => n.id));
36905
+ const map3 = mesh.magiKindPanels ?? {};
36748
36906
  map3[key2] = normalized;
36749
- stored.magiKindPanels = map3;
36907
+ mesh.magiKindPanels = map3;
36908
+ mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
36750
36909
  saveMeshConfig(stored);
36751
36910
  return normalized;
36752
36911
  }
36753
- function removeMagiKindPanel(kind) {
36912
+ function removeMagiKindPanel(kind, meshId) {
36754
36913
  let key2;
36755
36914
  try {
36756
36915
  key2 = normalizeMagiTaskKindKey(kind);
@@ -36758,21 +36917,47 @@ ${error48.message || ""}`;
36758
36917
  return false;
36759
36918
  }
36760
36919
  const stored = loadMeshConfig();
36761
- if (!stored.magiKindPanels || !stored.magiKindPanels[key2]) return false;
36762
- delete stored.magiKindPanels[key2];
36920
+ const mesh = resolveScopedMesh(stored, meshId);
36921
+ if (!mesh?.magiKindPanels?.[key2]) return false;
36922
+ delete mesh.magiKindPanels[key2];
36923
+ if (Object.keys(mesh.magiKindPanels).length === 0) delete mesh.magiKindPanels;
36924
+ mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
36763
36925
  saveMeshConfig(stored);
36764
36926
  return true;
36765
36927
  }
36766
- function getDifficultyBrains() {
36767
- const stored = loadMeshConfig().difficultyBrains;
36928
+ function pruneMagiKindPanelsForRemovedNode(mesh, nodeId) {
36929
+ const panels = mesh.magiKindPanels;
36930
+ if (!panels) return false;
36931
+ let changed = false;
36932
+ for (const [kind, slots] of Object.entries(panels)) {
36933
+ if (!Array.isArray(slots)) continue;
36934
+ const kept = slots.filter((slot) => slot.nodeId !== nodeId);
36935
+ if (kept.length === slots.length) continue;
36936
+ changed = true;
36937
+ if (kept.length === 0) delete panels[kind];
36938
+ else panels[kind] = kept;
36939
+ }
36940
+ if (changed && Object.keys(panels).length === 0) delete mesh.magiKindPanels;
36941
+ return changed;
36942
+ }
36943
+ function getDifficultyBrains(meshId) {
36944
+ const config2 = loadMeshConfig();
36945
+ const stored = resolveScopedMesh(config2, meshId)?.difficultyBrains;
36768
36946
  const normalized = normalizeDifficultyBrainMap(stored);
36769
36947
  return Object.keys(normalized).length > 0 ? normalized : { ...DEFAULT_DIFFICULTY_BRAINS };
36770
36948
  }
36771
- function setDifficultyBrains(map3) {
36949
+ function setDifficultyBrains(map3, meshId) {
36772
36950
  const normalized = normalizeDifficultyBrainMap(map3);
36773
36951
  const stored = loadMeshConfig();
36774
- if (Object.keys(normalized).length > 0) stored.difficultyBrains = normalized;
36775
- else delete stored.difficultyBrains;
36952
+ const mesh = resolveScopedMesh(stored, meshId);
36953
+ if (!mesh) {
36954
+ throw new Error(
36955
+ meshId?.trim() ? `invalid_difficulty_brains: mesh '${meshId.trim()}' not found` : `difficulty_brains_mesh_ambiguous: this machine hosts ${stored.meshes.length} meshes, so a difficulty-brain write must name its mesh explicitly (meshId). Presets are per mesh \u2014 they decide which model a task runs on, so writing to the wrong mesh changes what it costs.`
36956
+ );
36957
+ }
36958
+ if (Object.keys(normalized).length > 0) mesh.difficultyBrains = normalized;
36959
+ else delete mesh.difficultyBrains;
36960
+ mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
36776
36961
  saveMeshConfig(stored);
36777
36962
  return normalized;
36778
36963
  }
@@ -36808,12 +36993,12 @@ ${error48.message || ""}`;
36808
36993
  return true;
36809
36994
  });
36810
36995
  }
36811
- function resolveNodeCapabilitySlots(node) {
36996
+ function resolveNodeCapabilitySlots(node, meshId) {
36812
36997
  const explicit = normalizeNodeCapabilitySlots(node?.policy?.slots);
36813
36998
  if (explicit.length) return explicit;
36814
36999
  let difficultyBrains;
36815
37000
  try {
36816
- difficultyBrains = getDifficultyBrains();
37001
+ difficultyBrains = getDifficultyBrains(meshId);
36817
37002
  } catch {
36818
37003
  difficultyBrains = void 0;
36819
37004
  }
@@ -39298,9 +39483,20 @@ Next step: ${nextStep}`;
39298
39483
  }
39299
39484
  continue;
39300
39485
  }
39486
+ if (validated.intendedFor && !validated.intendedFor.sessionId && daemonIdsEquivalent(validated.intendedFor.daemonId, drainer.daemonId)) {
39487
+ const liveSessions = ctx.countLiveCoordinatorSessions?.(validated.intendedFor.daemonId) ?? 0;
39488
+ if (liveSessions > 1) {
39489
+ warnV2Once(
39490
+ `${event.meshId}::${eventId}::ambiguous-unicast`,
39491
+ `v2 unicast ${event.event} on mesh ${event.meshId} carries NO coordinator session but ${liveSessions} coordinator sessions are live on ${validated.intendedFor.daemonId} \u2014 delivery is first-come-first-served and may reach the wrong coordinator. Delivered to ${coordinatorIdentityKey(drainer)}. The EMITTING path must stamp targetCoordinatorSessionId.`
39492
+ );
39493
+ bump("v2AmbiguousUnicastDelivered");
39494
+ }
39495
+ }
39301
39496
  if (validated.intendedFor && identityDeliversTo(validated.intendedFor, drainer)) {
39302
39497
  ctx.batchSeen.add(eventId);
39303
39498
  bump("v2Delivered");
39499
+ LOG2.debug("MeshEventsV2", `unicast ${event.event} (mesh ${event.meshId}, eventId ${eventId}) delivered to ${coordinatorIdentityKey(drainer)}; intendedFor=${coordinatorIdentityKey(validated.intendedFor)}`);
39304
39500
  kept.push(event);
39305
39501
  continue;
39306
39502
  }
@@ -39315,6 +39511,7 @@ Next step: ${nextStep}`;
39315
39511
  kept.push(event);
39316
39512
  continue;
39317
39513
  }
39514
+ LOG2.debug("MeshEventsV2", `unicast ${event.event} (mesh ${event.meshId}, eventId ${eventId}) NOT delivered to ${coordinatorIdentityKey(drainer)}; intendedFor=${validated.intendedFor ? coordinatorIdentityKey(validated.intendedFor) : "none"} \u2014 left for its own drainer`);
39318
39515
  bump("v2RoutedAway");
39319
39516
  }
39320
39517
  return kept;
@@ -39767,7 +39964,12 @@ Next step: ${nextStep}`;
39767
39964
  try {
39768
39965
  const store = MeshRuntimeStore.getInstance();
39769
39966
  if (store.pendingEventCount(meshId) > 0) {
39770
- for (const row of store.drainPendingEvents(meshId, daemonIds.length > 0 ? daemonIds : void 0, onlyEvents ? { onlyEvents } : void 0)) {
39967
+ const drainedBy = drainer ? JSON.stringify(drainer) : null;
39968
+ for (const row of store.drainPendingEvents(
39969
+ meshId,
39970
+ daemonIds.length > 0 ? daemonIds : void 0,
39971
+ { ...onlyEvents ? { onlyEvents } : {}, drainedBy }
39972
+ )) {
39771
39973
  const event = row.payload;
39772
39974
  if (event) pushUnique(event);
39773
39975
  }
@@ -39806,7 +40008,8 @@ Next step: ${nextStep}`;
39806
40008
  const routed = routeV2EventsForDrainer(merged, drainer, {
39807
40009
  alreadyDrained: (eventId) => priorDrainedEventIds.has(eventId),
39808
40010
  batchSeen: /* @__PURE__ */ new Set(),
39809
- countMetrics: true
40011
+ countMetrics: true,
40012
+ ...opts?.countLiveCoordinatorSessions ? { countLiveCoordinatorSessions: opts.countLiveCoordinatorSessions } : {}
39810
40013
  });
39811
40014
  return reconcilePendingMeshCoordinatorEvents(meshId, routed);
39812
40015
  }
@@ -40026,6 +40229,11 @@ Next step: ${nextStep}`;
40026
40229
  /** unicast events re-attributed to the drainer via daemon-core match (a
40027
40230
  * coordinatorRunId change orphaned them). */
40028
40231
  v2ReattributedToDrainer: 0,
40232
+ /** REFINE-EVENT-SESSION-SCOPED-UNICAST: unicast events delivered while addressed only
40233
+ * at DAEMON granularity (no sessionId in intendedFor) even though >1 coordinator
40234
+ * session was live on that daemon — i.e. delivered by race, not by address. Non-zero
40235
+ * here names an emit path that still fails to stamp targetCoordinatorSessionId. */
40236
+ v2AmbiguousUnicastDelivered: 0,
40029
40237
  /** v1 (unversioned) events passed through as broadcast (rollout baseline). */
40030
40238
  v1BroadcastAccepted: 0,
40031
40239
  /** T6 enforce: v2 events that FAILED validation and were QUARANTINED (held back
@@ -40828,7 +41036,7 @@ Next step: ${nextStep}`;
40828
41036
  const taskDifficulty = isMeshTaskDifficulty(opts?.difficulty) ? opts.difficulty : void 0;
40829
41037
  if (isMeshTaskDifficulty(opts?.difficulty)) {
40830
41038
  try {
40831
- const preset = getDifficultyBrains()[opts.difficulty];
41039
+ const preset = getDifficultyBrains(meshId)[opts.difficulty];
40832
41040
  if (preset) {
40833
41041
  if (!effectiveModel && preset.model) effectiveModel = preset.model;
40834
41042
  if (!effectiveThinkingLevel && preset.thinkingLevel) effectiveThinkingLevel = preset.thinkingLevel;
@@ -41784,7 +41992,15 @@ Next step: ${nextStep}`;
41784
41992
  event_id TEXT,
41785
41993
  scope TEXT,
41786
41994
  dispatched_by TEXT,
41787
- intended_for TEXT
41995
+ intended_for TEXT,
41996
+ -- REFINE-EVENT-SESSION-SCOPED-UNICAST: WHO consumed this row. The ledger
41997
+ -- previously recorded only THAT an event was drained, never by which
41998
+ -- coordinator identity \u2014 so a mis-delivered unicast (a sibling session
41999
+ -- consuming another coordinator's event) left no evidence and had to be
42000
+ -- inferred. Written at drain time as the JSON-serialized drainer
42001
+ -- CoordinatorIdentity. NULL on rows drained before this column existed
42002
+ -- and on any drain whose caller passed no identity (daemon-level drain).
42003
+ drained_by TEXT
41788
42004
  );
41789
42005
 
41790
42006
  CREATE INDEX IF NOT EXISTS idx_mesh_pending_events_mesh_drained
@@ -42030,7 +42246,7 @@ Next step: ${nextStep}`;
42030
42246
  this.db.exec(`ALTER TABLE mesh_missions ADD COLUMN close_candidate_emitted_at TEXT`);
42031
42247
  }
42032
42248
  const pendingCols = this.tableColumns("mesh_pending_events");
42033
- for (const col of ["protocol_version", "event_id", "scope", "dispatched_by", "intended_for"]) {
42249
+ for (const col of ["protocol_version", "event_id", "scope", "dispatched_by", "intended_for", "drained_by"]) {
42034
42250
  if (!pendingCols.has(col)) {
42035
42251
  this.db.exec(`ALTER TABLE mesh_pending_events ADD COLUMN ${col} TEXT`);
42036
42252
  }
@@ -43299,8 +43515,8 @@ Next step: ${nextStep}`;
43299
43515
  const ids = rows.map((r) => r.id);
43300
43516
  const now = Date.now();
43301
43517
  this.db.prepare(
43302
- `UPDATE mesh_pending_events SET drained = 1, drained_at = ? WHERE id IN (${ids.map(() => "?").join(",")})`
43303
- ).run(now, ...ids);
43518
+ `UPDATE mesh_pending_events SET drained = 1, drained_at = ?, drained_by = ? WHERE id IN (${ids.map(() => "?").join(",")})`
43519
+ ).run(now, opts?.drainedBy ?? null, ...ids);
43304
43520
  return rows.map((r) => ({
43305
43521
  id: r.id,
43306
43522
  event: r.event,
@@ -43334,6 +43550,30 @@ Next step: ${nextStep}`;
43334
43550
  })()
43335
43551
  }));
43336
43552
  }
43553
+ /**
43554
+ * REFINE-EVENT-SESSION-SCOPED-UNICAST — drain attribution audit. Returns the most
43555
+ * recent pending-event rows for a mesh with WHO drained each one, so a suspected
43556
+ * mis-delivery ("my refine result went to another coordinator session") is answered
43557
+ * from the ledger instead of inferred from timing. `drainedBy` is the serialized
43558
+ * drainer CoordinatorIdentity, or null when the row is still queued, was drained
43559
+ * before this column existed, or was drained by a caller that passed no identity.
43560
+ */
43561
+ recentDrainedPendingEvents(meshId, limit = 100) {
43562
+ const rows = this.db.prepare(
43563
+ `SELECT id, event, scope, intended_for, drained_by, drained, queued_at, drained_at
43564
+ FROM mesh_pending_events WHERE mesh_id = ? ORDER BY queued_at DESC LIMIT ?`
43565
+ ).all(meshId, Math.max(1, limit));
43566
+ return rows.map((r) => ({
43567
+ id: r.id,
43568
+ event: r.event,
43569
+ scope: r.scope ?? null,
43570
+ intendedFor: r.intended_for ?? null,
43571
+ drainedBy: r.drained_by ?? null,
43572
+ drained: r.drained === 1,
43573
+ queuedAt: r.queued_at,
43574
+ drainedAt: r.drained_at ?? null
43575
+ }));
43576
+ }
43337
43577
  hasPendingEventFingerprint(meshId, fingerprint) {
43338
43578
  const row = this.db.prepare(
43339
43579
  "SELECT 1 FROM mesh_pending_events WHERE mesh_id = ? AND fingerprint = ? AND drained = 0 LIMIT 1"
@@ -43508,7 +43748,7 @@ Next step: ${nextStep}`;
43508
43748
  requeueDrainedPendingEventByFingerprint(meshId, fingerprint) {
43509
43749
  if (!fingerprint) return false;
43510
43750
  const changes = this.db.prepare(
43511
- `UPDATE mesh_pending_events SET drained = 0, drained_at = NULL
43751
+ `UPDATE mesh_pending_events SET drained = 0, drained_at = NULL, drained_by = NULL
43512
43752
  WHERE mesh_id = ? AND fingerprint = ? AND drained = 1`
43513
43753
  ).run(meshId, fingerprint).changes;
43514
43754
  if (changes > 0) this.maybeCheckpointWal();
@@ -54047,6 +54287,77 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54047
54287
  ]);
54048
54288
  }
54049
54289
  });
54290
+ function normalizeLiteral(model) {
54291
+ return model.toLowerCase().replace(/[_/\\().,]+/g, " ").replace(/-+/g, " ").replace(/\s+/g, " ").trim();
54292
+ }
54293
+ function canonicalizeModelName(model) {
54294
+ if (typeof model !== "string") return void 0;
54295
+ const literal2 = normalizeLiteral(model);
54296
+ if (!literal2) return void 0;
54297
+ const tokens = literal2.split(" ").filter((t) => t && !MODIFIER_WORDS.has(t));
54298
+ const family = CLAUDE_FAMILIES.find((f) => tokens.includes(f));
54299
+ let version2;
54300
+ if (family) {
54301
+ const after = tokens.slice(tokens.indexOf(family) + 1).filter((t) => /^\d+$/.test(t));
54302
+ if (after.length) version2 = after.join(".");
54303
+ else {
54304
+ const dotted = tokens.find((t) => /^\d+(\.\d+)+$/.test(t));
54305
+ if (dotted) version2 = dotted;
54306
+ }
54307
+ }
54308
+ return { family, version: version2, literal: literal2 };
54309
+ }
54310
+ function modelNamesEquivalent(a, b) {
54311
+ const ca = canonicalizeModelName(a);
54312
+ const cb = canonicalizeModelName(b);
54313
+ if (!ca || !cb) return false;
54314
+ if (ca.family && cb.family) {
54315
+ if (ca.family !== cb.family) return false;
54316
+ if (ca.version && cb.version) return ca.version === cb.version;
54317
+ return true;
54318
+ }
54319
+ if (ca.family || cb.family) return false;
54320
+ return ca.literal === cb.literal;
54321
+ }
54322
+ function isModelAllowedBySlot(model, slot) {
54323
+ if (!slot) return true;
54324
+ const declared = typeof slot.model === "string" ? slot.model.trim() : "";
54325
+ const requested = typeof model === "string" ? model.trim() : "";
54326
+ if (!declared) return !requested;
54327
+ if (!requested) return true;
54328
+ return modelNamesEquivalent(requested, declared);
54329
+ }
54330
+ function decideSlotForModel(input) {
54331
+ const { requestedModel, slots } = input;
54332
+ const declaring = slots.filter((s2) => isModelAllowedBySlot(requestedModel, s2.slot));
54333
+ if (!declaring.length) {
54334
+ const declaredModels = slots.map((s2) => typeof s2.slot.model === "string" && s2.slot.model.trim() ? s2.slot.model.trim() : "(provider default)").filter((v, i, a) => a.indexOf(v) === i);
54335
+ return { outcome: "notify", reason: SLOT_MODEL_ABSENT_SKIP_REASON, declaredModels };
54336
+ }
54337
+ const free = declaring.find((s2) => s2.available);
54338
+ if (free) {
54339
+ const declared = typeof free.slot.model === "string" && free.slot.model.trim() ? free.slot.model.trim() : void 0;
54340
+ return { outcome: "run", slot: free.slot, model: declared };
54341
+ }
54342
+ return {
54343
+ outcome: "wait",
54344
+ reason: SLOT_MODEL_BUSY_SKIP_REASON,
54345
+ busySlots: declaring.map((s2) => s2.slot)
54346
+ };
54347
+ }
54348
+ var CLAUDE_FAMILIES;
54349
+ var MODIFIER_WORDS;
54350
+ var SLOT_MODEL_BUSY_SKIP_REASON;
54351
+ var SLOT_MODEL_ABSENT_SKIP_REASON;
54352
+ var init_slot_model_enforcement = __esm2({
54353
+ "src/mesh/slot-model-enforcement.ts"() {
54354
+ "use strict";
54355
+ CLAUDE_FAMILIES = ["opus", "sonnet", "haiku"];
54356
+ MODIFIER_WORDS = /* @__PURE__ */ new Set(["thinking", "latest", "preview", "claude", "anthropic"]);
54357
+ SLOT_MODEL_BUSY_SKIP_REASON = "slot_for_model_busy";
54358
+ SLOT_MODEL_ABSENT_SKIP_REASON = "no_slot_declares_requested_model";
54359
+ }
54360
+ });
54050
54361
  function encodeDuplicateMeshDispatchCode(holderSessionId) {
54051
54362
  const holder = typeof holderSessionId === "string" ? holderSessionId.trim() : "";
54052
54363
  return holder ? `${DUPLICATE_MESH_DISPATCH_CODE}:${holder}` : DUPLICATE_MESH_DISPATCH_CODE;
@@ -54111,11 +54422,34 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54111
54422
  if (!workspace) return null;
54112
54423
  try {
54113
54424
  const result = loadRepoMeshJsonConfig(workspace);
54114
- return result.sourceType === "repo_file" && result.config ? result.config : null;
54425
+ if (result.sourceType !== "repo_file" || !result.config) return null;
54426
+ if (!isConfigPathInsideWorkspace(result.path, workspace)) return null;
54427
+ return result.config;
54115
54428
  } catch {
54116
54429
  return null;
54117
54430
  }
54118
54431
  }
54432
+ function isConfigPathInsideWorkspace(configPath, workspace) {
54433
+ if (typeof configPath !== "string" || !configPath) return false;
54434
+ const toPosix = (value) => value.replace(/\\/g, "/").replace(/\/+$/, "");
54435
+ const ws = toPosix(workspace);
54436
+ const target = toPosix(configPath);
54437
+ return !!ws && (target === ws || target.startsWith(`${ws}/`));
54438
+ }
54439
+ function warnUnreadableRepoConfigForNode(node, providerType) {
54440
+ const workspace = typeof node?.workspace === "string" && node.workspace.trim() ? node.workspace.trim() : "";
54441
+ if (!workspace) return;
54442
+ const nodeId = readNonEmptyString(node?.id) || readNonEmptyString(node?.nodeId) || "unknown-node";
54443
+ LOG2.warn(
54444
+ "MeshQueue",
54445
+ `repo mesh.json unreadable from this daemon for node=${nodeId} workspace=${workspace} provider=${providerType || "unknown"} \u2014 delegated auto-approve MODE falls back to the provider default here; the worker daemon re-resolves it from its own checkout at launch`
54446
+ );
54447
+ }
54448
+ function delegatedWorkerAutoApproveSettingsForNode(mesh, node, provider, providerType) {
54449
+ const repoConfig = loadRepoConfigForNode(node);
54450
+ if (!repoConfig) warnUnreadableRepoConfigForNode(node, providerType);
54451
+ return delegatedWorkerAutoApproveSettings(mesh?.policy, node?.policy, provider, repoConfig, providerType);
54452
+ }
54119
54453
  function getMeshWithCache(components, meshId) {
54120
54454
  const localMesh = getMesh(meshId);
54121
54455
  const cachedMesh = components.router?.getCachedInlineMesh(meshId);
@@ -54395,7 +54729,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54395
54729
  return false;
54396
54730
  }
54397
54731
  const capabilityTags = buildMeshNodeCapabilityTags(node, providerType);
54398
- const providerMaxParallel = resolveProviderMaxParallel(resolveNodeCapabilitySlots(node), providerType);
54732
+ const providerMaxParallel = resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), providerType);
54399
54733
  const nodeIsWorktree = node?.isLocalWorktree === true;
54400
54734
  const assignedTranscriptProfile = resolveClaimingSessionTranscriptProfile(components, sessionId);
54401
54735
  const task = claimNextTask(meshId, nodeId, sessionId, capabilityTags, {
@@ -54532,11 +54866,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54532
54866
  meshNodeFor: meshId,
54533
54867
  meshNodeId: nodeId,
54534
54868
  launchedByCoordinator: true,
54535
- ...delegatedWorkerAutoApproveSettings(
54536
- mesh?.policy,
54537
- node?.policy,
54869
+ ...delegatedWorkerAutoApproveSettingsForNode(
54870
+ mesh,
54871
+ node,
54538
54872
  components.providerLoader?.getMeta(providerType),
54539
- loadRepoConfigForNode(node),
54540
54873
  providerType
54541
54874
  ),
54542
54875
  ...localDaemonId ? { meshCoordinatorDaemonId: localDaemonId } : {},
@@ -54696,6 +55029,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54696
55029
  summary: "the node's workspace is dirty, so auto-launch is blocked to avoid clobbering uncommitted changes",
54697
55030
  nextAction: "Clean or commit the node's working tree (or fast-forward it); the task will then auto-assign."
54698
55031
  };
55032
+ if (reason === SLOT_MODEL_ABSENT_SKIP_REASON) return {
55033
+ summary: "no capability slot on the node declares the model this task resolved to (its difficulty\u2192brain preset picked a model the node was never configured to run)",
55034
+ nextAction: "Re-enqueue with a difficulty/model the node's slots declare, target a node that declares this model, or add a slot for it. The task is NOT run on a substitute model \u2014 an undeclared model is never launched."
55035
+ };
54699
55036
  return {
54700
55037
  summary: `it cannot be dispatched (${reason})`,
54701
55038
  nextAction: "Inspect the node/mesh state with mesh_status and resolve the blocker, or re-enqueue the task."
@@ -54887,8 +55224,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54887
55224
  }
54888
55225
  return score;
54889
55226
  }
54890
- function bestSlotForTask(node, task) {
54891
- const slots = resolveNodeCapabilitySlots(node);
55227
+ function bestSlotForTask(node, task, meshId) {
55228
+ const slots = resolveNodeCapabilitySlots(node, meshId);
54892
55229
  if (!slots.length) return null;
54893
55230
  let best = null;
54894
55231
  for (const slot of slots) {
@@ -54897,8 +55234,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54897
55234
  }
54898
55235
  return best;
54899
55236
  }
54900
- function nodeFitnessForTask(node, task) {
54901
- return bestSlotForTask(node, task)?.score ?? 0;
55237
+ function nodeFitnessForTask(node, task, meshId) {
55238
+ return bestSlotForTask(node, task, meshId)?.score ?? 0;
54902
55239
  }
54903
55240
  function orderEligibleNodes(meshId, strategy, nodes, opts) {
54904
55241
  if (strategy === "first_eligible" || nodes.length <= 1) {
@@ -54907,7 +55244,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54907
55244
  if (strategy === "fitness" && opts?.task) {
54908
55245
  const task = opts.task;
54909
55246
  return [...nodes].sort((a, b) => {
54910
- const fitDelta = nodeFitnessForTask(b.node, task) - nodeFitnessForTask(a.node, task);
55247
+ const fitDelta = nodeFitnessForTask(b.node, task, meshId) - nodeFitnessForTask(a.node, task, meshId);
54911
55248
  if (fitDelta !== 0) return fitDelta;
54912
55249
  const prioDelta = resolveNodeSchedulingPriority(b.node?.policy) - resolveNodeSchedulingPriority(a.node?.policy);
54913
55250
  if (prioDelta !== 0) return prioDelta;
@@ -54936,6 +55273,13 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54936
55273
  function activeProviderAssignedCount(meshId, nodeId, providerType) {
54937
55274
  return getQueue(meshId, { status: ["assigned"] }).filter((task) => daemonIdsEquivalent(task.assignedNodeId, nodeId) && task.assignedProviderType === providerType).length;
54938
55275
  }
55276
+ function slotHasCapacity(meshId, nodeId, node, slot) {
55277
+ const providerType = typeof slot.provider === "string" ? slot.provider.trim() : "";
55278
+ if (!providerType) return false;
55279
+ const cap = resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), providerType);
55280
+ if (cap === void 0) return true;
55281
+ return activeProviderAssignedCount(meshId, nodeId, providerType) < cap;
55282
+ }
54939
55283
  function sessionHasActiveAssignment(meshId, sessionId) {
54940
55284
  if (getQueue(meshId, { status: ["assigned"] }).some((task) => sessionIdsEquivalent(task.assignedSessionId, sessionId))) {
54941
55285
  return true;
@@ -55073,10 +55417,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55073
55417
  retractActionableSkipIfPreviouslyNotified(meshId, taskId);
55074
55418
  }
55075
55419
  }
55076
- async function resolveUsableProvider(components, nodeId, node, requiredTags, task) {
55420
+ async function resolveUsableProvider(components, nodeId, node, meshId, requiredTags, task) {
55077
55421
  const providerLoader = components.providerLoader;
55078
55422
  if (!providerLoader) return { reason: "provider_loader_unavailable" };
55079
- const slots = resolveNodeCapabilitySlots(node);
55423
+ const slots = resolveNodeCapabilitySlots(node, meshId);
55080
55424
  if (!slots.length) return { reason: "missing_provider_priority" };
55081
55425
  const orderedSlots = task ? [...slots].sort((a, b) => scoreSlotForTask(b, task) - scoreSlotForTask(a, task)) : slots;
55082
55426
  const failed = [];
@@ -55110,7 +55454,12 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55110
55454
  return {
55111
55455
  providerType: normalizedType,
55112
55456
  ...slot.model ? { model: slot.model } : {},
55113
- ...slot.thinkingLevel ? { thinkingLevel: slot.thinkingLevel } : {}
55457
+ ...slot.thinkingLevel ? { thinkingLevel: slot.thinkingLevel } : {},
55458
+ // The slot that won selection. Returned so the caller can enforce
55459
+ // "the launch model must be one this slot declares" — a preset
55460
+ // model must not widen what the operator configured. See
55461
+ // slot-model-enforcement.ts.
55462
+ slot
55114
55463
  };
55115
55464
  }
55116
55465
  failed.push(`${requestedType}: not detected`);
@@ -55247,7 +55596,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55247
55596
  if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
55248
55597
  if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
55249
55598
  if (task.requiredTags?.length) {
55250
- const slotProviders = resolveNodeCapabilitySlots(node).map((s2) => s2.provider).filter(Boolean);
55599
+ const slotProviders = resolveNodeCapabilitySlots(node, meshId).map((s2) => s2.provider).filter(Boolean);
55251
55600
  const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
55252
55601
  const providerCandidates = priorities.length ? priorities : [void 0];
55253
55602
  return providerCandidates.some(
@@ -55336,18 +55685,36 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55336
55685
  }
55337
55686
  autoLaunchInProgress.add(launchKey);
55338
55687
  try {
55339
- const resolved = await resolveUsableProvider(components, nodeId, node, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags });
55688
+ const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags });
55340
55689
  if (!resolved.providerType) {
55341
55690
  markSkip(nodeId, resolved.reason || "provider_unusable");
55342
55691
  continue;
55343
55692
  }
55344
- const rawEffectiveModel = typeof task.model === "string" && task.model.trim() ? task.model.trim() : resolved.model;
55693
+ const requestedModel = typeof task.model === "string" && task.model.trim() ? task.model.trim() : resolved.model;
55345
55694
  const effectiveThinkingLevel = typeof task.thinkingLevel === "string" && task.thinkingLevel.trim() ? task.thinkingLevel.trim() : resolved.thinkingLevel;
55695
+ const slotDecision = decideSlotForModel({
55696
+ requestedModel,
55697
+ slots: resolveNodeCapabilitySlots(node, meshId).map((slot) => ({
55698
+ slot,
55699
+ available: slotHasCapacity(meshId, nodeId, node, slot)
55700
+ }))
55701
+ });
55702
+ if (slotDecision.outcome === "wait") {
55703
+ LOG2.info("MeshQueue", `SLOT MODEL GUARD: model '${requestedModel}' is declared on node ${nodeId} but every matching slot is at its maxParallel cap (task ${task.id}); leaving the task queued until a slot goes idle`);
55704
+ markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
55705
+ continue;
55706
+ }
55707
+ if (slotDecision.outcome === "notify") {
55708
+ LOG2.warn("MeshQueue", `SLOT MODEL GUARD: no slot on node ${nodeId} declares model '${requestedModel}' (declared: ${slotDecision.declaredModels.join(", ") || "none"}) for task ${task.id}; not launching \u2014 surfacing to the coordinator to re-drive`);
55709
+ markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
55710
+ continue;
55711
+ }
55712
+ const rawEffectiveModel = slotDecision.model;
55346
55713
  const effectiveModel = isModelCompatibleWithProvider(rawEffectiveModel, resolved.providerType) ? rawEffectiveModel : void 0;
55347
55714
  if (rawEffectiveModel && effectiveModel === void 0) {
55348
55715
  LOG2.info("MeshQueue", `CODEX-400 GUARD: dropped incompatible launch model '${rawEffectiveModel}' for non-Anthropic provider '${resolved.providerType}' on node ${nodeId} (task ${task.id}); provider will use its own default model`);
55349
55716
  }
55350
- const providerCap = resolveProviderMaxParallel(resolveNodeCapabilitySlots(node), resolved.providerType);
55717
+ const providerCap = resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), resolved.providerType);
55351
55718
  if (providerCap !== void 0 && activeProviderAssignedCount(meshId, nodeId, resolved.providerType) >= providerCap) {
55352
55719
  markSkip(nodeId, "max_provider_parallel_reached", { providerType: resolved.providerType });
55353
55720
  continue;
@@ -55361,11 +55728,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55361
55728
  // Coordinator-dispatched worker: auto-approve unless mesh/node policy
55362
55729
  // opts out (default true). Lands in settingsOverride and beats the
55363
55730
  // global per-provider-type boolean/mode through explicit opposite-key clearing.
55364
- ...delegatedWorkerAutoApproveSettings(
55365
- mesh?.policy,
55366
- node?.policy,
55731
+ ...delegatedWorkerAutoApproveSettingsForNode(
55732
+ mesh,
55733
+ node,
55367
55734
  components.providerLoader?.getMeta(resolved.providerType),
55368
- loadRepoConfigForNode(node),
55369
55735
  resolved.providerType
55370
55736
  ),
55371
55737
  launchedByCoordinator: true,
@@ -55441,7 +55807,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55441
55807
  const requiredTags = Array.isArray(task.requiredTags) ? task.requiredTags.filter((t) => !!t) : [];
55442
55808
  const routingDecision = {
55443
55809
  source: "autoLaunch",
55444
- fitnessScore: nodeFitnessForTask(node, { difficulty: task.difficulty, requiredTags: task.requiredTags }),
55810
+ fitnessScore: nodeFitnessForTask(node, { difficulty: task.difficulty, requiredTags: task.requiredTags }, meshId),
55445
55811
  ...skippedCandidates.length ? { skippedCandidates } : {},
55446
55812
  requiredTagsResult: {
55447
55813
  required: requiredTags,
@@ -55885,6 +56251,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55885
56251
  init_mesh_clone_grace();
55886
56252
  init_mesh_task_inflight();
55887
56253
  init_model_provider_compat();
56254
+ init_slot_model_enforcement();
55888
56255
  init_mesh_turn_ledger();
55889
56256
  init_mesh_duplicate_dispatch();
55890
56257
  IDLE_AUTO_FAST_FORWARD_THROTTLE_MS = 30 * 60 * 1e3;
@@ -55917,7 +56284,13 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55917
56284
  "provider_loader_unavailable",
55918
56285
  "provider_priority_unusable",
55919
56286
  "provider_unusable",
55920
- "dirty_workspace"
56287
+ "dirty_workspace",
56288
+ // SLOT MODEL GUARD (absent): no slot on the node declares the task's model.
56289
+ // Permanent — no amount of waiting produces a slot, so the coordinator must
56290
+ // re-drive (adjust difficulty, target another node, ask the owner). Its
56291
+ // busy counterpart SLOT_MODEL_BUSY_SKIP_REASON is deliberately NOT listed:
56292
+ // that one clears on its own when the slot goes idle.
56293
+ SLOT_MODEL_ABSENT_SKIP_REASON
55921
56294
  ];
55922
56295
  TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON = "target_node_bootstrap_pending";
55923
56296
  lastActionableSkipNotified = /* @__PURE__ */ new Map();
@@ -61308,7 +61681,15 @@ ${cleanBody}`;
61308
61681
  try {
61309
61682
  pendingEvents = drainPendingMeshCoordinatorEvents(
61310
61683
  meshId,
61311
- drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId
61684
+ drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId,
61685
+ {
61686
+ // AMBIGUOUS-UNICAST guard (REFINE-EVENT-SESSION-SCOPED-UNICAST): observability
61687
+ // only — it warns when a session-less unicast event is delivered while several
61688
+ // coordinator sessions are live and racing for it. Never changes delivery, so
61689
+ // an event can't be stranded by it. targetCoordinators is this daemon's live
61690
+ // coordinator set for the mesh, which is exactly the racing population.
61691
+ countLiveCoordinatorSessions: () => new Set(targetCoordinators.map((c) => c.sessionId).filter(Boolean)).size
61692
+ }
61312
61693
  );
61313
61694
  } catch (e) {
61314
61695
  LOG2.warn("MeshReconcile", `Drain failed for mesh ${meshId}: ${e?.message || e}`);
@@ -71152,6 +71533,7 @@ ${lastSnapshot}`;
71152
71533
  buildChatMessageSignature: () => buildChatMessageSignature,
71153
71534
  buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
71154
71535
  buildClaudeInteractiveToolResult: () => buildClaudeInteractiveToolResult,
71536
+ buildCloudStatusReportPayload: () => buildCloudStatusReportPayload,
71155
71537
  buildCompactStaleDirectWorkSummary: () => buildCompactStaleDirectWorkSummary,
71156
71538
  buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
71157
71539
  buildIdleReminderMessage: () => buildIdleReminderMessage,
@@ -71451,6 +71833,7 @@ ${lastSnapshot}`;
71451
71833
  resolveNotBefore: () => resolveNotBefore,
71452
71834
  resolveProviderChannel: () => resolveProviderChannel,
71453
71835
  resolveProviderMaxParallel: () => resolveProviderMaxParallel,
71836
+ resolveScopedMeshId: () => resolveScopedMeshId,
71454
71837
  resolveSessionHostAppName: () => resolveSessionHostAppName,
71455
71838
  resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution2,
71456
71839
  resolveSessionTurnPresentation: () => resolveSessionTurnPresentation,
@@ -73026,7 +73409,7 @@ ${lastSnapshot}`;
73026
73409
  const bootstrapLoaded = loadMeshWorktreeBootstrapConfig(mesh, workspace);
73027
73410
  let magiKindPanels = {};
73028
73411
  try {
73029
- magiKindPanels = listMagiKindPanelsReadOnly();
73412
+ magiKindPanels = listMagiKindPanelsReadOnly(typeof mesh?.id === "string" ? mesh.id : void 0);
73030
73413
  } catch {
73031
73414
  magiKindPanels = {};
73032
73415
  }
@@ -85484,6 +85867,84 @@ ${body}
85484
85867
  init_hash();
85485
85868
  init_coordinator_registry();
85486
85869
  init_mesh_duplicate_dispatch();
85870
+ init_mesh_json_config();
85871
+ init_logger();
85872
+ init_repo_mesh_types();
85873
+ function resolveDelegatedWorkerAutoApproveModeForLaunch(input) {
85874
+ const settings = input.settings;
85875
+ const envelopeMode = typeof settings?.autoApproveMode === "string" && settings.autoApproveMode.trim() ? settings.autoApproveMode.trim() : void 0;
85876
+ if (settings?.launchedByCoordinator !== true) {
85877
+ return { autoApproveMode: envelopeMode, changed: false, source: "not_delegated" };
85878
+ }
85879
+ if (settings.autoApprove === false) {
85880
+ return { autoApproveMode: void 0, changed: false, source: "enable_gate_off" };
85881
+ }
85882
+ const providerType = typeof input.providerType === "string" ? input.providerType.trim() : "";
85883
+ if (!providerType || !input.provider?.autoApproveModes) {
85884
+ return { autoApproveMode: envelopeMode, changed: false, source: "no_provider_modes" };
85885
+ }
85886
+ const workspace = typeof input.workspace === "string" ? input.workspace.trim() : "";
85887
+ if (!workspace) {
85888
+ return { autoApproveMode: envelopeMode, changed: false, source: "no_repo_config" };
85889
+ }
85890
+ let repoConfig = null;
85891
+ try {
85892
+ const result = loadRepoMeshJsonConfig(workspace);
85893
+ const fromThisWorkspace = result.sourceType === "repo_file" && typeof result.path === "string" && isPathInsideWorkspace(result.path, workspace);
85894
+ repoConfig = fromThisWorkspace && result.config ? result.config : null;
85895
+ } catch {
85896
+ repoConfig = null;
85897
+ }
85898
+ const requestedMode = repoConfig?.providerDefaults?.autoApproveModes?.[providerType];
85899
+ const requestedModeId = typeof requestedMode === "string" && requestedMode.trim() ? requestedMode.trim() : "";
85900
+ if (!requestedModeId) {
85901
+ return { autoApproveMode: envelopeMode, changed: false, source: "no_repo_config" };
85902
+ }
85903
+ const dangerousAllowed = settings.delegatedWorkerDangerousModeAllow === true;
85904
+ const resolved = resolveDelegatedWorkerAutoApprove(
85905
+ { delegatedWorkerAutoApprove: true, delegatedWorkerDangerousModeAllow: dangerousAllowed },
85906
+ void 0,
85907
+ input.provider,
85908
+ repoConfig,
85909
+ providerType
85910
+ );
85911
+ if (typeof resolved !== "string") {
85912
+ return { autoApproveMode: envelopeMode, changed: false, source: "no_repo_config" };
85913
+ }
85914
+ return {
85915
+ autoApproveMode: resolved,
85916
+ changed: resolved !== envelopeMode,
85917
+ source: "worker_repo_file"
85918
+ };
85919
+ }
85920
+ function isPathInsideWorkspace(configPath, workspace) {
85921
+ const normalize4 = (value) => value.replace(/[\\/]+$/, "");
85922
+ const ws = normalize4(workspace);
85923
+ if (!ws) return false;
85924
+ const separatorAgnostic = (value) => value.replace(/\\/g, "/");
85925
+ const normalizedPath = separatorAgnostic(configPath);
85926
+ const normalizedWs = separatorAgnostic(ws);
85927
+ return normalizedPath === normalizedWs || normalizedPath.startsWith(`${normalizedWs}/`);
85928
+ }
85929
+ function logDelegatedWorkerModeDelivery(resolution, context) {
85930
+ if (resolution.source !== "worker_repo_file" && resolution.source !== "no_repo_config") return;
85931
+ const node = context.meshNodeId || "unknown-node";
85932
+ const workspace = context.workspace || "unknown-workspace";
85933
+ const provider = context.providerType || "unknown-provider";
85934
+ if (resolution.source === "worker_repo_file" && resolution.changed) {
85935
+ LOG2.info(
85936
+ "MeshWorker",
85937
+ `auto-approve mode re-resolved from worker repo config: node=${node} workspace=${workspace} provider=${provider} coordinatorMode=${context.envelopeMode ?? "(none)"} workerMode=${resolution.autoApproveMode}`
85938
+ );
85939
+ return;
85940
+ }
85941
+ if (resolution.source === "no_repo_config" && context.envelopeMode) {
85942
+ LOG2.warn(
85943
+ "MeshWorker",
85944
+ `auto-approve mode NOT confirmed by worker repo config (keeping coordinator value): node=${node} workspace=${workspace} provider=${provider} mode=${context.envelopeMode} \u2014 no readable .adhdev/mesh.json in this workspace`
85945
+ );
85946
+ }
85947
+ }
85487
85948
  init_summary_metadata();
85488
85949
  var os19 = __toESM2(require("os"));
85489
85950
  var crypto5 = __toESM2(require("crypto"));
@@ -87981,6 +88442,48 @@ ${body}
87981
88442
  }
87982
88443
  return screenText;
87983
88444
  }
88445
+ /**
88446
+ * Is `reread` a re-render of the SAME picker page as `landed`?
88447
+ *
88448
+ * Guards the return-pass screenText swap in captureClaudeTuiPrompt, which
88449
+ * replaces a page's entire raw screen and therefore must never be handed a
88450
+ * frame belonging to a different question.
88451
+ *
88452
+ * WHAT WE COMPARE — the question line, via the same parser the capture
88453
+ * itself uses (readFocusedClaudeTuiQuestion). Rationale:
88454
+ * - The question text is the one field that is per-page, always rendered
88455
+ * (it is the parse anchor — a page without it yields no question at all),
88456
+ * and stable across the redraw we are waiting on. The redraw races the
88457
+ * option-row GLYPH COLUMN, not the question line.
88458
+ * - The header is NOT usable on its own: on the headered variant every page
88459
+ * renders the identical nav line, and `page.header` is assigned by index
88460
+ * from that shared line rather than read from the page body — so it is
88461
+ * equal across pages by construction and would accept any frame.
88462
+ * - The option-label set is rejected as the primary key: it is drawn in the
88463
+ * very region that is mid-redraw, and rows can be clipped or scrolled out
88464
+ * of the captured frame (the same truncation that forced the headerless
88465
+ * parser to stop requiring the freeform escape hatch). Comparing it would
88466
+ * reject legitimate repairs — exactly the frames this pass exists to fix.
88467
+ *
88468
+ * STRICTNESS — deliberately asymmetric, because the two error directions are
88469
+ * not equally costly. Wrongly ALLOWING a swap corrupts a question into a
88470
+ * duplicate of another (the reported user-visible defect). Wrongly BLOCKING
88471
+ * one merely leaves the forward-pass capture in place — at worst a
88472
+ * multi-select page stays flagged single-select, which the live status-tick
88473
+ * upgrade (maybeUpgradeClaudeTuiMultiSelect) then repairs anyway. So this
88474
+ * blocks only on POSITIVE EVIDENCE of a different page: if either side fails
88475
+ * to parse we return true and defer to the pre-existing glyph gate, keeping
88476
+ * behaviour identical to before for every frame whose identity we cannot
88477
+ * read. Comparison is whitespace-normalised so a reflow or trailing-pad
88478
+ * difference does not read as a different question.
88479
+ */
88480
+ claudeTuiPagesLookLikeSameQuestion(landed, reread) {
88481
+ const landedQuestion = readFocusedClaudeTuiQuestion(landed.screenText);
88482
+ const rereadQuestion = readFocusedClaudeTuiQuestion(reread);
88483
+ if (!landedQuestion || !rereadQuestion) return true;
88484
+ const normalize4 = (text) => text.replace(/\s+/g, " ").trim();
88485
+ return normalize4(landedQuestion.question) === normalize4(rereadQuestion.question);
88486
+ }
87984
88487
  async captureClaudeTuiPrompt(firstScreen, headers) {
87985
88488
  const pages = [{ screenText: firstScreen, header: headers[0] }];
87986
88489
  for (let index = 1; index < headers.length; index += 1) {
@@ -87993,7 +88496,7 @@ ${body}
87993
88496
  await new Promise((resolve29) => setTimeout(resolve29, _SpecCliAdapter.CLAUDE_TUI_PAGE_POLL_INTERVAL_MS));
87994
88497
  const reread = await this.snapshotSettledClaudeTuiPage();
87995
88498
  const landed = pages[index - 1];
87996
- if (landed && !detectClaudeTuiMultiSelect(landed.screenText) && detectClaudeTuiMultiSelect(reread)) {
88499
+ if (landed && !detectClaudeTuiMultiSelect(landed.screenText) && detectClaudeTuiMultiSelect(reread) && this.claudeTuiPagesLookLikeSameQuestion(landed, reread)) {
87997
88500
  landed.screenText = reread;
87998
88501
  }
87999
88502
  }
@@ -94794,7 +95297,29 @@ Run 'adhdev doctor' for detailed diagnostics.`
94794
95297
  if (!cliType) throw new Error("cliType required");
94795
95298
  const providerType = this.providerLoader.resolveAlias(cliType);
94796
95299
  const provLookup = this.providerLoader.getMeta(providerType);
94797
- const settingsOverride = args?.settings && typeof args.settings === "object" ? args.settings : void 0;
95300
+ let settingsOverride = args?.settings && typeof args.settings === "object" ? args.settings : void 0;
95301
+ if (settingsOverride?.launchedByCoordinator === true) {
95302
+ const envelopeMode = typeof settingsOverride.autoApproveMode === "string" ? settingsOverride.autoApproveMode : void 0;
95303
+ const modeResolution = resolveDelegatedWorkerAutoApproveModeForLaunch({
95304
+ workspace: dir,
95305
+ providerType,
95306
+ provider: provLookup,
95307
+ settings: settingsOverride
95308
+ });
95309
+ logDelegatedWorkerModeDelivery(modeResolution, {
95310
+ workspace: dir,
95311
+ providerType,
95312
+ meshNodeId: typeof settingsOverride.meshNodeId === "string" ? settingsOverride.meshNodeId : void 0,
95313
+ envelopeMode
95314
+ });
95315
+ if (modeResolution.changed && modeResolution.autoApproveMode) {
95316
+ settingsOverride = {
95317
+ ...settingsOverride,
95318
+ autoApproveMode: modeResolution.autoApproveMode,
95319
+ autoApprove: void 0
95320
+ };
95321
+ }
95322
+ }
94798
95323
  const delegatedLaunch = settingsOverride?.launchedByCoordinator === true ? buildCoordinatorDelegatedCliLaunchOptions({
94799
95324
  cliType,
94800
95325
  workspace: dir,
@@ -100893,7 +101418,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
100893
101418
  }
100894
101419
  return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
100895
101420
  },
100896
- create_mesh: async (_ctx, args) => {
101421
+ create_mesh: async (ctx, args) => {
100897
101422
  const name = typeof args?.name === "string" ? args.name.trim() : "";
100898
101423
  const repoIdentity = typeof args?.repoIdentity === "string" ? args.repoIdentity.trim() : "";
100899
101424
  const repoRemoteUrl = typeof args?.repoRemoteUrl === "string" ? args.repoRemoteUrl.trim() : void 0;
@@ -100902,12 +101427,71 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
100902
101427
  try {
100903
101428
  const { createMesh: createMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
100904
101429
  const meshHost = args?.meshHost && typeof args.meshHost === "object" && !Array.isArray(args.meshHost) ? args.meshHost : void 0;
100905
- const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy, meshHost });
101430
+ const requestedHostDaemonId = typeof args?.hostDaemonId === "string" && args.hostDaemonId.trim() ? args.hostDaemonId.trim() : ctx.deps.statusInstanceId || "";
101431
+ const mesh = createMesh2({
101432
+ name,
101433
+ repoIdentity,
101434
+ repoRemoteUrl,
101435
+ defaultBranch,
101436
+ policy: args?.policy,
101437
+ meshHost,
101438
+ ...requestedHostDaemonId ? { hostDaemonId: requestedHostDaemonId } : {}
101439
+ });
100906
101440
  return { success: true, mesh };
100907
101441
  } catch (e) {
100908
101442
  return { success: false, error: e.message };
100909
101443
  }
100910
101444
  },
101445
+ // HOST-PIN-WRITER — establish this mesh's host daemon as a DELIBERATE operator act.
101446
+ //
101447
+ // The dashboard's first-setup flow lets the operator pick which connected daemon
101448
+ // becomes the host; this is the command that actually persists that choice. It is a
101449
+ // separate surface from coordinator launch on purpose: the pin is effectively
101450
+ // permanent ("Fixed when the mesh is created — it cannot be reassigned here"), so it
101451
+ // must not be an incidental side effect of a button whose stated job is launching a
101452
+ // session. A mis-click must not permanently re-home a mesh.
101453
+ //
101454
+ // Reassignment to a DIFFERENT daemon is refused unless the caller passes
101455
+ // force:true — the mutator returns code 'host_already_pinned' and changes nothing.
101456
+ // Re-pinning the same daemon is a no-op, so retries/races are safe.
101457
+ set_mesh_host: async (ctx, args) => {
101458
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
101459
+ const hostDaemonId = typeof args?.hostDaemonId === "string" ? args.hostDaemonId.trim() : "";
101460
+ const hostNodeId = typeof args?.hostNodeId === "string" ? args.hostNodeId.trim() : "";
101461
+ if (!meshId) return { success: false, error: "meshId required" };
101462
+ if (!hostDaemonId && !hostNodeId) return { success: false, error: "hostDaemonId or hostNodeId required" };
101463
+ try {
101464
+ const { setMeshHostPin: setMeshHostPin2, getMesh: getMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101465
+ const result = setMeshHostPin2(meshId, {
101466
+ ...hostDaemonId ? { hostDaemonId } : {},
101467
+ ...hostNodeId ? { hostNodeId } : {},
101468
+ force: args?.force === true
101469
+ });
101470
+ if (!result) return { success: false, error: "Mesh not found" };
101471
+ if (!result.applied && result.reason !== "already_pinned_same") {
101472
+ return {
101473
+ success: false,
101474
+ code: result.reason,
101475
+ error: result.reason === "host_already_pinned" ? `Mesh host is already pinned to ${result.hostDaemonId}. Pass force:true to reassign it.` : result.reason === "not_host_role" ? "This daemon joined the mesh as a member; its host lives on the daemon it paired with." : "hostDaemonId or hostNodeId required",
101476
+ meshId,
101477
+ meshHost: resolveMeshHostStatus(result.mesh)
101478
+ };
101479
+ }
101480
+ const fresh = getMesh2(meshId) || result.mesh;
101481
+ if (ctx.getCachedInlineMesh(meshId)) ctx.inlineMeshCache.set(meshId, fresh);
101482
+ ctx.invalidateAggregateMeshStatus(meshId);
101483
+ return {
101484
+ success: true,
101485
+ code: result.reason,
101486
+ meshId,
101487
+ applied: result.applied,
101488
+ meshHost: resolveMeshHostStatus(fresh),
101489
+ mesh: fresh
101490
+ };
101491
+ } catch (e) {
101492
+ return { success: false, error: e.message };
101493
+ }
101494
+ },
100911
101495
  update_mesh: async (ctx, args) => {
100912
101496
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
100913
101497
  if (!meshId) return { success: false, error: "meshId required" };
@@ -101184,19 +101768,37 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
101184
101768
  }
101185
101769
  },
101186
101770
  // ─── MAGI kind → panel bindings (MAGI-KIND-PANEL, machine-local config) ───
101187
- // Per-task_kind slot lists in ~/.adhdev/meshes.json `magiKindPanels` — the SOLE
101188
- // MAGI panel-resolution surface (the former named-panel magi_panel_* handlers were
101189
- // removed). Owner-only gating: intentionally NOT listed in
101771
+ // Per-task_kind slot lists stored PER MESH in ~/.adhdev/meshes.json
101772
+ // (`meshes[].magiKindPanels`) — the SOLE MAGI panel-resolution surface (the former
101773
+ // named-panel magi_panel_* handlers were removed). `meshId` is optional on all three
101774
+ // so existing callers keep working: it resolves to the sole mesh on a single-mesh
101775
+ // machine, and is REQUIRED (loud error, never a silent pick) when several meshes
101776
+ // exist. Owner-only gating: intentionally NOT listed in
101190
101777
  // canPeerUsePrivilegedShareCommand (daemon-cloud data-channel-router), so a peer
101191
101778
  // holding ANY share permission hits its `default → false` branch — identical
101192
101779
  // owner-only gating to create_mesh / update_mesh / list_meshes. A trusted peer (no
101193
101780
  // permission = the owner) passes the top `!permission → true` guard. set/remove are
101194
101781
  // WRITE commands; list is read-only. normalizeMagiSlots (inside setMagiKindPanel)
101195
- // surfaces invalid_magi_kind_panel: … messages verbatim for the editor.
101196
- magi_kind_panel_list: async (_ctx, _args) => {
101782
+ // surfaces invalid_magi_kind_panel: … messages verbatim for the editor, including
101783
+ // a nodeId that is not a member of the target mesh.
101784
+ magi_kind_panel_list: async (_ctx, args) => {
101785
+ const requestedMeshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
101197
101786
  try {
101198
- const { listMagiKindPanels: listMagiKindPanels2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101199
- return { success: true, kindPanels: listMagiKindPanels2() };
101787
+ const { listMagiKindPanels: listMagiKindPanels2, resolveScopedMeshId: resolveScopedMeshId2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101788
+ const meshId = requestedMeshId || resolveScopedMeshId2();
101789
+ return {
101790
+ success: true,
101791
+ kindPanels: listMagiKindPanels2(requestedMeshId || void 0),
101792
+ scope: {
101793
+ kind: "mesh",
101794
+ storage: "machine_local",
101795
+ meshId: meshId ?? null,
101796
+ resolvedFrom: requestedMeshId ? "explicit" : meshId ? "sole_mesh" : "ambiguous",
101797
+ ...requestedMeshId || meshId ? {} : {
101798
+ note: "Several meshes are configured and no meshId was given, so no panels could be read. Pass meshId."
101799
+ }
101800
+ }
101801
+ };
101200
101802
  } catch (e) {
101201
101803
  return { success: false, error: e.message };
101202
101804
  }
@@ -101204,10 +101806,12 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
101204
101806
  magi_kind_panel_set: async (_ctx, args) => {
101205
101807
  const kind = typeof args?.kind === "string" ? args.kind.trim() : "";
101206
101808
  if (!kind) return { success: false, error: "invalid_magi_kind_panel: task_kind is required" };
101809
+ const requestedMeshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
101207
101810
  try {
101208
- const { setMagiKindPanel: setMagiKindPanel2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101209
- const slots = setMagiKindPanel2(kind, args?.slots);
101210
- return { success: true, kind, slots };
101811
+ const { setMagiKindPanel: setMagiKindPanel2, resolveScopedMeshId: resolveScopedMeshId2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101812
+ const slots = setMagiKindPanel2(kind, args?.slots, requestedMeshId || void 0);
101813
+ const meshId = requestedMeshId || resolveScopedMeshId2();
101814
+ return { success: true, kind, slots, meshId: meshId ?? null };
101211
101815
  } catch (e) {
101212
101816
  return { success: false, error: e.message };
101213
101817
  }
@@ -101215,30 +101819,52 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
101215
101819
  magi_kind_panel_remove: async (_ctx, args) => {
101216
101820
  const kind = typeof args?.kind === "string" ? args.kind.trim() : "";
101217
101821
  if (!kind) return { success: false, error: "invalid_magi_kind_panel: task_kind is required" };
101822
+ const requestedMeshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
101218
101823
  try {
101219
- const { removeMagiKindPanel: removeMagiKindPanel2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101220
- const removed = removeMagiKindPanel2(kind);
101221
- return { success: true, removed };
101824
+ const { removeMagiKindPanel: removeMagiKindPanel2, resolveScopedMeshId: resolveScopedMeshId2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101825
+ const removed = removeMagiKindPanel2(kind, requestedMeshId || void 0);
101826
+ const meshId = requestedMeshId || resolveScopedMeshId2();
101827
+ return { success: true, removed, meshId: meshId ?? null };
101222
101828
  } catch (e) {
101223
101829
  return { success: false, error: e.message };
101224
101830
  }
101225
101831
  },
101226
- // ─── Brain routing: per-difficulty brain presets (machine-local) ───
101227
- // getDifficultyBrains returns the seeded defaults when nothing is configured,
101228
- // so the editor always shows a usable mapping. set replaces the whole map.
101229
- difficulty_brains_get: async (_ctx, _args) => {
101832
+ // ─── Brain routing: per-difficulty brain presets (PER MESH, machine-local) ───
101833
+ // getDifficultyBrains returns the seeded defaults when the mesh has nothing
101834
+ // configured, so the editor always shows a usable mapping. set replaces the whole
101835
+ // map for ONE mesh. `meshId` is optional and resolves to the sole mesh, so
101836
+ // existing callers keep working; with several meshes a write must name its mesh
101837
+ // (these presets choose the model a task runs on — writing to the wrong mesh
101838
+ // changes what that mesh costs).
101839
+ difficulty_brains_get: async (_ctx, args) => {
101840
+ const requestedMeshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
101230
101841
  try {
101231
- const { getDifficultyBrains: getDifficultyBrains2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101232
- return { success: true, difficultyBrains: getDifficultyBrains2() };
101842
+ const { getDifficultyBrains: getDifficultyBrains2, resolveScopedMeshId: resolveScopedMeshId2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101843
+ const meshId = requestedMeshId || resolveScopedMeshId2();
101844
+ return {
101845
+ success: true,
101846
+ difficultyBrains: getDifficultyBrains2(requestedMeshId || void 0),
101847
+ scope: {
101848
+ kind: "mesh",
101849
+ storage: "machine_local",
101850
+ meshId: meshId ?? null,
101851
+ resolvedFrom: requestedMeshId ? "explicit" : meshId ? "sole_mesh" : "ambiguous",
101852
+ ...requestedMeshId || meshId ? {} : {
101853
+ note: "Several meshes are configured and no meshId was given, so these are the shipped defaults, not any mesh's saved presets. Pass meshId."
101854
+ }
101855
+ }
101856
+ };
101233
101857
  } catch (e) {
101234
101858
  return { success: false, error: e.message };
101235
101859
  }
101236
101860
  },
101237
101861
  difficulty_brains_set: async (_ctx, args) => {
101862
+ const requestedMeshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
101238
101863
  try {
101239
- const { setDifficultyBrains: setDifficultyBrains2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101240
- const difficultyBrains = setDifficultyBrains2(args?.difficultyBrains);
101241
- return { success: true, difficultyBrains };
101864
+ const { setDifficultyBrains: setDifficultyBrains2, resolveScopedMeshId: resolveScopedMeshId2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
101865
+ const difficultyBrains = setDifficultyBrains2(args?.difficultyBrains, requestedMeshId || void 0);
101866
+ const meshId = requestedMeshId || resolveScopedMeshId2();
101867
+ return { success: true, difficultyBrains, meshId: meshId ?? null };
101242
101868
  } catch (e) {
101243
101869
  return { success: false, error: e.message };
101244
101870
  }
@@ -102810,6 +103436,26 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
102810
103436
  init_mesh_coordinator();
102811
103437
  init_dist();
102812
103438
  init_repo_mesh_types();
103439
+ async function backfillMeshHostPinAfterLaunch(opts) {
103440
+ try {
103441
+ const existingPin = typeof opts.mesh?.meshHost?.hostDaemonId === "string" ? opts.mesh.meshHost.hostDaemonId.trim() : "";
103442
+ if (existingPin) return;
103443
+ const { getMesh: getMesh2, setMeshHostPin: setMeshHostPin2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
103444
+ if (!getMesh2(opts.meshId)) return;
103445
+ const nodeDaemonId = typeof opts.coordinatorNode?.daemonId === "string" && opts.coordinatorNode.daemonId.trim() ? opts.coordinatorNode.daemonId.trim() : typeof opts.coordinatorNode?.daemon_id === "string" && opts.coordinatorNode.daemon_id.trim() ? opts.coordinatorNode.daemon_id.trim() : opts.localDaemonId || "";
103446
+ const nodeId = String(normalizeMeshNodeId(opts.coordinatorNode) || "");
103447
+ if (!nodeDaemonId && !nodeId) return;
103448
+ const result = setMeshHostPin2(opts.meshId, {
103449
+ ...nodeDaemonId ? { hostDaemonId: nodeDaemonId } : {},
103450
+ ...nodeId ? { hostNodeId: nodeId } : {}
103451
+ });
103452
+ if (result?.applied) {
103453
+ LOG2.info("MeshCoordinator", `Pinned mesh ${opts.meshId} host to ${result.hostDaemonId || nodeDaemonId} (node ${result.hostNodeId || nodeId}) after coordinator launch`);
103454
+ }
103455
+ } catch (error48) {
103456
+ LOG2.warn("MeshCoordinator", `Host pin backfill skipped for mesh ${opts.meshId}: ${error48?.message || String(error48)}`);
103457
+ }
103458
+ }
102813
103459
  var meshCoordinatorLaunchHandlers = {
102814
103460
  launch_mesh_coordinator: async (ctx, args) => {
102815
103461
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
@@ -102889,10 +103535,10 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
102889
103535
  return void 0;
102890
103536
  }
102891
103537
  };
102892
- const loadMagiKindPanelsBestEffort = async () => {
103538
+ const loadMagiKindPanelsBestEffort = async (forMeshId) => {
102893
103539
  try {
102894
103540
  const { listMagiKindPanels: listMagiKindPanels2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
102895
- return listMagiKindPanels2();
103541
+ return listMagiKindPanels2(forMeshId);
102896
103542
  } catch {
102897
103543
  return void 0;
102898
103544
  }
@@ -102994,7 +103640,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
102994
103640
  if (coordinatorSetup.kind === "cli_command") {
102995
103641
  let cliCmdSystemPrompt = "";
102996
103642
  try {
102997
- cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id), magiKindPanels: await loadMagiKindPanelsBestEffort() });
103643
+ cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id), magiKindPanels: await loadMagiKindPanelsBestEffort(meshId) });
102998
103644
  } catch (error48) {
102999
103645
  const message = error48?.message || String(error48);
103000
103646
  LOG2.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
@@ -103169,6 +103815,12 @@ ${ptyResult.output.slice(-2e3)}`);
103169
103815
  });
103170
103816
  } catch {
103171
103817
  }
103818
+ await backfillMeshHostPinAfterLaunch({
103819
+ meshId,
103820
+ mesh,
103821
+ coordinatorNode,
103822
+ localDaemonId: ctx.deps.statusInstanceId
103823
+ });
103172
103824
  return {
103173
103825
  success: true,
103174
103826
  meshId,
@@ -103191,7 +103843,7 @@ ${ptyResult.output.slice(-2e3)}`);
103191
103843
  }
103192
103844
  let systemPrompt = "";
103193
103845
  try {
103194
- systemPrompt = buildCoordinatorSystemPrompt2({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id), magiKindPanels: await loadMagiKindPanelsBestEffort() });
103846
+ systemPrompt = buildCoordinatorSystemPrompt2({ mesh: effectiveMesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildEffectiveOperatingNotes(mesh.id), magiKindPanels: await loadMagiKindPanelsBestEffort(meshId) });
103195
103847
  } catch (error48) {
103196
103848
  const message = error48?.message || String(error48);
103197
103849
  LOG2.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
@@ -103367,6 +104019,12 @@ ${ptyResult.output.slice(-2e3)}`);
103367
104019
  });
103368
104020
  } catch {
103369
104021
  }
104022
+ await backfillMeshHostPinAfterLaunch({
104023
+ meshId,
104024
+ mesh,
104025
+ coordinatorNode,
104026
+ localDaemonId: ctx.deps.statusInstanceId
104027
+ });
103370
104028
  return {
103371
104029
  success: true,
103372
104030
  meshId,
@@ -105769,6 +106427,7 @@ ${mergeTreeErr?.stderr || ""}`;
105769
106427
  ...args.completedAt ? { completedAt: args.completedAt } : {},
105770
106428
  ...args.retryOfJobId ? { retryOfJobId: args.retryOfJobId } : {},
105771
106429
  ...args.coordinatorDaemonId ? { targetCoordinatorDaemonId: args.coordinatorDaemonId } : {},
106430
+ ...args.coordinatorSessionId ? { targetCoordinatorSessionId: args.coordinatorSessionId } : {},
105772
106431
  eventDelivery: { pendingEvents: true, ledger: true },
105773
106432
  evidence: {
105774
106433
  pendingEventsCommand: "get_pending_mesh_events",
@@ -105867,9 +106526,23 @@ ${mergeTreeErr?.stderr || ""}`;
105867
106526
  nodeLabel: handle.targetNodeId,
105868
106527
  nodeId: handle.targetNodeId,
105869
106528
  workspace: handle.workspace,
105870
- metadataEvent,
106529
+ metadataEvent: {
106530
+ ...metadataEvent,
106531
+ // REFINE-EVENT-SESSION-SCOPED-UNICAST: mirror the session INSIDE
106532
+ // metadataEvent too. handleMeshForwardEvent reads the coordinator session
106533
+ // anchor from `metadataEvent.meshCoordinatorSessionId` (a top-level field
106534
+ // alone is dropped when the event crosses a machine boundary), so this is
106535
+ // what survives the P2P relay for a remote-executing refine.
106536
+ ...handle.targetCoordinatorSessionId ? { meshCoordinatorSessionId: handle.targetCoordinatorSessionId } : {}
106537
+ },
105871
106538
  queuedAt: Date.now(),
105872
- ...handle.targetCoordinatorDaemonId ? { targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId } : {}
106539
+ ...handle.targetCoordinatorDaemonId ? { targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId } : {},
106540
+ // THE FIX: address the terminal event to the requesting coordinator SESSION,
106541
+ // not just its daemon. stampPendingEventV2 folds this into the v2 unicast
106542
+ // `intendedFor`, so identityDeliversTo's both-sides-session branch excludes a
106543
+ // sibling coordinator session on the same daemon. Absent (legacy requester) →
106544
+ // session-less intendedFor → daemon-level delivery exactly as before.
106545
+ ...handle.targetCoordinatorSessionId ? { targetCoordinatorSessionId: handle.targetCoordinatorSessionId } : {}
105873
106546
  };
105874
106547
  if (typeof self.deps.instanceManager?.getByCategory === "function") {
105875
106548
  const forwarded = handleMeshForwardEvent(
@@ -105893,6 +106566,14 @@ ${mergeTreeErr?.stderr || ""}`;
105893
106566
  // re-queued event self-fallbacks to THIS (the executing/worker)
105894
106567
  // daemon and the real coordinator's drain excludes it.
105895
106568
  ...handle.targetCoordinatorDaemonId ? { targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId } : {},
106569
+ // REFINE-EVENT-SESSION-SCOPED-UNICAST: carry the SESSION half of the
106570
+ // return address across the relay as well. buildRelayMetadataEvent
106571
+ // reads meshCoordinatorSessionId (falling back to
106572
+ // targetCoordinatorSessionId), so both spellings are supplied.
106573
+ ...handle.targetCoordinatorSessionId ? {
106574
+ targetCoordinatorSessionId: handle.targetCoordinatorSessionId,
106575
+ meshCoordinatorSessionId: handle.targetCoordinatorSessionId
106576
+ } : {},
105896
106577
  ...slimResult ? { result: slimResult } : {}
105897
106578
  }
105898
106579
  );
@@ -107317,6 +107998,7 @@ ${e?.stderr || ""}`;
107317
107998
  startedAt: args.startedAt || (/* @__PURE__ */ new Date()).toISOString(),
107318
107999
  ...args.completedAt ? { completedAt: args.completedAt } : {},
107319
108000
  ...args.coordinatorDaemonId ? { targetCoordinatorDaemonId: args.coordinatorDaemonId } : {},
108001
+ ...args.coordinatorSessionId ? { targetCoordinatorSessionId: args.coordinatorSessionId } : {},
107320
108002
  eventDelivery: { pendingEvents: true, ledger: true },
107321
108003
  evidence: {
107322
108004
  pendingEventsCommand: "get_pending_mesh_events",
@@ -107346,9 +108028,16 @@ ${e?.stderr || ""}`;
107346
108028
  meshId: handle.meshId,
107347
108029
  nodeLabel: handle.batchLabel,
107348
108030
  nodeId: handle.batchLabel,
107349
- metadataEvent,
108031
+ metadataEvent: {
108032
+ ...metadataEvent,
108033
+ // REFINE-EVENT-SESSION-SCOPED-UNICAST — see queueRefineJobEvent.
108034
+ ...handle.targetCoordinatorSessionId ? { meshCoordinatorSessionId: handle.targetCoordinatorSessionId } : {}
108035
+ },
107350
108036
  queuedAt: Date.now(),
107351
- ...handle.targetCoordinatorDaemonId ? { targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId } : {}
108037
+ ...handle.targetCoordinatorDaemonId ? { targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId } : {},
108038
+ // THE FIX (batch half) — address the batch terminal event to the requesting
108039
+ // coordinator SESSION so a sibling session cannot consume it.
108040
+ ...handle.targetCoordinatorSessionId ? { targetCoordinatorSessionId: handle.targetCoordinatorSessionId } : {}
107352
108041
  };
107353
108042
  if (typeof self.deps.instanceManager?.getByCategory === "function") {
107354
108043
  const forwarded = handleMeshForwardEvent(
@@ -107366,6 +108055,12 @@ ${e?.stderr || ""}`;
107366
108055
  // the sessionless batch job's terminal event must stay targeted
107367
108056
  // at the originating coordinator, not self-fallback to this daemon.
107368
108057
  ...handle.targetCoordinatorDaemonId ? { targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId } : {},
108058
+ // REFINE-EVENT-SESSION-SCOPED-UNICAST — session half of the return
108059
+ // address, both spellings (see queueRefineJobEvent).
108060
+ ...handle.targetCoordinatorSessionId ? {
108061
+ targetCoordinatorSessionId: handle.targetCoordinatorSessionId,
108062
+ meshCoordinatorSessionId: handle.targetCoordinatorSessionId
108063
+ } : {},
107369
108064
  ...result ? { result } : {}
107370
108065
  }
107371
108066
  );
@@ -107438,7 +108133,10 @@ ${e?.stderr || ""}`;
107438
108133
  completedAt,
107439
108134
  jobId: handle.jobId,
107440
108135
  interactionId: handle.interactionId,
107441
- coordinatorDaemonId: handle.targetCoordinatorDaemonId
108136
+ coordinatorDaemonId: handle.targetCoordinatorDaemonId,
108137
+ // REFINE-EVENT-SESSION-SCOPED-UNICAST — carry the requester's session onto the
108138
+ // terminal batch handle (see the single-node path).
108139
+ coordinatorSessionId: handle.targetCoordinatorSessionId
107442
108140
  });
107443
108141
  const terminal = { ...terminalHandle, result: normalizedResult };
107444
108142
  self.terminalRefineBatchJobs.set(key2, terminal);
@@ -107472,7 +108170,8 @@ ${e?.stderr || ""}`;
107472
108170
  rationale: planRecord.orderingRationale
107473
108171
  };
107474
108172
  const coordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : self.deps.statusInstanceId || void 0;
107475
- const handle = buildRefineBatchJobHandle(self, { meshId, nodeIds, order, coordinatorDaemonId });
108173
+ const coordinatorSessionId = typeof args?.coordinatorSessionId === "string" && args.coordinatorSessionId.trim() ? args.coordinatorSessionId.trim() : void 0;
108174
+ const handle = buildRefineBatchJobHandle(self, { meshId, nodeIds, order, coordinatorDaemonId, coordinatorSessionId });
107476
108175
  self.runningRefineBatchJobs.set(key2, handle);
107477
108176
  await appendRefineBatchJobLedger(self, "task_dispatched", handle);
107478
108177
  queueRefineBatchJobEvent(self, "refine:accepted", handle);
@@ -107563,7 +108262,12 @@ ${e?.stderr || ""}`;
107563
108262
  interactionId: handle.interactionId,
107564
108263
  retryOfJobId: handle.retryOfJobId,
107565
108264
  node: { daemonId: handle.targetDaemonId, workspace: handle.workspace },
107566
- coordinatorDaemonId: handle.targetCoordinatorDaemonId
108265
+ coordinatorDaemonId: handle.targetCoordinatorDaemonId,
108266
+ // REFINE-EVENT-SESSION-SCOPED-UNICAST: carry the requester's session from the
108267
+ // accepted handle onto the TERMINAL handle. Dropping it here would leave the
108268
+ // completed/failed event — the one the coordinator actually waits on — back at
108269
+ // daemon-level addressing, i.e. the original defect.
108270
+ coordinatorSessionId: handle.targetCoordinatorSessionId
107567
108271
  });
107568
108272
  const terminal = { ...terminalHandle, result: normalizedResult };
107569
108273
  self.terminalRefineJobs.set(key2, terminal);
@@ -107583,7 +108287,8 @@ ${e?.stderr || ""}`;
107583
108287
  if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
107584
108288
  if (!node.isLocalWorktree || !node.workspace) return { success: false, error: `Refinery requires a local worktree node` };
107585
108289
  const coordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : self.deps.statusInstanceId || void 0;
107586
- const handle = buildRefineJobHandle(self, { meshId, nodeId, node, retryOfJobId: terminal?.jobId, coordinatorDaemonId });
108290
+ const coordinatorSessionId = typeof args?.coordinatorSessionId === "string" && args.coordinatorSessionId.trim() ? args.coordinatorSessionId.trim() : void 0;
108291
+ const handle = buildRefineJobHandle(self, { meshId, nodeId, node, retryOfJobId: terminal?.jobId, coordinatorDaemonId, coordinatorSessionId });
107587
108292
  self.runningRefineJobs.set(key2, handle);
107588
108293
  await appendRefineJobLedger(self, "task_dispatched", handle);
107589
108294
  queueRefineJobEvent(self, "refine:accepted", handle);
@@ -109175,6 +109880,32 @@ ${e?.stderr || ""}`;
109175
109880
  init_logger();
109176
109881
  init_runtime_defaults();
109177
109882
  init_snapshot();
109883
+ function buildCloudStatusReportPayload(sessions, p2p, timestamp2) {
109884
+ const list = Array.isArray(sessions) ? sessions : [];
109885
+ return {
109886
+ sessions: list.map((raw) => {
109887
+ const session = raw || {};
109888
+ return {
109889
+ id: session.id,
109890
+ parentId: session.parentId ?? null,
109891
+ providerType: session.providerType,
109892
+ providerName: session.providerName || session.providerType,
109893
+ kind: session.kind,
109894
+ transport: session.transport,
109895
+ status: session.status,
109896
+ workspace: session.workspace ?? null,
109897
+ cdpConnected: session.cdpConnected,
109898
+ // Forward surfaceHidden/muted so the server can gate push notifications
109899
+ // for coordinator-hidden and user-muted sessions (the WS path is the
109900
+ // only one the server sees). Both are plain booleans, not content.
109901
+ surfaceHidden: session.surfaceHidden,
109902
+ muted: session.muted
109903
+ };
109904
+ }),
109905
+ p2p,
109906
+ timestamp: timestamp2
109907
+ };
109908
+ }
109178
109909
  var DaemonStatusReporter = class {
109179
109910
  deps;
109180
109911
  log;
@@ -109388,28 +110119,7 @@ ${e?.stderr || ""}`;
109388
110119
  }
109389
110120
  if (opts?.p2pOnly) return;
109390
110121
  if (!serverConnected || !serverConn) return;
109391
- const payloadSessions = Array.isArray(payload.sessions) ? payload.sessions : [];
109392
- const wsPayload = {
109393
- sessions: payloadSessions.map((session) => ({
109394
- id: session.id,
109395
- parentId: session.parentId,
109396
- providerType: session.providerType,
109397
- providerName: session.providerName || session.providerType,
109398
- kind: session.kind,
109399
- transport: session.transport,
109400
- status: session.status,
109401
- workspace: session.workspace ?? null,
109402
- title: session.title,
109403
- cdpConnected: session.cdpConnected,
109404
- summaryMetadata: session.summaryMetadata,
109405
- settings: session.settings,
109406
- // Forward surfaceHidden so the server can gate push notifications for
109407
- // coordinator-hidden sessions (the WS path is the only one the server sees).
109408
- surfaceHidden: session.surfaceHidden
109409
- })),
109410
- p2p: payload.p2p,
109411
- timestamp: now
109412
- };
110122
+ const wsPayload = buildCloudStatusReportPayload(payload.sessions, payload.p2p, now);
109413
110123
  const wsHash = this.simpleHash(JSON.stringify({
109414
110124
  ...wsPayload,
109415
110125
  timestamp: void 0