@adhdev/daemon-core 0.9.82-rc.310 → 0.9.82-rc.311

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.mjs CHANGED
@@ -26,6 +26,18 @@ var __copyProps = (to, from, except, desc) => {
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
 
28
28
  // src/repo-mesh-types.ts
29
+ function normalizeMeshSchedulingStrategy(value) {
30
+ if (typeof value !== "string") return DEFAULT_MESH_SCHEDULING_STRATEGY;
31
+ const trimmed = value.trim();
32
+ return MESH_SCHEDULING_STRATEGIES.includes(trimmed) ? trimmed : DEFAULT_MESH_SCHEDULING_STRATEGY;
33
+ }
34
+ function resolveNodeSchedulingPriority(nodePolicy) {
35
+ const raw = Number(nodePolicy?.schedulingPriority);
36
+ return Number.isFinite(raw) ? raw : 0;
37
+ }
38
+ function resolveAutoConvergeCodeChange(policy) {
39
+ return policy?.autoConvergeCodeChange === true;
40
+ }
29
41
  function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy) {
30
42
  if (typeof nodePolicy?.delegatedWorkerAutoApprove === "boolean") {
31
43
  return nodePolicy.delegatedWorkerAutoApprove;
@@ -53,10 +65,19 @@ function resolveProviderMaxParallel(nodePolicy, providerType) {
53
65
  if (!Number.isFinite(raw) || raw < 0) return void 0;
54
66
  return Math.floor(raw);
55
67
  }
56
- var DEFAULT_MESH_POLICY;
68
+ var MESH_SCHEDULING_STRATEGIES, DEFAULT_MESH_SCHEDULING_STRATEGY, MESH_CONVERGE_REFINE_TAG, MESH_CONVERGE_FAST_FORWARD_TAG, DEFAULT_MESH_POLICY;
57
69
  var init_repo_mesh_types = __esm({
58
70
  "src/repo-mesh-types.ts"() {
59
71
  "use strict";
72
+ MESH_SCHEDULING_STRATEGIES = [
73
+ "first_eligible",
74
+ "least_loaded",
75
+ "round_robin",
76
+ "priority_only"
77
+ ];
78
+ DEFAULT_MESH_SCHEDULING_STRATEGY = "first_eligible";
79
+ MESH_CONVERGE_REFINE_TAG = "converge=refine";
80
+ MESH_CONVERGE_FAST_FORWARD_TAG = "converge=fast_forward";
60
81
  DEFAULT_MESH_POLICY = {
61
82
  requirePreTaskCheckpoint: false,
62
83
  requirePostTaskCheckpoint: true,
@@ -290,10 +311,10 @@ function readInjected(value) {
290
311
  }
291
312
  function getDaemonBuildInfo() {
292
313
  if (cached) return cached;
293
- const commit = readInjected(true ? "33fe83f72a43bccb1b4bec7744ee5c6d8c11a325" : void 0) ?? "unknown";
294
- const commitShort = readInjected(true ? "33fe83f7" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
295
- const version = readInjected(true ? "0.9.82-rc.310" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
296
- const builtAt = readInjected(true ? "2026-06-17T09:34:54.806Z" : void 0);
314
+ const commit = readInjected(true ? "3902f3d63eabe7b9e34d0e849dc0fed2fd66c026" : void 0) ?? "unknown";
315
+ const commitShort = readInjected(true ? "3902f3d6" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
316
+ const version = readInjected(true ? "0.9.82-rc.311" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
317
+ const builtAt = readInjected(true ? "2026-06-17T12:17:11.243Z" : void 0);
297
318
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
298
319
  return cached;
299
320
  }
@@ -1543,6 +1564,17 @@ function mergeMeshPolicy(base, patch) {
1543
1564
  if (!SPAWNED_SESSION_VISIBILITY_MODES.has(String(policy.spawnedSessionVisibility))) {
1544
1565
  policy.spawnedSessionVisibility = "visible";
1545
1566
  }
1567
+ const normalizedStrategy = normalizeMeshSchedulingStrategy(policy.schedulingStrategy);
1568
+ if (normalizedStrategy === "first_eligible") {
1569
+ delete policy.schedulingStrategy;
1570
+ } else {
1571
+ policy.schedulingStrategy = normalizedStrategy;
1572
+ }
1573
+ if (policy.autoConvergeCodeChange === true) {
1574
+ policy.autoConvergeCodeChange = true;
1575
+ } else {
1576
+ delete policy.autoConvergeCodeChange;
1577
+ }
1546
1578
  return policy;
1547
1579
  }
1548
1580
  function normalizeAutoFastForwardPolicy(value) {
@@ -2833,6 +2865,7 @@ __export(mesh_work_queue_exports, {
2833
2865
  recordMeshToolCall: () => recordMeshToolCall,
2834
2866
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
2835
2867
  requeueTask: () => requeueTask,
2868
+ resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
2836
2869
  updateDirectDispatchStatus: () => updateDirectDispatchStatus,
2837
2870
  updateSessionTaskStatus: () => updateSessionTaskStatus,
2838
2871
  updateTaskStatus: () => updateTaskStatus,
@@ -2907,6 +2940,21 @@ function firstProviderPriority(policy) {
2907
2940
  if (!Array.isArray(raw)) return void 0;
2908
2941
  return raw.find((type) => typeof type === "string" && type.trim())?.trim();
2909
2942
  }
2943
+ function roleCapabilityTags(policy, providerType) {
2944
+ const roles = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerRoles : void 0;
2945
+ if (!Array.isArray(roles)) return [];
2946
+ const wantedProvider = typeof providerType === "string" && providerType.trim() ? providerType.trim().toLowerCase() : "";
2947
+ const out = [];
2948
+ for (const entry of roles) {
2949
+ if (!entry || typeof entry !== "object") continue;
2950
+ const type = typeof entry.providerType === "string" ? entry.providerType.trim().toLowerCase() : "";
2951
+ const role = typeof entry.role === "string" ? entry.role.trim().toLowerCase() : "";
2952
+ if (!role) continue;
2953
+ if (wantedProvider && type && type !== wantedProvider) continue;
2954
+ out.push(`role=${role}`);
2955
+ }
2956
+ return out;
2957
+ }
2910
2958
  function buildMeshNodeCapabilityTags(node, providerType) {
2911
2959
  const provider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : firstProviderPriority(node?.policy);
2912
2960
  const worktreeBranch = typeof node?.worktreeBranch === "string" && node.worktreeBranch.trim() ? node.worktreeBranch.trim() : null;
@@ -2918,7 +2966,25 @@ function buildMeshNodeCapabilityTags(node, providerType) {
2918
2966
  // Worktree nodes automatically expose a "worktree=<branch>" tag so that
2919
2967
  // mesh_enqueue_task with required_tags: ["worktree=<branch>"] routes
2920
2968
  // only to the matching worktree node.
2921
- ...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : []
2969
+ ...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : [],
2970
+ // Convergence routing: advertise how this node can land its work onto base.
2971
+ // - converge=refine: local worktree nodes (on ANY machine — refine_mesh_node
2972
+ // now forwards to the owning daemon) can run the Refinery merge → push →
2973
+ // cleanup against their own checkout, so they accept code_change tasks.
2974
+ // - converge=fast_forward: non-worktree nodes (the machine itself) can only
2975
+ // ff/push an already-converged branch; they are NOT a destination for
2976
+ // code_change work (a worktree is created first, and that worktree node
2977
+ // receives the task instead). Reuses the ordinary required-tags filter —
2978
+ // the load-balancing scheduler auto-injects converge=refine for code_change
2979
+ // so such work is hard-filtered onto refine-capable nodes.
2980
+ ...node?.isLocalWorktree === true ? ["converge=refine"] : ["converge=fast_forward"],
2981
+ // Role-based routing: advertise role=<x> for each (node, provider) role
2982
+ // declared in policy.providerRoles. Narrowed to the selected provider when
2983
+ // one is given so the chosen provider must match a task's required role;
2984
+ // when no provider is selected, all declared roles are advertised for the
2985
+ // node-level eligibility scan. Reuses the ordinary required-tags filter —
2986
+ // no separate role field/gate.
2987
+ ...roleCapabilityTags(node?.policy, providerType)
2922
2988
  ]);
2923
2989
  }
2924
2990
  function nodeSatisfiesRequiredTags(requiredTags, capabilityTags) {
@@ -2927,6 +2993,18 @@ function nodeSatisfiesRequiredTags(requiredTags, capabilityTags) {
2927
2993
  const available = new Set(normalizeMeshCapabilityTags(capabilityTags));
2928
2994
  return required.every((tag) => available.has(tag));
2929
2995
  }
2996
+ function resolveConvergeRequiredTags(meshId, taskMode, explicitRequiredTags, opts) {
2997
+ if (taskMode !== "code_change") return explicitRequiredTags;
2998
+ if (typeof opts?.targetNodeId === "string" && opts.targetNodeId.trim()) return explicitRequiredTags;
2999
+ let optedIn = false;
3000
+ try {
3001
+ optedIn = resolveAutoConvergeCodeChange(getMesh(meshId)?.policy);
3002
+ } catch {
3003
+ optedIn = false;
3004
+ }
3005
+ if (!optedIn) return explicitRequiredTags;
3006
+ return normalizeMeshCapabilityTags([...explicitRequiredTags, MESH_CONVERGE_REFINE_TAG]);
3007
+ }
2930
3008
  function withQueueLock(_meshId, fn) {
2931
3009
  return MeshRuntimeStore.getInstance().transaction(fn);
2932
3010
  }
@@ -2985,7 +3063,15 @@ function enqueueTask(meshId, message, opts) {
2985
3063
  taskMode: modeValidation.taskMode,
2986
3064
  targetNodeId: opts?.targetNodeId,
2987
3065
  targetSessionId: opts?.targetSessionId,
2988
- requiredTags: normalizeMeshCapabilityTags(opts?.requiredTags),
3066
+ // Convergence routing (opt-in): auto-inject converge=refine for code_change
3067
+ // tasks so they hard-filter onto refine-capable worktree nodes. No-op unless
3068
+ // the mesh opts in; explicit target_node_id / required_tags are preserved.
3069
+ requiredTags: resolveConvergeRequiredTags(
3070
+ meshId,
3071
+ modeValidation.taskMode,
3072
+ normalizeMeshCapabilityTags(opts?.requiredTags),
3073
+ { targetNodeId: opts?.targetNodeId }
3074
+ ),
2989
3075
  ...dependsOn.length > 0 ? { dependsOn } : {},
2990
3076
  ...typeof opts?.missionId === "string" && opts.missionId.trim() ? { missionId: opts.missionId.trim() } : {},
2991
3077
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -3245,6 +3331,7 @@ var init_mesh_work_queue = __esm({
3245
3331
  "src/mesh/mesh-work-queue.ts"() {
3246
3332
  "use strict";
3247
3333
  init_mesh_host_ownership();
3334
+ init_repo_mesh_types();
3248
3335
  init_mesh_runtime_store();
3249
3336
  init_mesh_config();
3250
3337
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
@@ -3538,6 +3625,17 @@ var init_mesh_runtime_store = __esm({
3538
3625
 
3539
3626
  CREATE INDEX IF NOT EXISTS idx_mesh_missions_mesh_status
3540
3627
  ON mesh_missions(mesh_id, status, updated_at);
3628
+
3629
+ -- Load-balancing scheduler: per-mesh round-robin rotation cursor. When
3630
+ -- the schedulingStrategy is 'round_robin', several eligible nodes tied at
3631
+ -- the least load are rotated by this cursor so the tie-break winner cycles
3632
+ -- across scheduling passes instead of always favouring the same array-order
3633
+ -- node. Persisted (not a module Map) so rotation survives daemon restarts
3634
+ -- and stays a single source of truth across scheduling entry points.
3635
+ CREATE TABLE IF NOT EXISTS mesh_scheduler_cursor (
3636
+ mesh_id TEXT PRIMARY KEY,
3637
+ cursor INTEGER NOT NULL DEFAULT 0
3638
+ );
3541
3639
  `);
3542
3640
  }
3543
3641
  hasCompletionFingerprint(fingerprint) {
@@ -3715,6 +3813,44 @@ var init_mesh_runtime_store = __esm({
3715
3813
  `).get(meshId, nodeId);
3716
3814
  return row !== void 0;
3717
3815
  }
3816
+ /**
3817
+ * Count active (status='assigned') tasks on a node, regardless of provider or
3818
+ * task mode. This is the load metric for least-loaded / round-robin ranking:
3819
+ * the scheduler prefers the node with the fewest active assignments so
3820
+ * untargeted work spreads instead of piling onto whichever node asks first.
3821
+ */
3822
+ nodeActiveAssignmentCount(meshId, nodeId) {
3823
+ const row = this.db.prepare(`
3824
+ SELECT COUNT(*) as count FROM mesh_queue
3825
+ WHERE mesh_id = ? AND status = 'assigned' AND assigned_node_id = ?
3826
+ `).get(meshId, nodeId);
3827
+ return row?.count ?? 0;
3828
+ }
3829
+ /**
3830
+ * Read the current per-mesh round-robin cursor (0 when unset). Used to rotate
3831
+ * the tie-break winner among nodes tied at the least load.
3832
+ */
3833
+ getSchedulerCursor(meshId) {
3834
+ const row = this.db.prepare(
3835
+ "SELECT cursor FROM mesh_scheduler_cursor WHERE mesh_id = ?"
3836
+ ).get(meshId);
3837
+ return row?.cursor ?? 0;
3838
+ }
3839
+ /**
3840
+ * Atomically advance the per-mesh round-robin cursor by one and return the
3841
+ * value that was current BEFORE the bump (the value the caller should rotate
3842
+ * by for this pass). UPSERT keeps it lock-free across concurrent passes.
3843
+ */
3844
+ bumpSchedulerCursor(meshId) {
3845
+ return this.transaction(() => {
3846
+ const current = this.getSchedulerCursor(meshId);
3847
+ this.db.prepare(`
3848
+ INSERT INTO mesh_scheduler_cursor (mesh_id, cursor) VALUES (?, ?)
3849
+ ON CONFLICT(mesh_id) DO UPDATE SET cursor = excluded.cursor
3850
+ `).run(meshId, current + 1);
3851
+ return current;
3852
+ });
3853
+ }
3718
3854
  /**
3719
3855
  * Count active (status='assigned') tasks on a (node, provider) combination,
3720
3856
  * matched by the assignedProviderType stamped on the payload at claim time.
@@ -8286,6 +8422,33 @@ function activeReadonlyAssignedCount(meshId) {
8286
8422
  function nodeHasActiveAssignment(meshId, nodeId) {
8287
8423
  return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
8288
8424
  }
8425
+ function nodeActiveLoad(meshId, nodeId) {
8426
+ return MeshRuntimeStore.getInstance().nodeActiveAssignmentCount(meshId, nodeId);
8427
+ }
8428
+ function resolveSchedulingStrategy(mesh) {
8429
+ return normalizeMeshSchedulingStrategy(mesh?.policy?.schedulingStrategy);
8430
+ }
8431
+ function orderEligibleNodes(meshId, strategy, nodes, opts) {
8432
+ if (strategy === "first_eligible" || nodes.length <= 1) {
8433
+ return nodes;
8434
+ }
8435
+ const priorityOf = (n) => resolveNodeSchedulingPriority(n.node?.policy);
8436
+ let rotation = 0;
8437
+ if (strategy === "round_robin") {
8438
+ const cursor = opts?.bumpCursor ? MeshRuntimeStore.getInstance().bumpSchedulerCursor(meshId) : MeshRuntimeStore.getInstance().getSchedulerCursor(meshId);
8439
+ rotation = (cursor % nodes.length + nodes.length) % nodes.length;
8440
+ }
8441
+ const rotationRank = (index) => (index - rotation + nodes.length) % nodes.length;
8442
+ return [...nodes].sort((a, b) => {
8443
+ const prioDelta = priorityOf(b) - priorityOf(a);
8444
+ if (prioDelta !== 0) return prioDelta;
8445
+ if (strategy === "least_loaded" || strategy === "round_robin") {
8446
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
8447
+ if (loadDelta !== 0) return loadDelta;
8448
+ }
8449
+ return rotationRank(a.index) - rotationRank(b.index);
8450
+ });
8451
+ }
8289
8452
  function activeProviderAssignedCount(meshId, nodeId, providerType) {
8290
8453
  return getQueue(meshId, { status: ["assigned"] }).filter((task) => task.assignedNodeId === nodeId && task.assignedProviderType === providerType).length;
8291
8454
  }
@@ -8422,7 +8585,14 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
8422
8585
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
8423
8586
  continue;
8424
8587
  }
8425
- for (const node of candidateNodes) {
8588
+ const strategy = resolveSchedulingStrategy(mesh);
8589
+ const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
8590
+ meshId,
8591
+ strategy,
8592
+ candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
8593
+ { bumpCursor: true }
8594
+ ).map((c) => c.node);
8595
+ for (const node of orderedCandidateNodes) {
8426
8596
  const nodeId = readMeshNodeId(node);
8427
8597
  if (!nodeId) continue;
8428
8598
  const launchKey = `${meshId}:${nodeId}`;
@@ -8549,6 +8719,8 @@ async function triggerMeshQueue(components, meshId) {
8549
8719
  noIdleMeshSessionAvailable: true
8550
8720
  };
8551
8721
  }
8722
+ const strategy = resolveSchedulingStrategy(mesh);
8723
+ const localCandidates = [];
8552
8724
  const cliInstances = components.instanceManager.getByCategory("cli");
8553
8725
  for (const inst of cliInstances) {
8554
8726
  const state = inst.getState();
@@ -8571,7 +8743,7 @@ async function triggerMeshQueue(components, meshId) {
8571
8743
  const providerType = state.type || readNonEmptyString2(settings.providerType);
8572
8744
  if (providerType) {
8573
8745
  localIdleSessionsChecked += 1;
8574
- tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
8746
+ localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) => readMeshNodeId(n) === nodeId) });
8575
8747
  } else {
8576
8748
  skippedSessions.push({
8577
8749
  nodeId,
@@ -8585,18 +8757,49 @@ async function triggerMeshQueue(components, meshId) {
8585
8757
  remoteSessions = MeshRuntimeStore.getInstance().getRemoteIdleSessions();
8586
8758
  } catch {
8587
8759
  }
8760
+ const remoteCandidates = [];
8588
8761
  for (const idle of remoteSessions) {
8589
8762
  const node = mesh.nodes.find((n) => n.id === idle.nodeId);
8590
8763
  if (node) {
8591
8764
  remoteIdleSessionsChecked += 1;
8592
- const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
8593
- if (assigned) {
8594
- try {
8595
- MeshRuntimeStore.getInstance().deleteRemoteIdleSession(idle.nodeId, idle.sessionId);
8596
- } catch {
8597
- }
8765
+ remoteCandidates.push({ nodeId: idle.nodeId, sessionId: idle.sessionId, providerType: idle.providerType, origin: "remote", node });
8766
+ }
8767
+ }
8768
+ const assignIdleCandidate = (candidate) => {
8769
+ const assigned = tryAssignQueueTask(components, meshId, candidate.nodeId, candidate.sessionId, candidate.providerType);
8770
+ if (assigned && candidate.origin === "remote") {
8771
+ try {
8772
+ MeshRuntimeStore.getInstance().deleteRemoteIdleSession(candidate.nodeId, candidate.sessionId);
8773
+ } catch {
8598
8774
  }
8599
8775
  }
8776
+ };
8777
+ if (strategy === "first_eligible") {
8778
+ for (const candidate of localCandidates) assignIdleCandidate(candidate);
8779
+ for (const candidate of remoteCandidates) assignIdleCandidate(candidate);
8780
+ } else {
8781
+ const pool = [...localCandidates, ...remoteCandidates];
8782
+ const baseIndex = /* @__PURE__ */ new Map();
8783
+ pool.forEach((c, i) => {
8784
+ if (!baseIndex.has(c.nodeId)) baseIndex.set(c.nodeId, i);
8785
+ });
8786
+ const uniqueNodes = [...new Set(pool.map((c) => c.nodeId))].map((nodeId, index) => ({ nodeId, node: pool.find((c) => c.nodeId === nodeId)?.node, index }));
8787
+ const ranked = orderEligibleNodes(meshId, strategy, uniqueNodes, { bumpCursor: true });
8788
+ const rankIndex = new Map(ranked.map((r, i) => [r.nodeId, i]));
8789
+ const remaining = [...pool];
8790
+ while (remaining.length > 0) {
8791
+ remaining.sort((a, b) => {
8792
+ const aPrio = resolveNodeSchedulingPriority(a.node?.policy);
8793
+ const bPrio = resolveNodeSchedulingPriority(b.node?.policy);
8794
+ if (aPrio !== bPrio) return bPrio - aPrio;
8795
+ if (strategy === "least_loaded" || strategy === "round_robin") {
8796
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
8797
+ if (loadDelta !== 0) return loadDelta;
8798
+ }
8799
+ return (rankIndex.get(a.nodeId) ?? 0) - (rankIndex.get(b.nodeId) ?? 0);
8800
+ });
8801
+ assignIdleCandidate(remaining.shift());
8802
+ }
8600
8803
  }
8601
8804
  autoLaunchStarted = await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
8602
8805
  const afterQueue = getQueue(meshId);
@@ -11972,7 +12175,7 @@ var init_cli_state_engine = __esm({
11972
12175
  scheduleSettle() {
11973
12176
  if (this.settleTimer) clearTimeout(this.settleTimer);
11974
12177
  const epoch = this.responseEpoch;
11975
- const delay = Math.max(
12178
+ const delay2 = Math.max(
11976
12179
  this.timeouts.outputSettle,
11977
12180
  this.submitPendingUntil > Date.now() ? this.submitPendingUntil - Date.now() + this.timeouts.outputSettle : 0
11978
12181
  );
@@ -11980,7 +12183,7 @@ var init_cli_state_engine = __esm({
11980
12183
  this.settleTimer = null;
11981
12184
  if (epoch !== this.responseEpoch) return;
11982
12185
  this.evaluateSettled(this.transport.getSnapshot());
11983
- }, delay);
12186
+ }, delay2);
11984
12187
  }
11985
12188
  /** Called from sendMessage in transport once a turn scope is established. */
11986
12189
  onTurnStarted(turnScope) {
@@ -17795,9 +17998,9 @@ function readString6(value) {
17795
17998
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
17796
17999
  }
17797
18000
  function summarizeMessage(message) {
17798
- const oneLine = message.replace(/\s+/g, " ").trim();
17799
- const title = oneLine.length > 96 ? `${oneLine.slice(0, 93)}...` : oneLine;
17800
- return { title: title || "(untitled task)", summary: oneLine };
18001
+ const oneLine2 = message.replace(/\s+/g, " ").trim();
18002
+ const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
18003
+ return { title: title || "(untitled task)", summary: oneLine2 };
17801
18004
  }
17802
18005
  function elapsedSince(value, now) {
17803
18006
  const started = value ? new Date(value).getTime() : Number.NaN;
@@ -20903,6 +21106,11 @@ function collapseReplayAssistantTurns(messages, historyBehavior) {
20903
21106
  continue;
20904
21107
  }
20905
21108
  if (message.role === "assistant") {
21109
+ const isActivity = message.kind === "tool" || message.kind === "terminal" || message.kind === "thought";
21110
+ if (isActivity) {
21111
+ collapsed.push(message);
21112
+ continue;
21113
+ }
20906
21114
  if (sawAssistantSinceLastUser) continue;
20907
21115
  sawAssistantSinceLastUser = true;
20908
21116
  collapsed.push(message);
@@ -26046,7 +26254,8 @@ function buildReadChatCommandResult(payload, args, h) {
26046
26254
  const sessionIdHint = typeof args?.targetSessionId === "string" ? args.targetSessionId : typeof args?.sessionId === "string" ? args.sessionId : "";
26047
26255
  const providerHint = typeof args?.cliType === "string" ? args.cliType : typeof args?.providerType === "string" ? args.providerType : typeof args?.agentType === "string" ? args.agentType : "";
26048
26256
  const filteredMessages = h ? maybeHideCoordinatorPromptMessage(h, providerHint, sessionIdHint, messages) : messages;
26049
- const visibleMessages = filterUserFacingChatMessages(filteredMessages);
26257
+ const includeActivity = args?.includeActivity === true || args?.includeActivity === "true";
26258
+ const visibleMessages = includeActivity ? filteredMessages.filter((m) => isUserFacingChatMessage(m) || isActivityChatMessage(m)) : filterUserFacingChatMessages(filteredMessages);
26050
26259
  const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
26051
26260
  const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
26052
26261
  const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
@@ -28415,6 +28624,9 @@ function normalizeProviderScriptArgs(args, scriptName) {
28415
28624
  }
28416
28625
  function buildControlScriptResult(scriptName, payload) {
28417
28626
  if (!payload || typeof payload !== "object") return {};
28627
+ if (payload.controlResult && typeof payload.controlResult === "object") {
28628
+ return { controlResult: payload.controlResult };
28629
+ }
28418
28630
  const legacyListPayload = (() => {
28419
28631
  if (Array.isArray(payload.options)) return payload;
28420
28632
  if (/^listmodels$/i.test(scriptName) && Array.isArray(payload.models)) {
@@ -30265,12 +30477,12 @@ var FsmDriver = class {
30265
30477
  scheduleSpawnPrime() {
30266
30478
  const seqs = this.spec.send_on_spawn;
30267
30479
  if (!Array.isArray(seqs) || seqs.length === 0) return;
30268
- const delay = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
30480
+ const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
30269
30481
  setTimeout(() => {
30270
30482
  for (const seq of seqs) {
30271
30483
  if (typeof seq === "string" && seq.length > 0) this.adapter.send_keys(seq);
30272
30484
  }
30273
- }, delay);
30485
+ }, delay2);
30274
30486
  }
30275
30487
  dispatch(cmd) {
30276
30488
  switch (cmd.kind) {
@@ -30636,11 +30848,11 @@ var FsmDriver = class {
30636
30848
  const armed = this.delegateTimers.has(d.id);
30637
30849
  const shouldFire = d.when_state === currentStateId;
30638
30850
  if (shouldFire && !armed) {
30639
- const delay = d.after_duration_ms ?? 0;
30851
+ const delay2 = d.after_duration_ms ?? 0;
30640
30852
  const t = setTimeout(() => {
30641
30853
  this.fireDelegate(d);
30642
30854
  this.delegateTimers.delete(d.id);
30643
- }, delay);
30855
+ }, delay2);
30644
30856
  this.delegateTimers.set(d.id, t);
30645
30857
  } else if (!shouldFire && armed) {
30646
30858
  clearTimeout(this.delegateTimers.get(d.id));
@@ -30694,7 +30906,15 @@ var FsmDriver = class {
30694
30906
  this.adapter.send_keys(a.keys);
30695
30907
  return;
30696
30908
  case "open_picker":
30697
- this.adapter.send_keys(a.trigger_keys);
30909
+ {
30910
+ const m = /^([\s\S]*?)([\r\n]+)$/.exec(a.trigger_keys);
30911
+ if (m && m[1]) {
30912
+ this.adapter.send_keys(m[1]);
30913
+ setTimeout(() => this.adapter.send_keys(m[2]), 200);
30914
+ } else {
30915
+ this.adapter.send_keys(a.trigger_keys);
30916
+ }
30917
+ }
30698
30918
  this.pickerInProgress = { control_id: ctl.id, spec: ctl };
30699
30919
  return;
30700
30920
  case "attach_image": {
@@ -30879,8 +31099,7 @@ function executeJsonl(src, input) {
30879
31099
  for (let i = 0; i < lines.length; i += 1) {
30880
31100
  const rec = lines[i];
30881
31101
  if (filter && !filter(rec)) continue;
30882
- const msg = projectMessage(rec, src.message_map, i, lines.length, mtime);
30883
- if (msg) {
31102
+ for (const msg of projectMessages(rec, src.message_map, i, lines.length, mtime)) {
30884
31103
  if (transcriptWorkspace) msg.workspace = transcriptWorkspace;
30885
31104
  messages.push(msg);
30886
31105
  }
@@ -30960,8 +31179,9 @@ function executeSqlite(src, input) {
30960
31179
  const mtime = safeMtimeMs(resolved);
30961
31180
  const messages = [];
30962
31181
  for (let i = 0; i < messageRows.length; i += 1) {
30963
- const msg = projectMessage(messageRows[i], src.message_map, i, messageRows.length, mtime);
30964
- if (msg) messages.push(msg);
31182
+ for (const msg of projectMessages(messageRows[i], src.message_map, i, messageRows.length, mtime)) {
31183
+ messages.push(msg);
31184
+ }
30965
31185
  }
30966
31186
  if (messages.length === 0) return null;
30967
31187
  return {
@@ -31359,11 +31579,42 @@ function jsonPathGet(record, expr) {
31359
31579
  }
31360
31580
  return cur;
31361
31581
  }
31362
- function projectMessage(record, map, index, total, sourceMtimeMs) {
31582
+ function projectMessages(record, map, index, total, sourceMtimeMs) {
31363
31583
  const roleRaw = jsonPathGet(record, map.role);
31364
- const contentRaw = jsonPathGet(record, map.content);
31365
31584
  const role = normalizeRole(roleRaw);
31366
- let content = stringifyContent(contentRaw);
31585
+ let receivedAt = sourceMtimeMs - (total - 1 - index) * 1e3;
31586
+ if (map.timestamp_ms) {
31587
+ const tsRaw = jsonPathGet(record, map.timestamp_ms);
31588
+ const parsed = parseTimestamp(tsRaw);
31589
+ if (parsed != null) receivedAt = parsed;
31590
+ }
31591
+ const kindRaw = map.kind ? jsonPathGet(record, map.kind) : void 0;
31592
+ const kind = typeof kindRaw === "string" && kindRaw ? kindRaw : "standard";
31593
+ const out = [];
31594
+ if (map.tools) {
31595
+ const recordTool = projectToolBlock(record, role, map.tools);
31596
+ if (recordTool) {
31597
+ out.push({ ...recordTool, receivedAt });
31598
+ return out;
31599
+ }
31600
+ }
31601
+ const contentRaw = jsonPathGet(record, map.content);
31602
+ const content = cleanContent(stringifyContent(contentRaw), map);
31603
+ if (content) out.push({ role, content, receivedAt, kind });
31604
+ if (map.tools && Array.isArray(contentRaw)) {
31605
+ let nudge = 1;
31606
+ for (const block2 of contentRaw) {
31607
+ const tool = projectToolBlock(block2, role, map.tools);
31608
+ if (tool) {
31609
+ out.push({ ...tool, receivedAt: receivedAt + nudge });
31610
+ nudge += 1;
31611
+ }
31612
+ }
31613
+ }
31614
+ return out;
31615
+ }
31616
+ function cleanContent(input, map) {
31617
+ let content = input;
31367
31618
  if (content && map.content_strip) {
31368
31619
  for (const tag of map.content_strip) {
31369
31620
  const safeTag = tag.replace(/[.+^${}()|[\]\\]/g, "\\$&");
@@ -31379,17 +31630,33 @@ function projectMessage(record, map, index, total, sourceMtimeMs) {
31379
31630
  content = content.replace(open, "").replace(close, "");
31380
31631
  }
31381
31632
  }
31382
- if (content) content = content.trim();
31383
- if (!content) return null;
31384
- let receivedAt = sourceMtimeMs - (total - 1 - index) * 1e3;
31385
- if (map.timestamp_ms) {
31386
- const tsRaw = jsonPathGet(record, map.timestamp_ms);
31387
- const parsed = parseTimestamp(tsRaw);
31388
- if (parsed != null) receivedAt = parsed;
31633
+ return content ? content.trim() : "";
31634
+ }
31635
+ var DEFAULT_TOOL_CALL_TYPES = ["tool_use", "function_call", "custom_tool_call"];
31636
+ var DEFAULT_TOOL_RESULT_TYPES = ["tool_result", "function_call_output", "custom_tool_call_output"];
31637
+ function projectToolBlock(block2, role, tmap) {
31638
+ void role;
31639
+ if (block2 == null || typeof block2 !== "object") return null;
31640
+ const typeVal = String(jsonPathGet(block2, tmap.block_type || "$.type") ?? "");
31641
+ if (!typeVal) return null;
31642
+ const callTypes = tmap.call_types ?? DEFAULT_TOOL_CALL_TYPES;
31643
+ const resultTypes = tmap.result_types ?? DEFAULT_TOOL_RESULT_TYPES;
31644
+ if (callTypes.includes(typeVal)) {
31645
+ const name = String(jsonPathGet(block2, tmap.call_name || "$.name") ?? "tool").trim() || "tool";
31646
+ const args = oneLine(stringifyContent(jsonPathGet(block2, tmap.call_args || "$.input")), 240);
31647
+ const content = args ? `\u2197 ${name}: ${args}` : `\u2197 ${name}`;
31648
+ return { role: "assistant", content, receivedAt: 0, kind: "tool" };
31649
+ }
31650
+ if (resultTypes.includes(typeVal)) {
31651
+ const result = oneLine(stringifyContent(jsonPathGet(block2, tmap.result_content || "$.content")), 600);
31652
+ if (!result) return null;
31653
+ return { role: "assistant", content: `\u2198 ${result}`, receivedAt: 0, kind: "tool" };
31389
31654
  }
31390
- const kindRaw = map.kind ? jsonPathGet(record, map.kind) : void 0;
31391
- const kind = typeof kindRaw === "string" && kindRaw ? kindRaw : "standard";
31392
- return { role, content, receivedAt, kind };
31655
+ return null;
31656
+ }
31657
+ function oneLine(s, max) {
31658
+ const flat = s.replace(/\s+/g, " ").trim();
31659
+ return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
31393
31660
  }
31394
31661
  function parseTimestamp(v) {
31395
31662
  if (v == null) return null;
@@ -31530,6 +31797,9 @@ import * as fs13 from "fs";
31530
31797
  function stripAnsi3(text) {
31531
31798
  return String(text || "").replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
31532
31799
  }
31800
+ function delay(ms) {
31801
+ return new Promise((resolve24) => setTimeout(resolve24, ms));
31802
+ }
31533
31803
  var SpecCliAdapter = class _SpecCliAdapter {
31534
31804
  cliType;
31535
31805
  cliName;
@@ -31751,9 +32021,15 @@ var SpecCliAdapter = class _SpecCliAdapter {
31751
32021
  * drives the dispatch:
31752
32022
  *
31753
32023
  * send_keys → click_control (e.g. stop)
31754
- * open_picker → click_control then resolve when extract_choices
31755
- * surface; choice index comes from args.choiceIndex
31756
- * or args.choice (string label match), defaulting to 0
32024
+ * open_picker → two roles, driven by the screen, not a hardcoded list:
32025
+ * - LIST (no choice arg): open the picker, wait for it
32026
+ * to render, parse the on-screen options via
32027
+ * `extract_choices`, and return them as
32028
+ * `controlResult.options` (+ `currentValue`). This is
32029
+ * how the dashboard's Model/Mode controls learn what is
32030
+ * actually selectable in this CLI right now.
32031
+ * - SELECT (args.choiceIndex / args.choiceLabel): drive
32032
+ * the picker to that option using `submit_key`.
31757
32033
  * attach_image → attach_image dispatch; expects args.blob (data url
31758
32034
  * or base64) and args.mime
31759
32035
  *
@@ -31778,11 +32054,128 @@ var SpecCliAdapter = class _SpecCliAdapter {
31778
32054
  this.driver.dispatch({ kind: "attach_image", blob, mime });
31779
32055
  return Promise.resolve({ ok: true, effects: [{ type: "attached_image", controlId: ctl.id }] });
31780
32056
  }
32057
+ if (action.type === "open_picker") {
32058
+ const choiceIndex = typeof flat.choiceIndex === "number" ? flat.choiceIndex : typeof flat.choiceIndex === "string" && flat.choiceIndex.trim() ? Number(flat.choiceIndex) : void 0;
32059
+ const choiceLabel = typeof flat.choiceLabel === "string" ? flat.choiceLabel : typeof flat.choice === "string" ? flat.choice : void 0;
32060
+ if (typeof choiceIndex === "number" && Number.isFinite(choiceIndex) || choiceLabel && choiceLabel.trim()) {
32061
+ return this.selectPickerChoice(ctl, action, choiceIndex, choiceLabel);
32062
+ }
32063
+ return this.openPickerAndListChoices(ctl, action);
32064
+ }
31781
32065
  this.driver.dispatch({ kind: "click_control", control_id: ctl.id, payload: flat });
31782
- const effects = [];
31783
- if (action.type === "open_picker") effects.push({ type: "opened_picker", controlId: ctl.id });
31784
- else if (action.type === "send_keys") effects.push({ type: "sent_keys", controlId: ctl.id });
31785
- return Promise.resolve({ ok: true, effects });
32066
+ return Promise.resolve({ ok: true, effects: [{ type: "sent_keys", controlId: ctl.id }] });
32067
+ }
32068
+ /**
32069
+ * Open an `open_picker` control and return the options the CLI is showing,
32070
+ * parsed live from the screen via `extract_choices`. Nothing is selected —
32071
+ * the picker is left open so a follow-up SELECT invoke can commit a choice.
32072
+ */
32073
+ async openPickerAndListChoices(ctl, action) {
32074
+ this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
32075
+ const ready = await this.waitForPickerRendered(action);
32076
+ const options = this.extractPickerChoices(action);
32077
+ const currentValue = options.find((o) => o.current)?.label;
32078
+ return {
32079
+ ok: true,
32080
+ effects: [{ type: "opened_picker", controlId: ctl.id }],
32081
+ controlResult: {
32082
+ options: options.map((o) => ({ value: o.label, label: o.label, current: o.current })),
32083
+ ...currentValue ? { currentValue } : {},
32084
+ source: "screen-parse",
32085
+ ...ready ? {} : { warning: "picker_render_timeout" }
32086
+ }
32087
+ };
32088
+ }
32089
+ /**
32090
+ * Drive an already-listable picker to a specific option. The option can be
32091
+ * named (choiceLabel — matched against the parsed on-screen labels) or
32092
+ * positional (choiceIndex — the on-screen number). The actual keystrokes
32093
+ * come from the spec's `submit_key` with `{index}` substituted, so the spec
32094
+ * — not this code — decides how a selection is keyed for each CLI.
32095
+ */
32096
+ async selectPickerChoice(ctl, action, choiceIndex, choiceLabel) {
32097
+ this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
32098
+ await this.waitForPickerRendered(action);
32099
+ const options = this.extractPickerChoices(action);
32100
+ let index = choiceIndex;
32101
+ if ((index == null || !Number.isFinite(index)) && choiceLabel) {
32102
+ const needle = choiceLabel.trim().toLowerCase();
32103
+ const match = options.find((o) => o.label.toLowerCase().includes(needle));
32104
+ if (!match) {
32105
+ return { ok: false, error: `choice not found on screen: ${choiceLabel}`, controlResult: { options: options.map((o) => ({ value: o.label, label: o.label })) } };
32106
+ }
32107
+ index = match.index;
32108
+ }
32109
+ if (index == null || !Number.isFinite(index)) {
32110
+ return { ok: false, error: "choiceIndex or choiceLabel required to select" };
32111
+ }
32112
+ const keys = (action.submit_key || "{index}\r").replace(/\{index\}/g, String(index));
32113
+ this.driver.dispatch({ kind: "pty_write", data: keys });
32114
+ const selected = options.find((o) => o.index === index);
32115
+ return {
32116
+ ok: true,
32117
+ effects: [{ type: "selected_choice", controlId: ctl.id }],
32118
+ controlResult: {
32119
+ ok: true,
32120
+ ...selected ? { currentValue: selected.label } : {},
32121
+ selectedIndex: index
32122
+ }
32123
+ };
32124
+ }
32125
+ /** Poll the live screen until the picker's `wait_for` condition matches,
32126
+ * up to a short budget. Returns true if it rendered, false on timeout. */
32127
+ async waitForPickerRendered(action) {
32128
+ const wf = action.wait_for;
32129
+ if (!wf?.regex) {
32130
+ await delay(250);
32131
+ return true;
32132
+ }
32133
+ const re = new RegExp(wf.regex, wf.flags ?? "i");
32134
+ const deadline = Date.now() + 2500;
32135
+ while (Date.now() < deadline) {
32136
+ await delay(120);
32137
+ const hay = this.readScreenSectionText(wf.section);
32138
+ if (re.test(hay)) return true;
32139
+ }
32140
+ return false;
32141
+ }
32142
+ /** Parse the picker's `extract_choices` pattern against the live screen.
32143
+ * Each match yields { index, label, current }. `current` is true for the
32144
+ * line the CLI marks with its cursor glyph (❯ ›). Purely screen-driven —
32145
+ * no model/mode names are baked in. */
32146
+ extractPickerChoices(action) {
32147
+ const ec = action.extract_choices;
32148
+ if (!ec?.pattern) return [];
32149
+ const text = this.readScreenSectionText(ec.section);
32150
+ const out = [];
32151
+ const seen = /* @__PURE__ */ new Set();
32152
+ for (const rawLine of text.split("\n")) {
32153
+ const line = rawLine.replace(/\r$/, "");
32154
+ const m = new RegExp(ec.pattern, ec.flags ?? "").exec(line);
32155
+ if (!m) continue;
32156
+ const idx = Number(m[1]);
32157
+ if (!Number.isFinite(idx) || seen.has(idx)) continue;
32158
+ const label = (m[2] ?? "").replace(/\s+/g, " ").trim();
32159
+ if (!label) continue;
32160
+ const current = /^\s*[❯›>]/.test(line) || /[✔✓●]\s*$/.test(label);
32161
+ seen.add(idx);
32162
+ out.push({ index: idx, label, current });
32163
+ }
32164
+ return out;
32165
+ }
32166
+ /** Live text of a named screen section (or the whole screen when no
32167
+ * section is named), resolved from the driver's current sections. */
32168
+ readScreenSectionText(sectionId) {
32169
+ try {
32170
+ const sections = this.driver.getSections();
32171
+ if (sectionId && sections) {
32172
+ const hit = sections.find((s) => s.id === sectionId);
32173
+ if (hit) return hit.text;
32174
+ }
32175
+ return this.driver.getScreen();
32176
+ } catch {
32177
+ return "";
32178
+ }
31786
32179
  }
31787
32180
  getDebugSnapshot() {
31788
32181
  let screen = "";
@@ -42003,6 +42396,43 @@ function readMeshTimeoutEnvMs(name, defaultMs) {
42003
42396
  }
42004
42397
  var MESH_DIRECT_PROBE_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_TIMEOUT_MS", 25e3);
42005
42398
  var MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS", 25e3);
42399
+ var MESH_DIRECT_PROBE_REUSE_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_REUSE_MS", 12e3);
42400
+ var MeshGitProbeCache = class {
42401
+ constructor(reuseMs, now = Date.now) {
42402
+ this.reuseMs = reuseMs;
42403
+ this.now = now;
42404
+ }
42405
+ inflight = /* @__PURE__ */ new Map();
42406
+ recent = /* @__PURE__ */ new Map();
42407
+ key(daemonId, workspace) {
42408
+ return `${daemonId}::${workspace}`;
42409
+ }
42410
+ /**
42411
+ * Run `probe` for this peer, but reuse a fresh recent result or an in-flight
42412
+ * probe for the same key when one is available. `probe` is only invoked when
42413
+ * neither gate is satisfied.
42414
+ */
42415
+ async probe(daemonId, workspace, probe) {
42416
+ const key = this.key(daemonId, workspace);
42417
+ const cached2 = this.recent.get(key);
42418
+ if (cached2 && this.now() - cached2.at < this.reuseMs) {
42419
+ return cached2.value;
42420
+ }
42421
+ const existing = this.inflight.get(key);
42422
+ if (existing) return existing;
42423
+ const pending = (async () => {
42424
+ const result = await probe();
42425
+ if (result) this.recent.set(key, { at: this.now(), value: result });
42426
+ return result;
42427
+ })();
42428
+ this.inflight.set(key, pending);
42429
+ try {
42430
+ return await pending;
42431
+ } finally {
42432
+ if (this.inflight.get(key) === pending) this.inflight.delete(key);
42433
+ }
42434
+ }
42435
+ };
42006
42436
  async function probeRemoteMeshGitStatus(args) {
42007
42437
  if (!args.dispatchMeshCommand) return null;
42008
42438
  const remoteResult = await Promise.race([
@@ -42096,7 +42526,7 @@ async function hydrateInlineMeshDirectTruth(args) {
42096
42526
  continue;
42097
42527
  }
42098
42528
  peerAttemptedCount += 1;
42099
- const remoteGit = await probeRemoteMeshGitStatusWithRetry({
42529
+ const runProbe = () => probeRemoteMeshGitStatusWithRetry({
42100
42530
  dispatchMeshCommand: args.dispatchMeshCommand,
42101
42531
  daemonId,
42102
42532
  workspace,
@@ -42104,6 +42534,7 @@ async function hydrateInlineMeshDirectTruth(args) {
42104
42534
  retryTimeoutMs: MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
42105
42535
  getConnection: args.getMeshPeerConnectionStatus
42106
42536
  });
42537
+ const remoteGit = args.probeCache ? await args.probeCache.probe(daemonId, workspace, runProbe) : await runProbe();
42107
42538
  if (remoteGit) {
42108
42539
  recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
42109
42540
  peerConfirmedCount += 1;
@@ -43426,6 +43857,10 @@ var DaemonCommandRouter = class {
43426
43857
  inlineMeshCache = /* @__PURE__ */ new Map();
43427
43858
  /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
43428
43859
  aggregateMeshStatusCache = /* @__PURE__ */ new Map();
43860
+ /** Shared per-peer git_status probe dedup + recently-probed reuse gate.
43861
+ * Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
43862
+ * loop cannot storm a slow peer with back-to-back refreshUpstream probes. */
43863
+ meshGitProbeCache = new MeshGitProbeCache(MESH_DIRECT_PROBE_REUSE_MS);
43429
43864
  /** In-memory async Refinery jobs keyed by meshId:nodeId to reject/return duplicate in-flight requests. */
43430
43865
  runningRefineJobs = /* @__PURE__ */ new Map();
43431
43866
  /** Terminal async Refinery jobs preserve a clear answer after the worktree node has been removed. */
@@ -46328,7 +46763,8 @@ ${hintLines.join("\n")}` : "",
46328
46763
  getMeshPeerConnectionStatus: this.deps.getMeshPeerConnectionStatus,
46329
46764
  statusInstanceId: this.deps.statusInstanceId,
46330
46765
  localMachineId: loadConfig().machineId || "",
46331
- probeRemotePeers
46766
+ probeRemotePeers,
46767
+ probeCache: this.meshGitProbeCache
46332
46768
  });
46333
46769
  const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
46334
46770
  const sourceOfTruth = {
@@ -46932,6 +47368,22 @@ ${hintLines.join("\n")}` : "",
46932
47368
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
46933
47369
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
46934
47370
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
47371
+ {
47372
+ const meshRecordForForward = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
47373
+ const forwardNode = meshRecordForForward?.mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
47374
+ const nodeDaemonId = typeof forwardNode?.daemonId === "string" ? forwardNode.daemonId.trim() : void 0;
47375
+ const selfDaemonId = this.deps.statusInstanceId;
47376
+ const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
47377
+ if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
47378
+ const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : void 0;
47379
+ const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "refine_mesh_node", {
47380
+ ...typeof args === "object" && args !== null ? args : {},
47381
+ coordinatorDaemonId: callerCoordinatorDaemonId || selfDaemonId,
47382
+ _meshDirectDispatch: true
47383
+ });
47384
+ return forwarded ?? { success: false, error: "no response from remote node" };
47385
+ }
47386
+ }
46935
47387
  const isDryRun = args?.dryRun !== false && args?.execute !== true;
46936
47388
  if (isDryRun) {
46937
47389
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
@@ -47904,6 +48356,7 @@ ${ptyResult.output.slice(-2e3)}`);
47904
48356
  const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
47905
48357
  const localMachineId = loadConfig().machineId || "";
47906
48358
  const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
48359
+ const meshGitProbeCache = this.meshGitProbeCache;
47907
48360
  const directTruth = requireDirectPeerTruth ? await hydrateInlineMeshDirectTruth({
47908
48361
  mesh,
47909
48362
  meshSource: meshRecord.source,
@@ -47914,7 +48367,8 @@ ${ptyResult.output.slice(-2e3)}`);
47914
48367
  // Standing-state model: only an explicit refresh fans
47915
48368
  // out a blocking peer git probe. Default loads return
47916
48369
  // held truth so one slow peer can't block the graph.
47917
- probeRemotePeers: refreshRequested
48370
+ probeRemotePeers: refreshRequested,
48371
+ probeCache: meshGitProbeCache
47918
48372
  }) : {
47919
48373
  directEvidenceCount: 0,
47920
48374
  localConfirmedCount: 0,
@@ -48075,7 +48529,7 @@ ${ptyResult.output.slice(-2e3)}`);
48075
48529
  }
48076
48530
  remoteProbeApplied = true;
48077
48531
  } else if (refreshRequested && !isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
48078
- const remoteGit = await probeRemoteMeshGitStatusWithRetry({
48532
+ const runNodeProbe = () => probeRemoteMeshGitStatusWithRetry({
48079
48533
  dispatchMeshCommand: this.deps.dispatchMeshCommand,
48080
48534
  daemonId,
48081
48535
  workspace,
@@ -48086,6 +48540,7 @@ ${ptyResult.output.slice(-2e3)}`);
48086
48540
  status.connection = connection;
48087
48541
  }
48088
48542
  });
48543
+ const remoteGit = await meshGitProbeCache.probe(daemonId, workspace, runNodeProbe);
48089
48544
  if (remoteGit) {
48090
48545
  status.git = remoteGit;
48091
48546
  status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
@@ -56909,6 +57364,7 @@ export {
56909
57364
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
56910
57365
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
56911
57366
  DEFAULT_MESH_POLICY,
57367
+ DEFAULT_MESH_SCHEDULING_STRATEGY,
56912
57368
  DEFAULT_SESSION_HOST_APP_NAME,
56913
57369
  DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
56914
57370
  DEFAULT_SESSION_HOST_READY_TIMEOUT_MS,
@@ -56934,9 +57390,12 @@ export {
56934
57390
  InMemoryGitSnapshotStore,
56935
57391
  LOG,
56936
57392
  MAX_LEDGER_SLICE_LIMIT,
57393
+ MESH_CONVERGE_FAST_FORWARD_TAG,
57394
+ MESH_CONVERGE_REFINE_TAG,
56937
57395
  MESH_MISSION_STATUSES,
56938
57396
  MESH_REFINE_CONFIG_LOCATIONS,
56939
57397
  MESH_REFINE_CONFIG_SCHEMA,
57398
+ MESH_SCHEDULING_STRATEGIES,
56940
57399
  MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
56941
57400
  MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
56942
57401
  MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
@@ -57137,6 +57596,7 @@ export {
57137
57596
  normalizeManagedStatus,
57138
57597
  normalizeMeshCapabilityTags,
57139
57598
  normalizeMeshDaemonRole,
57599
+ normalizeMeshSchedulingStrategy,
57140
57600
  normalizeMeshTaskMode,
57141
57601
  normalizeMeshWorkerResult,
57142
57602
  normalizeMessageParts,
@@ -57174,7 +57634,9 @@ export {
57174
57634
  resetConfig,
57175
57635
  resetDebugRuntimeConfig,
57176
57636
  resetState,
57637
+ resolveAutoConvergeCodeChange,
57177
57638
  resolveChatMessageKind,
57639
+ resolveConvergeRequiredTags,
57178
57640
  resolveCurrentGlobalInstallSurface,
57179
57641
  resolveDebugRuntimeConfig,
57180
57642
  resolveDelegatedWorkerAutoApprove,
@@ -57182,6 +57644,7 @@ export {
57182
57644
  resolveGitRepository,
57183
57645
  resolveMeshHostStatus,
57184
57646
  resolveMeshRefineValidationPlan,
57647
+ resolveNodeSchedulingPriority,
57185
57648
  resolveSessionHostAppName,
57186
57649
  resolveSessionHostAppNameResolution,
57187
57650
  resolveWorktreePath,