@adhdev/daemon-core 0.9.82-rc.259 → 0.9.82-rc.260

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
@@ -2657,6 +2657,7 @@ __export(mesh_work_queue_exports, {
2657
2657
  nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
2658
2658
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
2659
2659
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
2660
+ recordDirectDispatchTask: () => recordDirectDispatchTask,
2660
2661
  recordMeshToolCall: () => recordMeshToolCall,
2661
2662
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
2662
2663
  requeueTask: () => requeueTask,
@@ -2822,6 +2823,37 @@ function enqueueTask(meshId, message, opts) {
2822
2823
  return entry;
2823
2824
  });
2824
2825
  }
2826
+ function recordDirectDispatchTask(meshId, message, opts) {
2827
+ const missionId = typeof opts.missionId === "string" ? opts.missionId.trim() : "";
2828
+ if (!missionId) return null;
2829
+ const taskId = typeof opts.id === "string" ? opts.id.trim() : "";
2830
+ if (!taskId) return null;
2831
+ const modeValidation = validateMeshTaskModeRequest(opts.taskMode, message);
2832
+ if (!modeValidation.valid) {
2833
+ throw new Error(`live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`);
2834
+ }
2835
+ const now = opts.dispatchedAt && opts.dispatchedAt.trim() ? opts.dispatchedAt : (/* @__PURE__ */ new Date()).toISOString();
2836
+ return withQueueLock(meshId, () => {
2837
+ if (MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId)) {
2838
+ return null;
2839
+ }
2840
+ const entry = {
2841
+ id: taskId,
2842
+ meshId,
2843
+ message,
2844
+ status: "assigned",
2845
+ ...modeValidation.taskMode ? { taskMode: modeValidation.taskMode } : {},
2846
+ missionId,
2847
+ ...opts.assignedNodeId ? { targetNodeId: opts.assignedNodeId, assignedNodeId: opts.assignedNodeId } : {},
2848
+ ...opts.assignedSessionId ? { targetSessionId: opts.assignedSessionId, assignedSessionId: opts.assignedSessionId } : {},
2849
+ dispatchTimestamp: now,
2850
+ createdAt: now,
2851
+ updatedAt: now
2852
+ };
2853
+ MeshRuntimeStore.getInstance().insertQueueEntry(entry);
2854
+ return entry;
2855
+ });
2856
+ }
2825
2857
  function getQueue(meshId, opts) {
2826
2858
  return MeshRuntimeStore.getInstance().getQueueEntries(meshId, opts?.status?.length ? opts.status : void 0);
2827
2859
  }
@@ -4401,9 +4433,48 @@ function buildMeshAsyncRefineJobs(args) {
4401
4433
  return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);
4402
4434
  });
4403
4435
  }
4436
+ function jobActivityTime(job) {
4437
+ const raw = job.lastUpdatedAt || job.completedAt || job.startedAt || "";
4438
+ const t = new Date(raw).getTime();
4439
+ return Number.isFinite(t) ? t : 0;
4440
+ }
4441
+ function summarizeMeshAsyncRefineJobs(jobs) {
4442
+ const activeJobs = [];
4443
+ const terminalJobs = [];
4444
+ for (const job of jobs) {
4445
+ if (TERMINAL_REFINE_STATUSES.has(job.status)) terminalJobs.push(job);
4446
+ else activeJobs.push(job);
4447
+ }
4448
+ let newest = 0;
4449
+ for (const job of jobs) newest = Math.max(newest, jobActivityTime(job));
4450
+ const cutoff = newest - STALE_TERMINAL_REFINE_WINDOW_MS;
4451
+ const terminalByRecency = [...terminalJobs].sort(
4452
+ (a, b) => jobActivityTime(b) - jobActivityTime(a)
4453
+ );
4454
+ const freshTerminal = terminalByRecency.filter((job) => jobActivityTime(job) >= cutoff).slice(0, RECENT_TERMINAL_REFINE_CAP);
4455
+ const freshTerminalIds = new Set(freshTerminal.map((job) => job.jobId));
4456
+ const byStatus = {};
4457
+ for (const job of activeJobs) {
4458
+ byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
4459
+ }
4460
+ for (const job of freshTerminal) {
4461
+ byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
4462
+ }
4463
+ const staleTerminal = terminalJobs.length - freshTerminalIds.size;
4464
+ return {
4465
+ total: activeJobs.length + freshTerminal.length,
4466
+ byStatus,
4467
+ staleTerminal,
4468
+ activeJobs
4469
+ };
4470
+ }
4471
+ var TERMINAL_REFINE_STATUSES, STALE_TERMINAL_REFINE_WINDOW_MS, RECENT_TERMINAL_REFINE_CAP;
4404
4472
  var init_mesh_refine_status = __esm({
4405
4473
  "src/mesh/mesh-refine-status.ts"() {
4406
4474
  "use strict";
4475
+ TERMINAL_REFINE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed"]);
4476
+ STALE_TERMINAL_REFINE_WINDOW_MS = 6 * 60 * 60 * 1e3;
4477
+ RECENT_TERMINAL_REFINE_CAP = 8;
4407
4478
  }
4408
4479
  });
4409
4480
 
