@adhdev/daemon-standalone 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
@@ -29735,6 +29735,18 @@ var require_dist3 = __commonJS({
29735
29735
  mod
29736
29736
  ));
29737
29737
  var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
29738
+ function normalizeMeshSchedulingStrategy(value) {
29739
+ if (typeof value !== "string") return DEFAULT_MESH_SCHEDULING_STRATEGY;
29740
+ const trimmed = value.trim();
29741
+ return MESH_SCHEDULING_STRATEGIES.includes(trimmed) ? trimmed : DEFAULT_MESH_SCHEDULING_STRATEGY;
29742
+ }
29743
+ function resolveNodeSchedulingPriority(nodePolicy) {
29744
+ const raw = Number(nodePolicy?.schedulingPriority);
29745
+ return Number.isFinite(raw) ? raw : 0;
29746
+ }
29747
+ function resolveAutoConvergeCodeChange(policy) {
29748
+ return policy?.autoConvergeCodeChange === true;
29749
+ }
29738
29750
  function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy) {
29739
29751
  if (typeof nodePolicy?.delegatedWorkerAutoApprove === "boolean") {
29740
29752
  return nodePolicy.delegatedWorkerAutoApprove;
@@ -29762,10 +29774,23 @@ var require_dist3 = __commonJS({
29762
29774
  if (!Number.isFinite(raw) || raw < 0) return void 0;
29763
29775
  return Math.floor(raw);
29764
29776
  }
29777
+ var MESH_SCHEDULING_STRATEGIES;
29778
+ var DEFAULT_MESH_SCHEDULING_STRATEGY;
29779
+ var MESH_CONVERGE_REFINE_TAG;
29780
+ var MESH_CONVERGE_FAST_FORWARD_TAG;
29765
29781
  var DEFAULT_MESH_POLICY;
29766
29782
  var init_repo_mesh_types = __esm2({
29767
29783
  "src/repo-mesh-types.ts"() {
29768
29784
  "use strict";
29785
+ MESH_SCHEDULING_STRATEGIES = [
29786
+ "first_eligible",
29787
+ "least_loaded",
29788
+ "round_robin",
29789
+ "priority_only"
29790
+ ];
29791
+ DEFAULT_MESH_SCHEDULING_STRATEGY = "first_eligible";
29792
+ MESH_CONVERGE_REFINE_TAG = "converge=refine";
29793
+ MESH_CONVERGE_FAST_FORWARD_TAG = "converge=fast_forward";
29769
29794
  DEFAULT_MESH_POLICY = {
29770
29795
  requirePreTaskCheckpoint: false,
29771
29796
  requirePostTaskCheckpoint: true,
@@ -30004,10 +30029,10 @@ var require_dist3 = __commonJS({
30004
30029
  }
30005
30030
  function getDaemonBuildInfo() {
30006
30031
  if (cached2) return cached2;
30007
- const commit = readInjected(true ? "33fe83f72a43bccb1b4bec7744ee5c6d8c11a325" : void 0) ?? "unknown";
30008
- const commitShort = readInjected(true ? "33fe83f7" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30009
- const version2 = readInjected(true ? "0.9.82-rc.310" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30010
- const builtAt = readInjected(true ? "2026-06-17T09:35:20.983Z" : void 0);
30032
+ const commit = readInjected(true ? "3902f3d63eabe7b9e34d0e849dc0fed2fd66c026" : void 0) ?? "unknown";
30033
+ const commitShort = readInjected(true ? "3902f3d6" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30034
+ const version2 = readInjected(true ? "0.9.82-rc.311" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30035
+ const builtAt = readInjected(true ? "2026-06-17T12:22:27.984Z" : void 0);
30011
30036
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
30012
30037
  return cached2;
30013
30038
  }
@@ -31261,6 +31286,17 @@ ${error48.message || ""}`;
31261
31286
  if (!SPAWNED_SESSION_VISIBILITY_MODES.has(String(policy.spawnedSessionVisibility))) {
31262
31287
  policy.spawnedSessionVisibility = "visible";
31263
31288
  }
31289
+ const normalizedStrategy = normalizeMeshSchedulingStrategy(policy.schedulingStrategy);
31290
+ if (normalizedStrategy === "first_eligible") {
31291
+ delete policy.schedulingStrategy;
31292
+ } else {
31293
+ policy.schedulingStrategy = normalizedStrategy;
31294
+ }
31295
+ if (policy.autoConvergeCodeChange === true) {
31296
+ policy.autoConvergeCodeChange = true;
31297
+ } else {
31298
+ delete policy.autoConvergeCodeChange;
31299
+ }
31264
31300
  return policy;
31265
31301
  }
31266
31302
  function normalizeAutoFastForwardPolicy(value) {
@@ -32573,6 +32609,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32573
32609
  recordMeshToolCall: () => recordMeshToolCall,
32574
32610
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
32575
32611
  requeueTask: () => requeueTask,
32612
+ resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
32576
32613
  updateDirectDispatchStatus: () => updateDirectDispatchStatus,
32577
32614
  updateSessionTaskStatus: () => updateSessionTaskStatus,
32578
32615
  updateTaskStatus: () => updateTaskStatus,
@@ -32646,6 +32683,21 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32646
32683
  if (!Array.isArray(raw)) return void 0;
32647
32684
  return raw.find((type) => typeof type === "string" && type.trim())?.trim();
32648
32685
  }
32686
+ function roleCapabilityTags(policy, providerType) {
32687
+ const roles = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerRoles : void 0;
32688
+ if (!Array.isArray(roles)) return [];
32689
+ const wantedProvider = typeof providerType === "string" && providerType.trim() ? providerType.trim().toLowerCase() : "";
32690
+ const out = [];
32691
+ for (const entry of roles) {
32692
+ if (!entry || typeof entry !== "object") continue;
32693
+ const type = typeof entry.providerType === "string" ? entry.providerType.trim().toLowerCase() : "";
32694
+ const role = typeof entry.role === "string" ? entry.role.trim().toLowerCase() : "";
32695
+ if (!role) continue;
32696
+ if (wantedProvider && type && type !== wantedProvider) continue;
32697
+ out.push(`role=${role}`);
32698
+ }
32699
+ return out;
32700
+ }
32649
32701
  function buildMeshNodeCapabilityTags(node, providerType) {
32650
32702
  const provider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : firstProviderPriority(node?.policy);
32651
32703
  const worktreeBranch = typeof node?.worktreeBranch === "string" && node.worktreeBranch.trim() ? node.worktreeBranch.trim() : null;
@@ -32657,7 +32709,25 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32657
32709
  // Worktree nodes automatically expose a "worktree=<branch>" tag so that
32658
32710
  // mesh_enqueue_task with required_tags: ["worktree=<branch>"] routes
32659
32711
  // only to the matching worktree node.
32660
- ...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : []
32712
+ ...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : [],
32713
+ // Convergence routing: advertise how this node can land its work onto base.
32714
+ // - converge=refine: local worktree nodes (on ANY machine — refine_mesh_node
32715
+ // now forwards to the owning daemon) can run the Refinery merge → push →
32716
+ // cleanup against their own checkout, so they accept code_change tasks.
32717
+ // - converge=fast_forward: non-worktree nodes (the machine itself) can only
32718
+ // ff/push an already-converged branch; they are NOT a destination for
32719
+ // code_change work (a worktree is created first, and that worktree node
32720
+ // receives the task instead). Reuses the ordinary required-tags filter —
32721
+ // the load-balancing scheduler auto-injects converge=refine for code_change
32722
+ // so such work is hard-filtered onto refine-capable nodes.
32723
+ ...node?.isLocalWorktree === true ? ["converge=refine"] : ["converge=fast_forward"],
32724
+ // Role-based routing: advertise role=<x> for each (node, provider) role
32725
+ // declared in policy.providerRoles. Narrowed to the selected provider when
32726
+ // one is given so the chosen provider must match a task's required role;
32727
+ // when no provider is selected, all declared roles are advertised for the
32728
+ // node-level eligibility scan. Reuses the ordinary required-tags filter —
32729
+ // no separate role field/gate.
32730
+ ...roleCapabilityTags(node?.policy, providerType)
32661
32731
  ]);
32662
32732
  }
32663
32733
  function nodeSatisfiesRequiredTags(requiredTags, capabilityTags) {
@@ -32666,6 +32736,18 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32666
32736
  const available = new Set(normalizeMeshCapabilityTags(capabilityTags));
32667
32737
  return required2.every((tag) => available.has(tag));
32668
32738
  }
32739
+ function resolveConvergeRequiredTags(meshId, taskMode, explicitRequiredTags, opts) {
32740
+ if (taskMode !== "code_change") return explicitRequiredTags;
32741
+ if (typeof opts?.targetNodeId === "string" && opts.targetNodeId.trim()) return explicitRequiredTags;
32742
+ let optedIn = false;
32743
+ try {
32744
+ optedIn = resolveAutoConvergeCodeChange(getMesh(meshId)?.policy);
32745
+ } catch {
32746
+ optedIn = false;
32747
+ }
32748
+ if (!optedIn) return explicitRequiredTags;
32749
+ return normalizeMeshCapabilityTags([...explicitRequiredTags, MESH_CONVERGE_REFINE_TAG]);
32750
+ }
32669
32751
  function withQueueLock(_meshId, fn) {
32670
32752
  return MeshRuntimeStore.getInstance().transaction(fn);
32671
32753
  }
@@ -32724,7 +32806,15 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32724
32806
  taskMode: modeValidation.taskMode,
32725
32807
  targetNodeId: opts?.targetNodeId,
32726
32808
  targetSessionId: opts?.targetSessionId,
32727
- requiredTags: normalizeMeshCapabilityTags(opts?.requiredTags),
32809
+ // Convergence routing (opt-in): auto-inject converge=refine for code_change
32810
+ // tasks so they hard-filter onto refine-capable worktree nodes. No-op unless
32811
+ // the mesh opts in; explicit target_node_id / required_tags are preserved.
32812
+ requiredTags: resolveConvergeRequiredTags(
32813
+ meshId,
32814
+ modeValidation.taskMode,
32815
+ normalizeMeshCapabilityTags(opts?.requiredTags),
32816
+ { targetNodeId: opts?.targetNodeId }
32817
+ ),
32728
32818
  ...dependsOn.length > 0 ? { dependsOn } : {},
32729
32819
  ...typeof opts?.missionId === "string" && opts.missionId.trim() ? { missionId: opts.missionId.trim() } : {},
32730
32820
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -32992,6 +33082,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32992
33082
  "use strict";
32993
33083
  import_crypto5 = require("crypto");
32994
33084
  init_mesh_host_ownership();
33085
+ init_repo_mesh_types();
32995
33086
  init_mesh_runtime_store();
32996
33087
  init_mesh_config();
32997
33088
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
@@ -33289,6 +33380,17 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
33289
33380
 
33290
33381
  CREATE INDEX IF NOT EXISTS idx_mesh_missions_mesh_status
33291
33382
  ON mesh_missions(mesh_id, status, updated_at);
33383
+
33384
+ -- Load-balancing scheduler: per-mesh round-robin rotation cursor. When
33385
+ -- the schedulingStrategy is 'round_robin', several eligible nodes tied at
33386
+ -- the least load are rotated by this cursor so the tie-break winner cycles
33387
+ -- across scheduling passes instead of always favouring the same array-order
33388
+ -- node. Persisted (not a module Map) so rotation survives daemon restarts
33389
+ -- and stays a single source of truth across scheduling entry points.
33390
+ CREATE TABLE IF NOT EXISTS mesh_scheduler_cursor (
33391
+ mesh_id TEXT PRIMARY KEY,
33392
+ cursor INTEGER NOT NULL DEFAULT 0
33393
+ );
33292
33394
  `);
33293
33395
  }
33294
33396
  hasCompletionFingerprint(fingerprint) {
@@ -33466,6 +33568,44 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
33466
33568
  `).get(meshId, nodeId);
33467
33569
  return row !== void 0;
33468
33570
  }
33571
+ /**
33572
+ * Count active (status='assigned') tasks on a node, regardless of provider or
33573
+ * task mode. This is the load metric for least-loaded / round-robin ranking:
33574
+ * the scheduler prefers the node with the fewest active assignments so
33575
+ * untargeted work spreads instead of piling onto whichever node asks first.
33576
+ */
33577
+ nodeActiveAssignmentCount(meshId, nodeId) {
33578
+ const row = this.db.prepare(`
33579
+ SELECT COUNT(*) as count FROM mesh_queue
33580
+ WHERE mesh_id = ? AND status = 'assigned' AND assigned_node_id = ?
33581
+ `).get(meshId, nodeId);
33582
+ return row?.count ?? 0;
33583
+ }
33584
+ /**
33585
+ * Read the current per-mesh round-robin cursor (0 when unset). Used to rotate
33586
+ * the tie-break winner among nodes tied at the least load.
33587
+ */
33588
+ getSchedulerCursor(meshId) {
33589
+ const row = this.db.prepare(
33590
+ "SELECT cursor FROM mesh_scheduler_cursor WHERE mesh_id = ?"
33591
+ ).get(meshId);
33592
+ return row?.cursor ?? 0;
33593
+ }
33594
+ /**
33595
+ * Atomically advance the per-mesh round-robin cursor by one and return the
33596
+ * value that was current BEFORE the bump (the value the caller should rotate
33597
+ * by for this pass). UPSERT keeps it lock-free across concurrent passes.
33598
+ */
33599
+ bumpSchedulerCursor(meshId) {
33600
+ return this.transaction(() => {
33601
+ const current = this.getSchedulerCursor(meshId);
33602
+ this.db.prepare(`
33603
+ INSERT INTO mesh_scheduler_cursor (mesh_id, cursor) VALUES (?, ?)
33604
+ ON CONFLICT(mesh_id) DO UPDATE SET cursor = excluded.cursor
33605
+ `).run(meshId, current + 1);
33606
+ return current;
33607
+ });
33608
+ }
33469
33609
  /**
33470
33610
  * Count active (status='assigned') tasks on a (node, provider) combination,
33471
33611
  * matched by the assignedProviderType stamped on the payload at claim time.
@@ -38051,6 +38191,33 @@ Next step: ${nextStep}`;
38051
38191
  function nodeHasActiveAssignment(meshId, nodeId) {
38052
38192
  return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
38053
38193
  }
38194
+ function nodeActiveLoad(meshId, nodeId) {
38195
+ return MeshRuntimeStore.getInstance().nodeActiveAssignmentCount(meshId, nodeId);
38196
+ }
38197
+ function resolveSchedulingStrategy(mesh) {
38198
+ return normalizeMeshSchedulingStrategy(mesh?.policy?.schedulingStrategy);
38199
+ }
38200
+ function orderEligibleNodes(meshId, strategy, nodes, opts) {
38201
+ if (strategy === "first_eligible" || nodes.length <= 1) {
38202
+ return nodes;
38203
+ }
38204
+ const priorityOf = (n) => resolveNodeSchedulingPriority(n.node?.policy);
38205
+ let rotation = 0;
38206
+ if (strategy === "round_robin") {
38207
+ const cursor = opts?.bumpCursor ? MeshRuntimeStore.getInstance().bumpSchedulerCursor(meshId) : MeshRuntimeStore.getInstance().getSchedulerCursor(meshId);
38208
+ rotation = (cursor % nodes.length + nodes.length) % nodes.length;
38209
+ }
38210
+ const rotationRank = (index) => (index - rotation + nodes.length) % nodes.length;
38211
+ return [...nodes].sort((a, b) => {
38212
+ const prioDelta = priorityOf(b) - priorityOf(a);
38213
+ if (prioDelta !== 0) return prioDelta;
38214
+ if (strategy === "least_loaded" || strategy === "round_robin") {
38215
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
38216
+ if (loadDelta !== 0) return loadDelta;
38217
+ }
38218
+ return rotationRank(a.index) - rotationRank(b.index);
38219
+ });
38220
+ }
38054
38221
  function activeProviderAssignedCount(meshId, nodeId, providerType) {
38055
38222
  return getQueue(meshId, { status: ["assigned"] }).filter((task) => task.assignedNodeId === nodeId && task.assignedProviderType === providerType).length;
38056
38223
  }
@@ -38187,7 +38354,14 @@ Next step: ${nextStep}`;
38187
38354
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
38188
38355
  continue;
38189
38356
  }
38190
- for (const node of candidateNodes) {
38357
+ const strategy = resolveSchedulingStrategy(mesh);
38358
+ const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
38359
+ meshId,
38360
+ strategy,
38361
+ candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
38362
+ { bumpCursor: true }
38363
+ ).map((c) => c.node);
38364
+ for (const node of orderedCandidateNodes) {
38191
38365
  const nodeId = readMeshNodeId(node);
38192
38366
  if (!nodeId) continue;
38193
38367
  const launchKey = `${meshId}:${nodeId}`;
@@ -38314,6 +38488,8 @@ Next step: ${nextStep}`;
38314
38488
  noIdleMeshSessionAvailable: true
38315
38489
  };
38316
38490
  }
38491
+ const strategy = resolveSchedulingStrategy(mesh);
38492
+ const localCandidates = [];
38317
38493
  const cliInstances = components.instanceManager.getByCategory("cli");
38318
38494
  for (const inst of cliInstances) {
38319
38495
  const state = inst.getState();
@@ -38336,7 +38512,7 @@ Next step: ${nextStep}`;
38336
38512
  const providerType = state.type || readNonEmptyString2(settings.providerType);
38337
38513
  if (providerType) {
38338
38514
  localIdleSessionsChecked += 1;
38339
- tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
38515
+ localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) => readMeshNodeId(n) === nodeId) });
38340
38516
  } else {
38341
38517
  skippedSessions.push({
38342
38518
  nodeId,
@@ -38350,18 +38526,49 @@ Next step: ${nextStep}`;
38350
38526
  remoteSessions = MeshRuntimeStore.getInstance().getRemoteIdleSessions();
38351
38527
  } catch {
38352
38528
  }
38529
+ const remoteCandidates = [];
38353
38530
  for (const idle of remoteSessions) {
38354
38531
  const node = mesh.nodes.find((n) => n.id === idle.nodeId);
38355
38532
  if (node) {
38356
38533
  remoteIdleSessionsChecked += 1;
38357
- const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
38358
- if (assigned) {
38359
- try {
38360
- MeshRuntimeStore.getInstance().deleteRemoteIdleSession(idle.nodeId, idle.sessionId);
38361
- } catch {
38362
- }
38534
+ remoteCandidates.push({ nodeId: idle.nodeId, sessionId: idle.sessionId, providerType: idle.providerType, origin: "remote", node });
38535
+ }
38536
+ }
38537
+ const assignIdleCandidate = (candidate) => {
38538
+ const assigned = tryAssignQueueTask(components, meshId, candidate.nodeId, candidate.sessionId, candidate.providerType);
38539
+ if (assigned && candidate.origin === "remote") {
38540
+ try {
38541
+ MeshRuntimeStore.getInstance().deleteRemoteIdleSession(candidate.nodeId, candidate.sessionId);
38542
+ } catch {
38363
38543
  }
38364
38544
  }
38545
+ };
38546
+ if (strategy === "first_eligible") {
38547
+ for (const candidate of localCandidates) assignIdleCandidate(candidate);
38548
+ for (const candidate of remoteCandidates) assignIdleCandidate(candidate);
38549
+ } else {
38550
+ const pool = [...localCandidates, ...remoteCandidates];
38551
+ const baseIndex = /* @__PURE__ */ new Map();
38552
+ pool.forEach((c, i) => {
38553
+ if (!baseIndex.has(c.nodeId)) baseIndex.set(c.nodeId, i);
38554
+ });
38555
+ const uniqueNodes = [...new Set(pool.map((c) => c.nodeId))].map((nodeId, index) => ({ nodeId, node: pool.find((c) => c.nodeId === nodeId)?.node, index }));
38556
+ const ranked = orderEligibleNodes(meshId, strategy, uniqueNodes, { bumpCursor: true });
38557
+ const rankIndex = new Map(ranked.map((r, i) => [r.nodeId, i]));
38558
+ const remaining = [...pool];
38559
+ while (remaining.length > 0) {
38560
+ remaining.sort((a, b) => {
38561
+ const aPrio = resolveNodeSchedulingPriority(a.node?.policy);
38562
+ const bPrio = resolveNodeSchedulingPriority(b.node?.policy);
38563
+ if (aPrio !== bPrio) return bPrio - aPrio;
38564
+ if (strategy === "least_loaded" || strategy === "round_robin") {
38565
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
38566
+ if (loadDelta !== 0) return loadDelta;
38567
+ }
38568
+ return (rankIndex.get(a.nodeId) ?? 0) - (rankIndex.get(b.nodeId) ?? 0);
38569
+ });
38570
+ assignIdleCandidate(remaining.shift());
38571
+ }
38365
38572
  }
38366
38573
  autoLaunchStarted = await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
38367
38574
  const afterQueue = getQueue(meshId);
@@ -41743,7 +41950,7 @@ ${cont}` : cont;
41743
41950
  scheduleSettle() {
41744
41951
  if (this.settleTimer) clearTimeout(this.settleTimer);
41745
41952
  const epoch = this.responseEpoch;
41746
- const delay = Math.max(
41953
+ const delay2 = Math.max(
41747
41954
  this.timeouts.outputSettle,
41748
41955
  this.submitPendingUntil > Date.now() ? this.submitPendingUntil - Date.now() + this.timeouts.outputSettle : 0
41749
41956
  );
@@ -41751,7 +41958,7 @@ ${cont}` : cont;
41751
41958
  this.settleTimer = null;
41752
41959
  if (epoch !== this.responseEpoch) return;
41753
41960
  this.evaluateSettled(this.transport.getSnapshot());
41754
- }, delay);
41961
+ }, delay2);
41755
41962
  }
41756
41963
  /** Called from sendMessage in transport once a turn scope is established. */
41757
41964
  onTurnStarted(turnScope) {
@@ -45262,6 +45469,7 @@ ${lastSnapshot}`;
45262
45469
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS: () => DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
45263
45470
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
45264
45471
  DEFAULT_MESH_POLICY: () => DEFAULT_MESH_POLICY,
45472
+ DEFAULT_MESH_SCHEDULING_STRATEGY: () => DEFAULT_MESH_SCHEDULING_STRATEGY,
45265
45473
  DEFAULT_SESSION_HOST_APP_NAME: () => DEFAULT_SESSION_HOST_APP_NAME,
45266
45474
  DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS2,
45267
45475
  DEFAULT_SESSION_HOST_READY_TIMEOUT_MS: () => DEFAULT_SESSION_HOST_READY_TIMEOUT_MS2,
@@ -45287,9 +45495,12 @@ ${lastSnapshot}`;
45287
45495
  InMemoryGitSnapshotStore: () => InMemoryGitSnapshotStore,
45288
45496
  LOG: () => LOG2,
45289
45497
  MAX_LEDGER_SLICE_LIMIT: () => MAX_LEDGER_SLICE_LIMIT,
45498
+ MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
45499
+ MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
45290
45500
  MESH_MISSION_STATUSES: () => MESH_MISSION_STATUSES,
45291
45501
  MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
45292
45502
  MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
45503
+ MESH_SCHEDULING_STRATEGIES: () => MESH_SCHEDULING_STRATEGIES,
45293
45504
  MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
45294
45505
  MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
45295
45506
  MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
@@ -45490,6 +45701,7 @@ ${lastSnapshot}`;
45490
45701
  normalizeManagedStatus: () => normalizeManagedStatus,
45491
45702
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
45492
45703
  normalizeMeshDaemonRole: () => normalizeMeshDaemonRole,
45704
+ normalizeMeshSchedulingStrategy: () => normalizeMeshSchedulingStrategy,
45493
45705
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
45494
45706
  normalizeMeshWorkerResult: () => normalizeMeshWorkerResult,
45495
45707
  normalizeMessageParts: () => normalizeMessageParts,
@@ -45527,7 +45739,9 @@ ${lastSnapshot}`;
45527
45739
  resetConfig: () => resetConfig,
45528
45740
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
45529
45741
  resetState: () => resetState,
45742
+ resolveAutoConvergeCodeChange: () => resolveAutoConvergeCodeChange,
45530
45743
  resolveChatMessageKind: () => resolveChatMessageKind,
45744
+ resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
45531
45745
  resolveCurrentGlobalInstallSurface: () => resolveCurrentGlobalInstallSurface,
45532
45746
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
45533
45747
  resolveDelegatedWorkerAutoApprove: () => resolveDelegatedWorkerAutoApprove,
@@ -45535,6 +45749,7 @@ ${lastSnapshot}`;
45535
45749
  resolveGitRepository: () => resolveGitRepository,
45536
45750
  resolveMeshHostStatus: () => resolveMeshHostStatus,
45537
45751
  resolveMeshRefineValidationPlan: () => resolveMeshRefineValidationPlan,
45752
+ resolveNodeSchedulingPriority: () => resolveNodeSchedulingPriority,
45538
45753
  resolveSessionHostAppName: () => resolveSessionHostAppName,
45539
45754
  resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution2,
45540
45755
  resolveWorktreePath: () => resolveWorktreePath,
@@ -47863,9 +48078,9 @@ ${lastSnapshot}`;
47863
48078
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
47864
48079
  }
47865
48080
  function summarizeMessage(message) {
47866
- const oneLine = message.replace(/\s+/g, " ").trim();
47867
- const title = oneLine.length > 96 ? `${oneLine.slice(0, 93)}...` : oneLine;
47868
- return { title: title || "(untitled task)", summary: oneLine };
48081
+ const oneLine2 = message.replace(/\s+/g, " ").trim();
48082
+ const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
48083
+ return { title: title || "(untitled task)", summary: oneLine2 };
47869
48084
  }
47870
48085
  function elapsedSince(value, now) {
47871
48086
  const started = value ? new Date(value).getTime() : Number.NaN;
@@ -50937,6 +51152,11 @@ ${cleanBody}`;
50937
51152
  continue;
50938
51153
  }
50939
51154
  if (message.role === "assistant") {
51155
+ const isActivity = message.kind === "tool" || message.kind === "terminal" || message.kind === "thought";
51156
+ if (isActivity) {
51157
+ collapsed.push(message);
51158
+ continue;
51159
+ }
50940
51160
  if (sawAssistantSinceLastUser) continue;
50941
51161
  sawAssistantSinceLastUser = true;
50942
51162
  collapsed.push(message);
@@ -56028,7 +56248,8 @@ ${effect.notification.body || ""}`.trim();
56028
56248
  const sessionIdHint = typeof args?.targetSessionId === "string" ? args.targetSessionId : typeof args?.sessionId === "string" ? args.sessionId : "";
56029
56249
  const providerHint = typeof args?.cliType === "string" ? args.cliType : typeof args?.providerType === "string" ? args.providerType : typeof args?.agentType === "string" ? args.agentType : "";
56030
56250
  const filteredMessages = h ? maybeHideCoordinatorPromptMessage(h, providerHint, sessionIdHint, messages) : messages;
56031
- const visibleMessages = filterUserFacingChatMessages(filteredMessages);
56251
+ const includeActivity = args?.includeActivity === true || args?.includeActivity === "true";
56252
+ const visibleMessages = includeActivity ? filteredMessages.filter((m) => isUserFacingChatMessage(m) || isActivityChatMessage(m)) : filterUserFacingChatMessages(filteredMessages);
56032
56253
  const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
56033
56254
  const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
56034
56255
  const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
@@ -58387,6 +58608,9 @@ ${effect.notification.body || ""}`.trim();
58387
58608
  }
58388
58609
  function buildControlScriptResult(scriptName, payload) {
58389
58610
  if (!payload || typeof payload !== "object") return {};
58611
+ if (payload.controlResult && typeof payload.controlResult === "object") {
58612
+ return { controlResult: payload.controlResult };
58613
+ }
58390
58614
  const legacyListPayload = (() => {
58391
58615
  if (Array.isArray(payload.options)) return payload;
58392
58616
  if (/^listmodels$/i.test(scriptName) && Array.isArray(payload.models)) {
@@ -60217,12 +60441,12 @@ ${formatManifestValidationIssues2(validation.issues)}`,
60217
60441
  scheduleSpawnPrime() {
60218
60442
  const seqs = this.spec.send_on_spawn;
60219
60443
  if (!Array.isArray(seqs) || seqs.length === 0) return;
60220
- const delay = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
60444
+ const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
60221
60445
  setTimeout(() => {
60222
60446
  for (const seq of seqs) {
60223
60447
  if (typeof seq === "string" && seq.length > 0) this.adapter.send_keys(seq);
60224
60448
  }
60225
- }, delay);
60449
+ }, delay2);
60226
60450
  }
60227
60451
  dispatch(cmd) {
60228
60452
  switch (cmd.kind) {
@@ -60588,11 +60812,11 @@ ${formatManifestValidationIssues2(validation.issues)}`,
60588
60812
  const armed = this.delegateTimers.has(d.id);
60589
60813
  const shouldFire = d.when_state === currentStateId;
60590
60814
  if (shouldFire && !armed) {
60591
- const delay = d.after_duration_ms ?? 0;
60815
+ const delay2 = d.after_duration_ms ?? 0;
60592
60816
  const t = setTimeout(() => {
60593
60817
  this.fireDelegate(d);
60594
60818
  this.delegateTimers.delete(d.id);
60595
- }, delay);
60819
+ }, delay2);
60596
60820
  this.delegateTimers.set(d.id, t);
60597
60821
  } else if (!shouldFire && armed) {
60598
60822
  clearTimeout(this.delegateTimers.get(d.id));
@@ -60646,7 +60870,15 @@ ${formatManifestValidationIssues2(validation.issues)}`,
60646
60870
  this.adapter.send_keys(a.keys);
60647
60871
  return;
60648
60872
  case "open_picker":
60649
- this.adapter.send_keys(a.trigger_keys);
60873
+ {
60874
+ const m = /^([\s\S]*?)([\r\n]+)$/.exec(a.trigger_keys);
60875
+ if (m && m[1]) {
60876
+ this.adapter.send_keys(m[1]);
60877
+ setTimeout(() => this.adapter.send_keys(m[2]), 200);
60878
+ } else {
60879
+ this.adapter.send_keys(a.trigger_keys);
60880
+ }
60881
+ }
60650
60882
  this.pickerInProgress = { control_id: ctl.id, spec: ctl };
60651
60883
  return;
60652
60884
  case "attach_image": {
@@ -60829,8 +61061,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
60829
61061
  for (let i = 0; i < lines.length; i += 1) {
60830
61062
  const rec = lines[i];
60831
61063
  if (filter && !filter(rec)) continue;
60832
- const msg = projectMessage(rec, src.message_map, i, lines.length, mtime);
60833
- if (msg) {
61064
+ for (const msg of projectMessages(rec, src.message_map, i, lines.length, mtime)) {
60834
61065
  if (transcriptWorkspace) msg.workspace = transcriptWorkspace;
60835
61066
  messages.push(msg);
60836
61067
  }
@@ -60910,8 +61141,9 @@ ${formatManifestValidationIssues2(validation.issues)}`,
60910
61141
  const mtime = safeMtimeMs(resolved);
60911
61142
  const messages = [];
60912
61143
  for (let i = 0; i < messageRows.length; i += 1) {
60913
- const msg = projectMessage(messageRows[i], src.message_map, i, messageRows.length, mtime);
60914
- if (msg) messages.push(msg);
61144
+ for (const msg of projectMessages(messageRows[i], src.message_map, i, messageRows.length, mtime)) {
61145
+ messages.push(msg);
61146
+ }
60915
61147
  }
60916
61148
  if (messages.length === 0) return null;
60917
61149
  return {
@@ -61309,11 +61541,42 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61309
61541
  }
61310
61542
  return cur;
61311
61543
  }
61312
- function projectMessage(record2, map2, index, total, sourceMtimeMs) {
61544
+ function projectMessages(record2, map2, index, total, sourceMtimeMs) {
61313
61545
  const roleRaw = jsonPathGet(record2, map2.role);
61314
- const contentRaw = jsonPathGet(record2, map2.content);
61315
61546
  const role = normalizeRole(roleRaw);
61316
- let content = stringifyContent(contentRaw);
61547
+ let receivedAt = sourceMtimeMs - (total - 1 - index) * 1e3;
61548
+ if (map2.timestamp_ms) {
61549
+ const tsRaw = jsonPathGet(record2, map2.timestamp_ms);
61550
+ const parsed = parseTimestamp(tsRaw);
61551
+ if (parsed != null) receivedAt = parsed;
61552
+ }
61553
+ const kindRaw = map2.kind ? jsonPathGet(record2, map2.kind) : void 0;
61554
+ const kind = typeof kindRaw === "string" && kindRaw ? kindRaw : "standard";
61555
+ const out = [];
61556
+ if (map2.tools) {
61557
+ const recordTool = projectToolBlock(record2, role, map2.tools);
61558
+ if (recordTool) {
61559
+ out.push({ ...recordTool, receivedAt });
61560
+ return out;
61561
+ }
61562
+ }
61563
+ const contentRaw = jsonPathGet(record2, map2.content);
61564
+ const content = cleanContent(stringifyContent(contentRaw), map2);
61565
+ if (content) out.push({ role, content, receivedAt, kind });
61566
+ if (map2.tools && Array.isArray(contentRaw)) {
61567
+ let nudge = 1;
61568
+ for (const block2 of contentRaw) {
61569
+ const tool = projectToolBlock(block2, role, map2.tools);
61570
+ if (tool) {
61571
+ out.push({ ...tool, receivedAt: receivedAt + nudge });
61572
+ nudge += 1;
61573
+ }
61574
+ }
61575
+ }
61576
+ return out;
61577
+ }
61578
+ function cleanContent(input, map2) {
61579
+ let content = input;
61317
61580
  if (content && map2.content_strip) {
61318
61581
  for (const tag of map2.content_strip) {
61319
61582
  const safeTag = tag.replace(/[.+^${}()|[\]\\]/g, "\\$&");
@@ -61329,17 +61592,33 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61329
61592
  content = content.replace(open, "").replace(close, "");
61330
61593
  }
61331
61594
  }
61332
- if (content) content = content.trim();
61333
- if (!content) return null;
61334
- let receivedAt = sourceMtimeMs - (total - 1 - index) * 1e3;
61335
- if (map2.timestamp_ms) {
61336
- const tsRaw = jsonPathGet(record2, map2.timestamp_ms);
61337
- const parsed = parseTimestamp(tsRaw);
61338
- if (parsed != null) receivedAt = parsed;
61595
+ return content ? content.trim() : "";
61596
+ }
61597
+ var DEFAULT_TOOL_CALL_TYPES = ["tool_use", "function_call", "custom_tool_call"];
61598
+ var DEFAULT_TOOL_RESULT_TYPES = ["tool_result", "function_call_output", "custom_tool_call_output"];
61599
+ function projectToolBlock(block2, role, tmap) {
61600
+ void role;
61601
+ if (block2 == null || typeof block2 !== "object") return null;
61602
+ const typeVal = String(jsonPathGet(block2, tmap.block_type || "$.type") ?? "");
61603
+ if (!typeVal) return null;
61604
+ const callTypes = tmap.call_types ?? DEFAULT_TOOL_CALL_TYPES;
61605
+ const resultTypes = tmap.result_types ?? DEFAULT_TOOL_RESULT_TYPES;
61606
+ if (callTypes.includes(typeVal)) {
61607
+ const name = String(jsonPathGet(block2, tmap.call_name || "$.name") ?? "tool").trim() || "tool";
61608
+ const args = oneLine(stringifyContent(jsonPathGet(block2, tmap.call_args || "$.input")), 240);
61609
+ const content = args ? `\u2197 ${name}: ${args}` : `\u2197 ${name}`;
61610
+ return { role: "assistant", content, receivedAt: 0, kind: "tool" };
61611
+ }
61612
+ if (resultTypes.includes(typeVal)) {
61613
+ const result = oneLine(stringifyContent(jsonPathGet(block2, tmap.result_content || "$.content")), 600);
61614
+ if (!result) return null;
61615
+ return { role: "assistant", content: `\u2198 ${result}`, receivedAt: 0, kind: "tool" };
61339
61616
  }
61340
- const kindRaw = map2.kind ? jsonPathGet(record2, map2.kind) : void 0;
61341
- const kind = typeof kindRaw === "string" && kindRaw ? kindRaw : "standard";
61342
- return { role, content, receivedAt, kind };
61617
+ return null;
61618
+ }
61619
+ function oneLine(s, max) {
61620
+ const flat = s.replace(/\s+/g, " ").trim();
61621
+ return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
61343
61622
  }
61344
61623
  function parseTimestamp(v) {
61345
61624
  if (v == null) return null;
@@ -61478,6 +61757,9 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61478
61757
  function stripAnsi3(text) {
61479
61758
  return String(text || "").replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
61480
61759
  }
61760
+ function delay(ms) {
61761
+ return new Promise((resolve24) => setTimeout(resolve24, ms));
61762
+ }
61481
61763
  var SpecCliAdapter = class _SpecCliAdapter {
61482
61764
  cliType;
61483
61765
  cliName;
@@ -61699,9 +61981,15 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61699
61981
  * drives the dispatch:
61700
61982
  *
61701
61983
  * send_keys → click_control (e.g. stop)
61702
- * open_picker → click_control then resolve when extract_choices
61703
- * surface; choice index comes from args.choiceIndex
61704
- * or args.choice (string label match), defaulting to 0
61984
+ * open_picker → two roles, driven by the screen, not a hardcoded list:
61985
+ * - LIST (no choice arg): open the picker, wait for it
61986
+ * to render, parse the on-screen options via
61987
+ * `extract_choices`, and return them as
61988
+ * `controlResult.options` (+ `currentValue`). This is
61989
+ * how the dashboard's Model/Mode controls learn what is
61990
+ * actually selectable in this CLI right now.
61991
+ * - SELECT (args.choiceIndex / args.choiceLabel): drive
61992
+ * the picker to that option using `submit_key`.
61705
61993
  * attach_image → attach_image dispatch; expects args.blob (data url
61706
61994
  * or base64) and args.mime
61707
61995
  *
@@ -61726,11 +62014,128 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61726
62014
  this.driver.dispatch({ kind: "attach_image", blob, mime });
61727
62015
  return Promise.resolve({ ok: true, effects: [{ type: "attached_image", controlId: ctl.id }] });
61728
62016
  }
62017
+ if (action.type === "open_picker") {
62018
+ const choiceIndex = typeof flat.choiceIndex === "number" ? flat.choiceIndex : typeof flat.choiceIndex === "string" && flat.choiceIndex.trim() ? Number(flat.choiceIndex) : void 0;
62019
+ const choiceLabel = typeof flat.choiceLabel === "string" ? flat.choiceLabel : typeof flat.choice === "string" ? flat.choice : void 0;
62020
+ if (typeof choiceIndex === "number" && Number.isFinite(choiceIndex) || choiceLabel && choiceLabel.trim()) {
62021
+ return this.selectPickerChoice(ctl, action, choiceIndex, choiceLabel);
62022
+ }
62023
+ return this.openPickerAndListChoices(ctl, action);
62024
+ }
61729
62025
  this.driver.dispatch({ kind: "click_control", control_id: ctl.id, payload: flat });
61730
- const effects = [];
61731
- if (action.type === "open_picker") effects.push({ type: "opened_picker", controlId: ctl.id });
61732
- else if (action.type === "send_keys") effects.push({ type: "sent_keys", controlId: ctl.id });
61733
- return Promise.resolve({ ok: true, effects });
62026
+ return Promise.resolve({ ok: true, effects: [{ type: "sent_keys", controlId: ctl.id }] });
62027
+ }
62028
+ /**
62029
+ * Open an `open_picker` control and return the options the CLI is showing,
62030
+ * parsed live from the screen via `extract_choices`. Nothing is selected —
62031
+ * the picker is left open so a follow-up SELECT invoke can commit a choice.
62032
+ */
62033
+ async openPickerAndListChoices(ctl, action) {
62034
+ this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
62035
+ const ready = await this.waitForPickerRendered(action);
62036
+ const options = this.extractPickerChoices(action);
62037
+ const currentValue = options.find((o) => o.current)?.label;
62038
+ return {
62039
+ ok: true,
62040
+ effects: [{ type: "opened_picker", controlId: ctl.id }],
62041
+ controlResult: {
62042
+ options: options.map((o) => ({ value: o.label, label: o.label, current: o.current })),
62043
+ ...currentValue ? { currentValue } : {},
62044
+ source: "screen-parse",
62045
+ ...ready ? {} : { warning: "picker_render_timeout" }
62046
+ }
62047
+ };
62048
+ }
62049
+ /**
62050
+ * Drive an already-listable picker to a specific option. The option can be
62051
+ * named (choiceLabel — matched against the parsed on-screen labels) or
62052
+ * positional (choiceIndex — the on-screen number). The actual keystrokes
62053
+ * come from the spec's `submit_key` with `{index}` substituted, so the spec
62054
+ * — not this code — decides how a selection is keyed for each CLI.
62055
+ */
62056
+ async selectPickerChoice(ctl, action, choiceIndex, choiceLabel) {
62057
+ this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
62058
+ await this.waitForPickerRendered(action);
62059
+ const options = this.extractPickerChoices(action);
62060
+ let index = choiceIndex;
62061
+ if ((index == null || !Number.isFinite(index)) && choiceLabel) {
62062
+ const needle = choiceLabel.trim().toLowerCase();
62063
+ const match = options.find((o) => o.label.toLowerCase().includes(needle));
62064
+ if (!match) {
62065
+ return { ok: false, error: `choice not found on screen: ${choiceLabel}`, controlResult: { options: options.map((o) => ({ value: o.label, label: o.label })) } };
62066
+ }
62067
+ index = match.index;
62068
+ }
62069
+ if (index == null || !Number.isFinite(index)) {
62070
+ return { ok: false, error: "choiceIndex or choiceLabel required to select" };
62071
+ }
62072
+ const keys = (action.submit_key || "{index}\r").replace(/\{index\}/g, String(index));
62073
+ this.driver.dispatch({ kind: "pty_write", data: keys });
62074
+ const selected = options.find((o) => o.index === index);
62075
+ return {
62076
+ ok: true,
62077
+ effects: [{ type: "selected_choice", controlId: ctl.id }],
62078
+ controlResult: {
62079
+ ok: true,
62080
+ ...selected ? { currentValue: selected.label } : {},
62081
+ selectedIndex: index
62082
+ }
62083
+ };
62084
+ }
62085
+ /** Poll the live screen until the picker's `wait_for` condition matches,
62086
+ * up to a short budget. Returns true if it rendered, false on timeout. */
62087
+ async waitForPickerRendered(action) {
62088
+ const wf = action.wait_for;
62089
+ if (!wf?.regex) {
62090
+ await delay(250);
62091
+ return true;
62092
+ }
62093
+ const re = new RegExp(wf.regex, wf.flags ?? "i");
62094
+ const deadline = Date.now() + 2500;
62095
+ while (Date.now() < deadline) {
62096
+ await delay(120);
62097
+ const hay = this.readScreenSectionText(wf.section);
62098
+ if (re.test(hay)) return true;
62099
+ }
62100
+ return false;
62101
+ }
62102
+ /** Parse the picker's `extract_choices` pattern against the live screen.
62103
+ * Each match yields { index, label, current }. `current` is true for the
62104
+ * line the CLI marks with its cursor glyph (❯ ›). Purely screen-driven —
62105
+ * no model/mode names are baked in. */
62106
+ extractPickerChoices(action) {
62107
+ const ec = action.extract_choices;
62108
+ if (!ec?.pattern) return [];
62109
+ const text = this.readScreenSectionText(ec.section);
62110
+ const out = [];
62111
+ const seen = /* @__PURE__ */ new Set();
62112
+ for (const rawLine of text.split("\n")) {
62113
+ const line = rawLine.replace(/\r$/, "");
62114
+ const m = new RegExp(ec.pattern, ec.flags ?? "").exec(line);
62115
+ if (!m) continue;
62116
+ const idx = Number(m[1]);
62117
+ if (!Number.isFinite(idx) || seen.has(idx)) continue;
62118
+ const label = (m[2] ?? "").replace(/\s+/g, " ").trim();
62119
+ if (!label) continue;
62120
+ const current = /^\s*[❯›>]/.test(line) || /[✔✓●]\s*$/.test(label);
62121
+ seen.add(idx);
62122
+ out.push({ index: idx, label, current });
62123
+ }
62124
+ return out;
62125
+ }
62126
+ /** Live text of a named screen section (or the whole screen when no
62127
+ * section is named), resolved from the driver's current sections. */
62128
+ readScreenSectionText(sectionId) {
62129
+ try {
62130
+ const sections = this.driver.getSections();
62131
+ if (sectionId && sections) {
62132
+ const hit = sections.find((s) => s.id === sectionId);
62133
+ if (hit) return hit.text;
62134
+ }
62135
+ return this.driver.getScreen();
62136
+ } catch {
62137
+ return "";
62138
+ }
61734
62139
  }
61735
62140
  getDebugSnapshot() {
61736
62141
  let screen = "";
@@ -71882,6 +72287,43 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
71882
72287
  }
71883
72288
  var MESH_DIRECT_PROBE_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_TIMEOUT_MS", 25e3);
71884
72289
  var MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS", 25e3);
72290
+ var MESH_DIRECT_PROBE_REUSE_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_REUSE_MS", 12e3);
72291
+ var MeshGitProbeCache = class {
72292
+ constructor(reuseMs, now = Date.now) {
72293
+ this.reuseMs = reuseMs;
72294
+ this.now = now;
72295
+ }
72296
+ inflight = /* @__PURE__ */ new Map();
72297
+ recent = /* @__PURE__ */ new Map();
72298
+ key(daemonId, workspace) {
72299
+ return `${daemonId}::${workspace}`;
72300
+ }
72301
+ /**
72302
+ * Run `probe` for this peer, but reuse a fresh recent result or an in-flight
72303
+ * probe for the same key when one is available. `probe` is only invoked when
72304
+ * neither gate is satisfied.
72305
+ */
72306
+ async probe(daemonId, workspace, probe) {
72307
+ const key = this.key(daemonId, workspace);
72308
+ const cached22 = this.recent.get(key);
72309
+ if (cached22 && this.now() - cached22.at < this.reuseMs) {
72310
+ return cached22.value;
72311
+ }
72312
+ const existing = this.inflight.get(key);
72313
+ if (existing) return existing;
72314
+ const pending = (async () => {
72315
+ const result = await probe();
72316
+ if (result) this.recent.set(key, { at: this.now(), value: result });
72317
+ return result;
72318
+ })();
72319
+ this.inflight.set(key, pending);
72320
+ try {
72321
+ return await pending;
72322
+ } finally {
72323
+ if (this.inflight.get(key) === pending) this.inflight.delete(key);
72324
+ }
72325
+ }
72326
+ };
71885
72327
  async function probeRemoteMeshGitStatus(args) {
71886
72328
  if (!args.dispatchMeshCommand) return null;
71887
72329
  const remoteResult = await Promise.race([
@@ -71975,7 +72417,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
71975
72417
  continue;
71976
72418
  }
71977
72419
  peerAttemptedCount += 1;
71978
- const remoteGit = await probeRemoteMeshGitStatusWithRetry({
72420
+ const runProbe = () => probeRemoteMeshGitStatusWithRetry({
71979
72421
  dispatchMeshCommand: args.dispatchMeshCommand,
71980
72422
  daemonId,
71981
72423
  workspace,
@@ -71983,6 +72425,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
71983
72425
  retryTimeoutMs: MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
71984
72426
  getConnection: args.getMeshPeerConnectionStatus
71985
72427
  });
72428
+ const remoteGit = args.probeCache ? await args.probeCache.probe(daemonId, workspace, runProbe) : await runProbe();
71986
72429
  if (remoteGit) {
71987
72430
  recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
71988
72431
  peerConfirmedCount += 1;
@@ -73305,6 +73748,10 @@ ${e?.stderr || ""}`
73305
73748
  inlineMeshCache = /* @__PURE__ */ new Map();
73306
73749
  /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
73307
73750
  aggregateMeshStatusCache = /* @__PURE__ */ new Map();
73751
+ /** Shared per-peer git_status probe dedup + recently-probed reuse gate.
73752
+ * Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
73753
+ * loop cannot storm a slow peer with back-to-back refreshUpstream probes. */
73754
+ meshGitProbeCache = new MeshGitProbeCache(MESH_DIRECT_PROBE_REUSE_MS);
73308
73755
  /** In-memory async Refinery jobs keyed by meshId:nodeId to reject/return duplicate in-flight requests. */
73309
73756
  runningRefineJobs = /* @__PURE__ */ new Map();
73310
73757
  /** Terminal async Refinery jobs preserve a clear answer after the worktree node has been removed. */
@@ -76207,7 +76654,8 @@ ${hintLines.join("\n")}` : "",
76207
76654
  getMeshPeerConnectionStatus: this.deps.getMeshPeerConnectionStatus,
76208
76655
  statusInstanceId: this.deps.statusInstanceId,
76209
76656
  localMachineId: loadConfig2().machineId || "",
76210
- probeRemotePeers
76657
+ probeRemotePeers,
76658
+ probeCache: this.meshGitProbeCache
76211
76659
  });
76212
76660
  const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
76213
76661
  const sourceOfTruth = {
@@ -76811,6 +77259,22 @@ ${hintLines.join("\n")}` : "",
76811
77259
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
76812
77260
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
76813
77261
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
77262
+ {
77263
+ const meshRecordForForward = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
77264
+ const forwardNode = meshRecordForForward?.mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
77265
+ const nodeDaemonId = typeof forwardNode?.daemonId === "string" ? forwardNode.daemonId.trim() : void 0;
77266
+ const selfDaemonId = this.deps.statusInstanceId;
77267
+ const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
77268
+ if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
77269
+ const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : void 0;
77270
+ const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "refine_mesh_node", {
77271
+ ...typeof args === "object" && args !== null ? args : {},
77272
+ coordinatorDaemonId: callerCoordinatorDaemonId || selfDaemonId,
77273
+ _meshDirectDispatch: true
77274
+ });
77275
+ return forwarded ?? { success: false, error: "no response from remote node" };
77276
+ }
77277
+ }
76814
77278
  const isDryRun = args?.dryRun !== false && args?.execute !== true;
76815
77279
  if (isDryRun) {
76816
77280
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
@@ -77783,6 +78247,7 @@ ${ptyResult.output.slice(-2e3)}`);
77783
78247
  const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
77784
78248
  const localMachineId = loadConfig2().machineId || "";
77785
78249
  const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
78250
+ const meshGitProbeCache = this.meshGitProbeCache;
77786
78251
  const directTruth = requireDirectPeerTruth ? await hydrateInlineMeshDirectTruth({
77787
78252
  mesh,
77788
78253
  meshSource: meshRecord.source,
@@ -77793,7 +78258,8 @@ ${ptyResult.output.slice(-2e3)}`);
77793
78258
  // Standing-state model: only an explicit refresh fans
77794
78259
  // out a blocking peer git probe. Default loads return
77795
78260
  // held truth so one slow peer can't block the graph.
77796
- probeRemotePeers: refreshRequested
78261
+ probeRemotePeers: refreshRequested,
78262
+ probeCache: meshGitProbeCache
77797
78263
  }) : {
77798
78264
  directEvidenceCount: 0,
77799
78265
  localConfirmedCount: 0,
@@ -77954,7 +78420,7 @@ ${ptyResult.output.slice(-2e3)}`);
77954
78420
  }
77955
78421
  remoteProbeApplied = true;
77956
78422
  } else if (refreshRequested && !isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
77957
- const remoteGit = await probeRemoteMeshGitStatusWithRetry({
78423
+ const runNodeProbe = () => probeRemoteMeshGitStatusWithRetry({
77958
78424
  dispatchMeshCommand: this.deps.dispatchMeshCommand,
77959
78425
  daemonId,
77960
78426
  workspace,
@@ -77965,6 +78431,7 @@ ${ptyResult.output.slice(-2e3)}`);
77965
78431
  status.connection = connection;
77966
78432
  }
77967
78433
  });
78434
+ const remoteGit = await meshGitProbeCache.probe(daemonId, workspace, runNodeProbe);
77968
78435
  if (remoteGit) {
77969
78436
  status.git = remoteGit;
77970
78437
  status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";