@adhdev/daemon-core 0.9.82-rc.351 → 0.9.82-rc.353

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -311,10 +311,10 @@ function readInjected(value) {
311
311
  }
312
312
  function getDaemonBuildInfo() {
313
313
  if (cached) return cached;
314
- const commit = readInjected(true ? "dad22ef2758130d10a3d55e3c419f0095d5e44b7" : void 0) ?? "unknown";
315
- const commitShort = readInjected(true ? "dad22ef2" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
316
- const version = readInjected(true ? "0.9.82-rc.351" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
317
- const builtAt = readInjected(true ? "2026-06-22T04:04:59.936Z" : void 0);
314
+ const commit = readInjected(true ? "a680b74d9b940b82f691ebb85a725a1a72445bfc" : void 0) ?? "unknown";
315
+ const commitShort = readInjected(true ? "a680b74d" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
316
+ const version = readInjected(true ? "0.9.82-rc.353" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
317
+ const builtAt = readInjected(true ? "2026-06-22T09:11:13.499Z" : void 0);
318
318
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
319
319
  return cached;
320
320
  }
@@ -2703,6 +2703,257 @@ var init_mesh_config = __esm({
2703
2703
  }
2704
2704
  });
2705
2705
 
2706
+ // ../mesh-shared/dist/index.mjs
2707
+ function readRecord(value) {
2708
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2709
+ }
2710
+ function readString3(...values) {
2711
+ for (const value of values) {
2712
+ if (typeof value !== "string") continue;
2713
+ const trimmed = value.trim();
2714
+ if (trimmed) return trimmed;
2715
+ }
2716
+ return void 0;
2717
+ }
2718
+ function readNumber(...values) {
2719
+ for (const value of values) {
2720
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2721
+ }
2722
+ return void 0;
2723
+ }
2724
+ function readBoolean(...values) {
2725
+ for (const value of values) {
2726
+ if (typeof value === "boolean") return value;
2727
+ }
2728
+ return void 0;
2729
+ }
2730
+ function joinRepoPath(root, relativePath) {
2731
+ const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
2732
+ const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
2733
+ if (!normalizedPath) return void 0;
2734
+ if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
2735
+ if (!normalizedRoot) return void 0;
2736
+ return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, "")}`;
2737
+ }
2738
+ function scoreGitUpstreamFreshness(status) {
2739
+ switch (status) {
2740
+ case "fresh":
2741
+ return 30;
2742
+ case "no_upstream":
2743
+ return 4;
2744
+ case "unchecked":
2745
+ case void 0:
2746
+ return 0;
2747
+ case "stale":
2748
+ return -10;
2749
+ case "unavailable":
2750
+ return -15;
2751
+ default:
2752
+ return 0;
2753
+ }
2754
+ }
2755
+ function readGitSubmodules(value, parentRepoRoot) {
2756
+ if (!Array.isArray(value)) return void 0;
2757
+ const submodules = value.map((entry) => {
2758
+ const submodule = readRecord(entry);
2759
+ const path42 = readString3(submodule.path);
2760
+ const commit = readString3(submodule.commit);
2761
+ const repoPath = readString3(submodule.repoPath, submodule.repo_root) ?? joinRepoPath(parentRepoRoot, path42);
2762
+ if (!path42 || !commit) return null;
2763
+ const result = {
2764
+ path: path42,
2765
+ commit,
2766
+ dirty: readBoolean(submodule.dirty) ?? false,
2767
+ outOfSync: readBoolean(submodule.outOfSync, submodule.out_of_sync) ?? false,
2768
+ lastCheckedAt: readNumber(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now()
2769
+ };
2770
+ if (repoPath) result.repoPath = repoPath;
2771
+ const error = readString3(submodule.error);
2772
+ if (error) result.error = error;
2773
+ return result;
2774
+ }).filter((entry) => entry !== null);
2775
+ return submodules.length > 0 ? submodules : void 0;
2776
+ }
2777
+ function hasGitStatusEvidence(status) {
2778
+ return readBoolean(status.isGitRepo) !== void 0 || Boolean(readString3(status.branch, status.upstream, status.upstreamStatus, status.upstream_status, status.headCommit)) || Boolean(readString3(status.repoRoot, status.repo_root, status.workspace)) || readNumber(
2779
+ status.ahead,
2780
+ status.behind,
2781
+ status.staged,
2782
+ status.modified,
2783
+ status.untracked,
2784
+ status.deleted,
2785
+ status.renamed,
2786
+ status.lastCheckedAt,
2787
+ status.last_checked_at
2788
+ ) !== void 0 || Array.isArray(status.submodules) && status.submodules.length > 0;
2789
+ }
2790
+ function normalizeGitStatus(status, node, options) {
2791
+ const explicitIsGitRepo = readBoolean(status.isGitRepo);
2792
+ if (!Object.keys(status).length || !hasGitStatusEvidence(status)) return void 0;
2793
+ const isGitRepo = explicitIsGitRepo ?? true;
2794
+ const conflictFiles = Array.isArray(status.conflictFiles) ? status.conflictFiles.filter((entry) => typeof entry === "string") : [];
2795
+ const conflictCount = readNumber(status.conflicts) ?? conflictFiles.length;
2796
+ const hasConflicts = readBoolean(status.hasConflicts) ?? conflictCount > 0;
2797
+ const repoRoot = readString3(status.repoRoot, status.repo_root, node.repoRoot, node.repo_root, status.workspace, node.workspace) || void 0;
2798
+ const submodules = readGitSubmodules(status.submodules, repoRoot);
2799
+ const upstreamStatus = readString3(status.upstreamStatus, status.upstream_status);
2800
+ const upstreamFetchedAt = readNumber(status.upstreamFetchedAt, status.upstream_fetched_at);
2801
+ const upstreamFetchError = readString3(status.upstreamFetchError, status.upstream_fetch_error);
2802
+ const error = readString3(status.error);
2803
+ const staged = readNumber(status.staged) ?? 0;
2804
+ const modified = readNumber(status.modified) ?? 0;
2805
+ const untracked = readNumber(status.untracked) ?? 0;
2806
+ const deleted = readNumber(status.deleted) ?? 0;
2807
+ const renamed = readNumber(status.renamed) ?? 0;
2808
+ return {
2809
+ workspace: readString3(status.workspace, node.workspace) || "",
2810
+ repoRoot: repoRoot ?? null,
2811
+ isGitRepo,
2812
+ branch: readString3(status.branch) ?? null,
2813
+ headCommit: readString3(status.headCommit) ?? null,
2814
+ headMessage: readString3(status.headMessage) ?? null,
2815
+ upstream: readString3(status.upstream) ?? null,
2816
+ upstreamStatus: upstreamStatus ?? "unchecked",
2817
+ ...upstreamFetchedAt !== void 0 ? { upstreamFetchedAt } : {},
2818
+ ...upstreamFetchError ? { upstreamFetchError } : {},
2819
+ ahead: readNumber(status.ahead) ?? 0,
2820
+ behind: readNumber(status.behind) ?? 0,
2821
+ staged,
2822
+ modified,
2823
+ untracked,
2824
+ deleted,
2825
+ renamed,
2826
+ dirty: readBoolean(status.dirty, status.isDirty, status.is_dirty) ?? (staged + modified + untracked + deleted + renamed > 0 || hasConflicts),
2827
+ hasConflicts,
2828
+ conflictFiles,
2829
+ stashCount: readNumber(status.stashCount, status.stash_count) ?? 0,
2830
+ lastCheckedAt: options?.lastCheckedAt ?? readNumber(status.lastCheckedAt, status.last_checked_at) ?? Date.now(),
2831
+ ...submodules ? { submodules } : {},
2832
+ ...error ? { error } : {}
2833
+ };
2834
+ }
2835
+ function scoreGitStatusCandidate(git) {
2836
+ if (!git) return Number.NEGATIVE_INFINITY;
2837
+ let score = 0;
2838
+ if (git.isGitRepo === true) score += 50;
2839
+ if (git.isGitRepo === false) score -= 10;
2840
+ if (git.branch) score += 20;
2841
+ if (git.headCommit) score += 20;
2842
+ if (git.upstream) score += 10;
2843
+ score += scoreGitUpstreamFreshness(git.upstreamStatus);
2844
+ if (typeof git.ahead === "number") score += 2;
2845
+ if (typeof git.behind === "number") score += 2;
2846
+ if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length;
2847
+ if (git.error) score -= 20;
2848
+ return score;
2849
+ }
2850
+ function pickBestTransitGitStatus(node, options) {
2851
+ const rawGit = readRecord(node.lastGit ?? node.last_git);
2852
+ const gitResult = readRecord(rawGit.result);
2853
+ const directStatus = readRecord(rawGit.status);
2854
+ const nestedStatus = readRecord(gitResult.status);
2855
+ const rawProbe = readRecord(node.lastProbe ?? node.last_probe);
2856
+ const probeGit = readRecord(rawProbe.git);
2857
+ const probeGitResult = readRecord(probeGit.result);
2858
+ const probeDirectStatus = readRecord(probeGit.status);
2859
+ const probeNestedStatus = readRecord(probeGitResult.status);
2860
+ const lastCheckedAt = options?.lastCheckedAt;
2861
+ let best = null;
2862
+ for (const status of [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus]) {
2863
+ const normalized = normalizeGitStatus(status, node, { lastCheckedAt: lastCheckedAt ?? Date.now() });
2864
+ if (!normalized) continue;
2865
+ const score = scoreGitStatusCandidate(normalized);
2866
+ if (!best || score > best.score) best = { git: normalized, score };
2867
+ }
2868
+ return best?.git;
2869
+ }
2870
+ function normalizeMeshNodeId(node) {
2871
+ const record = node && typeof node === "object" ? node : {};
2872
+ return readString3(record.id, record.nodeId, record.node_id);
2873
+ }
2874
+ function meshNodeIdMatches(node, candidateId) {
2875
+ if (!candidateId) return false;
2876
+ const trimmed = candidateId.trim();
2877
+ if (!trimmed) return false;
2878
+ return normalizeMeshNodeId(node) === trimmed;
2879
+ }
2880
+ function machineCoreFromDaemonId(id) {
2881
+ const trimmed = readString3(id);
2882
+ if (!trimmed) return void 0;
2883
+ for (const prefix of DAEMON_ID_PREFIXES) {
2884
+ if (trimmed.startsWith(prefix)) {
2885
+ const core = trimmed.slice(prefix.length).trim();
2886
+ return core || void 0;
2887
+ }
2888
+ }
2889
+ return trimmed;
2890
+ }
2891
+ function daemonIdsEquivalent(a, b) {
2892
+ const coreA = machineCoreFromDaemonId(a);
2893
+ const coreB = machineCoreFromDaemonId(b);
2894
+ if (!coreA || !coreB) return false;
2895
+ return coreA === coreB;
2896
+ }
2897
+ function expandDaemonIdForms(ids) {
2898
+ const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
2899
+ const out = [];
2900
+ const seen = /* @__PURE__ */ new Set();
2901
+ const add = (value) => {
2902
+ if (!value || seen.has(value)) return;
2903
+ seen.add(value);
2904
+ out.push(value);
2905
+ };
2906
+ for (const raw of list) add(readString3(raw));
2907
+ for (const raw of list) {
2908
+ const core = machineCoreFromDaemonId(readString3(raw));
2909
+ if (!core || !core.startsWith("mach_")) continue;
2910
+ add(core);
2911
+ for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`);
2912
+ }
2913
+ return out;
2914
+ }
2915
+ function summarizeGitShape(status) {
2916
+ const record = readRecord(status);
2917
+ if (!Object.keys(record).length) return null;
2918
+ const submodules = Array.isArray(record.submodules) ? record.submodules.map((entry) => {
2919
+ const sub = readRecord(entry);
2920
+ return {
2921
+ path: readString3(sub.path) ?? null,
2922
+ commit: readString3(sub.commit)?.slice(0, 12) ?? null,
2923
+ dirty: readBoolean(sub.dirty) ?? false,
2924
+ outOfSync: readBoolean(sub.outOfSync, sub.out_of_sync) ?? false
2925
+ };
2926
+ }) : [];
2927
+ return {
2928
+ isGitRepo: readBoolean(record.isGitRepo),
2929
+ workspace: readString3(record.workspace) ?? null,
2930
+ repoRoot: readString3(record.repoRoot, record.repo_root) ?? null,
2931
+ branch: readString3(record.branch) ?? null,
2932
+ upstream: readString3(record.upstream) ?? null,
2933
+ upstreamStatus: readString3(record.upstreamStatus, record.upstream_status) ?? null,
2934
+ headCommit: readString3(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
2935
+ ahead: readNumber(record.ahead) ?? null,
2936
+ behind: readNumber(record.behind) ?? null,
2937
+ dirtyCounts: {
2938
+ staged: readNumber(record.staged) ?? 0,
2939
+ modified: readNumber(record.modified) ?? 0,
2940
+ untracked: readNumber(record.untracked) ?? 0,
2941
+ deleted: readNumber(record.deleted) ?? 0,
2942
+ renamed: readNumber(record.renamed) ?? 0
2943
+ },
2944
+ lastCheckedAt: readNumber(record.lastCheckedAt, record.last_checked_at) ?? null,
2945
+ submoduleCount: submodules.length,
2946
+ submodules
2947
+ };
2948
+ }
2949
+ var DAEMON_ID_PREFIXES;
2950
+ var init_dist = __esm({
2951
+ "../mesh-shared/dist/index.mjs"() {
2952
+ "use strict";
2953
+ DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
2954
+ }
2955
+ });
2956
+
2706
2957
  // src/mesh/coordinator-prompt.ts
2707
2958
  var coordinator_prompt_exports = {};
2708
2959
  __export(coordinator_prompt_exports, {
@@ -3193,7 +3444,7 @@ function installGlobalInterceptor() {
3193
3444
  function getLogPath() {
3194
3445
  return currentLogFile;
3195
3446
  }
3196
- var LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
3447
+ var LEVEL_NUM, LEVEL_LABEL, currentLevel, ADHDEV_HOME, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
3197
3448
  var init_logger = __esm({
3198
3449
  "src/logging/logger.ts"() {
3199
3450
  "use strict";
@@ -3201,7 +3452,8 @@ var init_logger = __esm({
3201
3452
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
3202
3453
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
3203
3454
  currentLevel = "info";
3204
- LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os3.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os3.homedir(), "Library", "Logs", "adhdev") : path9.join(os3.homedir(), ".local", "share", "adhdev", "logs");
3455
+ ADHDEV_HOME = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path9.join(os3.homedir(), ".adhdev");
3456
+ LOG_DIR = path9.join(ADHDEV_HOME, "logs");
3205
3457
  MAX_LOG_SIZE = 5 * 1024 * 1024;
3206
3458
  MAX_LOG_DAYS = 7;
3207
3459
  try {
@@ -4001,6 +4253,7 @@ __export(mesh_work_queue_exports, {
4001
4253
  nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
4002
4254
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
4003
4255
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
4256
+ reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
4004
4257
  recordDirectDispatchTask: () => recordDirectDispatchTask,
4005
4258
  recordMeshToolCall: () => recordMeshToolCall,
4006
4259
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
@@ -4437,6 +4690,52 @@ function requeueTask(meshId, taskId, opts) {
4437
4690
  return entry;
4438
4691
  });
4439
4692
  }
4693
+ function reclaimStrandedAssignedTask(meshId, taskId, opts) {
4694
+ requireMeshHostQueueOwner(opts);
4695
+ return withQueueLock(meshId, () => {
4696
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
4697
+ if (!entry) return null;
4698
+ if (entry.status !== "assigned") return null;
4699
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4700
+ const reason = opts?.reason || "assigned_stranded_dispatch_unconfirmed";
4701
+ const reclaims = (entry.strandedReclaimCount || 0) + 1;
4702
+ const prevNode = entry.assignedNodeId;
4703
+ const prevSession = entry.assignedSessionId;
4704
+ delete entry.assignedNodeId;
4705
+ delete entry.assignedSessionId;
4706
+ delete entry.assignedProviderType;
4707
+ delete entry.dispatchTimestamp;
4708
+ entry.strandedReclaimCount = reclaims;
4709
+ entry.updatedAt = now;
4710
+ if (reclaims > MAX_STRANDED_RECLAIMS) {
4711
+ entry.status = "failed";
4712
+ entry.cancelReason = `stranded_dispatch_unrecovered: reclaimed ${reclaims - 1} time(s) without a confirmed dispatch`;
4713
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
4714
+ propagateDependencyFailure(meshId, taskId);
4715
+ } else {
4716
+ entry.status = "pending";
4717
+ entry.requeuedAt = now;
4718
+ entry.requeueReason = reason;
4719
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
4720
+ }
4721
+ try {
4722
+ appendLedgerEntry(meshId, {
4723
+ kind: "task_reclaimed",
4724
+ nodeId: prevNode,
4725
+ sessionId: prevSession,
4726
+ payload: {
4727
+ taskId,
4728
+ reason,
4729
+ ...typeof opts?.ageMs === "number" ? { ageMs: opts.ageMs } : {},
4730
+ reclaimCount: reclaims,
4731
+ outcome: entry.status
4732
+ }
4733
+ });
4734
+ } catch {
4735
+ }
4736
+ return entry;
4737
+ });
4738
+ }
4440
4739
  function updateSessionTaskStatus(meshId, sessionId, status, opts) {
4441
4740
  return withQueueLock(meshId, () => {
4442
4741
  const store = MeshRuntimeStore.getInstance();
@@ -4550,7 +4849,7 @@ function recordMeshToolCall(opts) {
4550
4849
  return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
4551
4850
  }
4552
4851
  }
4553
- var ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS;
4852
+ var ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS;
4554
4853
  var init_mesh_work_queue = __esm({
4555
4854
  "src/mesh/mesh-work-queue.ts"() {
4556
4855
  "use strict";
@@ -4559,6 +4858,7 @@ var init_mesh_work_queue = __esm({
4559
4858
  init_mesh_runtime_store();
4560
4859
  init_mesh_config();
4561
4860
  init_logger();
4861
+ init_mesh_ledger();
4562
4862
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
4563
4863
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
4564
4864
  MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
@@ -4611,6 +4911,7 @@ var init_mesh_work_queue = __esm({
4611
4911
  ]);
4612
4912
  GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
4613
4913
  DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
4914
+ MAX_STRANDED_RECLAIMS = 3;
4614
4915
  }
4615
4916
  });
4616
4917
 
@@ -5496,6 +5797,22 @@ var init_mesh_runtime_store = __esm({
5496
5797
  updatedAt: r.updated_at
5497
5798
  }));
5498
5799
  }
5800
+ /**
5801
+ * Bug B watchdog support: true when at least one delivery record for the task has
5802
+ * reached a confirmed-handed-off status (delivered / acked / completed). The
5803
+ * assigned-stranded watchdog uses this to distinguish a dispatch that was never
5804
+ * confirmed (reclaimable) from one that WAS handed to the worker (a genuinely
5805
+ * in-flight or completion-lost task, which is PHASE 4's responsibility, not this
5806
+ * watchdog's). Indexed by (mesh_id, task_id).
5807
+ */
5808
+ taskHasConfirmedDelivery(meshId, taskId) {
5809
+ const row = this.db.prepare(`
5810
+ SELECT 1 FROM mesh_session_delivery
5811
+ WHERE mesh_id = ? AND task_id = ? AND status IN ('delivered','acked','completed')
5812
+ LIMIT 1
5813
+ `).get(meshId, taskId);
5814
+ return !!row;
5815
+ }
5499
5816
  expireStaleSessionDeliveries(meshId) {
5500
5817
  const now = (/* @__PURE__ */ new Date()).toISOString();
5501
5818
  this.db.prepare(`
@@ -6244,10 +6561,10 @@ var init_mesh_missions = __esm({
6244
6561
  });
6245
6562
 
6246
6563
  // src/mesh/mesh-refine-status.ts
6247
- function readString3(value) {
6564
+ function readString4(value) {
6248
6565
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
6249
6566
  }
6250
- function readRecord(value) {
6567
+ function readRecord2(value) {
6251
6568
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
6252
6569
  }
6253
6570
  function eventStatus(event, fallback) {
@@ -6270,7 +6587,7 @@ function instructionForStatus(status) {
6270
6587
  return "Refine job failed; inspect result/finalBranchConvergenceState in mesh_task_history, fix the blocker, then rerun mesh_refine_node when ready.";
6271
6588
  }
6272
6589
  function mergeJob(jobs, patch) {
6273
- const jobId = readString3(patch.jobId);
6590
+ const jobId = readString4(patch.jobId);
6274
6591
  if (!jobId) return;
6275
6592
  const previous = jobs.get(jobId);
6276
6593
  const status = patch.status || previous?.status || "running";
@@ -6288,54 +6605,54 @@ function mergeJob(jobs, patch) {
6288
6605
  function buildMeshAsyncRefineJobs(args) {
6289
6606
  const jobs = /* @__PURE__ */ new Map();
6290
6607
  for (const entry of args.ledgerEntries || []) {
6291
- const payload = readRecord(entry.payload);
6608
+ const payload = readRecord2(entry.payload);
6292
6609
  if (payload?.source !== "refine_mesh_node_async_job") continue;
6293
- const refineJob = readRecord(payload.refineJob);
6294
- const result = readRecord(payload.result);
6295
- const finalState = readRecord(payload.finalBranchConvergenceState) || readRecord(result?.finalBranchConvergenceState);
6296
- const jobId = readString3(refineJob?.jobId);
6610
+ const refineJob = readRecord2(payload.refineJob);
6611
+ const result = readRecord2(payload.result);
6612
+ const finalState = readRecord2(payload.finalBranchConvergenceState) || readRecord2(result?.finalBranchConvergenceState);
6613
+ const jobId = readString4(refineJob?.jobId);
6297
6614
  if (!jobId) continue;
6298
- const status = ledgerStatus(entry.kind, readString3(refineJob?.status));
6615
+ const status = ledgerStatus(entry.kind, readString4(refineJob?.status));
6299
6616
  mergeJob(jobs, {
6300
6617
  jobId,
6301
- interactionId: readString3(refineJob?.interactionId),
6618
+ interactionId: readString4(refineJob?.interactionId),
6302
6619
  status,
6303
- meshId: readString3(refineJob?.meshId) || args.meshId,
6304
- nodeId: readString3(refineJob?.nodeId) || entry.nodeId,
6305
- targetNodeId: readString3(refineJob?.nodeId) || entry.nodeId,
6306
- targetDaemonId: readString3(refineJob?.targetDaemonId),
6307
- workspace: readString3(refineJob?.workspace),
6308
- branch: readString3(result?.branch) || readString3(finalState?.branch),
6309
- into: readString3(result?.into) || readString3(finalState?.baseBranch),
6310
- startedAt: readString3(refineJob?.startedAt),
6311
- completedAt: readString3(refineJob?.completedAt),
6312
- retryOfJobId: readString3(refineJob?.retryOfJobId) || readString3(payload.retryOfJobId),
6620
+ meshId: readString4(refineJob?.meshId) || args.meshId,
6621
+ nodeId: readString4(refineJob?.nodeId) || entry.nodeId,
6622
+ targetNodeId: readString4(refineJob?.nodeId) || entry.nodeId,
6623
+ targetDaemonId: readString4(refineJob?.targetDaemonId),
6624
+ workspace: readString4(refineJob?.workspace),
6625
+ branch: readString4(result?.branch) || readString4(finalState?.branch),
6626
+ into: readString4(result?.into) || readString4(finalState?.baseBranch),
6627
+ startedAt: readString4(refineJob?.startedAt),
6628
+ completedAt: readString4(refineJob?.completedAt),
6629
+ retryOfJobId: readString4(refineJob?.retryOfJobId) || readString4(payload.retryOfJobId),
6313
6630
  lastLedgerKind: entry.kind,
6314
6631
  lastUpdatedAt: entry.timestamp
6315
6632
  });
6316
6633
  }
6317
6634
  for (const event of args.pendingEvents || []) {
6318
- const metadata = readRecord(event.metadataEvent);
6635
+ const metadata = readRecord2(event.metadataEvent);
6319
6636
  if (metadata?.source !== "refine_mesh_node_async_job") continue;
6320
- const result = readRecord(metadata.result);
6321
- const finalState = readRecord(result?.finalBranchConvergenceState);
6322
- const jobId = readString3(metadata.jobId);
6637
+ const result = readRecord2(metadata.result);
6638
+ const finalState = readRecord2(result?.finalBranchConvergenceState);
6639
+ const jobId = readString4(metadata.jobId);
6323
6640
  if (!jobId) continue;
6324
- const status = eventStatus(event.event, readString3(metadata.status));
6641
+ const status = eventStatus(event.event, readString4(metadata.status));
6325
6642
  mergeJob(jobs, {
6326
6643
  jobId,
6327
- interactionId: readString3(metadata.interactionId),
6644
+ interactionId: readString4(metadata.interactionId),
6328
6645
  ...status ? { status } : {},
6329
- meshId: readString3(metadata.meshId) || event.meshId || args.meshId,
6330
- nodeId: readString3(metadata.nodeId) || event.nodeId,
6331
- targetNodeId: readString3(metadata.nodeId) || event.nodeId,
6332
- targetDaemonId: readString3(metadata.targetDaemonId),
6333
- workspace: readString3(metadata.workspace) || event.workspace,
6334
- branch: readString3(result?.branch) || readString3(finalState?.branch),
6335
- into: readString3(result?.into) || readString3(finalState?.baseBranch),
6336
- startedAt: readString3(metadata.startedAt),
6337
- completedAt: readString3(metadata.completedAt),
6338
- retryOfJobId: readString3(metadata.retryOfJobId),
6646
+ meshId: readString4(metadata.meshId) || event.meshId || args.meshId,
6647
+ nodeId: readString4(metadata.nodeId) || event.nodeId,
6648
+ targetNodeId: readString4(metadata.nodeId) || event.nodeId,
6649
+ targetDaemonId: readString4(metadata.targetDaemonId),
6650
+ workspace: readString4(metadata.workspace) || event.workspace,
6651
+ branch: readString4(result?.branch) || readString4(finalState?.branch),
6652
+ into: readString4(result?.into) || readString4(finalState?.baseBranch),
6653
+ startedAt: readString4(metadata.startedAt),
6654
+ completedAt: readString4(metadata.completedAt),
6655
+ retryOfJobId: readString4(metadata.retryOfJobId),
6339
6656
  lastEvent: event.event,
6340
6657
  lastUpdatedAt: new Date(event.queuedAt).toISOString()
6341
6658
  });
@@ -6396,10 +6713,10 @@ var mesh_review_inbox_exports = {};
6396
6713
  __export(mesh_review_inbox_exports, {
6397
6714
  deriveMeshReviewInboxItems: () => deriveMeshReviewInboxItems
6398
6715
  });
6399
- function readString4(value) {
6716
+ function readString5(value) {
6400
6717
  return typeof value === "string" && value.trim() ? value.trim() : null;
6401
6718
  }
6402
- function readRecord2(value) {
6719
+ function readRecord3(value) {
6403
6720
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
6404
6721
  }
6405
6722
  function readStringArray3(value, max) {
@@ -6409,20 +6726,20 @@ function readStringArray3(value, max) {
6409
6726
  }
6410
6727
  function isLocalNodeStatus(node) {
6411
6728
  if (node.isLocalWorktree === true) return true;
6412
- const connection = readRecord2(node.connection);
6413
- return readString4(connection?.state) === "self";
6729
+ const connection = readRecord3(node.connection);
6730
+ return readString5(connection?.state) === "self";
6414
6731
  }
6415
6732
  function readNodeConvergence(node) {
6416
- const convergence = readRecord2(node.branchConvergence);
6417
- const status = readString4(convergence?.status);
6733
+ const convergence = readRecord3(node.branchConvergence);
6734
+ const status = readString5(convergence?.status);
6418
6735
  if (!convergence || !status) return null;
6419
6736
  return {
6420
6737
  status,
6421
- reason: readString4(convergence.reason),
6422
- nextStep: readString4(convergence.nextStep),
6738
+ reason: readString5(convergence.reason),
6739
+ nextStep: readString5(convergence.nextStep),
6423
6740
  needsConvergence: convergence.needsConvergence === true,
6424
- branch: readString4(convergence.branch),
6425
- defaultBranch: readString4(convergence.defaultBranch)
6741
+ branch: readString5(convergence.branch),
6742
+ defaultBranch: readString5(convergence.defaultBranch)
6426
6743
  };
6427
6744
  }
6428
6745
  function isMergeCandidate(convergence) {
@@ -6430,15 +6747,15 @@ function isMergeCandidate(convergence) {
6430
6747
  return convergence.status === "cleanup_candidate" && convergence.reason === "clean_non_default_worktree_branch";
6431
6748
  }
6432
6749
  function readWorkerArtifact(value) {
6433
- const worker = readRecord2(value);
6750
+ const worker = readRecord3(value);
6434
6751
  if (!worker) return null;
6435
6752
  const changed = readStringArray3(worker.changedFiles, MAX_CHANGED_FILES);
6436
6753
  return {
6437
- status: readString4(worker.status) ?? "unknown",
6438
- ...readString4(worker.classification) ? { classification: readString4(worker.classification) } : {},
6754
+ status: readString5(worker.status) ?? "unknown",
6755
+ ...readString5(worker.classification) ? { classification: readString5(worker.classification) } : {},
6439
6756
  changedFiles: changed.values,
6440
6757
  ...changed.truncated ? { changedFilesTruncated: true } : {},
6441
- validationResults: Array.isArray(worker.validationResults) ? worker.validationResults.map((item) => readRecord2(item)).filter((item) => item !== null) : [],
6758
+ validationResults: Array.isArray(worker.validationResults) ? worker.validationResults.map((item) => readRecord3(item)).filter((item) => item !== null) : [],
6442
6759
  errors: readStringArray3(worker.errors, 20).values,
6443
6760
  requiresUserAction: worker.requiresUserAction === true
6444
6761
  };
@@ -6452,43 +6769,43 @@ function resolveNodeEvidence(nodeId, ledgerEntries) {
6452
6769
  for (let i = ledgerEntries.length - 1; i >= 0; i--) {
6453
6770
  const entry = ledgerEntries[i];
6454
6771
  if (entry.nodeId !== nodeId || !isTerminalLedgerKind(entry.kind)) continue;
6455
- const payload = readRecord2(entry.payload) ?? {};
6772
+ const payload = readRecord3(entry.payload) ?? {};
6456
6773
  if (!evidence.available) {
6457
6774
  if (payload.source === "refine_mesh_node_async_job") {
6458
- const result = readRecord2(payload.result);
6459
- const validationSummary = readRecord2(result?.validationSummary);
6775
+ const result = readRecord3(payload.result);
6776
+ const validationSummary = readRecord3(result?.validationSummary);
6460
6777
  evidence = {
6461
6778
  available: true,
6462
6779
  kind: entry.kind,
6463
6780
  source: "refine_job",
6464
6781
  timestamp: entry.timestamp,
6465
6782
  ...entry.sessionId ? { sessionId: entry.sessionId } : {},
6466
- bootstrap: readRecord2(validationSummary?.bootstrap),
6783
+ bootstrap: readRecord3(validationSummary?.bootstrap),
6467
6784
  validation: validationSummary ? Object.fromEntries(Object.entries(validationSummary).filter(([key]) => key !== "bootstrap")) : null,
6468
- checkpoint: readRecord2(result?.checkpoint),
6785
+ checkpoint: readRecord3(result?.checkpoint),
6469
6786
  worker: null,
6470
- ...readRecord2(payload.finalBranchConvergenceState) ?? readRecord2(result?.finalBranchConvergenceState) ? { finalBranchConvergenceState: readRecord2(payload.finalBranchConvergenceState) ?? readRecord2(result?.finalBranchConvergenceState) } : {},
6471
- ...readRecord2(payload.refineJob) ? { refineJob: readRecord2(payload.refineJob) } : {}
6787
+ ...readRecord3(payload.finalBranchConvergenceState) ?? readRecord3(result?.finalBranchConvergenceState) ? { finalBranchConvergenceState: readRecord3(payload.finalBranchConvergenceState) ?? readRecord3(result?.finalBranchConvergenceState) } : {},
6788
+ ...readRecord3(payload.refineJob) ? { refineJob: readRecord3(payload.refineJob) } : {}
6472
6789
  };
6473
6790
  } else {
6474
- const envelope = readRecord2(payload.evidence);
6791
+ const envelope = readRecord3(payload.evidence);
6475
6792
  evidence = {
6476
6793
  available: true,
6477
6794
  kind: entry.kind,
6478
6795
  source: "task_completion",
6479
6796
  timestamp: entry.timestamp,
6480
- ...readString4(payload.taskId) ? { taskId: readString4(payload.taskId) } : {},
6797
+ ...readString5(payload.taskId) ? { taskId: readString5(payload.taskId) } : {},
6481
6798
  ...entry.sessionId ? { sessionId: entry.sessionId } : {},
6482
6799
  bootstrap: null,
6483
- validation: readRecord2(envelope?.validation),
6484
- checkpoint: readRecord2(envelope?.checkpoint),
6800
+ validation: readRecord3(envelope?.validation),
6801
+ checkpoint: readRecord3(envelope?.checkpoint),
6485
6802
  worker: readWorkerArtifact(envelope?.workerResult ?? payload.workerResult)
6486
6803
  };
6487
6804
  }
6488
6805
  }
6489
6806
  if (!transcriptHandle) {
6490
- const envelope = readRecord2(payload.evidence);
6491
- transcriptHandle = readRecord2(envelope?.transcriptHandle);
6807
+ const envelope = readRecord3(payload.evidence);
6808
+ transcriptHandle = readRecord3(envelope?.transcriptHandle);
6492
6809
  }
6493
6810
  if (evidence.available && transcriptHandle) break;
6494
6811
  }
@@ -6498,11 +6815,11 @@ function hasBlockedReviewRefineResult(nodeId, ledgerEntries) {
6498
6815
  for (let i = ledgerEntries.length - 1; i >= 0; i--) {
6499
6816
  const entry = ledgerEntries[i];
6500
6817
  if (entry.nodeId !== nodeId || !isTerminalLedgerKind(entry.kind)) continue;
6501
- const payload = readRecord2(entry.payload) ?? {};
6818
+ const payload = readRecord3(entry.payload) ?? {};
6502
6819
  if (payload.source !== "refine_mesh_node_async_job") continue;
6503
- const result = readRecord2(payload.result);
6504
- const finalState = readRecord2(payload.finalBranchConvergenceState) ?? readRecord2(result?.finalBranchConvergenceState);
6505
- return readString4(finalState?.status) === "blocked_review" || readString4(result?.code) === "blocked_review";
6820
+ const result = readRecord3(payload.result);
6821
+ const finalState = readRecord3(payload.finalBranchConvergenceState) ?? readRecord3(result?.finalBranchConvergenceState);
6822
+ return readString5(finalState?.status) === "blocked_review" || readString5(result?.code) === "blocked_review";
6506
6823
  }
6507
6824
  return false;
6508
6825
  }
@@ -6511,7 +6828,7 @@ function deriveMeshReviewInboxItems(args) {
6511
6828
  const excludedRemoteNodeIds = [];
6512
6829
  const refineJobs = buildMeshAsyncRefineJobs({ ledgerEntries: args.ledgerEntries });
6513
6830
  for (const node of args.nodes) {
6514
- const nodeId = readString4(node.nodeId) ?? readString4(node.id);
6831
+ const nodeId = readString5(node.nodeId) ?? readString5(node.id);
6515
6832
  if (!nodeId) continue;
6516
6833
  if (!isLocalNodeStatus(node)) {
6517
6834
  excludedRemoteNodeIds.push(nodeId);
@@ -6533,8 +6850,8 @@ function deriveMeshReviewInboxItems(args) {
6533
6850
  ) ?? null;
6534
6851
  items.push({
6535
6852
  nodeId,
6536
- workspace: readString4(node.workspace),
6537
- branch: convergence.branch ?? readString4(node.worktreeBranch),
6853
+ workspace: readString5(node.workspace),
6854
+ branch: convergence.branch ?? readString5(node.worktreeBranch),
6538
6855
  defaultBranch: convergence.defaultBranch,
6539
6856
  isLocalWorktree: node.isLocalWorktree === true,
6540
6857
  reviewReason,
@@ -7745,220 +8062,6 @@ var init_mesh_fast_forward = __esm({
7745
8062
  }
7746
8063
  });
7747
8064
 
7748
- // ../mesh-shared/dist/index.mjs
7749
- function readRecord3(value) {
7750
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
7751
- }
7752
- function readString5(...values) {
7753
- for (const value of values) {
7754
- if (typeof value !== "string") continue;
7755
- const trimmed = value.trim();
7756
- if (trimmed) return trimmed;
7757
- }
7758
- return void 0;
7759
- }
7760
- function readNumber(...values) {
7761
- for (const value of values) {
7762
- if (typeof value === "number" && Number.isFinite(value)) return value;
7763
- }
7764
- return void 0;
7765
- }
7766
- function readBoolean(...values) {
7767
- for (const value of values) {
7768
- if (typeof value === "boolean") return value;
7769
- }
7770
- return void 0;
7771
- }
7772
- function joinRepoPath(root, relativePath) {
7773
- const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
7774
- const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
7775
- if (!normalizedPath) return void 0;
7776
- if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
7777
- if (!normalizedRoot) return void 0;
7778
- return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, "")}`;
7779
- }
7780
- function scoreGitUpstreamFreshness(status) {
7781
- switch (status) {
7782
- case "fresh":
7783
- return 30;
7784
- case "no_upstream":
7785
- return 4;
7786
- case "unchecked":
7787
- case void 0:
7788
- return 0;
7789
- case "stale":
7790
- return -10;
7791
- case "unavailable":
7792
- return -15;
7793
- default:
7794
- return 0;
7795
- }
7796
- }
7797
- function readGitSubmodules(value, parentRepoRoot) {
7798
- if (!Array.isArray(value)) return void 0;
7799
- const submodules = value.map((entry) => {
7800
- const submodule = readRecord3(entry);
7801
- const path42 = readString5(submodule.path);
7802
- const commit = readString5(submodule.commit);
7803
- const repoPath = readString5(submodule.repoPath, submodule.repo_root) ?? joinRepoPath(parentRepoRoot, path42);
7804
- if (!path42 || !commit) return null;
7805
- const result = {
7806
- path: path42,
7807
- commit,
7808
- dirty: readBoolean(submodule.dirty) ?? false,
7809
- outOfSync: readBoolean(submodule.outOfSync, submodule.out_of_sync) ?? false,
7810
- lastCheckedAt: readNumber(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now()
7811
- };
7812
- if (repoPath) result.repoPath = repoPath;
7813
- const error = readString5(submodule.error);
7814
- if (error) result.error = error;
7815
- return result;
7816
- }).filter((entry) => entry !== null);
7817
- return submodules.length > 0 ? submodules : void 0;
7818
- }
7819
- function hasGitStatusEvidence(status) {
7820
- return readBoolean(status.isGitRepo) !== void 0 || Boolean(readString5(status.branch, status.upstream, status.upstreamStatus, status.upstream_status, status.headCommit)) || Boolean(readString5(status.repoRoot, status.repo_root, status.workspace)) || readNumber(
7821
- status.ahead,
7822
- status.behind,
7823
- status.staged,
7824
- status.modified,
7825
- status.untracked,
7826
- status.deleted,
7827
- status.renamed,
7828
- status.lastCheckedAt,
7829
- status.last_checked_at
7830
- ) !== void 0 || Array.isArray(status.submodules) && status.submodules.length > 0;
7831
- }
7832
- function normalizeGitStatus(status, node, options) {
7833
- const explicitIsGitRepo = readBoolean(status.isGitRepo);
7834
- if (!Object.keys(status).length || !hasGitStatusEvidence(status)) return void 0;
7835
- const isGitRepo = explicitIsGitRepo ?? true;
7836
- const conflictFiles = Array.isArray(status.conflictFiles) ? status.conflictFiles.filter((entry) => typeof entry === "string") : [];
7837
- const conflictCount = readNumber(status.conflicts) ?? conflictFiles.length;
7838
- const hasConflicts = readBoolean(status.hasConflicts) ?? conflictCount > 0;
7839
- const repoRoot = readString5(status.repoRoot, status.repo_root, node.repoRoot, node.repo_root, status.workspace, node.workspace) || void 0;
7840
- const submodules = readGitSubmodules(status.submodules, repoRoot);
7841
- const upstreamStatus = readString5(status.upstreamStatus, status.upstream_status);
7842
- const upstreamFetchedAt = readNumber(status.upstreamFetchedAt, status.upstream_fetched_at);
7843
- const upstreamFetchError = readString5(status.upstreamFetchError, status.upstream_fetch_error);
7844
- const error = readString5(status.error);
7845
- const staged = readNumber(status.staged) ?? 0;
7846
- const modified = readNumber(status.modified) ?? 0;
7847
- const untracked = readNumber(status.untracked) ?? 0;
7848
- const deleted = readNumber(status.deleted) ?? 0;
7849
- const renamed = readNumber(status.renamed) ?? 0;
7850
- return {
7851
- workspace: readString5(status.workspace, node.workspace) || "",
7852
- repoRoot: repoRoot ?? null,
7853
- isGitRepo,
7854
- branch: readString5(status.branch) ?? null,
7855
- headCommit: readString5(status.headCommit) ?? null,
7856
- headMessage: readString5(status.headMessage) ?? null,
7857
- upstream: readString5(status.upstream) ?? null,
7858
- upstreamStatus: upstreamStatus ?? "unchecked",
7859
- ...upstreamFetchedAt !== void 0 ? { upstreamFetchedAt } : {},
7860
- ...upstreamFetchError ? { upstreamFetchError } : {},
7861
- ahead: readNumber(status.ahead) ?? 0,
7862
- behind: readNumber(status.behind) ?? 0,
7863
- staged,
7864
- modified,
7865
- untracked,
7866
- deleted,
7867
- renamed,
7868
- dirty: readBoolean(status.dirty, status.isDirty, status.is_dirty) ?? (staged + modified + untracked + deleted + renamed > 0 || hasConflicts),
7869
- hasConflicts,
7870
- conflictFiles,
7871
- stashCount: readNumber(status.stashCount, status.stash_count) ?? 0,
7872
- lastCheckedAt: options?.lastCheckedAt ?? readNumber(status.lastCheckedAt, status.last_checked_at) ?? Date.now(),
7873
- ...submodules ? { submodules } : {},
7874
- ...error ? { error } : {}
7875
- };
7876
- }
7877
- function scoreGitStatusCandidate(git) {
7878
- if (!git) return Number.NEGATIVE_INFINITY;
7879
- let score = 0;
7880
- if (git.isGitRepo === true) score += 50;
7881
- if (git.isGitRepo === false) score -= 10;
7882
- if (git.branch) score += 20;
7883
- if (git.headCommit) score += 20;
7884
- if (git.upstream) score += 10;
7885
- score += scoreGitUpstreamFreshness(git.upstreamStatus);
7886
- if (typeof git.ahead === "number") score += 2;
7887
- if (typeof git.behind === "number") score += 2;
7888
- if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length;
7889
- if (git.error) score -= 20;
7890
- return score;
7891
- }
7892
- function pickBestTransitGitStatus(node, options) {
7893
- const rawGit = readRecord3(node.lastGit ?? node.last_git);
7894
- const gitResult = readRecord3(rawGit.result);
7895
- const directStatus = readRecord3(rawGit.status);
7896
- const nestedStatus = readRecord3(gitResult.status);
7897
- const rawProbe = readRecord3(node.lastProbe ?? node.last_probe);
7898
- const probeGit = readRecord3(rawProbe.git);
7899
- const probeGitResult = readRecord3(probeGit.result);
7900
- const probeDirectStatus = readRecord3(probeGit.status);
7901
- const probeNestedStatus = readRecord3(probeGitResult.status);
7902
- const lastCheckedAt = options?.lastCheckedAt;
7903
- let best = null;
7904
- for (const status of [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus]) {
7905
- const normalized = normalizeGitStatus(status, node, { lastCheckedAt: lastCheckedAt ?? Date.now() });
7906
- if (!normalized) continue;
7907
- const score = scoreGitStatusCandidate(normalized);
7908
- if (!best || score > best.score) best = { git: normalized, score };
7909
- }
7910
- return best?.git;
7911
- }
7912
- function normalizeMeshNodeId(node) {
7913
- const record = node && typeof node === "object" ? node : {};
7914
- return readString5(record.id, record.nodeId, record.node_id);
7915
- }
7916
- function meshNodeIdMatches(node, candidateId) {
7917
- if (!candidateId) return false;
7918
- const trimmed = candidateId.trim();
7919
- if (!trimmed) return false;
7920
- return normalizeMeshNodeId(node) === trimmed;
7921
- }
7922
- function summarizeGitShape(status) {
7923
- const record = readRecord3(status);
7924
- if (!Object.keys(record).length) return null;
7925
- const submodules = Array.isArray(record.submodules) ? record.submodules.map((entry) => {
7926
- const sub = readRecord3(entry);
7927
- return {
7928
- path: readString5(sub.path) ?? null,
7929
- commit: readString5(sub.commit)?.slice(0, 12) ?? null,
7930
- dirty: readBoolean(sub.dirty) ?? false,
7931
- outOfSync: readBoolean(sub.outOfSync, sub.out_of_sync) ?? false
7932
- };
7933
- }) : [];
7934
- return {
7935
- isGitRepo: readBoolean(record.isGitRepo),
7936
- workspace: readString5(record.workspace) ?? null,
7937
- repoRoot: readString5(record.repoRoot, record.repo_root) ?? null,
7938
- branch: readString5(record.branch) ?? null,
7939
- upstream: readString5(record.upstream) ?? null,
7940
- upstreamStatus: readString5(record.upstreamStatus, record.upstream_status) ?? null,
7941
- headCommit: readString5(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
7942
- ahead: readNumber(record.ahead) ?? null,
7943
- behind: readNumber(record.behind) ?? null,
7944
- dirtyCounts: {
7945
- staged: readNumber(record.staged) ?? 0,
7946
- modified: readNumber(record.modified) ?? 0,
7947
- untracked: readNumber(record.untracked) ?? 0,
7948
- deleted: readNumber(record.deleted) ?? 0,
7949
- renamed: readNumber(record.renamed) ?? 0
7950
- },
7951
- lastCheckedAt: readNumber(record.lastCheckedAt, record.last_checked_at) ?? null,
7952
- submoduleCount: submodules.length,
7953
- submodules
7954
- };
7955
- }
7956
- var init_dist = __esm({
7957
- "../mesh-shared/dist/index.mjs"() {
7958
- "use strict";
7959
- }
7960
- });
7961
-
7962
8065
  // src/mesh/mesh-active-work.ts
7963
8066
  function readString6(value) {
7964
8067
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
@@ -8615,17 +8718,7 @@ import { appendFileSync as appendFileSync2, existsSync as existsSync14, readFile
8615
8718
  import { join as join15 } from "path";
8616
8719
  import { randomUUID as randomUUID7 } from "crypto";
8617
8720
  function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
8618
- const raw = Array.isArray(coordinatorDaemonId) ? coordinatorDaemonId : coordinatorDaemonId != null ? [coordinatorDaemonId] : [];
8619
- const seen = /* @__PURE__ */ new Set();
8620
- const out = [];
8621
- for (const id of raw) {
8622
- if (typeof id !== "string") continue;
8623
- const trimmed = id.trim();
8624
- if (!trimmed || seen.has(trimmed)) continue;
8625
- seen.add(trimmed);
8626
- out.push(trimmed);
8627
- }
8628
- return out;
8721
+ return expandDaemonIdForms(coordinatorDaemonId);
8629
8722
  }
8630
8723
  function readRefineJobId2(event) {
8631
8724
  const metadata = readRecord4(event.metadataEvent) || event;
@@ -9012,6 +9105,7 @@ var init_mesh_events_pending = __esm({
9012
9105
  init_mesh_ledger();
9013
9106
  init_mesh_runtime_store();
9014
9107
  init_mesh_events_utils();
9108
+ init_dist();
9015
9109
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
9016
9110
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
9017
9111
  MAX_PENDING_EVENTS_KEEP = 50;
@@ -12466,12 +12560,9 @@ var init_snapshot = __esm({
12466
12560
  // src/mesh/mesh-events-coordinator.ts
12467
12561
  import { existsSync as existsSync17 } from "fs";
12468
12562
  function resolveCoordinatorDrainDaemonIds(components) {
12469
- const ids = /* @__PURE__ */ new Set();
12470
12563
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
12471
- if (statusInstanceId) ids.add(statusInstanceId);
12472
12564
  const machineId = readNonEmptyString2(loadConfig().machineId);
12473
- if (machineId) ids.add(machineId);
12474
- return [...ids];
12565
+ return expandDaemonIdForms([statusInstanceId, machineId]);
12475
12566
  }
12476
12567
  function getCachedMeshByWorkspace(workspace) {
12477
12568
  const now = Date.now();
@@ -12622,6 +12713,55 @@ function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
12622
12713
  return void 0;
12623
12714
  }
12624
12715
  }
12716
+ function deliverTaskToSession(dispatchThunk, ctx) {
12717
+ const delivery = createSessionDelivery({
12718
+ meshId: ctx.meshId,
12719
+ nodeId: ctx.nodeId,
12720
+ sessionId: ctx.sessionId,
12721
+ providerType: ctx.providerType,
12722
+ taskId: ctx.task.id,
12723
+ kind: "task",
12724
+ message: ctx.task.message,
12725
+ status: "delivering",
12726
+ ...ctx.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: ctx.sourceCoordinatorSessionId } : {},
12727
+ ...ctx.sourceCoordinatorDaemonId ? { sourceCoordinatorDaemonId: ctx.sourceCoordinatorDaemonId } : {}
12728
+ });
12729
+ let dispatchPromise;
12730
+ try {
12731
+ dispatchPromise = Promise.resolve(dispatchThunk());
12732
+ } catch (e) {
12733
+ dispatchPromise = Promise.reject(e);
12734
+ }
12735
+ let timer;
12736
+ const guarded = Promise.race([
12737
+ dispatchPromise,
12738
+ new Promise((_, reject) => {
12739
+ timer = setTimeout(
12740
+ () => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
12741
+ DISPATCH_CONFIRM_TIMEOUT_MS
12742
+ );
12743
+ if (typeof timer?.unref === "function") timer.unref();
12744
+ })
12745
+ ]);
12746
+ guarded.then(() => {
12747
+ if (timer) clearTimeout(timer);
12748
+ updateSessionDeliveryStatus(delivery.id, "delivered");
12749
+ }).catch((e) => {
12750
+ if (timer) clearTimeout(timer);
12751
+ LOG.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
12752
+ updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
12753
+ updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
12754
+ try {
12755
+ appendLedgerEntry(ctx.meshId, {
12756
+ kind: "dispatch_failed",
12757
+ nodeId: ctx.nodeId,
12758
+ sessionId: ctx.sessionId,
12759
+ payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: true, transport: ctx.transport }
12760
+ });
12761
+ } catch {
12762
+ }
12763
+ });
12764
+ }
12625
12765
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
12626
12766
  const mesh = getMeshWithCache(components, meshId);
12627
12767
  const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
@@ -12640,46 +12780,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
12640
12780
  if (!isLocalNode) {
12641
12781
  const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
12642
12782
  const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
12643
- const delivery2 = createSessionDelivery({
12644
- meshId,
12645
- nodeId,
12646
- sessionId,
12647
- providerType,
12648
- taskId: task.id,
12649
- kind: "task",
12650
- message: task.message,
12651
- status: "delivering",
12652
- ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
12653
- ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
12654
- });
12655
- components.dispatchMeshCommand(node.daemonId, "agent_command", {
12656
- targetSessionId: sessionId,
12657
- cliType: providerType,
12658
- action: "send_chat",
12659
- message: task.message,
12660
- meshContext: {
12783
+ const dispatchMeshCommand = components.dispatchMeshCommand;
12784
+ const remoteDaemonId = node.daemonId;
12785
+ deliverTaskToSession(
12786
+ () => dispatchMeshCommand(remoteDaemonId, "agent_command", {
12787
+ targetSessionId: sessionId,
12788
+ cliType: providerType,
12789
+ action: "send_chat",
12790
+ message: task.message,
12791
+ meshContext: {
12792
+ meshId,
12793
+ nodeId,
12794
+ taskId: task.id,
12795
+ ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
12796
+ ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
12797
+ }
12798
+ }),
12799
+ {
12661
12800
  meshId,
12662
12801
  nodeId,
12663
- taskId: task.id,
12664
- ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
12665
- ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
12666
- }
12667
- }).then(() => {
12668
- updateSessionDeliveryStatus(delivery2.id, "delivered");
12669
- }).catch((e) => {
12670
- LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
12671
- updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
12672
- updateTaskStatus(meshId, task.id, "pending");
12673
- try {
12674
- appendLedgerEntry(meshId, {
12675
- kind: "dispatch_failed",
12676
- nodeId,
12677
- sessionId,
12678
- payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
12679
- });
12680
- } catch {
12802
+ sessionId,
12803
+ providerType,
12804
+ task,
12805
+ transport: "remote",
12806
+ ...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
12807
+ ...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
12681
12808
  }
12682
- });
12809
+ );
12683
12810
  return true;
12684
12811
  }
12685
12812
  }
@@ -12701,39 +12828,24 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
12701
12828
  }
12702
12829
  } catch {
12703
12830
  }
12704
- const delivery = createSessionDelivery({
12705
- meshId,
12706
- nodeId,
12707
- sessionId,
12708
- providerType,
12709
- taskId: task.id,
12710
- kind: "task",
12711
- message: task.message,
12712
- status: "delivering",
12713
- ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
12714
- ...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
12715
- });
12716
- components.cliManager.handleCliCommand("agent_command", {
12717
- targetSessionId: sessionId,
12718
- cliType: providerType,
12719
- action: "send_chat",
12720
- message: task.message
12721
- }).then(() => {
12722
- updateSessionDeliveryStatus(delivery.id, "delivered");
12723
- }).catch((e) => {
12724
- LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
12725
- updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
12726
- updateTaskStatus(meshId, task.id, "pending");
12727
- try {
12728
- appendLedgerEntry(meshId, {
12729
- kind: "dispatch_failed",
12730
- nodeId,
12731
- sessionId,
12732
- payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
12733
- });
12734
- } catch {
12831
+ deliverTaskToSession(
12832
+ () => components.cliManager.handleCliCommand("agent_command", {
12833
+ targetSessionId: sessionId,
12834
+ cliType: providerType,
12835
+ action: "send_chat",
12836
+ message: task.message
12837
+ }),
12838
+ {
12839
+ meshId,
12840
+ nodeId,
12841
+ sessionId,
12842
+ providerType,
12843
+ task,
12844
+ transport: "local",
12845
+ ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
12846
+ ...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
12735
12847
  }
12736
- });
12848
+ );
12737
12849
  return true;
12738
12850
  }
12739
12851
  function sweepExpiredCooldowns() {
@@ -12998,7 +13110,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
12998
13110
  }
12999
13111
  }
13000
13112
  const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
13001
- if (task.targetNodeId && readMeshNodeId(node) !== task.targetNodeId) return false;
13113
+ if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
13002
13114
  if (task.requiredTags?.length) {
13003
13115
  const priorities = normalizeProviderPriority(node?.policy);
13004
13116
  const providerCandidates = priorities.length ? priorities : [void 0];
@@ -13009,7 +13121,12 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
13009
13121
  return true;
13010
13122
  }) : [];
13011
13123
  if (!candidateNodes.length) {
13012
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
13124
+ const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
13125
+ markAutoLaunch(meshId, task.id, {
13126
+ status: "skipped",
13127
+ reason: targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
13128
+ nodeId: task.targetNodeId
13129
+ });
13013
13130
  continue;
13014
13131
  }
13015
13132
  const strategy = resolveSchedulingStrategy(mesh);
@@ -13960,7 +14077,7 @@ function setupMeshEventForwarding(components) {
13960
14077
  });
13961
14078
  });
13962
14079
  }
13963
- var REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
14080
+ var REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, DISPATCH_CONFIRM_TIMEOUT_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
13964
14081
  var init_mesh_events_coordinator = __esm({
13965
14082
  "src/mesh/mesh-events-coordinator.ts"() {
13966
14083
  "use strict";
@@ -13988,6 +14105,7 @@ var init_mesh_events_coordinator = __esm({
13988
14105
  idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
13989
14106
  INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
13990
14107
  RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
14108
+ DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
13991
14109
  autoLaunchInProgress = /* @__PURE__ */ new Set();
13992
14110
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
13993
14111
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
@@ -14043,12 +14161,9 @@ function resolveReconcileIntervalMs() {
14043
14161
  return DEFAULT_RECONCILE_INTERVAL_MS;
14044
14162
  }
14045
14163
  function resolveCoordinatorDaemonIds(components) {
14046
- const ids = /* @__PURE__ */ new Set();
14047
14164
  const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
14048
- if (statusInstanceId) ids.add(statusInstanceId);
14049
14165
  const machineId = readNonEmptyString2(loadConfig().machineId);
14050
- if (machineId) ids.add(machineId);
14051
- return [...ids];
14166
+ return expandDaemonIdForms([statusInstanceId, machineId]);
14052
14167
  }
14053
14168
  function daemonHostsMesh(mesh, daemonIds) {
14054
14169
  const host = mesh.meshHost;
@@ -14132,6 +14247,24 @@ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldFo
14132
14247
  }
14133
14248
  }
14134
14249
  }
14250
+ function recoverStrandedAssignedDispatches(meshId, store) {
14251
+ const assigned = getQueue(meshId, { status: ["assigned"] });
14252
+ if (!assigned.length) return;
14253
+ const nowMs = Date.now();
14254
+ for (const row of assigned) {
14255
+ const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
14256
+ if (!Number.isFinite(dispatchedAtMs)) continue;
14257
+ if (nowMs - dispatchedAtMs < ASSIGNED_STRANDED_DEADLINE_MS) continue;
14258
+ if (store.taskHasConfirmedDelivery(meshId, row.id)) continue;
14259
+ const reclaimed = reclaimStrandedAssignedTask(meshId, row.id, {
14260
+ reason: "assigned_stranded_dispatch_unconfirmed",
14261
+ ageMs: nowMs - dispatchedAtMs
14262
+ });
14263
+ if (reclaimed) {
14264
+ LOG.warn("MeshReconcile", `Reclaimed stranded assigned task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}, dispatched ${Math.round((nowMs - dispatchedAtMs) / 1e3)}s ago, never confirmed delivered \u2192 ${reclaimed.status})`);
14265
+ }
14266
+ }
14267
+ }
14135
14268
  async function runMeshReconcileTick(components) {
14136
14269
  const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
14137
14270
  const drainDaemonIds = resolveCoordinatorDaemonIds(components);
@@ -14161,6 +14294,17 @@ async function runMeshReconcileTick(components) {
14161
14294
  }
14162
14295
  }
14163
14296
  }
14297
+ if (store) {
14298
+ for (const mesh of listMeshes()) {
14299
+ const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
14300
+ if (!daemonHostsMesh(mesh, selfIds)) continue;
14301
+ try {
14302
+ recoverStrandedAssignedDispatches(mesh.id, store);
14303
+ } catch (e) {
14304
+ LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
14305
+ }
14306
+ }
14307
+ }
14164
14308
  for (const mesh of listMeshes()) {
14165
14309
  const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
14166
14310
  if (!daemonHostsMesh(mesh, selfIds)) continue;
@@ -14548,7 +14692,7 @@ function setupMeshReconcileLoop(components) {
14548
14692
  }
14549
14693
  };
14550
14694
  }
14551
- var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, STRICT_SESSION_MATCH_TTL_MS;
14695
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS;
14552
14696
  var init_mesh_reconcile_loop = __esm({
14553
14697
  "src/mesh/mesh-reconcile-loop.ts"() {
14554
14698
  "use strict";
@@ -14561,6 +14705,7 @@ var init_mesh_reconcile_loop = __esm({
14561
14705
  init_mesh_events_coordinator();
14562
14706
  init_mesh_unresolved_forward_outbox();
14563
14707
  init_mesh_events_utils();
14708
+ init_dist();
14564
14709
  init_mesh_work_queue();
14565
14710
  init_mesh_ledger();
14566
14711
  init_mesh_active_work();
@@ -14569,6 +14714,7 @@ var init_mesh_reconcile_loop = __esm({
14569
14714
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
14570
14715
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
14571
14716
  heldEventLedgerRecorded = /* @__PURE__ */ new Set();
14717
+ ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
14572
14718
  STRICT_SESSION_MATCH_TTL_MS = 6e4;
14573
14719
  }
14574
14720
  });
@@ -14601,6 +14747,84 @@ var init_mesh_events = __esm({
14601
14747
  }
14602
14748
  });
14603
14749
 
14750
+ // src/providers/approval-utils.ts
14751
+ function normalizeApprovalLabel(value) {
14752
+ return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
14753
+ }
14754
+ function isNegativeApprovalLabel(value) {
14755
+ const label = normalizeApprovalLabel(value);
14756
+ return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
14757
+ }
14758
+ function hasNegativeApprovalOption(buttons) {
14759
+ return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
14760
+ }
14761
+ function getApprovalPositiveHints(provider) {
14762
+ const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
14763
+ return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
14764
+ }
14765
+ function pickApprovalButton(buttons, provider) {
14766
+ const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
14767
+ if (labels.length === 0) {
14768
+ return { index: -1, label: "" };
14769
+ }
14770
+ const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
14771
+ const hints = getApprovalPositiveHints(provider);
14772
+ for (const hint of hints) {
14773
+ const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
14774
+ if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
14775
+ const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
14776
+ if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
14777
+ const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
14778
+ if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
14779
+ }
14780
+ return { index: -1, label: "" };
14781
+ }
14782
+ function pickAutoApprovalButton(buttons) {
14783
+ const labels = (buttons || []).map((button) => String(button || "").trim());
14784
+ const index = labels.findIndex(Boolean);
14785
+ return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
14786
+ }
14787
+ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
14788
+ const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
14789
+ const cleanMessage = String(modalMessage || "").trim();
14790
+ if (cleanMessage) lines.push(cleanMessage);
14791
+ return lines.join("\n");
14792
+ }
14793
+ function looksLikeActiveApprovalPromptText(content) {
14794
+ const text = content.trim();
14795
+ if (!text || text.length > 2e3) return false;
14796
+ const hasApprovalQuestion = /do you want to (?:proceed|allow|run|make this edit|create)/i.test(text) || /this command requires approval/i.test(text) || /quick safety check/i.test(text) || /is this a project you trust/i.test(text);
14797
+ const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
14798
+ if (hasApprovalQuestion && hasNumberedChoices) return true;
14799
+ const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
14800
+ const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
14801
+ const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
14802
+ if (hasDontAskAgain && hasNoOption) return true;
14803
+ if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
14804
+ return false;
14805
+ }
14806
+ var DEFAULT_APPROVAL_POSITIVE_HINTS;
14807
+ var init_approval_utils = __esm({
14808
+ "src/providers/approval-utils.ts"() {
14809
+ "use strict";
14810
+ DEFAULT_APPROVAL_POSITIVE_HINTS = [
14811
+ "yes",
14812
+ "allow once",
14813
+ "approve",
14814
+ "accept",
14815
+ "continue",
14816
+ "run",
14817
+ "proceed",
14818
+ "confirm",
14819
+ "save",
14820
+ "ok",
14821
+ "trust",
14822
+ "allow",
14823
+ "always allow"
14824
+ ];
14825
+ }
14826
+ });
14827
+
14604
14828
  // src/logging/debug-config.ts
14605
14829
  function normalizeCategories(categories) {
14606
14830
  if (!Array.isArray(categories)) return [];
@@ -15587,6 +15811,27 @@ function compileSettledPromptMatchers(spec) {
15587
15811
  });
15588
15812
  return { prompt, footers };
15589
15813
  }
15814
+ function extractButtonLabels(spec, text) {
15815
+ if (!text) return [];
15816
+ const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
15817
+ const buttonRe = compile2(spec.buttonPattern, flags);
15818
+ const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
15819
+ const out = [];
15820
+ for (const line of text.split("\n")) {
15821
+ buttonRe.lastIndex = 0;
15822
+ const m = buttonRe.exec(line);
15823
+ if (!m) continue;
15824
+ const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
15825
+ if (captured && captured.trim()) out.push(captured.trim());
15826
+ }
15827
+ return out;
15828
+ }
15829
+ function buttonBlockApprovalCue(spec, text) {
15830
+ const labels = extractButtonLabels(spec, text);
15831
+ if (labels.length < 2) return false;
15832
+ if (pickApprovalButton(labels).index < 0) return false;
15833
+ return hasNegativeApprovalOption(labels);
15834
+ }
15590
15835
  function modalMatches(spec, input) {
15591
15836
  const text = input.screenText ?? "";
15592
15837
  const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
@@ -15595,6 +15840,7 @@ function modalMatches(spec, input) {
15595
15840
  const re = compile2(variant.regex, variant.flags ?? "i");
15596
15841
  if (re.test(text)) return true;
15597
15842
  }
15843
+ if (buttonBlockApprovalCue(spec, text)) return true;
15598
15844
  return false;
15599
15845
  }
15600
15846
  function evaluateGroup(group, spec, input, compiled) {
@@ -15649,6 +15895,7 @@ var init_detect_status = __esm({
15649
15895
  "src/providers/sdk/v1/builders/cli/detect-status.ts"() {
15650
15896
  "use strict";
15651
15897
  init_visible_region();
15898
+ init_approval_utils();
15652
15899
  DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
15653
15900
  }
15654
15901
  });
@@ -21194,6 +21441,7 @@ function getSavedProviderSessions(state, filters) {
21194
21441
 
21195
21442
  // src/index.ts
21196
21443
  init_mesh_config();
21444
+ init_dist();
21197
21445
  init_coordinator_prompt();
21198
21446
  init_mesh_missions();
21199
21447
  init_mesh_task_stats();
@@ -25746,76 +25994,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
25746
25994
  return normalized;
25747
25995
  }
25748
25996
 
25749
- // src/providers/approval-utils.ts
25750
- var DEFAULT_APPROVAL_POSITIVE_HINTS = [
25751
- "yes",
25752
- "allow once",
25753
- "approve",
25754
- "accept",
25755
- "continue",
25756
- "run",
25757
- "proceed",
25758
- "confirm",
25759
- "save",
25760
- "ok",
25761
- "trust",
25762
- "allow",
25763
- "always allow"
25764
- ];
25765
- function normalizeApprovalLabel(value) {
25766
- return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
25767
- }
25768
- function isNegativeApprovalLabel(value) {
25769
- const label = normalizeApprovalLabel(value);
25770
- return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
25771
- }
25772
- function getApprovalPositiveHints(provider) {
25773
- const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
25774
- return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
25775
- }
25776
- function pickApprovalButton(buttons, provider) {
25777
- const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
25778
- if (labels.length === 0) {
25779
- return { index: -1, label: "" };
25780
- }
25781
- const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
25782
- const hints = getApprovalPositiveHints(provider);
25783
- for (const hint of hints) {
25784
- const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
25785
- if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
25786
- const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
25787
- if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
25788
- const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
25789
- if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
25790
- }
25791
- return { index: -1, label: "" };
25792
- }
25793
- function pickAutoApprovalButton(buttons) {
25794
- const labels = (buttons || []).map((button) => String(button || "").trim());
25795
- const index = labels.findIndex(Boolean);
25796
- return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
25797
- }
25798
- function formatAutoApprovalMessage(modalMessage, buttonLabel) {
25799
- const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
25800
- const cleanMessage = String(modalMessage || "").trim();
25801
- if (cleanMessage) lines.push(cleanMessage);
25802
- return lines.join("\n");
25803
- }
25804
- function looksLikeActiveApprovalPromptText(content) {
25805
- const text = content.trim();
25806
- if (!text || text.length > 2e3) return false;
25807
- const hasApprovalQuestion = /do you want to (?:proceed|allow|run|make this edit|create)/i.test(text) || /this command requires approval/i.test(text) || /quick safety check/i.test(text) || /is this a project you trust/i.test(text);
25808
- const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
25809
- if (hasApprovalQuestion && hasNumberedChoices) return true;
25810
- const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
25811
- const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
25812
- const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
25813
- if (hasDontAskAgain && hasNoOption) return true;
25814
- if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
25815
- return false;
25816
- }
25817
-
25818
25997
  // src/providers/ide-provider-instance.ts
25998
+ init_approval_utils();
25819
25999
  init_provider_patch_state();
25820
26000
  init_chat_message_normalization();
25821
26001
  init_open_panel_support();
@@ -26899,6 +27079,7 @@ import * as fs7 from "fs";
26899
27079
  import * as os10 from "os";
26900
27080
  import * as path16 from "path";
26901
27081
  import { randomUUID as randomUUID11 } from "crypto";
27082
+ init_approval_utils();
26902
27083
  init_coordinator_registry();
26903
27084
  init_logger();
26904
27085
 
@@ -34887,6 +35068,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
34887
35068
  // src/providers/cli-provider-instance.ts
34888
35069
  init_logger();
34889
35070
  init_control_effects();
35071
+ init_approval_utils();
34890
35072
  init_provider_patch_state();
34891
35073
 
34892
35074
  // src/providers/provider-session-id.ts
@@ -35148,6 +35330,20 @@ var CliProviderInstance = class _CliProviderInstance {
35148
35330
  * keystroke until the modal *content* has settled.
35149
35331
  */
35150
35332
  static AUTO_APPROVE_SETTLE_MS = 600;
35333
+ /**
35334
+ * Busy-side hysteresis for the settle gate. A momentary `generating` flip
35335
+ * while the SAME approval modal's button block is still on screen (its
35336
+ * question line scrolled out of the captured frame, only the buttons + a
35337
+ * residual `esc to interrupt` spinner remain) briefly reports
35338
+ * status!=waiting_approval. Without hysteresis that flip wipes the settle
35339
+ * clock, and the modal→generating→modal flap restarts the 600ms window
35340
+ * every time so auto-approve never fires. We keep the in-progress settle
35341
+ * gate warm across an inactive blip up to this bound; only once the modal
35342
+ * has genuinely stayed gone this long (a real resolution → idle) is the
35343
+ * gate cleared. Bounded so a genuinely new, later approval still re-settles
35344
+ * from scratch rather than firing on a stale timestamp.
35345
+ */
35346
+ static AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
35151
35347
  adapter;
35152
35348
  context = null;
35153
35349
  events = [];
@@ -35169,6 +35365,10 @@ var CliProviderInstance = class _CliProviderInstance {
35169
35365
  pendingAutoApprovalSignature = "";
35170
35366
  pendingAutoApprovalSince = 0;
35171
35367
  autoApproveSettleTimer = null;
35368
+ // Wall-clock when auto-approve first observed status!=waiting_approval while
35369
+ // a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
35370
+ // brief generating flip does not immediately wipe the settle clock.
35371
+ autoApproveInactiveSince = 0;
35172
35372
  controlValues = {};
35173
35373
  summaryMetadata = void 0;
35174
35374
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -35989,14 +36189,28 @@ var CliProviderInstance = class _CliProviderInstance {
35989
36189
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
35990
36190
  if (!autoApproveActive) {
35991
36191
  this.lastAutoApprovalSignature = "";
36192
+ if (this.pendingAutoApprovalSince) {
36193
+ if (!this.autoApproveInactiveSince) this.autoApproveInactiveSince = now;
36194
+ const goneForMs = now - this.autoApproveInactiveSince;
36195
+ if (goneForMs < _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS) {
36196
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
36197
+ this.autoApproveSettleTimer = setTimeout(() => {
36198
+ this.autoApproveSettleTimer = null;
36199
+ this.recheckAutoApproveSettled();
36200
+ }, _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS - goneForMs + 20);
36201
+ return autoApproveActive;
36202
+ }
36203
+ }
35992
36204
  this.pendingAutoApprovalSignature = "";
35993
36205
  this.pendingAutoApprovalSince = 0;
36206
+ this.autoApproveInactiveSince = 0;
35994
36207
  if (this.autoApproveSettleTimer) {
35995
36208
  clearTimeout(this.autoApproveSettleTimer);
35996
36209
  this.autoApproveSettleTimer = null;
35997
36210
  }
35998
36211
  return autoApproveActive;
35999
36212
  }
36213
+ this.autoApproveInactiveSince = 0;
36000
36214
  const modal = adapterStatus.activeModal;
36001
36215
  const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
36002
36216
  if (!modal || buttons.length === 0) {
@@ -36006,18 +36220,18 @@ var CliProviderInstance = class _CliProviderInstance {
36006
36220
  if (buttonIndex < 0) {
36007
36221
  return autoApproveActive;
36008
36222
  }
36009
- const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
36010
- const signature = [
36011
- approvalEntrySeq,
36223
+ const modalSignature = [
36012
36224
  typeof modal?.message === "string" ? modal.message.trim() : "",
36013
36225
  buttons.join("|"),
36014
36226
  buttonIndex
36015
36227
  ].join("::");
36016
- if (this.autoApproveBusy && signature === this.lastAutoApprovalSignature) {
36228
+ const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
36229
+ const busySignature = `${approvalEntrySeq}::${modalSignature}`;
36230
+ if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
36017
36231
  return autoApproveActive;
36018
36232
  }
36019
- if (signature !== this.pendingAutoApprovalSignature) {
36020
- this.pendingAutoApprovalSignature = signature;
36233
+ if (modalSignature !== this.pendingAutoApprovalSignature) {
36234
+ this.pendingAutoApprovalSignature = modalSignature;
36021
36235
  this.pendingAutoApprovalSince = now;
36022
36236
  }
36023
36237
  const settledForMs = now - this.pendingAutoApprovalSince;
@@ -36034,9 +36248,10 @@ var CliProviderInstance = class _CliProviderInstance {
36034
36248
  this.autoApproveSettleTimer = null;
36035
36249
  }
36036
36250
  this.autoApproveBusy = true;
36037
- this.lastAutoApprovalSignature = signature;
36251
+ this.lastAutoApprovalSignature = busySignature;
36038
36252
  this.pendingAutoApprovalSignature = "";
36039
36253
  this.pendingAutoApprovalSince = 0;
36254
+ this.autoApproveInactiveSince = 0;
36040
36255
  if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
36041
36256
  this.autoApproveBusyTimer = setTimeout(() => {
36042
36257
  this.autoApproveBusy = false;
@@ -38823,9 +39038,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
38823
39038
  }
38824
39039
  }
38825
39040
  }
38826
- for (const [k, a] of this.adapters) {
38827
- if (a.cliType === agentType) {
38828
- return { adapter: a, key: k };
39041
+ if (!opts?.instanceKey) {
39042
+ for (const [k, a] of this.adapters) {
39043
+ if (a.cliType === agentType) {
39044
+ return { adapter: a, key: k };
39045
+ }
38829
39046
  }
38830
39047
  }
38831
39048
  return null;
@@ -42928,7 +43145,8 @@ init_logger();
42928
43145
  import * as fs23 from "fs";
42929
43146
  import * as path35 from "path";
42930
43147
  import * as os26 from "os";
42931
- var LOG_DIR2 = process.platform === "win32" ? path35.join(process.env.LOCALAPPDATA || process.env.APPDATA || path35.join(os26.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path35.join(os26.homedir(), "Library", "Logs", "adhdev") : path35.join(os26.homedir(), ".local", "share", "adhdev", "logs");
43148
+ var ADHDEV_HOME2 = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path35.join(os26.homedir(), ".adhdev");
43149
+ var LOG_DIR2 = path35.join(ADHDEV_HOME2, "logs");
42932
43150
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
42933
43151
  var MAX_DAYS = 7;
42934
43152
  try {
@@ -43783,13 +44001,14 @@ async function waitForPidExit(pid, timeoutMs) {
43783
44001
  }
43784
44002
  }
43785
44003
  }
43786
- function stopSessionHostProcesses(appName) {
44004
+ async function stopSessionHostProcesses(appName) {
43787
44005
  const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
44006
+ let killedPid = null;
43788
44007
  try {
43789
44008
  if (fs25.existsSync(pidFile)) {
43790
44009
  const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
43791
44010
  if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
43792
- killPid(pid);
44011
+ if (killPid(pid)) killedPid = pid;
43793
44012
  }
43794
44013
  }
43795
44014
  } catch {
@@ -43799,6 +44018,15 @@ function stopSessionHostProcesses(appName) {
43799
44018
  } catch {
43800
44019
  }
43801
44020
  }
44021
+ if (killedPid !== null) {
44022
+ await waitForPidExit(killedPid, 15e3);
44023
+ }
44024
+ }
44025
+ function isRetriableInstallLockError(error) {
44026
+ const code = error?.code;
44027
+ if (code === "EBUSY" || code === "EPERM") return true;
44028
+ const text = `${error?.message || ""} ${error?.stderr || ""}`;
44029
+ return /\bEBUSY\b|\bEPERM\b|resource busy or locked/i.test(text);
43802
44030
  }
43803
44031
  function removeDaemonPidFile() {
43804
44032
  const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
@@ -43878,22 +44106,37 @@ async function runDaemonUpgradeHelper(payload) {
43878
44106
  appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
43879
44107
  await waitForPidExit(payload.parentPid, 15e3);
43880
44108
  }
43881
- stopSessionHostProcesses(sessionHostAppName);
44109
+ await stopSessionHostProcesses(sessionHostAppName);
43882
44110
  removeDaemonPidFile();
43883
44111
  cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
43884
44112
  const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
43885
44113
  appendUpgradeLog(`Installing ${spec}`);
43886
- const installOutput = execFileSync5(
43887
- installCommand.command,
43888
- installCommand.args,
43889
- {
43890
- encoding: "utf8",
43891
- stdio: "pipe",
43892
- maxBuffer: 20 * 1024 * 1024,
43893
- env: buildInstallEnvWithNodeOnPath(),
43894
- ...installCommand.execOptions
44114
+ const maxInstallAttempts = process.platform === "win32" ? 3 : 1;
44115
+ let installOutput = "";
44116
+ for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
44117
+ try {
44118
+ installOutput = String(execFileSync5(
44119
+ installCommand.command,
44120
+ installCommand.args,
44121
+ {
44122
+ encoding: "utf8",
44123
+ stdio: "pipe",
44124
+ maxBuffer: 20 * 1024 * 1024,
44125
+ env: buildInstallEnvWithNodeOnPath(),
44126
+ ...installCommand.execOptions
44127
+ }
44128
+ ));
44129
+ break;
44130
+ } catch (error) {
44131
+ if (attempt < maxInstallAttempts && isRetriableInstallLockError(error)) {
44132
+ appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"}); cleaning staging and retrying after backoff`);
44133
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
44134
+ await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
44135
+ continue;
44136
+ }
44137
+ throw error;
43895
44138
  }
43896
- );
44139
+ }
43897
44140
  if (installOutput.trim()) {
43898
44141
  appendUpgradeLog(installOutput.trim());
43899
44142
  }
@@ -46156,7 +46399,14 @@ var MESH_FORWARDABLE_SESSION_COMMANDS = /* @__PURE__ */ new Set([
46156
46399
  "resolve_action",
46157
46400
  "set_mode",
46158
46401
  "change_model",
46159
- "set_thought_level"
46402
+ "set_thought_level",
46403
+ // agent_command (send_chat / clear_history / stop) is session-scoped too: a command
46404
+ // explicitly naming a targetSessionId MUST reach that session wherever it lives, never a
46405
+ // different local session. Without forwarding, a misrouted/relayed send_chat for a REMOTE
46406
+ // worker session that reaches the wrong daemon used to fuzzy-inject the task body into that
46407
+ // daemon's own CLI session (TASKECHO coordinator self-echo). Forwarding to the owning daemon
46408
+ // delivers it to the real worker instead. (findAdapter is also fail-closed as the backstop.)
46409
+ "agent_command"
46160
46410
  ]);
46161
46411
  var READ_DEBUG_ENABLED2 = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
46162
46412
  function normalizeCommandSource(source) {
@@ -46509,7 +46759,7 @@ var DaemonCommandRouter = class {
46509
46759
  if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
46510
46760
  const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
46511
46761
  if (!nodeDaemonId) continue;
46512
- if (selfDaemonId && nodeDaemonId === selfDaemonId) return void 0;
46762
+ if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
46513
46763
  return nodeDaemonId;
46514
46764
  }
46515
46765
  return void 0;
@@ -46664,6 +46914,38 @@ var DaemonCommandRouter = class {
46664
46914
  if (record?.meta?.meshNodeId === nodeId) return true;
46665
46915
  return false;
46666
46916
  }
46917
+ /**
46918
+ * Best-effort recursive removal of a managed worktree directory.
46919
+ *
46920
+ * The git-registry de-registration is the safety-critical step of worktree
46921
+ * teardown; a leftover directory must never gate dropping the node from the
46922
+ * mesh. On Windows, `fs.rmSync` can throw EINVAL/EPERM/EBUSY on submodule
46923
+ * gitlink (`.git`) files, long paths, junctions, or while a just-stopped
46924
+ * delegate session is still releasing a handle/cwd on the directory. This
46925
+ * helper absorbs those errors (never throws), with bounded retries + backoff
46926
+ * to give handles time to release, and reports whether residue remains.
46927
+ */
46928
+ async bestEffortRemoveWorktreeDir(dir) {
46929
+ if (!dir || !fs26.existsSync(dir)) return { removed: true, residue: false };
46930
+ const sleep3 = (ms) => new Promise((resolve24) => setTimeout(resolve24, ms));
46931
+ const ABSORB = /* @__PURE__ */ new Set(["EINVAL", "EPERM", "EBUSY", "ENOTEMPTY", "EACCES", "EMFILE", "ENFILE"]);
46932
+ let lastErr;
46933
+ for (let attempt = 0; attempt < 4; attempt++) {
46934
+ try {
46935
+ fs26.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
46936
+ if (!fs26.existsSync(dir)) return { removed: true, residue: false };
46937
+ lastErr = new Error("directory still present after rmSync");
46938
+ } catch (e) {
46939
+ lastErr = e;
46940
+ const code = typeof e?.code === "string" ? e.code : "";
46941
+ if (code && !ABSORB.has(code)) {
46942
+ break;
46943
+ }
46944
+ }
46945
+ await sleep3(150 * (attempt + 1));
46946
+ }
46947
+ return fs26.existsSync(dir) ? { removed: false, residue: true, error: String(lastErr?.message || lastErr || "unknown rm error") } : { removed: true, residue: false };
46948
+ }
46667
46949
  async cleanupLocalWorktreeNode(args) {
46668
46950
  const workspace = typeof args.node?.workspace === "string" ? args.node.workspace.trim() : "";
46669
46951
  if (!workspace) {
@@ -46718,11 +47000,31 @@ var DaemonCommandRouter = class {
46718
47000
  const entries = await listWorktrees2(repoRoot);
46719
47001
  const managedEntry = entries.find((entry) => normalizePath(entry.path) === actualPath);
46720
47002
  if (!managedEntry) {
47003
+ try {
47004
+ const { execFile: execFile5 } = await import("child_process");
47005
+ const { promisify: promisify8 } = await import("util");
47006
+ const execFileAsync4 = promisify8(execFile5);
47007
+ await execFileAsync4("git", ["worktree", "prune"], {
47008
+ cwd: repoRoot,
47009
+ encoding: "utf8",
47010
+ timeout: 3e4,
47011
+ maxBuffer: 4 * 1024 * 1024,
47012
+ windowsHide: true
47013
+ });
47014
+ } catch {
47015
+ }
47016
+ const rm = await this.bestEffortRemoveWorktreeDir(workspace);
46721
47017
  return {
46722
- success: false,
46723
- code: "mesh_worktree_cleanup_not_registered",
46724
- error: `Refusing to remove '${workspace}' because it is not registered in git worktree list for '${repoRoot}'`,
46725
- recoveryHint: "Inspect git worktree list --porcelain from the source repo. If the path was already removed, prune git worktrees before retrying."
47018
+ success: true,
47019
+ removedPath: workspace,
47020
+ repoRoot,
47021
+ reason: "worktree_unregistered_residue_recovered",
47022
+ recovered: true,
47023
+ ...rm.residue ? {
47024
+ residue: true,
47025
+ residueWarning: `Worktree was already de-registered from git but the directory could not be fully removed (leftover residue at '${workspace}'): ${rm.error || "unknown error"}. The node will be dropped from the mesh; remove the directory manually if needed.`,
47026
+ residueError: rm.error
47027
+ } : {}
46726
47028
  };
46727
47029
  }
46728
47030
  if (managedEntry.branch && managedEntry.branch !== args.node.worktreeBranch) {
@@ -46785,8 +47087,8 @@ var DaemonCommandRouter = class {
46785
47087
  convergence: forceFallbackConvergence
46786
47088
  };
46787
47089
  } catch (deinitError) {
47090
+ const rm = await this.bestEffortRemoveWorktreeDir(workspace);
46788
47091
  try {
46789
- fs26.rmSync(workspace, { recursive: true, force: true });
46790
47092
  await execFileAsync4("git", ["worktree", "prune"], {
46791
47093
  cwd: repoRoot,
46792
47094
  encoding: "utf8",
@@ -46794,23 +47096,22 @@ var DaemonCommandRouter = class {
46794
47096
  maxBuffer: GIT_MAX_BUFFER_CLEANUP,
46795
47097
  windowsHide: true
46796
47098
  });
46797
- return {
46798
- success: true,
46799
- removedPath: workspace,
46800
- repoRoot,
46801
- fallback: "fs_rm_worktree_prune",
46802
- forced: true,
46803
- reason: "working_trees_containing_submodules",
46804
- convergence: forceFallbackConvergence
46805
- };
46806
- } catch (rmError) {
46807
- return {
46808
- success: false,
46809
- code: "mesh_worktree_cleanup_failed",
46810
- error: `All removal fallbacks exhausted. deinit+remove: ${deinitError?.message || deinitError}; rmSync+prune: ${rmError?.message || rmError}`,
46811
- recoveryHint: "Manually remove the worktree directory and run git worktree prune from the source repo."
46812
- };
47099
+ } catch {
46813
47100
  }
47101
+ return {
47102
+ success: true,
47103
+ removedPath: workspace,
47104
+ repoRoot,
47105
+ fallback: "fs_rm_worktree_prune",
47106
+ forced: true,
47107
+ reason: "working_trees_containing_submodules",
47108
+ convergence: forceFallbackConvergence,
47109
+ ...rm.residue ? {
47110
+ residue: true,
47111
+ residueWarning: `Worktree was de-registered from git but the directory could not be fully removed (leftover residue at '${workspace}'): ${rm.error || "unknown error"}; deinit+remove first failed with: ${deinitError?.message || deinitError}. The node will be dropped from the mesh; remove the directory manually if needed.`,
47112
+ residueError: rm.error
47113
+ } : {}
47114
+ };
46814
47115
  }
46815
47116
  }
46816
47117
  return {
@@ -50209,7 +50510,14 @@ ${hintLines.join("\n")}` : "",
50209
50510
  } catch {
50210
50511
  }
50211
50512
  }
50212
- return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {}, ...worktreeCleanup ? { worktreeCleanup } : {} };
50513
+ const residueWarning = worktreeCleanup?.residue === true && typeof worktreeCleanup?.residueWarning === "string" ? worktreeCleanup.residueWarning : void 0;
50514
+ return {
50515
+ success: true,
50516
+ removed,
50517
+ ...residueWarning ? { residueWarning } : {},
50518
+ ...sessionCleanup ? { sessionCleanup } : {},
50519
+ ...worktreeCleanup ? { worktreeCleanup } : {}
50520
+ };
50213
50521
  } catch (e) {
50214
50522
  return { success: false, error: e.message };
50215
50523
  }
@@ -52655,6 +52963,7 @@ var DaemonAgentStreamManager = class {
52655
52963
 
52656
52964
  // src/agent-stream/poller.ts
52657
52965
  init_logger();
52966
+ init_approval_utils();
52658
52967
  init_chat_message_normalization();
52659
52968
  var AgentStreamPoller = class {
52660
52969
  deps;
@@ -60208,6 +60517,7 @@ export {
60208
60517
  createNativeHistoryDispatcher,
60209
60518
  createSessionDelivery,
60210
60519
  createWorktree,
60520
+ daemonIdsEquivalent,
60211
60521
  deleteDirectDispatchesByTaskId,
60212
60522
  deleteMesh,
60213
60523
  deriveMeshReviewInboxItems,
@@ -60221,6 +60531,7 @@ export {
60221
60531
  ensureSessionHostReady,
60222
60532
  evaluateFsm,
60223
60533
  execNpmCommandSync,
60534
+ expandDaemonIdForms,
60224
60535
  fastForwardMeshNode,
60225
60536
  filterActivityChatMessages,
60226
60537
  filterChatMessagesByVisibility,
@@ -60311,6 +60622,7 @@ export {
60311
60622
  loadMeshWorktreeBootstrapConfig,
60312
60623
  loadState,
60313
60624
  logCommand,
60625
+ machineCoreFromDaemonId,
60314
60626
  markSessionDeliveriesTerminal,
60315
60627
  markSetupComplete,
60316
60628
  markStaleDirectDispatches,