@@ -10403,6 +10474,26 @@ var init_cli_state_engine = __esm({
10403
10474
  // ── Approval ─────────────────────────────────────
10404
10475
  lastApprovalResolvedAt = 0;
10405
10476
  lastResolvedModalMessage = "";
10477
+ /**
10478
+ * Monotonic counter bumped every time the FSM *enters* waiting_approval
10479
+ * with a freshly captured modal (see `applyWaitingApproval`). It is the
10480
+ * single discriminator between "the same approval re-observed across TUI
10481
+ * paint flaps" and "a genuinely new, distinct approval".
10482
+ *
10483
+ * The message-equality cooldown below (`lastResolvedModalMessage`) cannot
10484
+ * tell these apart on its own: claude-cli routinely presents consecutive
10485
+ * approvals whose modal message text is identical (e.g. two back-to-back
10486
+ * Bash-command prompts). When that second approval arrived inside
10487
+ * `approvalCooldown`, the message-equality guard silently swallowed the
10488
+ * key write and the approval stuck forever — fatal under auto-approval.
10489
+ *
10490
+ * `approvalEntrySeq` increments on every fresh entry; `lastResolvedEntrySeq`
10491
+ * records which entry the cooldown belongs to. We only short-circuit the
10492
+ * write when we are still resolving *that same* entry — a new entry (new
10493
+ * seq) is always a real, distinct approval and must be written.
10494
+ */
10495
+ approvalEntrySeq = 0;
10496
+ lastResolvedEntrySeq = -1;
10406
10497
  /**
10407
10498
  * When the engine previously held a modal but the latest parse failed
10408
10499
  * to extract one, we record the timestamp here and only drop the modal
@@ -10508,6 +10599,7 @@ var init_cli_state_engine = __esm({
10508
10599
  if (parsed?.status === "waiting_approval" && parsedModal) {
10509
10600
  modal = parsedModal;
10510
10601
  this.activeModal = parsedModal;
10602
+ this.approvalEntrySeq++;
10511
10603
  if (this.currentStatus !== "waiting_approval") {
10512
10604
  this.setStatus("waiting_approval", "resolve_modal_parse");
10513
10605
  this.callbacks.onStatusChange();
@@ -10521,12 +10613,14 @@ var init_cli_state_engine = __esm({
10521
10613
  if (!modal || !buttonsValid) return;
10522
10614
  const currentModalMessage = typeof modal?.message === "string" ? modal.message.trim() : "";
10523
10615
  const inCooldown = !!this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
10524
- if (inCooldown && currentModalMessage === this.lastResolvedModalMessage) return;
10616
+ const sameEntryReResolve = this.approvalEntrySeq === this.lastResolvedEntrySeq;
10617
+ if (inCooldown && sameEntryReResolve && currentModalMessage === this.lastResolvedModalMessage) return;
10525
10618
  this.clearIdleFinishCandidate("resolve_modal");
10526
- this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal });
10619
+ this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal, approvalEntrySeq: this.approvalEntrySeq });
10527
10620
  this.activeModal = null;
10528
10621
  this.lastApprovalResolvedAt = Date.now();
10529
10622
  this.lastResolvedModalMessage = currentModalMessage;
10623
+ this.lastResolvedEntrySeq = this.approvalEntrySeq;
10530
10624
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
10531
10625
  if (this.approvalExitTimeout) {
10532
10626
  clearTimeout(this.approvalExitTimeout);
@@ -10883,7 +10977,7 @@ var init_cli_state_engine = __esm({
10883
10977
  this.callbacks.onStatusChange();
10884
10978
  return;
10885
10979
  }
10886
- if (!inCooldown) {
10980
+ if (!inCooldown || modal) {
10887
10981
  if (!modal) {
10888
10982
  LOG.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
10889
10983
  if (this.currentStatus === "waiting_approval" && this.activeModal) {
@@ -10906,6 +11000,7 @@ var init_cli_state_engine = __esm({
10906
11000
  const nextBtnCount = Array.isArray(modal.buttons) ? modal.buttons.length : 0;
10907
11001
  if (!prev || prevBtnCount !== nextBtnCount) {
10908
11002
  this.activeModal = modal;
11003
+ this.approvalEntrySeq++;
10909
11004
  this.callbacks.onStatusChange();
10910
11005
  }
10911
11006
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
@@ -12100,6 +12195,7 @@ ${lastSnapshot}`;
12100
12195
  messages: [],
12101
12196
  workingDir: this.workingDir,
12102
12197
  activeModal: effectiveModal,
12198
+ approvalEntrySeq: this.engine.approvalEntrySeq,
12103
12199
  pendingOutboundCount: this.pendingOutboundQueue.length,
12104
12200
  pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
12105
12201
  id: message.id,
@@ -16174,7 +16270,7 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
16174
16270
  if (!validation.valid) {
16175
16271
  return { status: "failed", required, configSource: loaded.path || loaded.source, configSourceType: "invalid", error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")), commandsRun: [] };
16176
16272
  }
16177
- const execFileAsync3 = promisify3(execFile3);
16273
+ const execFileAsync4 = promisify3(execFile3);
16178
16274
  const state = {
16179
16275
  status: "running",
16180
16276
  required,
@@ -16200,7 +16296,7 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
16200
16296
  const startedAt = Date.now();
16201
16297
  state.lastCommand = command.displayCommand;
16202
16298
  try {
16203
- const result = await execFileAsync3(command.command, command.args, {
16299
+ const result = await execFileAsync4(command.command, command.args, {
16204
16300
  cwd,
16205
16301
  encoding: "utf8",
16206
16302
  timeout: command.timeoutMs || DEFAULT_TIMEOUT_MS2,
@@ -31314,7 +31410,9 @@ var CliProviderInstance = class {
31314
31410
  if (buttonIndex < 0) {
31315
31411
  return autoApproveActive;
31316
31412
  }
31413
+ const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
31317
31414
  const signature = [
31415
+ approvalEntrySeq,
31318
31416
  typeof modal?.message === "string" ? modal.message.trim() : "",
31319
31417
  buttons.join("|"),
31320
31418
  buttonIndex
@@ -36991,8 +37089,8 @@ var ProviderLoader = class _ProviderLoader {
36991
37089
  }
36992
37090
  const https = __require("https");
36993
37091
  const { exec: exec7 } = __require("child_process");
36994
- const { promisify: promisify7 } = __require("util");
36995
- const execAsync5 = promisify7(exec7);
37092
+ const { promisify: promisify8 } = __require("util");
37093
+ const execAsync5 = promisify8(exec7);
36996
37094
  const metaPath = path30.join(this.upstreamDir, _ProviderLoader.META_FILE);
36997
37095
  let prevEtag = "";
36998
37096
  let prevTimestamp = 0;
@@ -38272,6 +38370,93 @@ init_mesh_routing();
38272
38370
  init_mesh_host_ownership();
38273
38371
  init_mesh_fast_forward();
38274
38372
 
38373
+ // src/mesh/mesh-refine-batch.ts
38374
+ import { execFile as execFile4 } from "child_process";
38375
+ import { promisify as promisify6 } from "util";
38376
+ var execFileAsync3 = promisify6(execFile4);
38377
+ var MAX_CHANGED_FILES2 = 500;
38378
+ function topLevel(path39) {
38379
+ const slash = path39.indexOf("/");
38380
+ return slash === -1 ? path39 : path39.slice(0, slash);
38381
+ }
38382
+ async function analyzeMeshRefineNodeChangeArea(args) {
38383
+ const { nodeId, workspace, branch, baseRef, branchRef, diffCwd, submodulePaths } = args;
38384
+ const base = {
38385
+ nodeId,
38386
+ workspace,
38387
+ branch,
38388
+ changedTopLevelPaths: [],
38389
+ changedFiles: [],
38390
+ touchedSubmodulePaths: [],
38391
+ touchesSubmodule: false,
38392
+ aheadCount: 0
38393
+ };
38394
+ try {
38395
+ let mergeBase = baseRef;
38396
+ try {
38397
+ const { stdout } = await execFileAsync3("git", ["merge-base", baseRef, branchRef], { cwd: diffCwd, encoding: "utf8" });
38398
+ const resolved = stdout.trim();
38399
+ if (resolved) mergeBase = resolved;
38400
+ } catch {
38401
+ }
38402
+ const { stdout: countStdout } = await execFileAsync3(
38403
+ "git",
38404
+ ["rev-list", "--count", `${mergeBase}..${branchRef}`],
38405
+ { cwd: diffCwd, encoding: "utf8" }
38406
+ );
38407
+ base.aheadCount = Number.parseInt(countStdout.trim(), 10) || 0;
38408
+ const { stdout: nameStdout } = await execFileAsync3(
38409
+ "git",
38410
+ ["diff", "--name-only", `${mergeBase}..${branchRef}`],
38411
+ { cwd: diffCwd, encoding: "utf8" }
38412
+ );
38413
+ const files = nameStdout.split("\n").map((line) => line.trim()).filter(Boolean).slice(0, MAX_CHANGED_FILES2);
38414
+ base.changedFiles = files;
38415
+ const topSet = /* @__PURE__ */ new Set();
38416
+ const submoduleSet = /* @__PURE__ */ new Set();
38417
+ for (const file of files) {
38418
+ const top = topLevel(file);
38419
+ topSet.add(top);
38420
+ if (submodulePaths.has(file) || submodulePaths.has(top)) {
38421
+ submoduleSet.add(submodulePaths.has(file) ? file : top);
38422
+ }
38423
+ }
38424
+ base.changedTopLevelPaths = [...topSet].sort();
38425
+ base.touchedSubmodulePaths = [...submoduleSet].sort();
38426
+ base.touchesSubmodule = submoduleSet.size > 0;
38427
+ return base;
38428
+ } catch (e) {
38429
+ base.error = e?.message || String(e);
38430
+ return base;
38431
+ }
38432
+ }
38433
+ function orderMeshRefineBatchNodes(changeAreas) {
38434
+ const areaById = {};
38435
+ for (const area of changeAreas) areaById[area.nodeId] = area;
38436
+ const ranked = [...changeAreas].sort((a, b) => {
38437
+ const aSub = a.touchesSubmodule ? 1 : 0;
38438
+ const bSub = b.touchesSubmodule ? 1 : 0;
38439
+ if (aSub !== bSub) return aSub - bSub;
38440
+ const aBreadth = a.changedTopLevelPaths.length;
38441
+ const bBreadth = b.changedTopLevelPaths.length;
38442
+ if (aBreadth !== bBreadth) return aBreadth - bBreadth;
38443
+ return a.nodeId.localeCompare(b.nodeId);
38444
+ });
38445
+ const rationale = [];
38446
+ const nonSub = ranked.filter((a) => !a.touchesSubmodule).map((a) => a.nodeId);
38447
+ const sub = ranked.filter((a) => a.touchesSubmodule).map((a) => a.nodeId);
38448
+ if (nonSub.length) {
38449
+ rationale.push(`Non-submodule nodes first (no submodule-main advance, conflict-free ordering): ${nonSub.join(", ")}`);
38450
+ }
38451
+ if (sub.length) {
38452
+ rationale.push(`Submodule-touching nodes last, serialized (each merge advances submodule main, forcing rebase of the next): ${sub.join(", ")}`);
38453
+ }
38454
+ for (const area of ranked) {
38455
+ if (area.error) rationale.push(`Node ${area.nodeId}: change-area analysis degraded (${area.error}); placed with neutral priority.`);
38456
+ }
38457
+ return { order: ranked.map((a) => a.nodeId), changeAreas: areaById, rationale };
38458
+ }
38459
+
38275
38460
  // src/mesh/preview-freshness.ts
