@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.js CHANGED
@@ -31,6 +31,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
33
  // src/repo-mesh-types.ts
34
+ function normalizeMeshSchedulingStrategy(value) {
35
+ if (typeof value !== "string") return DEFAULT_MESH_SCHEDULING_STRATEGY;
36
+ const trimmed = value.trim();
37
+ return MESH_SCHEDULING_STRATEGIES.includes(trimmed) ? trimmed : DEFAULT_MESH_SCHEDULING_STRATEGY;
38
+ }
39
+ function resolveNodeSchedulingPriority(nodePolicy) {
40
+ const raw = Number(nodePolicy?.schedulingPriority);
41
+ return Number.isFinite(raw) ? raw : 0;
42
+ }
43
+ function resolveAutoConvergeCodeChange(policy) {
44
+ return policy?.autoConvergeCodeChange === true;
45
+ }
34
46
  function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy) {
35
47
  if (typeof nodePolicy?.delegatedWorkerAutoApprove === "boolean") {
36
48
  return nodePolicy.delegatedWorkerAutoApprove;
@@ -58,10 +70,19 @@ function resolveProviderMaxParallel(nodePolicy, providerType) {
58
70
  if (!Number.isFinite(raw) || raw < 0) return void 0;
59
71
  return Math.floor(raw);
60
72
  }
61
- var DEFAULT_MESH_POLICY;
73
+ var MESH_SCHEDULING_STRATEGIES, DEFAULT_MESH_SCHEDULING_STRATEGY, MESH_CONVERGE_REFINE_TAG, MESH_CONVERGE_FAST_FORWARD_TAG, DEFAULT_MESH_POLICY;
62
74
  var init_repo_mesh_types = __esm({
63
75
  "src/repo-mesh-types.ts"() {
64
76
  "use strict";
77
+ MESH_SCHEDULING_STRATEGIES = [
78
+ "first_eligible",
79
+ "least_loaded",
80
+ "round_robin",
81
+ "priority_only"
82
+ ];
83
+ DEFAULT_MESH_SCHEDULING_STRATEGY = "first_eligible";
84
+ MESH_CONVERGE_REFINE_TAG = "converge=refine";
85
+ MESH_CONVERGE_FAST_FORWARD_TAG = "converge=fast_forward";
65
86
  DEFAULT_MESH_POLICY = {
66
87
  requirePreTaskCheckpoint: false,
67
88
  requirePostTaskCheckpoint: true,
@@ -295,10 +316,10 @@ function readInjected(value) {
295
316
  }
296
317
  function getDaemonBuildInfo() {
297
318
  if (cached) return cached;
298
- const commit = readInjected(true ? "33fe83f72a43bccb1b4bec7744ee5c6d8c11a325" : void 0) ?? "unknown";
299
- const commitShort = readInjected(true ? "33fe83f7" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
300
- const version = readInjected(true ? "0.9.82-rc.310" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
301
- const builtAt = readInjected(true ? "2026-06-17T09:34:54.806Z" : void 0);
319
+ const commit = readInjected(true ? "3902f3d63eabe7b9e34d0e849dc0fed2fd66c026" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "3902f3d6" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.311" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-17T12:17:11.243Z" : void 0);
302
323
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
303
324
  return cached;
304
325
  }
@@ -1545,6 +1566,17 @@ function mergeMeshPolicy(base, patch) {
1545
1566
  if (!SPAWNED_SESSION_VISIBILITY_MODES.has(String(policy.spawnedSessionVisibility))) {
1546
1567
  policy.spawnedSessionVisibility = "visible";
1547
1568
  }
1569
+ const normalizedStrategy = normalizeMeshSchedulingStrategy(policy.schedulingStrategy);
1570
+ if (normalizedStrategy === "first_eligible") {
1571
+ delete policy.schedulingStrategy;
1572
+ } else {
1573
+ policy.schedulingStrategy = normalizedStrategy;
1574
+ }
1575
+ if (policy.autoConvergeCodeChange === true) {
1576
+ policy.autoConvergeCodeChange = true;
1577
+ } else {
1578
+ delete policy.autoConvergeCodeChange;
1579
+ }
1548
1580
  return policy;
1549
1581
  }
1550
1582
  function normalizeAutoFastForwardPolicy(value) {
@@ -2838,6 +2870,7 @@ __export(mesh_work_queue_exports, {
2838
2870
  recordMeshToolCall: () => recordMeshToolCall,
2839
2871
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
2840
2872
  requeueTask: () => requeueTask,
2873
+ resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
2841
2874
  updateDirectDispatchStatus: () => updateDirectDispatchStatus,
2842
2875
  updateSessionTaskStatus: () => updateSessionTaskStatus,
2843
2876
  updateTaskStatus: () => updateTaskStatus,
@@ -2911,6 +2944,21 @@ function firstProviderPriority(policy) {
2911
2944
  if (!Array.isArray(raw)) return void 0;
2912
2945
  return raw.find((type) => typeof type === "string" && type.trim())?.trim();
2913
2946
  }
2947
+ function roleCapabilityTags(policy, providerType) {
2948
+ const roles = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerRoles : void 0;
2949
+ if (!Array.isArray(roles)) return [];
2950
+ const wantedProvider = typeof providerType === "string" && providerType.trim() ? providerType.trim().toLowerCase() : "";
2951
+ const out = [];
2952
+ for (const entry of roles) {
2953
+ if (!entry || typeof entry !== "object") continue;
2954
+ const type = typeof entry.providerType === "string" ? entry.providerType.trim().toLowerCase() : "";
2955
+ const role = typeof entry.role === "string" ? entry.role.trim().toLowerCase() : "";
2956
+ if (!role) continue;
2957
+ if (wantedProvider && type && type !== wantedProvider) continue;
2958
+ out.push(`role=${role}`);
2959
+ }
2960
+ return out;
2961
+ }
2914
2962
  function buildMeshNodeCapabilityTags(node, providerType) {
2915
2963
  const provider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : firstProviderPriority(node?.policy);
2916
2964
  const worktreeBranch = typeof node?.worktreeBranch === "string" && node.worktreeBranch.trim() ? node.worktreeBranch.trim() : null;
@@ -2922,7 +2970,25 @@ function buildMeshNodeCapabilityTags(node, providerType) {
2922
2970
  // Worktree nodes automatically expose a "worktree=<branch>" tag so that
2923
2971
  // mesh_enqueue_task with required_tags: ["worktree=<branch>"] routes
2924
2972
  // only to the matching worktree node.
2925
- ...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : []
2973
+ ...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : [],
2974
+ // Convergence routing: advertise how this node can land its work onto base.
2975
+ // - converge=refine: local worktree nodes (on ANY machine — refine_mesh_node
2976
+ // now forwards to the owning daemon) can run the Refinery merge → push →
2977
+ // cleanup against their own checkout, so they accept code_change tasks.
2978
+ // - converge=fast_forward: non-worktree nodes (the machine itself) can only
2979
+ // ff/push an already-converged branch; they are NOT a destination for
2980
+ // code_change work (a worktree is created first, and that worktree node
2981
+ // receives the task instead). Reuses the ordinary required-tags filter —
2982
+ // the load-balancing scheduler auto-injects converge=refine for code_change
2983
+ // so such work is hard-filtered onto refine-capable nodes.
2984
+ ...node?.isLocalWorktree === true ? ["converge=refine"] : ["converge=fast_forward"],
2985
+ // Role-based routing: advertise role=<x> for each (node, provider) role
2986
+ // declared in policy.providerRoles. Narrowed to the selected provider when
2987
+ // one is given so the chosen provider must match a task's required role;
2988
+ // when no provider is selected, all declared roles are advertised for the
2989
+ // node-level eligibility scan. Reuses the ordinary required-tags filter —
2990
+ // no separate role field/gate.
2991
+ ...roleCapabilityTags(node?.policy, providerType)
2926
2992
  ]);
2927
2993
  }
2928
2994
  function nodeSatisfiesRequiredTags(requiredTags, capabilityTags) {
@@ -2931,6 +2997,18 @@ function nodeSatisfiesRequiredTags(requiredTags, capabilityTags) {
2931
2997
  const available = new Set(normalizeMeshCapabilityTags(capabilityTags));
2932
2998
  return required.every((tag) => available.has(tag));
2933
2999
  }
3000
+ function resolveConvergeRequiredTags(meshId, taskMode, explicitRequiredTags, opts) {
3001
+ if (taskMode !== "code_change") return explicitRequiredTags;
3002
+ if (typeof opts?.targetNodeId === "string" && opts.targetNodeId.trim()) return explicitRequiredTags;
3003
+ let optedIn = false;
3004
+ try {
3005
+ optedIn = resolveAutoConvergeCodeChange(getMesh(meshId)?.policy);
3006
+ } catch {
3007
+ optedIn = false;
3008
+ }
3009
+ if (!optedIn) return explicitRequiredTags;
3010
+ return normalizeMeshCapabilityTags([...explicitRequiredTags, MESH_CONVERGE_REFINE_TAG]);
3011
+ }
2934
3012
  function withQueueLock(_meshId, fn) {
2935
3013
  return MeshRuntimeStore.getInstance().transaction(fn);
2936
3014
  }
@@ -2989,7 +3067,15 @@ function enqueueTask(meshId, message, opts) {
2989
3067
  taskMode: modeValidation.taskMode,
2990
3068
  targetNodeId: opts?.targetNodeId,
2991
3069
  targetSessionId: opts?.targetSessionId,
2992
- requiredTags: normalizeMeshCapabilityTags(opts?.requiredTags),
3070
+ // Convergence routing (opt-in): auto-inject converge=refine for code_change
3071
+ // tasks so they hard-filter onto refine-capable worktree nodes. No-op unless
3072
+ // the mesh opts in; explicit target_node_id / required_tags are preserved.
3073
+ requiredTags: resolveConvergeRequiredTags(
3074
+ meshId,
3075
+ modeValidation.taskMode,
3076
+ normalizeMeshCapabilityTags(opts?.requiredTags),
3077
+ { targetNodeId: opts?.targetNodeId }
3078
+ ),
2993
3079
  ...dependsOn.length > 0 ? { dependsOn } : {},
2994
3080
  ...typeof opts?.missionId === "string" && opts.missionId.trim() ? { missionId: opts.missionId.trim() } : {},
2995
3081
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -3250,6 +3336,7 @@ var init_mesh_work_queue = __esm({
3250
3336
  "use strict";
3251
3337
  import_crypto5 = require("crypto");
3252
3338
  init_mesh_host_ownership();
3339
+ init_repo_mesh_types();
3253
3340
  init_mesh_runtime_store();
3254
3341
  init_mesh_config();
3255
3342
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
@@ -3544,6 +3631,17 @@ var init_mesh_runtime_store = __esm({
3544
3631
 
3545
3632
  CREATE INDEX IF NOT EXISTS idx_mesh_missions_mesh_status
3546
3633
  ON mesh_missions(mesh_id, status, updated_at);
3634
+
3635
+ -- Load-balancing scheduler: per-mesh round-robin rotation cursor. When
3636
+ -- the schedulingStrategy is 'round_robin', several eligible nodes tied at
3637
+ -- the least load are rotated by this cursor so the tie-break winner cycles
3638
+ -- across scheduling passes instead of always favouring the same array-order
3639
+ -- node. Persisted (not a module Map) so rotation survives daemon restarts
3640
+ -- and stays a single source of truth across scheduling entry points.
3641
+ CREATE TABLE IF NOT EXISTS mesh_scheduler_cursor (
3642
+ mesh_id TEXT PRIMARY KEY,
3643
+ cursor INTEGER NOT NULL DEFAULT 0
3644
+ );
3547
3645
  `);
3548
3646
  }
3549
3647
  hasCompletionFingerprint(fingerprint) {
@@ -3721,6 +3819,44 @@ var init_mesh_runtime_store = __esm({
3721
3819
  `).get(meshId, nodeId);
3722
3820
  return row !== void 0;
3723
3821
  }
3822
+ /**
3823
+ * Count active (status='assigned') tasks on a node, regardless of provider or
3824
+ * task mode. This is the load metric for least-loaded / round-robin ranking:
3825
+ * the scheduler prefers the node with the fewest active assignments so
3826
+ * untargeted work spreads instead of piling onto whichever node asks first.
3827
+ */
3828
+ nodeActiveAssignmentCount(meshId, nodeId) {
3829
+ const row = this.db.prepare(`
3830
+ SELECT COUNT(*) as count FROM mesh_queue
3831
+ WHERE mesh_id = ? AND status = 'assigned' AND assigned_node_id = ?
3832
+ `).get(meshId, nodeId);
3833
+ return row?.count ?? 0;
3834
+ }
3835
+ /**
3836
+ * Read the current per-mesh round-robin cursor (0 when unset). Used to rotate
3837
+ * the tie-break winner among nodes tied at the least load.
3838
+ */
3839
+ getSchedulerCursor(meshId) {
3840
+ const row = this.db.prepare(
3841
+ "SELECT cursor FROM mesh_scheduler_cursor WHERE mesh_id = ?"
3842
+ ).get(meshId);
3843
+ return row?.cursor ?? 0;
3844
+ }
3845
+ /**
3846
+ * Atomically advance the per-mesh round-robin cursor by one and return the
3847
+ * value that was current BEFORE the bump (the value the caller should rotate
3848
+ * by for this pass). UPSERT keeps it lock-free across concurrent passes.
3849
+ */
3850
+ bumpSchedulerCursor(meshId) {
3851
+ return this.transaction(() => {
3852
+ const current = this.getSchedulerCursor(meshId);
3853
+ this.db.prepare(`
3854
+ INSERT INTO mesh_scheduler_cursor (mesh_id, cursor) VALUES (?, ?)
3855
+ ON CONFLICT(mesh_id) DO UPDATE SET cursor = excluded.cursor
3856
+ `).run(meshId, current + 1);
3857
+ return current;
3858
+ });
3859
+ }
3724
3860
  /**
3725
3861
  * Count active (status='assigned') tasks on a (node, provider) combination,
3726
3862
  * matched by the assignedProviderType stamped on the payload at claim time.
@@ -8292,6 +8428,33 @@ function activeReadonlyAssignedCount(meshId) {
8292
8428
  function nodeHasActiveAssignment(meshId, nodeId) {
8293
8429
  return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
8294
8430
  }
8431
+ function nodeActiveLoad(meshId, nodeId) {
8432
+ return MeshRuntimeStore.getInstance().nodeActiveAssignmentCount(meshId, nodeId);
8433
+ }
8434
+ function resolveSchedulingStrategy(mesh) {
8435
+ return normalizeMeshSchedulingStrategy(mesh?.policy?.schedulingStrategy);
8436
+ }
8437
+ function orderEligibleNodes(meshId, strategy, nodes, opts) {
8438
+ if (strategy === "first_eligible" || nodes.length <= 1) {
8439
+ return nodes;
8440
+ }
8441
+ const priorityOf = (n) => resolveNodeSchedulingPriority(n.node?.policy);
8442
+ let rotation = 0;
8443
+ if (strategy === "round_robin") {
8444
+ const cursor = opts?.bumpCursor ? MeshRuntimeStore.getInstance().bumpSchedulerCursor(meshId) : MeshRuntimeStore.getInstance().getSchedulerCursor(meshId);
8445
+ rotation = (cursor % nodes.length + nodes.length) % nodes.length;
8446
+ }
8447
+ const rotationRank = (index) => (index - rotation + nodes.length) % nodes.length;
8448
+ return [...nodes].sort((a, b) => {
8449
+ const prioDelta = priorityOf(b) - priorityOf(a);
8450
+ if (prioDelta !== 0) return prioDelta;
8451
+ if (strategy === "least_loaded" || strategy === "round_robin") {
8452
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
8453
+ if (loadDelta !== 0) return loadDelta;
8454
+ }
8455
+ return rotationRank(a.index) - rotationRank(b.index);
8456
+ });
8457
+ }
8295
8458
  function activeProviderAssignedCount(meshId, nodeId, providerType) {
8296
8459
  return getQueue(meshId, { status: ["assigned"] }).filter((task) => task.assignedNodeId === nodeId && task.assignedProviderType === providerType).length;
8297
8460
  }
@@ -8428,7 +8591,14 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
8428
8591
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
8429
8592
  continue;
8430
8593
  }
8431
- for (const node of candidateNodes) {
8594
+ const strategy = resolveSchedulingStrategy(mesh);
8595
+ const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
8596
+ meshId,
8597
+ strategy,
8598
+ candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
8599
+ { bumpCursor: true }
8600
+ ).map((c) => c.node);
8601
+ for (const node of orderedCandidateNodes) {
8432
8602
  const nodeId = readMeshNodeId(node);
8433
8603
  if (!nodeId) continue;
8434
8604
  const launchKey = `${meshId}:${nodeId}`;
@@ -8555,6 +8725,8 @@ async function triggerMeshQueue(components, meshId) {
8555
8725
  noIdleMeshSessionAvailable: true
8556
8726
  };
8557
8727
  }
8728
+ const strategy = resolveSchedulingStrategy(mesh);
8729
+ const localCandidates = [];
8558
8730
  const cliInstances = components.instanceManager.getByCategory("cli");
8559
8731
  for (const inst of cliInstances) {
8560
8732
  const state = inst.getState();
@@ -8577,7 +8749,7 @@ async function triggerMeshQueue(components, meshId) {
8577
8749
  const providerType = state.type || readNonEmptyString2(settings.providerType);
8578
8750
  if (providerType) {
8579
8751
  localIdleSessionsChecked += 1;
8580
- tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
8752
+ localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) => readMeshNodeId(n) === nodeId) });
8581
8753
  } else {
8582
8754
  skippedSessions.push({
8583
8755
  nodeId,
@@ -8591,18 +8763,49 @@ async function triggerMeshQueue(components, meshId) {
8591
8763
  remoteSessions = MeshRuntimeStore.getInstance().getRemoteIdleSessions();
8592
8764
  } catch {
8593
8765
  }
8766
+ const remoteCandidates = [];
8594
8767
  for (const idle of remoteSessions) {
8595
8768
  const node = mesh.nodes.find((n) => n.id === idle.nodeId);
8596
8769
  if (node) {
8597
8770
  remoteIdleSessionsChecked += 1;
8598
- const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
8599
- if (assigned) {
8600
- try {
8601
- MeshRuntimeStore.getInstance().deleteRemoteIdleSession(idle.nodeId, idle.sessionId);
8602
- } catch {
8603
- }
8771
+ remoteCandidates.push({ nodeId: idle.nodeId, sessionId: idle.sessionId, providerType: idle.providerType, origin: "remote", node });
8772
+ }
8773
+ }
8774
+ const assignIdleCandidate = (candidate) => {
8775
+ const assigned = tryAssignQueueTask(components, meshId, candidate.nodeId, candidate.sessionId, candidate.providerType);
8776
+ if (assigned && candidate.origin === "remote") {
8777
+ try {
8778
+ MeshRuntimeStore.getInstance().deleteRemoteIdleSession(candidate.nodeId, candidate.sessionId);
8779
+ } catch {
8604
8780
  }
8605
8781
  }
8782
+ };
8783
+ if (strategy === "first_eligible") {
8784
+ for (const candidate of localCandidates) assignIdleCandidate(candidate);
8785
+ for (const candidate of remoteCandidates) assignIdleCandidate(candidate);
8786
+ } else {
8787
+ const pool = [...localCandidates, ...remoteCandidates];
8788
+ const baseIndex = /* @__PURE__ */ new Map();
8789
+ pool.forEach((c, i) => {
8790
+ if (!baseIndex.has(c.nodeId)) baseIndex.set(c.nodeId, i);
8791
+ });
8792
+ const uniqueNodes = [...new Set(pool.map((c) => c.nodeId))].map((nodeId, index) => ({ nodeId, node: pool.find((c) => c.nodeId === nodeId)?.node, index }));
8793
+ const ranked = orderEligibleNodes(meshId, strategy, uniqueNodes, { bumpCursor: true });
8794
+ const rankIndex = new Map(ranked.map((r, i) => [r.nodeId, i]));
8795
+ const remaining = [...pool];
8796
+ while (remaining.length > 0) {
8797
+ remaining.sort((a, b) => {
8798
+ const aPrio = resolveNodeSchedulingPriority(a.node?.policy);
8799
+ const bPrio = resolveNodeSchedulingPriority(b.node?.policy);
8800
+ if (aPrio !== bPrio) return bPrio - aPrio;
8801
+ if (strategy === "least_loaded" || strategy === "round_robin") {
8802
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
8803
+ if (loadDelta !== 0) return loadDelta;
8804
+ }
8805
+ return (rankIndex.get(a.nodeId) ?? 0) - (rankIndex.get(b.nodeId) ?? 0);
8806
+ });
8807
+ assignIdleCandidate(remaining.shift());
8808
+ }
8606
8809
  }
8607
8810
  autoLaunchStarted = await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
8608
8811
  const afterQueue = getQueue(meshId);
@@ -11976,7 +12179,7 @@ var init_cli_state_engine = __esm({
11976
12179
  scheduleSettle() {
11977
12180
  if (this.settleTimer) clearTimeout(this.settleTimer);
11978
12181
  const epoch = this.responseEpoch;
11979
- const delay = Math.max(
12182
+ const delay2 = Math.max(
11980
12183
  this.timeouts.outputSettle,
11981
12184
  this.submitPendingUntil > Date.now() ? this.submitPendingUntil - Date.now() + this.timeouts.outputSettle : 0
11982
12185
  );
@@ -11984,7 +12187,7 @@ var init_cli_state_engine = __esm({
11984
12187
  this.settleTimer = null;
11985
12188
  if (epoch !== this.responseEpoch) return;
11986
12189
  this.evaluateSettled(this.transport.getSnapshot());
11987
- }, delay);
12190
+ }, delay2);
11988
12191
  }
11989
12192
  /** Called from sendMessage in transport once a turn scope is established. */
11990
12193
  onTurnStarted(turnScope) {
@@ -15492,6 +15695,7 @@ __export(index_exports, {
15492
15695
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS: () => DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
15493
15696
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
15494
15697
  DEFAULT_MESH_POLICY: () => DEFAULT_MESH_POLICY,
15698
+ DEFAULT_MESH_SCHEDULING_STRATEGY: () => DEFAULT_MESH_SCHEDULING_STRATEGY,
15495
15699
  DEFAULT_SESSION_HOST_APP_NAME: () => DEFAULT_SESSION_HOST_APP_NAME,
15496
15700
  DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
15497
15701
  DEFAULT_SESSION_HOST_READY_TIMEOUT_MS: () => DEFAULT_SESSION_HOST_READY_TIMEOUT_MS,
@@ -15517,9 +15721,12 @@ __export(index_exports, {
15517
15721
  InMemoryGitSnapshotStore: () => InMemoryGitSnapshotStore,
15518
15722
  LOG: () => LOG,
15519
15723
  MAX_LEDGER_SLICE_LIMIT: () => MAX_LEDGER_SLICE_LIMIT,
15724
+ MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
15725
+ MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
15520
15726
  MESH_MISSION_STATUSES: () => MESH_MISSION_STATUSES,
15521
15727
  MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
15522
15728
  MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
15729
+ MESH_SCHEDULING_STRATEGIES: () => MESH_SCHEDULING_STRATEGIES,
15523
15730
  MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
15524
15731
  MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
15525
15732
  MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
@@ -15720,6 +15927,7 @@ __export(index_exports, {
15720
15927
  normalizeManagedStatus: () => normalizeManagedStatus,
15721
15928
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
15722
15929
  normalizeMeshDaemonRole: () => normalizeMeshDaemonRole,
15930
+ normalizeMeshSchedulingStrategy: () => normalizeMeshSchedulingStrategy,
15723
15931
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
15724
15932
  normalizeMeshWorkerResult: () => normalizeMeshWorkerResult,
15725
15933
  normalizeMessageParts: () => normalizeMessageParts,
@@ -15757,7 +15965,9 @@ __export(index_exports, {
15757
15965
  resetConfig: () => resetConfig,
15758
15966
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
15759
15967
  resetState: () => resetState,
15968
+ resolveAutoConvergeCodeChange: () => resolveAutoConvergeCodeChange,
15760
15969
  resolveChatMessageKind: () => resolveChatMessageKind,
15970
+ resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
15761
15971
  resolveCurrentGlobalInstallSurface: () => resolveCurrentGlobalInstallSurface,
15762
15972
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
15763
15973
  resolveDelegatedWorkerAutoApprove: () => resolveDelegatedWorkerAutoApprove,
@@ -15765,6 +15975,7 @@ __export(index_exports, {
15765
15975
  resolveGitRepository: () => resolveGitRepository,
15766
15976
  resolveMeshHostStatus: () => resolveMeshHostStatus,
15767
15977
  resolveMeshRefineValidationPlan: () => resolveMeshRefineValidationPlan,
15978
+ resolveNodeSchedulingPriority: () => resolveNodeSchedulingPriority,
15768
15979
  resolveSessionHostAppName: () => resolveSessionHostAppName,
15769
15980
  resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution,
15770
15981
  resolveWorktreePath: () => resolveWorktreePath,
@@ -18139,9 +18350,9 @@ function readString6(value) {
18139
18350
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
18140
18351
  }
18141
18352
  function summarizeMessage(message) {
18142
- const oneLine = message.replace(/\s+/g, " ").trim();
18143
- const title = oneLine.length > 96 ? `${oneLine.slice(0, 93)}...` : oneLine;
18144
- return { title: title || "(untitled task)", summary: oneLine };
18353
+ const oneLine2 = message.replace(/\s+/g, " ").trim();
18354
+ const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
18355
+ return { title: title || "(untitled task)", summary: oneLine2 };
18145
18356
  }
18146
18357
  function elapsedSince(value, now) {
18147
18358
  const started = value ? new Date(value).getTime() : Number.NaN;
@@ -21247,6 +21458,11 @@ function collapseReplayAssistantTurns(messages, historyBehavior) {
21247
21458
  continue;
21248
21459
  }
21249
21460
  if (message.role === "assistant") {
21461
+ const isActivity = message.kind === "tool" || message.kind === "terminal" || message.kind === "thought";
21462
+ if (isActivity) {
21463
+ collapsed.push(message);
21464
+ continue;
21465
+ }
21250
21466
  if (sawAssistantSinceLastUser) continue;
21251
21467
  sawAssistantSinceLastUser = true;
21252
21468
  collapsed.push(message);
@@ -26390,7 +26606,8 @@ function buildReadChatCommandResult(payload, args, h) {
26390
26606
  const sessionIdHint = typeof args?.targetSessionId === "string" ? args.targetSessionId : typeof args?.sessionId === "string" ? args.sessionId : "";
26391
26607
  const providerHint = typeof args?.cliType === "string" ? args.cliType : typeof args?.providerType === "string" ? args.providerType : typeof args?.agentType === "string" ? args.agentType : "";
26392
26608
  const filteredMessages = h ? maybeHideCoordinatorPromptMessage(h, providerHint, sessionIdHint, messages) : messages;
26393
- const visibleMessages = filterUserFacingChatMessages(filteredMessages);
26609
+ const includeActivity = args?.includeActivity === true || args?.includeActivity === "true";
26610
+ const visibleMessages = includeActivity ? filteredMessages.filter((m) => isUserFacingChatMessage(m) || isActivityChatMessage(m)) : filterUserFacingChatMessages(filteredMessages);
26394
26611
  const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
26395
26612
  const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
26396
26613
  const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
@@ -28759,6 +28976,9 @@ function normalizeProviderScriptArgs(args, scriptName) {
28759
28976
  }
28760
28977
  function buildControlScriptResult(scriptName, payload) {
28761
28978
  if (!payload || typeof payload !== "object") return {};
28979
+ if (payload.controlResult && typeof payload.controlResult === "object") {
28980
+ return { controlResult: payload.controlResult };
28981
+ }
28762
28982
  const legacyListPayload = (() => {
28763
28983
  if (Array.isArray(payload.options)) return payload;
28764
28984
  if (/^listmodels$/i.test(scriptName) && Array.isArray(payload.models)) {
@@ -30609,12 +30829,12 @@ var FsmDriver = class {
30609
30829
  scheduleSpawnPrime() {
30610
30830
  const seqs = this.spec.send_on_spawn;
30611
30831
  if (!Array.isArray(seqs) || seqs.length === 0) return;
30612
- const delay = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
30832
+ const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
30613
30833
  setTimeout(() => {
30614
30834
  for (const seq of seqs) {
30615
30835
  if (typeof seq === "string" && seq.length > 0) this.adapter.send_keys(seq);
30616
30836
  }
30617
- }, delay);
30837
+ }, delay2);
30618
30838
  }
30619
30839
  dispatch(cmd) {
30620
30840
  switch (cmd.kind) {
@@ -30980,11 +31200,11 @@ var FsmDriver = class {
30980
31200
  const armed = this.delegateTimers.has(d.id);
30981
31201
  const shouldFire = d.when_state === currentStateId;
30982
31202
  if (shouldFire && !armed) {
30983
- const delay = d.after_duration_ms ?? 0;
31203
+ const delay2 = d.after_duration_ms ?? 0;
30984
31204
  const t = setTimeout(() => {
30985
31205
  this.fireDelegate(d);
30986
31206
  this.delegateTimers.delete(d.id);
30987
- }, delay);
31207
+ }, delay2);
30988
31208
  this.delegateTimers.set(d.id, t);
30989
31209
  } else if (!shouldFire && armed) {
30990
31210
  clearTimeout(this.delegateTimers.get(d.id));
@@ -31038,7 +31258,15 @@ var FsmDriver = class {
31038
31258
  this.adapter.send_keys(a.keys);
31039
31259
  return;
31040
31260
  case "open_picker":
31041
- this.adapter.send_keys(a.trigger_keys);
31261
+ {
31262
+ const m = /^([\s\S]*?)([\r\n]+)$/.exec(a.trigger_keys);
31263
+ if (m && m[1]) {
31264
+ this.adapter.send_keys(m[1]);
31265
+ setTimeout(() => this.adapter.send_keys(m[2]), 200);
31266
+ } else {
31267
+ this.adapter.send_keys(a.trigger_keys);
31268
+ }
31269
+ }
31042
31270
  this.pickerInProgress = { control_id: ctl.id, spec: ctl };
31043
31271
  return;
31044
31272
  case "attach_image": {
@@ -31223,8 +31451,7 @@ function executeJsonl(src, input) {
31223
31451
  for (let i = 0; i < lines.length; i += 1) {
31224
31452
  const rec = lines[i];
31225
31453
  if (filter && !filter(rec)) continue;
31226
- const msg = projectMessage(rec, src.message_map, i, lines.length, mtime);
31227
- if (msg) {
31454
+ for (const msg of projectMessages(rec, src.message_map, i, lines.length, mtime)) {
31228
31455
  if (transcriptWorkspace) msg.workspace = transcriptWorkspace;
31229
31456
  messages.push(msg);
31230
31457
  }
@@ -31304,8 +31531,9 @@ function executeSqlite(src, input) {
31304
31531
  const mtime = safeMtimeMs(resolved);
31305
31532
  const messages = [];
31306
31533
  for (let i = 0; i < messageRows.length; i += 1) {
31307
- const msg = projectMessage(messageRows[i], src.message_map, i, messageRows.length, mtime);
31308
- if (msg) messages.push(msg);
31534
+ for (const msg of projectMessages(messageRows[i], src.message_map, i, messageRows.length, mtime)) {
31535
+ messages.push(msg);
31536
+ }
31309
31537
  }
31310
31538
  if (messages.length === 0) return null;
31311
31539
  return {
@@ -31703,11 +31931,42 @@ function jsonPathGet(record, expr) {
31703
31931
  }
31704
31932
  return cur;
31705
31933
  }
31706
- function projectMessage(record, map, index, total, sourceMtimeMs) {
31934
+ function projectMessages(record, map, index, total, sourceMtimeMs) {
31707
31935
  const roleRaw = jsonPathGet(record, map.role);
31708
- const contentRaw = jsonPathGet(record, map.content);
31709
31936
  const role = normalizeRole(roleRaw);
31710
- let content = stringifyContent(contentRaw);
31937
+ let receivedAt = sourceMtimeMs - (total - 1 - index) * 1e3;
31938
+ if (map.timestamp_ms) {
31939
+ const tsRaw = jsonPathGet(record, map.timestamp_ms);
31940
+ const parsed = parseTimestamp(tsRaw);
31941
+ if (parsed != null) receivedAt = parsed;
31942
+ }
31943
+ const kindRaw = map.kind ? jsonPathGet(record, map.kind) : void 0;
31944
+ const kind = typeof kindRaw === "string" && kindRaw ? kindRaw : "standard";
31945
+ const out = [];
31946
+ if (map.tools) {
31947
+ const recordTool = projectToolBlock(record, role, map.tools);
31948
+ if (recordTool) {
31949
+ out.push({ ...recordTool, receivedAt });
31950
+ return out;
31951
+ }
31952
+ }
31953
+ const contentRaw = jsonPathGet(record, map.content);
31954
+ const content = cleanContent(stringifyContent(contentRaw), map);
31955
+ if (content) out.push({ role, content, receivedAt, kind });
31956
+ if (map.tools && Array.isArray(contentRaw)) {
31957
+ let nudge = 1;
31958
+ for (const block2 of contentRaw) {
31959
+ const tool = projectToolBlock(block2, role, map.tools);
31960
+ if (tool) {
31961
+ out.push({ ...tool, receivedAt: receivedAt + nudge });
31962
+ nudge += 1;
31963
+ }
31964
+ }
31965
+ }
31966
+ return out;
31967
+ }
31968
+ function cleanContent(input, map) {
31969
+ let content = input;
31711
31970
  if (content && map.content_strip) {
31712
31971
  for (const tag of map.content_strip) {
31713
31972
  const safeTag = tag.replace(/[.+^${}()|[\]\\]/g, "\\$&");
@@ -31723,17 +31982,33 @@ function projectMessage(record, map, index, total, sourceMtimeMs) {
31723
31982
  content = content.replace(open, "").replace(close, "");
31724
31983
  }
31725
31984
  }
31726
- if (content) content = content.trim();
31727
- if (!content) return null;
31728
- let receivedAt = sourceMtimeMs - (total - 1 - index) * 1e3;
31729
- if (map.timestamp_ms) {
31730
- const tsRaw = jsonPathGet(record, map.timestamp_ms);
31731
- const parsed = parseTimestamp(tsRaw);
31732
- if (parsed != null) receivedAt = parsed;
31985
+ return content ? content.trim() : "";
31986
+ }
31987
+ var DEFAULT_TOOL_CALL_TYPES = ["tool_use", "function_call", "custom_tool_call"];
31988
+ var DEFAULT_TOOL_RESULT_TYPES = ["tool_result", "function_call_output", "custom_tool_call_output"];
31989
+ function projectToolBlock(block2, role, tmap) {
31990
+ void role;
31991
+ if (block2 == null || typeof block2 !== "object") return null;
31992
+ const typeVal = String(jsonPathGet(block2, tmap.block_type || "$.type") ?? "");
31993
+ if (!typeVal) return null;
31994
+ const callTypes = tmap.call_types ?? DEFAULT_TOOL_CALL_TYPES;
31995
+ const resultTypes = tmap.result_types ?? DEFAULT_TOOL_RESULT_TYPES;
31996
+ if (callTypes.includes(typeVal)) {
31997
+ const name = String(jsonPathGet(block2, tmap.call_name || "$.name") ?? "tool").trim() || "tool";
31998
+ const args = oneLine(stringifyContent(jsonPathGet(block2, tmap.call_args || "$.input")), 240);
31999
+ const content = args ? `\u2197 ${name}: ${args}` : `\u2197 ${name}`;
32000
+ return { role: "assistant", content, receivedAt: 0, kind: "tool" };
32001
+ }
32002
+ if (resultTypes.includes(typeVal)) {
32003
+ const result = oneLine(stringifyContent(jsonPathGet(block2, tmap.result_content || "$.content")), 600);
32004
+ if (!result) return null;
32005
+ return { role: "assistant", content: `\u2198 ${result}`, receivedAt: 0, kind: "tool" };
31733
32006
  }
31734
- const kindRaw = map.kind ? jsonPathGet(record, map.kind) : void 0;
31735
- const kind = typeof kindRaw === "string" && kindRaw ? kindRaw : "standard";
31736
- return { role, content, receivedAt, kind };
32007
+ return null;
32008
+ }
32009
+ function oneLine(s, max) {
32010
+ const flat = s.replace(/\s+/g, " ").trim();
32011
+ return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
31737
32012
  }
31738
32013
  function parseTimestamp(v) {
31739
32014
  if (v == null) return null;
@@ -31874,6 +32149,9 @@ init_logger();
31874
32149
  function stripAnsi3(text) {
31875
32150
  return String(text || "").replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
31876
32151
  }
32152
+ function delay(ms) {
32153
+ return new Promise((resolve24) => setTimeout(resolve24, ms));
32154
+ }
31877
32155
  var SpecCliAdapter = class _SpecCliAdapter {
31878
32156
  cliType;
31879
32157
  cliName;
@@ -32095,9 +32373,15 @@ var SpecCliAdapter = class _SpecCliAdapter {
32095
32373
  * drives the dispatch:
32096
32374
  *
32097
32375
  * send_keys → click_control (e.g. stop)
32098
- * open_picker → click_control then resolve when extract_choices
32099
- * surface; choice index comes from args.choiceIndex
32100
- * or args.choice (string label match), defaulting to 0
32376
+ * open_picker → two roles, driven by the screen, not a hardcoded list:
32377
+ * - LIST (no choice arg): open the picker, wait for it
32378
+ * to render, parse the on-screen options via
32379
+ * `extract_choices`, and return them as
32380
+ * `controlResult.options` (+ `currentValue`). This is
32381
+ * how the dashboard's Model/Mode controls learn what is
32382
+ * actually selectable in this CLI right now.
32383
+ * - SELECT (args.choiceIndex / args.choiceLabel): drive
32384
+ * the picker to that option using `submit_key`.
32101
32385
  * attach_image → attach_image dispatch; expects args.blob (data url
32102
32386
  * or base64) and args.mime
32103
32387
  *
@@ -32122,11 +32406,128 @@ var SpecCliAdapter = class _SpecCliAdapter {
32122
32406
  this.driver.dispatch({ kind: "attach_image", blob, mime });
32123
32407
  return Promise.resolve({ ok: true, effects: [{ type: "attached_image", controlId: ctl.id }] });
32124
32408
  }
32409
+ if (action.type === "open_picker") {
32410
+ const choiceIndex = typeof flat.choiceIndex === "number" ? flat.choiceIndex : typeof flat.choiceIndex === "string" && flat.choiceIndex.trim() ? Number(flat.choiceIndex) : void 0;
32411
+ const choiceLabel = typeof flat.choiceLabel === "string" ? flat.choiceLabel : typeof flat.choice === "string" ? flat.choice : void 0;
32412
+ if (typeof choiceIndex === "number" && Number.isFinite(choiceIndex) || choiceLabel && choiceLabel.trim()) {
32413
+ return this.selectPickerChoice(ctl, action, choiceIndex, choiceLabel);
32414
+ }
32415
+ return this.openPickerAndListChoices(ctl, action);
32416
+ }
32125
32417
  this.driver.dispatch({ kind: "click_control", control_id: ctl.id, payload: flat });
32126
- const effects = [];
32127
- if (action.type === "open_picker") effects.push({ type: "opened_picker", controlId: ctl.id });
32128
- else if (action.type === "send_keys") effects.push({ type: "sent_keys", controlId: ctl.id });
32129
- return Promise.resolve({ ok: true, effects });
32418
+ return Promise.resolve({ ok: true, effects: [{ type: "sent_keys", controlId: ctl.id }] });
32419
+ }
32420
+ /**
32421
+ * Open an `open_picker` control and return the options the CLI is showing,
32422
+ * parsed live from the screen via `extract_choices`. Nothing is selected —
32423
+ * the picker is left open so a follow-up SELECT invoke can commit a choice.
32424
+ */
32425
+ async openPickerAndListChoices(ctl, action) {
32426
+ this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
32427
+ const ready = await this.waitForPickerRendered(action);
32428
+ const options = this.extractPickerChoices(action);
32429
+ const currentValue = options.find((o) => o.current)?.label;
32430
+ return {
32431
+ ok: true,
32432
+ effects: [{ type: "opened_picker", controlId: ctl.id }],
32433
+ controlResult: {
32434
+ options: options.map((o) => ({ value: o.label, label: o.label, current: o.current })),
32435
+ ...currentValue ? { currentValue } : {},
32436
+ source: "screen-parse",
32437
+ ...ready ? {} : { warning: "picker_render_timeout" }
32438
+ }
32439
+ };
32440
+ }
32441
+ /**
32442
+ * Drive an already-listable picker to a specific option. The option can be
32443
+ * named (choiceLabel — matched against the parsed on-screen labels) or
32444
+ * positional (choiceIndex — the on-screen number). The actual keystrokes
32445
+ * come from the spec's `submit_key` with `{index}` substituted, so the spec
32446
+ * — not this code — decides how a selection is keyed for each CLI.
32447
+ */
32448
+ async selectPickerChoice(ctl, action, choiceIndex, choiceLabel) {
32449
+ this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
32450
+ await this.waitForPickerRendered(action);
32451
+ const options = this.extractPickerChoices(action);
32452
+ let index = choiceIndex;
32453
+ if ((index == null || !Number.isFinite(index)) && choiceLabel) {
32454
+ const needle = choiceLabel.trim().toLowerCase();
32455
+ const match = options.find((o) => o.label.toLowerCase().includes(needle));
32456
+ if (!match) {
32457
+ return { ok: false, error: `choice not found on screen: ${choiceLabel}`, controlResult: { options: options.map((o) => ({ value: o.label, label: o.label })) } };
32458
+ }
32459
+ index = match.index;
32460
+ }
32461
+ if (index == null || !Number.isFinite(index)) {
32462
+ return { ok: false, error: "choiceIndex or choiceLabel required to select" };
32463
+ }
32464
+ const keys = (action.submit_key || "{index}\r").replace(/\{index\}/g, String(index));
32465
+ this.driver.dispatch({ kind: "pty_write", data: keys });
32466
+ const selected = options.find((o) => o.index === index);
32467
+ return {
32468
+ ok: true,
32469
+ effects: [{ type: "selected_choice", controlId: ctl.id }],
32470
+ controlResult: {
32471
+ ok: true,
32472
+ ...selected ? { currentValue: selected.label } : {},
32473
+ selectedIndex: index
32474
+ }
32475
+ };
32476
+ }
32477
+ /** Poll the live screen until the picker's `wait_for` condition matches,
32478
+ * up to a short budget. Returns true if it rendered, false on timeout. */
32479
+ async waitForPickerRendered(action) {
32480
+ const wf = action.wait_for;
32481
+ if (!wf?.regex) {
32482
+ await delay(250);
32483
+ return true;
32484
+ }
32485
+ const re = new RegExp(wf.regex, wf.flags ?? "i");
32486
+ const deadline = Date.now() + 2500;
32487
+ while (Date.now() < deadline) {
32488
+ await delay(120);
32489
+ const hay = this.readScreenSectionText(wf.section);
32490
+ if (re.test(hay)) return true;
32491
+ }
32492
+ return false;
32493
+ }
32494
+ /** Parse the picker's `extract_choices` pattern against the live screen.
32495
+ * Each match yields { index, label, current }. `current` is true for the
32496
+ * line the CLI marks with its cursor glyph (❯ ›). Purely screen-driven —
32497
+ * no model/mode names are baked in. */
32498
+ extractPickerChoices(action) {
32499
+ const ec = action.extract_choices;
32500
+ if (!ec?.pattern) return [];
32501
+ const text = this.readScreenSectionText(ec.section);
32502
+ const out = [];
32503
+ const seen = /* @__PURE__ */ new Set();
32504
+ for (const rawLine of text.split("\n")) {
32505
+ const line = rawLine.replace(/\r$/, "");
32506
+ const m = new RegExp(ec.pattern, ec.flags ?? "").exec(line);
32507
+ if (!m) continue;
32508
+ const idx = Number(m[1]);
32509
+ if (!Number.isFinite(idx) || seen.has(idx)) continue;
32510
+ const label = (m[2] ?? "").replace(/\s+/g, " ").trim();
32511
+ if (!label) continue;
32512
+ const current = /^\s*[❯›>]/.test(line) || /[✔✓●]\s*$/.test(label);
32513
+ seen.add(idx);
32514
+ out.push({ index: idx, label, current });
32515
+ }
32516
+ return out;
32517
+ }
32518
+ /** Live text of a named screen section (or the whole screen when no
32519
+ * section is named), resolved from the driver's current sections. */
32520
+ readScreenSectionText(sectionId) {
32521
+ try {
32522
+ const sections = this.driver.getSections();
32523
+ if (sectionId && sections) {
32524
+ const hit = sections.find((s) => s.id === sectionId);
32525
+ if (hit) return hit.text;
32526
+ }
32527
+ return this.driver.getScreen();
32528
+ } catch {
32529
+ return "";
32530
+ }
32130
32531
  }
32131
32532
  getDebugSnapshot() {
32132
32533
  let screen = "";
@@ -42342,6 +42743,43 @@ function readMeshTimeoutEnvMs(name, defaultMs) {
42342
42743
  }
42343
42744
  var MESH_DIRECT_PROBE_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_TIMEOUT_MS", 25e3);
42344
42745
  var MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS", 25e3);
42746
+ var MESH_DIRECT_PROBE_REUSE_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_REUSE_MS", 12e3);
42747
+ var MeshGitProbeCache = class {
42748
+ constructor(reuseMs, now = Date.now) {
42749
+ this.reuseMs = reuseMs;
42750
+ this.now = now;
42751
+ }
42752
+ inflight = /* @__PURE__ */ new Map();
42753
+ recent = /* @__PURE__ */ new Map();
42754
+ key(daemonId, workspace) {
42755
+ return `${daemonId}::${workspace}`;
42756
+ }
42757
+ /**
42758
+ * Run `probe` for this peer, but reuse a fresh recent result or an in-flight
42759
+ * probe for the same key when one is available. `probe` is only invoked when
42760
+ * neither gate is satisfied.
42761
+ */
42762
+ async probe(daemonId, workspace, probe) {
42763
+ const key = this.key(daemonId, workspace);
42764
+ const cached2 = this.recent.get(key);
42765
+ if (cached2 && this.now() - cached2.at < this.reuseMs) {
42766
+ return cached2.value;
42767
+ }
42768
+ const existing = this.inflight.get(key);
42769
+ if (existing) return existing;
42770
+ const pending = (async () => {
42771
+ const result = await probe();
42772
+ if (result) this.recent.set(key, { at: this.now(), value: result });
42773
+ return result;
42774
+ })();
42775
+ this.inflight.set(key, pending);
42776
+ try {
42777
+ return await pending;
42778
+ } finally {
42779
+ if (this.inflight.get(key) === pending) this.inflight.delete(key);
42780
+ }
42781
+ }
42782
+ };
42345
42783
  async function probeRemoteMeshGitStatus(args) {
42346
42784
  if (!args.dispatchMeshCommand) return null;
42347
42785
  const remoteResult = await Promise.race([
@@ -42435,7 +42873,7 @@ async function hydrateInlineMeshDirectTruth(args) {
42435
42873
  continue;
42436
42874
  }
42437
42875
  peerAttemptedCount += 1;
42438
- const remoteGit = await probeRemoteMeshGitStatusWithRetry({
42876
+ const runProbe = () => probeRemoteMeshGitStatusWithRetry({
42439
42877
  dispatchMeshCommand: args.dispatchMeshCommand,
42440
42878
  daemonId,
42441
42879
  workspace,
@@ -42443,6 +42881,7 @@ async function hydrateInlineMeshDirectTruth(args) {
42443
42881
  retryTimeoutMs: MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
42444
42882
  getConnection: args.getMeshPeerConnectionStatus
42445
42883
  });
42884
+ const remoteGit = args.probeCache ? await args.probeCache.probe(daemonId, workspace, runProbe) : await runProbe();
42446
42885
  if (remoteGit) {
42447
42886
  recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
42448
42887
  peerConfirmedCount += 1;
@@ -43765,6 +44204,10 @@ var DaemonCommandRouter = class {
43765
44204
  inlineMeshCache = /* @__PURE__ */ new Map();
43766
44205
  /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
43767
44206
  aggregateMeshStatusCache = /* @__PURE__ */ new Map();
44207
+ /** Shared per-peer git_status probe dedup + recently-probed reuse gate.
44208
+ * Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
44209
+ * loop cannot storm a slow peer with back-to-back refreshUpstream probes. */
44210
+ meshGitProbeCache = new MeshGitProbeCache(MESH_DIRECT_PROBE_REUSE_MS);
43768
44211
  /** In-memory async Refinery jobs keyed by meshId:nodeId to reject/return duplicate in-flight requests. */
43769
44212
  runningRefineJobs = /* @__PURE__ */ new Map();
43770
44213
  /** Terminal async Refinery jobs preserve a clear answer after the worktree node has been removed. */
@@ -46667,7 +47110,8 @@ ${hintLines.join("\n")}` : "",
46667
47110
  getMeshPeerConnectionStatus: this.deps.getMeshPeerConnectionStatus,
46668
47111
  statusInstanceId: this.deps.statusInstanceId,
46669
47112
  localMachineId: loadConfig().machineId || "",
46670
- probeRemotePeers
47113
+ probeRemotePeers,
47114
+ probeCache: this.meshGitProbeCache
46671
47115
  });
46672
47116
  const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
46673
47117
  const sourceOfTruth = {
@@ -47271,6 +47715,22 @@ ${hintLines.join("\n")}` : "",
47271
47715
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
47272
47716
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
47273
47717
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
47718
+ {
47719
+ const meshRecordForForward = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
47720
+ const forwardNode = meshRecordForForward?.mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
47721
+ const nodeDaemonId = typeof forwardNode?.daemonId === "string" ? forwardNode.daemonId.trim() : void 0;
47722
+ const selfDaemonId = this.deps.statusInstanceId;
47723
+ const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
47724
+ if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
47725
+ const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : void 0;
47726
+ const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "refine_mesh_node", {
47727
+ ...typeof args === "object" && args !== null ? args : {},
47728
+ coordinatorDaemonId: callerCoordinatorDaemonId || selfDaemonId,
47729
+ _meshDirectDispatch: true
47730
+ });
47731
+ return forwarded ?? { success: false, error: "no response from remote node" };
47732
+ }
47733
+ }
47274
47734
  const isDryRun = args?.dryRun !== false && args?.execute !== true;
47275
47735
  if (isDryRun) {
47276
47736
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
@@ -48243,6 +48703,7 @@ ${ptyResult.output.slice(-2e3)}`);
48243
48703
  const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
48244
48704
  const localMachineId = loadConfig().machineId || "";
48245
48705
  const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
48706
+ const meshGitProbeCache = this.meshGitProbeCache;
48246
48707
  const directTruth = requireDirectPeerTruth ? await hydrateInlineMeshDirectTruth({
48247
48708
  mesh,
48248
48709
  meshSource: meshRecord.source,
@@ -48253,7 +48714,8 @@ ${ptyResult.output.slice(-2e3)}`);
48253
48714
  // Standing-state model: only an explicit refresh fans
48254
48715
  // out a blocking peer git probe. Default loads return
48255
48716
  // held truth so one slow peer can't block the graph.
48256
- probeRemotePeers: refreshRequested
48717
+ probeRemotePeers: refreshRequested,
48718
+ probeCache: meshGitProbeCache
48257
48719
  }) : {
48258
48720
  directEvidenceCount: 0,
48259
48721
  localConfirmedCount: 0,
@@ -48414,7 +48876,7 @@ ${ptyResult.output.slice(-2e3)}`);
48414
48876
  }
48415
48877
  remoteProbeApplied = true;
48416
48878
  } else if (refreshRequested && !isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
48417
- const remoteGit = await probeRemoteMeshGitStatusWithRetry({
48879
+ const runNodeProbe = () => probeRemoteMeshGitStatusWithRetry({
48418
48880
  dispatchMeshCommand: this.deps.dispatchMeshCommand,
48419
48881
  daemonId,
48420
48882
  workspace,
@@ -48425,6 +48887,7 @@ ${ptyResult.output.slice(-2e3)}`);
48425
48887
  status.connection = connection;
48426
48888
  }
48427
48889
  });
48890
+ const remoteGit = await meshGitProbeCache.probe(daemonId, workspace, runNodeProbe);
48428
48891
  if (remoteGit) {
48429
48892
  status.git = remoteGit;
48430
48893
  status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
@@ -57242,6 +57705,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
57242
57705
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
57243
57706
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
57244
57707
  DEFAULT_MESH_POLICY,
57708
+ DEFAULT_MESH_SCHEDULING_STRATEGY,
57245
57709
  DEFAULT_SESSION_HOST_APP_NAME,
57246
57710
  DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
57247
57711
  DEFAULT_SESSION_HOST_READY_TIMEOUT_MS,
@@ -57267,9 +57731,12 @@ var V1_CONTRACT_VERSION = "1.0.0";
57267
57731
  InMemoryGitSnapshotStore,
57268
57732
  LOG,
57269
57733
  MAX_LEDGER_SLICE_LIMIT,
57734
+ MESH_CONVERGE_FAST_FORWARD_TAG,
57735
+ MESH_CONVERGE_REFINE_TAG,
57270
57736
  MESH_MISSION_STATUSES,
57271
57737
  MESH_REFINE_CONFIG_LOCATIONS,
57272
57738
  MESH_REFINE_CONFIG_SCHEMA,
57739
+ MESH_SCHEDULING_STRATEGIES,
57273
57740
  MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
57274
57741
  MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
57275
57742
  MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
@@ -57470,6 +57937,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
57470
57937
  normalizeManagedStatus,
57471
57938
  normalizeMeshCapabilityTags,
57472
57939
  normalizeMeshDaemonRole,
57940
+ normalizeMeshSchedulingStrategy,
57473
57941
  normalizeMeshTaskMode,
57474
57942
  normalizeMeshWorkerResult,
57475
57943
  normalizeMessageParts,
@@ -57507,7 +57975,9 @@ var V1_CONTRACT_VERSION = "1.0.0";
57507
57975
  resetConfig,
57508
57976
  resetDebugRuntimeConfig,
57509
57977
  resetState,
57978
+ resolveAutoConvergeCodeChange,
57510
57979
  resolveChatMessageKind,
57980
+ resolveConvergeRequiredTags,
57511
57981
  resolveCurrentGlobalInstallSurface,
57512
57982
  resolveDebugRuntimeConfig,
57513
57983
  resolveDelegatedWorkerAutoApprove,
@@ -57515,6 +57985,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
57515
57985
  resolveGitRepository,
57516
57986
  resolveMeshHostStatus,
57517
57987
  resolveMeshRefineValidationPlan,
57988
+ resolveNodeSchedulingPriority,
57518
57989
  resolveSessionHostAppName,
57519
57990
  resolveSessionHostAppNameResolution,
57520
57991
  resolveWorktreePath,