38276
38461
  import { execFileSync as execFileSync3 } from "child_process";
38277
38462
  import { existsSync as existsSync31, readFileSync as readFileSync24 } from "fs";
@@ -40263,10 +40448,10 @@ async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, cur
40263
40448
  }
40264
40449
  const commandArgs = ["submodule", "update", "--init", "--recursive", "--", ...updatePaths];
40265
40450
  try {
40266
- const { execFile: execFile4 } = await import("child_process");
40267
- const { promisify: promisify7 } = await import("util");
40268
- const execFileAsync3 = promisify7(execFile4);
40269
- const result = await execFileAsync3("git", commandArgs, {
40451
+ const { execFile: execFile5 } = await import("child_process");
40452
+ const { promisify: promisify8 } = await import("util");
40453
+ const execFileAsync4 = promisify8(execFile5);
40454
+ const result = await execFileAsync4("git", commandArgs, {
40270
40455
  cwd: repoRoot,
40271
40456
  encoding: "utf8",
40272
40457
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
@@ -40309,11 +40494,11 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
40309
40494
  const startedAt = Date.now();
40310
40495
  const entries = [];
40311
40496
  try {
40312
- const { execFile: execFile4 } = await import("child_process");
40313
- const { promisify: promisify7 } = await import("util");
40314
- const execFileAsync3 = promisify7(execFile4);
40497
+ const { execFile: execFile5 } = await import("child_process");
40498
+ const { promisify: promisify8 } = await import("util");
40499
+ const execFileAsync4 = promisify8(execFile5);
40315
40500
  const runGit3 = async (cwd, args) => {
40316
- const { stdout } = await execFileAsync3("git", args, {
40501
+ const { stdout } = await execFileAsync4("git", args, {
40317
40502
  cwd,
40318
40503
  encoding: "utf8",
40319
40504
  timeout: 3e4,
@@ -40328,7 +40513,7 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
40328
40513
  };
40329
40514
  const publishCommitToRemoteMain = async (submodulePath, commit, branch = "main") => {
40330
40515
  const refspec = `${commit}:refs/heads/${branch}`;
40331
- const { stdout, stderr } = await execFileAsync3("git", ["push", "origin", refspec], {
40516
+ const { stdout, stderr } = await execFileAsync4("git", ["push", "origin", refspec], {
40332
40517
  cwd: submodulePath,
40333
40518
  encoding: "utf8",
40334
40519
  timeout: 3e4,
@@ -40513,9 +40698,9 @@ function buildMeshRefineValidationPlan(mesh, workspace) {
40513
40698
  };
40514
40699
  }
40515
40700
  async function runMeshRefineValidationGate(mesh, workspace, opts) {
40516
- const { execFile: execFile4 } = await import("child_process");
40517
- const { promisify: promisify7 } = await import("util");
40518
- const execFileAsync3 = promisify7(execFile4);
40701
+ const { execFile: execFile5 } = await import("child_process");
40702
+ const { promisify: promisify8 } = await import("util");
40703
+ const execFileAsync4 = promisify8(execFile5);
40519
40704
  const selection = resolveMeshRefineValidationPlan(mesh, workspace);
40520
40705
  const summary = {
40521
40706
  status: "skipped",
@@ -40609,7 +40794,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
40609
40794
  const cwd = candidate.cwd ? pathResolve2(workspace, candidate.cwd) : workspace;
40610
40795
  const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
40611
40796
  try {
40612
- const result = await execFileAsync3(candidate.command, candidate.args, {
40797
+ const result = await execFileAsync4(candidate.command, candidate.args, {
40613
40798
  cwd,
40614
40799
  encoding: "utf8",
40615
40800
  timeout,
@@ -40651,7 +40836,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
40651
40836
  return summary;
40652
40837
  }
40653
40838
  try {
40654
- const result = await execFileAsync3(candidate.command, candidate.args, {
40839
+ const result = await execFileAsync4(candidate.command, candidate.args, {
40655
40840
  cwd,
40656
40841
  encoding: "utf8",
40657
40842
  timeout,
@@ -41219,19 +41404,19 @@ var DaemonCommandRouter = class {
41219
41404
  const isSubmoduleGuard = /working trees containing submodules cannot be moved or removed/i.test(message);
41220
41405
  const submoduleForceBlocked = isSubmoduleGuard && !forceFallbackConvergence.allow;
41221
41406
  if (isSubmoduleGuard && forceFallbackConvergence.allow) {
41222
- const { execFile: execFile4 } = await import("child_process");
41223
- const { promisify: promisify7 } = await import("util");
41224
- const execFileAsync3 = promisify7(execFile4);
41407
+ const { execFile: execFile5 } = await import("child_process");
41408
+ const { promisify: promisify8 } = await import("util");
41409
+ const execFileAsync4 = promisify8(execFile5);
41225
41410
  const GIT_TIMEOUT_CLEANUP = 3e4;
41226
41411
  const GIT_MAX_BUFFER_CLEANUP = 4 * 1024 * 1024;
41227
41412
  try {
41228
- await execFileAsync3("git", ["-C", workspace, "submodule", "deinit", "--all", "-f"], {
41413
+ await execFileAsync4("git", ["-C", workspace, "submodule", "deinit", "--all", "-f"], {
41229
41414
  encoding: "utf8",
41230
41415
  timeout: GIT_TIMEOUT_CLEANUP,
41231
41416
  maxBuffer: GIT_MAX_BUFFER_CLEANUP,
41232
41417
  windowsHide: true
41233
41418
  });
41234
- await execFileAsync3("git", ["worktree", "remove", "--force", workspace], {
41419
+ await execFileAsync4("git", ["worktree", "remove", "--force", workspace], {
41235
41420
  cwd: repoRoot,
41236
41421
  encoding: "utf8",
41237
41422
  timeout: GIT_TIMEOUT_CLEANUP,
@@ -41250,7 +41435,7 @@ var DaemonCommandRouter = class {
41250
41435
  } catch (deinitError) {
41251
41436
  try {
41252
41437
  fs23.rmSync(workspace, { recursive: true, force: true });
41253
- await execFileAsync3("git", ["worktree", "prune"], {
41438
+ await execFileAsync4("git", ["worktree", "prune"], {
41254
41439
  cwd: repoRoot,
41255
41440
  encoding: "utf8",
41256
41441
  timeout: GIT_TIMEOUT_CLEANUP,
@@ -41294,11 +41479,11 @@ var DaemonCommandRouter = class {
41294
41479
  if (refinedConvergence === "merged_pushed" || refinedConvergence === "merged_to_main") {
41295
41480
  return { allow: true, status: refinedConvergence, source: "node_refine_state" };
41296
41481
  }
41297
- const { execFile: execFile4 } = await import("child_process");
41298
- const { promisify: promisify7 } = await import("util");
41299
- const execFileAsync3 = promisify7(execFile4);
41482
+ const { execFile: execFile5 } = await import("child_process");
41483
+ const { promisify: promisify8 } = await import("util");
41484
+ const execFileAsync4 = promisify8(execFile5);
41300
41485
  const runGit3 = async (gitArgs, cwd) => {
41301
- const { stdout } = await execFileAsync3("git", gitArgs, {
41486
+ const { stdout } = await execFileAsync4("git", gitArgs, {
41302
41487
  cwd,
41303
41488
  encoding: "utf8",
41304
41489
  timeout: 3e4,
@@ -41770,30 +41955,30 @@ var DaemonCommandRouter = class {
41770
41955
  const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
41771
41956
  const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
41772
41957
  if (!repoRoot) return { success: false, error: "Source node repoRoot not found", refineStages };
41773
- const { execFile: execFile4 } = await import("child_process");
41774
- const { promisify: promisify7 } = await import("util");
41775
- const execFileAsync3 = promisify7(execFile4);
41958
+ const { execFile: execFile5 } = await import("child_process");
41959
+ const { promisify: promisify8 } = await import("util");
41960
+ const execFileAsync4 = promisify8(execFile5);
41776
41961
  const resolveStarted = Date.now();
41777
- const { stdout: branchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
41962
+ const { stdout: branchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
41778
41963
  const branch = branchStdout.trim();
41779
41964
  if (!branch) return { success: false, error: "Could not determine branch of the worktree node", refineStages };
41780
- const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
41965
+ const { stdout: baseBranchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
41781
41966
  const baseBranch = baseBranchStdout.trim();
41782
41967
  let fetchWarning;
41783
41968
  try {
41784
- await execFileAsync3("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
41969
+ await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
41785
41970
  } catch (e) {
41786
41971
  fetchWarning = `git fetch origin ${baseBranch} failed (proceeding with local HEAD): ${e?.message}`;
41787
41972
  }
41788
41973
  let baseHeadRaw;
41789
41974
  try {
41790
- const { stdout } = await execFileAsync3("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
41975
+ const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
41791
41976
  baseHeadRaw = stdout.trim();
41792
41977
  } catch {
41793
- const { stdout: localHead } = await execFileAsync3("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
41978
+ const { stdout: localHead } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
41794
41979
  baseHeadRaw = localHead.trim();
41795
41980
  }
41796
- const { stdout: branchHeadStdout } = await execFileAsync3("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
41981
+ const { stdout: branchHeadStdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
41797
41982
  const baseHead = baseHeadRaw;
41798
41983
  let branchHead = branchHeadStdout.trim();
41799
41984
  recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead, ...fetchWarning ? { fetchWarning } : {} });
@@ -41895,7 +42080,7 @@ ${tail}` : ""
41895
42080
  cwd: node.workspace,
41896
42081
  stdio: ["ignore", "pipe", "pipe"]
41897
42082
  });
41898
- const { stdout: rebasedHeadStdout } = await execFileAsync3("git", ["rev-parse", "HEAD"], { cwd: node.workspace, encoding: "utf8" });
42083
+ const { stdout: rebasedHeadStdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: node.workspace, encoding: "utf8" });
41899
42084
  branchHead = rebasedHeadStdout.trim();
41900
42085
  const rebasedPatchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
41901
42086
  recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", rebasedPatchEquivalence.status, autoRebaseStarted, {
@@ -42139,7 +42324,7 @@ ${tail}` : ""
42139
42324
  let mergeResult;
42140
42325
  const mergeStarted = Date.now();
42141
42326
  try {
42142
- const result = await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
42327
+ const result = await execFileAsync4("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
42143
42328
  mergeResult = {
42144
42329
  stdout: truncateValidationOutput(result.stdout),
42145
42330
  stderr: truncateValidationOutput(result.stderr),
@@ -42273,7 +42458,7 @@ ${tail}` : ""
42273
42458
  if (!requireApprovalForPush) {
42274
42459
  const pushStarted = Date.now();
42275
42460
  try {
42276
- await execFileAsync3("git", ["push", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
42461
+ await execFileAsync4("git", ["push", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
42277
42462
  pushResult = { pushed: true, remote: "origin", branch: baseBranch, durationMs: Date.now() - pushStarted };
42278
42463
  recordMeshRefineStage(refineStages, "push", "passed", pushStarted, pushResult);
42279
42464
  finalBranchConvergenceState.status = "merged_pushed";
@@ -42314,6 +42499,223 @@ ${tail}` : ""
42314
42499
  return { success: false, error: e.message, refineStages };
42315
42500
  }
42316
42501
  }
42502
+ /**
42503
+ * Batch refinery: converge multiple sibling worktree nodes onto the base branch
42504
+ * in one sequential pipeline, absorbing the rebase + patch-equivalence churn that
42505
+ * arises when several siblings touch the same submodule.
42506
+ *
42507
+ * Reuses executeMeshRefineNodeSynchronously per node — every node goes through the
42508
+ * exact same validation / patch-equivalence / submodule-reachability / merge / cleanup
42509
+ * gates, including its built-in auto-rebase onto fresh origin/<base>. Because each
42510
+ * node fetches origin/<base> at the start of its own refine, a node merged earlier in
42511
+ * the batch advances the base, and the next node's refine auto-rebases onto it before
42512
+ * re-running patch-equivalence. No force-push, no reset — conflicting nodes are
42513
+ * isolated as blocked_review while the rest of the batch proceeds.
42514
+ */
42515
+ async batchRefineMeshNodes(meshId, requestedNodeIds, args) {
42516
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
42517
+ const mesh = meshRecord?.mesh;
42518
+ if (!mesh) return { success: false, error: `Mesh '${meshId}' not found` };
42519
+ const allNodes = Array.isArray(mesh.nodes) ? mesh.nodes : [];
42520
+ const isConvergeable = (n) => n?.isLocalWorktree && typeof n.workspace === "string" && n.workspace;
42521
+ let targetNodes;
42522
+ if (Array.isArray(requestedNodeIds) && requestedNodeIds.length > 0) {
42523
+ targetNodes = [];
42524
+ const missing = [];
42525
+ const nonWorktree = [];
42526
+ for (const nodeId of requestedNodeIds) {
42527
+ const node = allNodes.find((n) => n.id === nodeId || n.nodeId === nodeId);
42528
+ if (!node) {
42529
+ missing.push(nodeId);
42530
+ continue;
42531
+ }
42532
+ if (!isConvergeable(node)) {
42533
+ nonWorktree.push(nodeId);
42534
+ continue;
42535
+ }
42536
+ targetNodes.push(node);
42537
+ }
42538
+ if (missing.length || nonWorktree.length) {
42539
+ return {
42540
+ success: false,
42541
+ error: "One or more requested nodes are not convergeable local worktree nodes.",
42542
+ ...missing.length ? { missingNodeIds: missing } : {},
42543
+ ...nonWorktree.length ? { nonWorktreeNodeIds: nonWorktree } : {}
42544
+ };
42545
+ }
42546
+ } else {
42547
+ targetNodes = allNodes.filter(isConvergeable);
42548
+ }
42549
+ if (targetNodes.length === 0) {
42550
+ return { success: true, batch: true, dryRun: args?.dryRun !== false, nodeCount: 0, order: [], results: [], note: "No convergeable local worktree nodes found." };
42551
+ }
42552
+ const { execFile: execFile5 } = await import("child_process");
42553
+ const { promisify: promisify8 } = await import("util");
42554
+ const execFileAsync4 = promisify8(execFile5);
42555
+ const resolveRepoRootFor = (node) => {
42556
+ const sourceNode = node.clonedFromNodeId ? allNodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : allNodes.find((n) => !n.isLocalWorktree);
42557
+ return sourceNode?.repoRoot || sourceNode?.workspace;
42558
+ };
42559
+ const repoRootBaseRef = /* @__PURE__ */ new Map();
42560
+ const submodulePathsByRepoRoot = /* @__PURE__ */ new Map();
42561
+ const resolveBaseRef = async (repoRoot) => {
42562
+ const cached = repoRootBaseRef.get(repoRoot);
42563
+ if (cached) return cached;
42564
+ let baseBranch = "main";
42565
+ try {
42566
+ const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
42567
+ if (stdout.trim()) baseBranch = stdout.trim();
42568
+ } catch {
42569
+ }
42570
+ let baseRef = "HEAD";
42571
+ try {
42572
+ await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
42573
+ } catch {
42574
+ }
42575
+ try {
42576
+ const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
42577
+ baseRef = stdout.trim();
42578
+ } catch {
42579
+ try {
42580
+ const { stdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
42581
+ baseRef = stdout.trim();
42582
+ } catch {
42583
+ }
42584
+ }
42585
+ repoRootBaseRef.set(repoRoot, baseRef);
42586
+ return baseRef;
42587
+ };
42588
+ const changeAreas = [];
42589
+ for (const node of targetNodes) {
42590
+ const repoRoot = resolveRepoRootFor(node);
42591
+ let branch = typeof node.worktreeBranch === "string" ? node.worktreeBranch : "";
42592
+ try {
42593
+ const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
42594
+ if (stdout.trim()) branch = stdout.trim();
42595
+ } catch {
42596
+ }
42597
+ if (!repoRoot || !branch) {
42598
+ changeAreas.push({
42599
+ nodeId: node.id,
42600
+ workspace: node.workspace,
42601
+ branch: branch || "(unknown)",
42602
+ changedTopLevelPaths: [],
42603
+ changedFiles: [],
42604
+ touchedSubmodulePaths: [],
42605
+ touchesSubmodule: false,
42606
+ aheadCount: 0,
42607
+ error: !repoRoot ? "source repoRoot not found" : "branch not resolved"
42608
+ });
42609
+ continue;
42610
+ }
42611
+ if (!submodulePathsByRepoRoot.has(repoRoot)) {
42612
+ let subPaths = /* @__PURE__ */ new Set();
42613
+ try {
42614
+ const { stdout } = await execFileAsync4("git", ["config", "--file", ".gitmodules", "--get-regexp", "path"], { cwd: repoRoot, encoding: "utf8" });
42615
+ for (const line of stdout.split("\n")) {
42616
+ const trimmed = line.trim();
42617
+ const spaceIdx = trimmed.indexOf(" ");
42618
+ if (spaceIdx === -1) continue;
42619
+ const value = trimmed.slice(spaceIdx + 1).trim();
42620
+ if (value) subPaths.add(value);
42621
+ }
42622
+ } catch {
42623
+ subPaths = /* @__PURE__ */ new Set();
42624
+ }
42625
+ submodulePathsByRepoRoot.set(repoRoot, subPaths);
42626
+ }
42627
+ const baseRef = await resolveBaseRef(repoRoot);
42628
+ let branchRef = branch;
42629
+ try {
42630
+ const { stdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
42631
+ branchRef = stdout.trim() || branch;
42632
+ } catch {
42633
+ }
42634
+ changeAreas.push(await analyzeMeshRefineNodeChangeArea({
42635
+ nodeId: node.id,
42636
+ workspace: node.workspace,
42637
+ branch,
42638
+ baseRef,
42639
+ branchRef,
42640
+ diffCwd: node.workspace,
42641
+ submodulePaths: submodulePathsByRepoRoot.get(repoRoot)
42642
+ }));
42643
+ }
42644
+ const ordering = orderMeshRefineBatchNodes(changeAreas);
42645
+ const orderedNodes = ordering.order.map((nodeId) => targetNodes.find((n) => n.id === nodeId || n.nodeId === nodeId)).filter((n) => !!n);
42646
+ const dryRun = args?.dryRun !== false && args?.execute !== true;
42647
+ if (dryRun) {
42648
+ return {
42649
+ success: true,
42650
+ batch: true,
42651
+ dryRun: true,
42652
+ nodeCount: orderedNodes.length,
42653
+ order: ordering.order,
42654
+ orderingRationale: ordering.rationale,
42655
+ changeAreas: ordering.changeAreas,
42656
+ plan: orderedNodes.map((node) => ({
42657
+ nodeId: node.id,
42658
+ workspace: node.workspace,
42659
+ validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
42660
+ mergeWillRun: false
42661
+ })),
42662
+ note: "Dry-run: no validation, rebase, or merge was executed. Re-run with execute=true to converge nodes in this order."
42663
+ };
42664
+ }
42665
+ const results = [];
42666
+ for (const node of orderedNodes) {
42667
+ let result;
42668
+ try {
42669
+ result = await this.executeMeshRefineNodeSynchronously(meshId, node.id, args);
42670
+ } catch (e) {
42671
+ result = { success: false, error: e?.message || String(e) };
42672
+ }
42673
+ const code = typeof result.code === "string" ? result.code : "";
42674
+ let convergence;
42675
+ if (code === "already_merged" && result.alreadyMergedViaOtherPath) {
42676
+ convergence = "skipped_patch_equivalent";
42677
+ } else if (result.success === true) {
42678
+ convergence = "merged_to_main";
42679
+ } else if (code === "merge_failed") {
42680
+ convergence = "not_mergeable";
42681
+ } else {
42682
+ convergence = "blocked_review";
42683
+ }
42684
+ const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
42685
+ const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s) => s.status === "failed").map((s) => s.stage).filter(Boolean).pop() : void 0;
42686
+ results.push({
42687
+ nodeId: node.id,
42688
+ workspace: node.workspace,
42689
+ convergence,
42690
+ ...code ? { code } : {},
42691
+ ...typeof result.blockedReason === "string" ? { reason: result.blockedReason } : {},
42692
+ ...stage ? { stage } : {},
42693
+ ...typeof result.error === "string" ? { error: result.error } : {},
42694
+ ...fbcs ? { finalBranchConvergenceState: fbcs } : {}
42695
+ });
42696
+ }
42697
+ const summary = {
42698
+ merged: results.filter((r) => r.convergence === "merged_to_main").length,
42699
+ skipped: results.filter((r) => r.convergence === "skipped_patch_equivalent").length,
42700
+ blocked: results.filter((r) => r.convergence === "blocked_review").length,
42701
+ notMergeable: results.filter((r) => r.convergence === "not_mergeable").length
42702
+ };
42703
+ const allConverged = summary.blocked === 0 && summary.notMergeable === 0;
42704
+ return {
42705
+ success: true,
42706
+ batch: true,
42707
+ dryRun: false,
42708
+ nodeCount: orderedNodes.length,
42709
+ order: ordering.order,
42710
+ orderingRationale: ordering.rationale,
42711
+ summary,
42712
+ allConverged,
42713
+ results,
42714
+ ...allConverged ? {} : {
42715
+ nextStep: "Resolve blocked_review / not_mergeable nodes manually (see per-node code/stage/error), then re-run mesh_refine_batch for the remaining nodes."
42716
+ }
42717
+ };
42718
+ }
42317
42719
  async finishMeshRefineJob(handle, args) {
42318
42720
  const key = this.buildRefineJobKey(handle.meshId, handle.targetNodeId);
42319
42721
  let result;
@@ -43857,6 +44259,12 @@ ${tail}` : ""
43857
44259
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
43858
44260
  return this.startMeshRefineJob(meshId, nodeId, args);
43859
44261
  }
44262
+ case "batch_refine_mesh_nodes": {
44263
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
44264
+ if (!meshId) return { success: false, error: "meshId required" };
44265
+ const requestedNodeIds = Array.isArray(args?.nodeIds) ? args.nodeIds.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim()) : void 0;
44266
+ return this.batchRefineMeshNodes(meshId, requestedNodeIds, args);
44267
+ }
43860
44268
  case "remove_mesh_node": {
43861
44269
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
43862
44270
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
@@ -52984,7 +53392,7 @@ function shouldAutoRestoreHostedSessionsOnStartup(env = process.env) {
52984
53392
 
52985
53393
  // src/installer.ts
52986
53394
  import { exec as exec6 } from "child_process";
52987
- import { promisify as promisify6 } from "util";
53395
+ import { promisify as promisify7 } from "util";
52988
53396
  var EXTENSION_CATALOG = [
52989
53397
  // AI Agent extensions
52990
53398
  {
@@ -53071,7 +53479,7 @@ var EXTENSION_CATALOG = [
53071
53479
  apiKeyName: "OpenAI/Anthropic API key"
53072
53480
  }
53073
53481
  ];
53074
- var execAsync4 = promisify6(exec6);
53482
+ var execAsync4 = promisify7(exec6);
53075
53483
  async function isExtensionInstalled(ide, marketplaceId) {
53076
53484
  if (!ide.cliCommand) return false;
53077
53485
  try {
@@ -53819,7 +54227,9 @@ export {
53819
54227
  ProviderCliAdapter,
53820
54228
  ProviderInstanceManager,
53821
54229
  ProviderLoader,
54230
+ RECENT_TERMINAL_REFINE_CAP,
53822
54231
  RawTerminalAttachment,
54232
+ STALE_TERMINAL_REFINE_WINDOW_MS,
53823
54233
  STANDALONE_CDP_SCAN_INTERVAL_MS,
53824
54234
  SessionHostPtyTransportFactory,
53825
54235
  TerminalAdapter,
@@ -54027,6 +54437,7 @@ export {
54027
54437
  reconcileDirectDispatchCompletionFromTranscript,
54028
54438
  recordCompletionConflict,
54029
54439
  recordDebugTrace,
54440
+ recordDirectDispatchTask,
54030
54441
  recordMeshToolCall,
54031
54442
  registerExtensionProviders,
54032
54443
  registerMeshCoordinator,
@@ -54063,6 +54474,7 @@ export {
54063
54474
  startLocalIpcServer,
54064
54475
  suggestMeshRefineConfig,
54065
54476
  summarizeGitStatus,
54477
+ summarizeMeshAsyncRefineJobs,
54066
54478
  summarizeMeshMission,
54067
54479
  summarizeMissionTasks,
54068
54480
  triggerMeshQueue,