@adhdev/daemon-standalone 0.9.77-rc.44 → 0.9.77-rc.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22210,7 +22210,33 @@ var require_dist2 = __commonJS({
22210
22210
  });
22211
22211
  } catch (error48) {
22212
22212
  const stderr = typeof error48.stderr === "string" ? error48.stderr : "";
22213
- throw new Error(`git worktree remove failed: ${stderr.trim() || error48.message}`);
22213
+ const stdout = typeof error48.stdout === "string" ? error48.stdout : "";
22214
+ const detail = `${stderr}
22215
+ ${stdout}
22216
+ ${error48.message || ""}`;
22217
+ if (opts.allowSubmoduleForceFallback && SUBMODULE_WORKTREE_REMOVE_RE.test(detail)) {
22218
+ try {
22219
+ await execFileAsync2("git", ["worktree", "remove", "--force", worktreePath], {
22220
+ cwd: repoRoot,
22221
+ encoding: "utf8",
22222
+ timeout: GIT_TIMEOUT_MS,
22223
+ maxBuffer: GIT_MAX_BUFFER,
22224
+ windowsHide: true
22225
+ });
22226
+ } catch (forceError) {
22227
+ const forceStderr = typeof forceError.stderr === "string" ? forceError.stderr : "";
22228
+ const forceStdout = typeof forceError.stdout === "string" ? forceError.stdout : "";
22229
+ throw new Error(`git worktree remove --force fallback failed: ${forceStderr.trim() || forceStdout.trim() || forceError.message}`);
22230
+ }
22231
+ return {
22232
+ success: true,
22233
+ removedPath: worktreePath,
22234
+ fallback: "git_worktree_remove_force_submodule",
22235
+ forced: true,
22236
+ reason: "working_trees_containing_submodules"
22237
+ };
22238
+ }
22239
+ throw new Error(`git worktree remove failed: ${stderr.trim() || stdout.trim() || error48.message}`);
22214
22240
  }
22215
22241
  return { success: true, removedPath: worktreePath };
22216
22242
  }
@@ -22269,6 +22295,7 @@ var require_dist2 = __commonJS({
22269
22295
  var WORKTREE_DIR_NAME;
22270
22296
  var GIT_TIMEOUT_MS;
22271
22297
  var GIT_MAX_BUFFER;
22298
+ var SUBMODULE_WORKTREE_REMOVE_RE;
22272
22299
  var init_git_worktree = __esm2({
22273
22300
  "src/git/git-worktree.ts"() {
22274
22301
  "use strict";
@@ -22281,6 +22308,7 @@ var require_dist2 = __commonJS({
22281
22308
  WORKTREE_DIR_NAME = ".adhdev-worktrees";
22282
22309
  GIT_TIMEOUT_MS = 3e4;
22283
22310
  GIT_MAX_BUFFER = 4 * 1024 * 1024;
22311
+ SUBMODULE_WORKTREE_REMOVE_RE = /working trees containing submodules cannot be moved or removed/i;
22284
22312
  }
22285
22313
  });
22286
22314
  var config_exports = {};
@@ -23112,6 +23140,7 @@ Follow these recovery rules:
23112
23140
  enqueueTask: () => enqueueTask,
23113
23141
  getMeshQueueStats: () => getMeshQueueStats,
23114
23142
  getQueue: () => getQueue,
23143
+ recordTaskAutoLaunch: () => recordTaskAutoLaunch,
23115
23144
  requeueTask: () => requeueTask,
23116
23145
  updateSessionTaskStatus: () => updateSessionTaskStatus,
23117
23146
  updateTaskStatus: () => updateTaskStatus
@@ -23187,6 +23216,19 @@ Follow these recovery rules:
23187
23216
  writeQueue(meshId, queue);
23188
23217
  return queue[idx];
23189
23218
  }
23219
+ function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
23220
+ const queue = readQueue(meshId);
23221
+ const idx = queue.findIndex((q) => q.id === taskId);
23222
+ if (idx === -1) return null;
23223
+ const now = (/* @__PURE__ */ new Date()).toISOString();
23224
+ queue[idx].autoLaunch = {
23225
+ ...autoLaunch,
23226
+ updatedAt: now
23227
+ };
23228
+ queue[idx].updatedAt = now;
23229
+ writeQueue(meshId, queue);
23230
+ return queue[idx];
23231
+ }
23190
23232
  function cancelTask(meshId, taskId, opts) {
23191
23233
  const queue = readQueue(meshId);
23192
23234
  const idx = queue.findIndex((q) => q.id === taskId);
@@ -23261,6 +23303,142 @@ Follow these recovery rules:
23261
23303
  init_mesh_ledger();
23262
23304
  }
23263
23305
  });
23306
+ function parseVersion(raw) {
23307
+ const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
23308
+ return match ? match[1] : raw.split("\n")[0].slice(0, 100);
23309
+ }
23310
+ function shellQuote(value) {
23311
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
23312
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
23313
+ }
23314
+ function expandHome(value) {
23315
+ const trimmed = value.trim();
23316
+ if (!trimmed.startsWith("~")) return trimmed;
23317
+ return path8.join(os22.homedir(), trimmed.slice(1));
23318
+ }
23319
+ function isExplicitCommandPath(command) {
23320
+ const trimmed = command.trim();
23321
+ return path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
23322
+ }
23323
+ function resolveCommandPath(command) {
23324
+ const trimmed = command.trim();
23325
+ if (!trimmed) return null;
23326
+ if (isExplicitCommandPath(trimmed)) {
23327
+ const expanded = expandHome(trimmed);
23328
+ const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
23329
+ return (0, import_fs5.existsSync)(candidate) ? candidate : null;
23330
+ }
23331
+ return null;
23332
+ }
23333
+ function execAsync(cmd, timeoutMs = 5e3) {
23334
+ return new Promise((resolve16) => {
23335
+ const child = (0, import_child_process.exec)(cmd, {
23336
+ encoding: "utf-8",
23337
+ timeout: timeoutMs,
23338
+ ...process.platform === "win32" ? { windowsHide: true } : {}
23339
+ }, (err, stdout) => {
23340
+ if (err || !stdout?.trim()) {
23341
+ resolve16(null);
23342
+ } else {
23343
+ resolve16(stdout.trim());
23344
+ }
23345
+ });
23346
+ child.on("error", () => resolve16(null));
23347
+ });
23348
+ }
23349
+ async function detectCLIs(providerLoader, options) {
23350
+ const platform10 = os22.platform();
23351
+ const whichCmd = platform10 === "win32" ? "where" : "which";
23352
+ const includeVersion = options?.includeVersion !== false;
23353
+ const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
23354
+ const results = await Promise.all(
23355
+ cliList.map(async (cli) => {
23356
+ try {
23357
+ const explicitPath = resolveCommandPath(cli.command);
23358
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
23359
+ if (!pathResult) return { ...cli, installed: false };
23360
+ const firstPath = explicitPath || pathResult.split("\n")[0];
23361
+ let version2;
23362
+ if (includeVersion) {
23363
+ const versionCommands = [
23364
+ `"${firstPath}" --version`,
23365
+ `"${firstPath}" -V`,
23366
+ `"${firstPath}" -v`,
23367
+ cli.versionCommand
23368
+ ].filter((v) => !!v);
23369
+ try {
23370
+ for (const versionCommand of versionCommands) {
23371
+ const versionResult = await execAsync(versionCommand, 3e3);
23372
+ if (versionResult) {
23373
+ version2 = parseVersion(versionResult);
23374
+ break;
23375
+ }
23376
+ }
23377
+ } catch {
23378
+ }
23379
+ }
23380
+ return { ...cli, installed: true, version: version2, path: firstPath };
23381
+ } catch {
23382
+ return { ...cli, installed: false };
23383
+ }
23384
+ })
23385
+ );
23386
+ return results;
23387
+ }
23388
+ async function detectCLI(cliId, providerLoader, options) {
23389
+ const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
23390
+ if (providerLoader) {
23391
+ const cliList = providerLoader.getCliDetectionList();
23392
+ const target = cliList.find((c) => c.id === resolvedId);
23393
+ if (target) {
23394
+ const platform10 = os22.platform();
23395
+ const whichCmd = platform10 === "win32" ? "where" : "which";
23396
+ try {
23397
+ const explicitPath = resolveCommandPath(target.command);
23398
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
23399
+ if (!pathResult) return null;
23400
+ const firstPath = explicitPath || pathResult.split("\n")[0];
23401
+ let version2;
23402
+ if (options?.includeVersion !== false) {
23403
+ const versionCommands = [
23404
+ `"${firstPath}" --version`,
23405
+ `"${firstPath}" -V`,
23406
+ `"${firstPath}" -v`,
23407
+ target.versionCommand
23408
+ ].filter((v) => !!v);
23409
+ try {
23410
+ for (const versionCommand of versionCommands) {
23411
+ const versionResult = await execAsync(versionCommand, 3e3);
23412
+ if (versionResult) {
23413
+ version2 = parseVersion(versionResult);
23414
+ break;
23415
+ }
23416
+ }
23417
+ } catch {
23418
+ }
23419
+ }
23420
+ return { ...target, installed: true, version: version2, path: firstPath };
23421
+ } catch {
23422
+ return null;
23423
+ }
23424
+ }
23425
+ }
23426
+ const all = await detectCLIs(providerLoader, options);
23427
+ return all.find((c) => c.id === resolvedId && c.installed) || null;
23428
+ }
23429
+ var import_child_process;
23430
+ var os22;
23431
+ var path8;
23432
+ var import_fs5;
23433
+ var init_cli_detector = __esm2({
23434
+ "src/detection/cli-detector.ts"() {
23435
+ "use strict";
23436
+ import_child_process = require("child_process");
23437
+ os22 = __toESM2(require("os"));
23438
+ path8 = __toESM2(require("path"));
23439
+ import_fs5 = require("fs");
23440
+ }
23441
+ });
23264
23442
  function setLogLevel(level) {
23265
23443
  currentLevel = level;
23266
23444
  daemonLog("Logger", `Log level set to: ${level}`, "info");
@@ -23275,13 +23453,13 @@ Follow these recovery rules:
23275
23453
  return LOG_DIR;
23276
23454
  }
23277
23455
  function getCurrentDaemonLogPath(date5 = /* @__PURE__ */ new Date()) {
23278
- return path8.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
23456
+ return path9.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
23279
23457
  }
23280
23458
  function checkDateRotation() {
23281
23459
  const today = getDateStr();
23282
23460
  if (today !== currentDate) {
23283
23461
  currentDate = today;
23284
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
23462
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
23285
23463
  cleanOldLogs();
23286
23464
  }
23287
23465
  }
@@ -23295,7 +23473,7 @@ Follow these recovery rules:
23295
23473
  const dateMatch = file2.match(/daemon-(\d{4}-\d{2}-\d{2})/);
23296
23474
  if (dateMatch && dateMatch[1] < cutoffStr) {
23297
23475
  try {
23298
- fs22.unlinkSync(path8.join(LOG_DIR, file2));
23476
+ fs22.unlinkSync(path9.join(LOG_DIR, file2));
23299
23477
  } catch {
23300
23478
  }
23301
23479
  }
@@ -23412,8 +23590,8 @@ Follow these recovery rules:
23412
23590
  writeToFile(`Log level: ${currentLevel}`);
23413
23591
  }
23414
23592
  var fs22;
23415
- var path8;
23416
- var os22;
23593
+ var path9;
23594
+ var os32;
23417
23595
  var LEVEL_NUM;
23418
23596
  var LEVEL_LABEL;
23419
23597
  var currentLevel;
@@ -23435,12 +23613,12 @@ Follow these recovery rules:
23435
23613
  "src/logging/logger.ts"() {
23436
23614
  "use strict";
23437
23615
  fs22 = __toESM2(require("fs"));
23438
- path8 = __toESM2(require("path"));
23439
- os22 = __toESM2(require("os"));
23616
+ path9 = __toESM2(require("path"));
23617
+ os32 = __toESM2(require("os"));
23440
23618
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
23441
23619
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
23442
23620
  currentLevel = "info";
23443
- LOG_DIR = process.platform === "win32" ? path8.join(process.env.LOCALAPPDATA || process.env.APPDATA || path8.join(os22.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path8.join(os22.homedir(), "Library", "Logs", "adhdev") : path8.join(os22.homedir(), ".local", "share", "adhdev", "logs");
23621
+ LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os32.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os32.homedir(), "Library", "Logs", "adhdev") : path9.join(os32.homedir(), ".local", "share", "adhdev", "logs");
23444
23622
  MAX_LOG_SIZE = 5 * 1024 * 1024;
23445
23623
  MAX_LOG_DAYS = 7;
23446
23624
  try {
@@ -23448,16 +23626,16 @@ Follow these recovery rules:
23448
23626
  } catch {
23449
23627
  }
23450
23628
  currentDate = getDateStr();
23451
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
23629
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
23452
23630
  cleanOldLogs();
23453
23631
  try {
23454
- const oldLog = path8.join(LOG_DIR, "daemon.log");
23632
+ const oldLog = path9.join(LOG_DIR, "daemon.log");
23455
23633
  if (fs22.existsSync(oldLog)) {
23456
23634
  const stat2 = fs22.statSync(oldLog);
23457
23635
  const oldDate = stat2.mtime.toISOString().slice(0, 10);
23458
- fs22.renameSync(oldLog, path8.join(LOG_DIR, `daemon-${oldDate}.log`));
23636
+ fs22.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
23459
23637
  }
23460
- const oldLogBackup = path8.join(LOG_DIR, "daemon.log.old");
23638
+ const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
23461
23639
  if (fs22.existsSync(oldLogBackup)) {
23462
23640
  fs22.unlinkSync(oldLogBackup);
23463
23641
  }
@@ -23489,7 +23667,7 @@ Follow these recovery rules:
23489
23667
  }
23490
23668
  };
23491
23669
  interceptorInstalled = false;
23492
- LOG_PATH = path8.join(LOG_DIR, `daemon-${getDateStr()}.log`);
23670
+ LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
23493
23671
  }
23494
23672
  });
23495
23673
  var mesh_events_exports = {};
@@ -23559,7 +23737,235 @@ Follow these recovery rules:
23559
23737
  });
23560
23738
  return true;
23561
23739
  }
23562
- function triggerMeshQueue(components, meshId) {
23740
+ function normalizeProviderPriority(policy) {
23741
+ const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
23742
+ if (!Array.isArray(raw)) return [];
23743
+ const seen = /* @__PURE__ */ new Set();
23744
+ return raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean).filter((type) => {
23745
+ if (seen.has(type)) return false;
23746
+ seen.add(type);
23747
+ return true;
23748
+ });
23749
+ }
23750
+ function isTerminalSessionStatus(status) {
23751
+ return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
23752
+ }
23753
+ function isIdleSessionState(state) {
23754
+ const status = readNonEmptyString(state?.status).toLowerCase();
23755
+ if (isTerminalSessionStatus(status)) return false;
23756
+ return status === "idle" || state?.activeChat?.status === "waiting_input";
23757
+ }
23758
+ function isDirtyNode(node) {
23759
+ return node?.health === "dirty" || node?.git?.dirty === true;
23760
+ }
23761
+ function isLaunchableNode(node) {
23762
+ if (!node || node.status === "disabled" || node.status === "removed") return false;
23763
+ const health = readNonEmptyString(node.health).toLowerCase();
23764
+ if (!health) return true;
23765
+ return health === "online" || health === "unknown";
23766
+ }
23767
+ function localAutoLaunchSkipReason(node) {
23768
+ const daemonId = readNonEmptyString(node?.daemonId);
23769
+ const machineId = readNonEmptyString(node?.machineId);
23770
+ const appConfig = loadConfig2();
23771
+ const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
23772
+ const cloudDaemonId = localMachineId ? `daemon_${localMachineId}` : "";
23773
+ const standaloneDaemonId = localMachineId ? `standalone_${localMachineId}` : "";
23774
+ const daemonMatchesLocal = !daemonId || daemonId === cloudDaemonId || daemonId === standaloneDaemonId;
23775
+ const machineMatchesLocal = !machineId || localMachineId && machineId === localMachineId;
23776
+ if (node?.isLocalWorktree === true) {
23777
+ return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
23778
+ }
23779
+ if (daemonId || machineId) {
23780
+ return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
23781
+ }
23782
+ return null;
23783
+ }
23784
+ function activeAssignedCount(meshId) {
23785
+ return getQueue(meshId, { status: ["assigned"] }).length;
23786
+ }
23787
+ function nodeHasActiveAssignment(meshId, nodeId) {
23788
+ return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
23789
+ }
23790
+ function liveSessionCountForNode(components, meshId, nodeId) {
23791
+ return components.instanceManager.getByCategory("cli").filter((inst) => {
23792
+ const state = inst.getState();
23793
+ const settings = state.settings || {};
23794
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
23795
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
23796
+ if (instNodeId !== nodeId) return false;
23797
+ const status = readNonEmptyString(state.status).toLowerCase();
23798
+ return !isTerminalSessionStatus(status);
23799
+ }).length;
23800
+ }
23801
+ function recordAutoLaunchEvent(meshId, args) {
23802
+ try {
23803
+ appendLedgerEntry(meshId, {
23804
+ kind: "session_auto_launch",
23805
+ nodeId: args.nodeId,
23806
+ sessionId: args.sessionId,
23807
+ providerType: args.providerType,
23808
+ payload: {
23809
+ phase: args.phase,
23810
+ taskId: args.taskId,
23811
+ reason: args.reason,
23812
+ error: args.error
23813
+ }
23814
+ });
23815
+ } catch (e) {
23816
+ LOG2.warn("MeshQueue", `Failed to record auto-launch ledger event: ${e?.message || e}`);
23817
+ }
23818
+ }
23819
+ function markAutoLaunch(meshId, taskId, args) {
23820
+ recordTaskAutoLaunch(meshId, taskId, {
23821
+ status: args.status,
23822
+ reason: args.reason || args.error,
23823
+ nodeId: args.nodeId,
23824
+ providerType: args.providerType,
23825
+ sessionId: args.sessionId
23826
+ });
23827
+ recordAutoLaunchEvent(meshId, {
23828
+ phase: args.status,
23829
+ taskId,
23830
+ nodeId: args.nodeId,
23831
+ providerType: args.providerType,
23832
+ sessionId: args.sessionId,
23833
+ reason: args.reason,
23834
+ error: args.error
23835
+ });
23836
+ }
23837
+ async function resolveUsableProvider(components, nodeId, node) {
23838
+ const providerPriority = normalizeProviderPriority(node?.policy);
23839
+ if (!providerPriority.length) return { reason: "missing_provider_priority" };
23840
+ const providerLoader = components.providerLoader;
23841
+ if (!providerLoader) return { reason: "provider_loader_unavailable" };
23842
+ const failed = [];
23843
+ for (const requestedType of providerPriority) {
23844
+ const normalizedType = typeof providerLoader.resolveAlias === "function" ? providerLoader.resolveAlias(requestedType) : requestedType;
23845
+ if (typeof providerLoader.isMachineProviderEnabled === "function" && !providerLoader.isMachineProviderEnabled(normalizedType)) {
23846
+ failed.push(`${requestedType}: disabled`);
23847
+ continue;
23848
+ }
23849
+ let detected;
23850
+ try {
23851
+ detected = await detectCLI(normalizedType, providerLoader, { includeVersion: false });
23852
+ } catch (e) {
23853
+ failed.push(`${requestedType}: detect failed: ${e?.message || e}`);
23854
+ continue;
23855
+ }
23856
+ if (typeof providerLoader.setCliDetectionResults === "function") {
23857
+ providerLoader.setCliDetectionResults([{
23858
+ id: normalizedType,
23859
+ installed: !!detected,
23860
+ path: detected?.path
23861
+ }], false);
23862
+ }
23863
+ components.onStatusChange?.();
23864
+ if (detected) return { providerType: normalizedType };
23865
+ failed.push(`${requestedType}: not detected`);
23866
+ }
23867
+ return { reason: `provider_priority_unusable: ${failed.join("; ") || nodeId}` };
23868
+ }
23869
+ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
23870
+ const queue = getQueue(meshId);
23871
+ const pending = queue.filter((task) => task.status === "pending");
23872
+ if (!pending.length) return false;
23873
+ const maxParallelTasks = Math.max(1, Math.floor(Number(mesh?.policy?.maxParallelTasks) || 2));
23874
+ for (const task of pending) {
23875
+ if (activeAssignedCount(meshId) >= maxParallelTasks) {
23876
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
23877
+ return false;
23878
+ }
23879
+ if (task.targetSessionId) {
23880
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
23881
+ continue;
23882
+ }
23883
+ const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => task.targetNodeId ? node?.id === task.targetNodeId : true) : [];
23884
+ if (!candidateNodes.length) {
23885
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_matching_node", nodeId: task.targetNodeId });
23886
+ continue;
23887
+ }
23888
+ for (const node of candidateNodes) {
23889
+ const nodeId = readNonEmptyString(node?.id);
23890
+ if (!nodeId) continue;
23891
+ const launchKey = `${meshId}:${nodeId}`;
23892
+ const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
23893
+ if (autoLaunchInProgress.has(launchKey)) {
23894
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_in_progress", nodeId });
23895
+ continue;
23896
+ }
23897
+ if (Date.now() < cooldownUntil) {
23898
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_cooldown", nodeId });
23899
+ continue;
23900
+ }
23901
+ if (isDirtyNode(node)) {
23902
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dirty_workspace", nodeId });
23903
+ continue;
23904
+ }
23905
+ if (!isLaunchableNode(node)) {
23906
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_not_launch_ready", nodeId });
23907
+ continue;
23908
+ }
23909
+ const localSkipReason = localAutoLaunchSkipReason(node);
23910
+ if (localSkipReason) {
23911
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: localSkipReason, nodeId });
23912
+ continue;
23913
+ }
23914
+ if (nodeHasActiveAssignment(meshId, nodeId)) {
23915
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_active_assignment", nodeId });
23916
+ continue;
23917
+ }
23918
+ const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
23919
+ if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
23920
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_concurrent_sessions_reached", nodeId });
23921
+ continue;
23922
+ }
23923
+ autoLaunchInProgress.add(launchKey);
23924
+ try {
23925
+ const resolved = await resolveUsableProvider(components, nodeId, node);
23926
+ if (!resolved.providerType) {
23927
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: resolved.reason || "provider_unusable", nodeId });
23928
+ continue;
23929
+ }
23930
+ markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType });
23931
+ const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
23932
+ cliType: resolved.providerType,
23933
+ dir: node.workspace,
23934
+ settings: {
23935
+ meshNodeFor: meshId,
23936
+ meshNodeId: nodeId,
23937
+ spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
23938
+ launchedByCoordinator: true,
23939
+ autoLaunchedForQueueTaskId: task.id
23940
+ }
23941
+ });
23942
+ if (!launchResult?.success) {
23943
+ const reason = launchResult?.error || "launch_cli_failed";
23944
+ markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
23945
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
23946
+ return false;
23947
+ }
23948
+ const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
23949
+ if (!sessionId) {
23950
+ markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
23951
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
23952
+ return false;
23953
+ }
23954
+ markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId });
23955
+ tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType);
23956
+ return true;
23957
+ } catch (e) {
23958
+ markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
23959
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
23960
+ return false;
23961
+ } finally {
23962
+ autoLaunchInProgress.delete(launchKey);
23963
+ }
23964
+ }
23965
+ }
23966
+ return false;
23967
+ }
23968
+ async function triggerMeshQueue(components, meshId) {
23563
23969
  const mesh = getMeshWithCache(components, meshId);
23564
23970
  if (!mesh) return;
23565
23971
  const cliInstances = components.instanceManager.getByCategory("cli");
@@ -23570,9 +23976,7 @@ Follow these recovery rules:
23570
23976
  if (instMeshId !== meshId) continue;
23571
23977
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
23572
23978
  if (!nodeId) continue;
23573
- const status = readNonEmptyString(state.status).toLowerCase();
23574
- if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
23575
- if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
23979
+ if (!isIdleSessionState(state)) continue;
23576
23980
  const sessionId = state.instanceId;
23577
23981
  const providerType = state.type || readNonEmptyString(settings.providerType);
23578
23982
  if (providerType) {
@@ -23588,6 +23992,7 @@ Follow these recovery rules:
23588
23992
  }
23589
23993
  }
23590
23994
  }
23995
+ await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
23591
23996
  }
23592
23997
  function buildMeshSystemMessage(args) {
23593
23998
  const metadata = formatCompletionMetadata(args.metadataEvent);
@@ -23869,10 +24274,15 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
23869
24274
  var pendingMeshCoordinatorEvents;
23870
24275
  var MESH_COORDINATOR_EVENTS;
23871
24276
  var EVENT_TO_LEDGER_KIND;
24277
+ var autoLaunchInProgress;
24278
+ var autoLaunchCooldownUntil;
24279
+ var AUTO_LAUNCH_COOLDOWN_MS;
23872
24280
  var init_mesh_events = __esm2({
23873
24281
  "src/mesh/mesh-events.ts"() {
23874
24282
  "use strict";
24283
+ init_config();
23875
24284
  init_mesh_config();
24285
+ init_cli_detector();
23876
24286
  init_logger();
23877
24287
  init_mesh_ledger();
23878
24288
  init_mesh_work_queue();
@@ -23893,6 +24303,9 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
23893
24303
  "agent:stopped": "task_failed",
23894
24304
  "monitor:long_generating": "task_stalled"
23895
24305
  };
24306
+ autoLaunchInProgress = /* @__PURE__ */ new Set();
24307
+ autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
24308
+ AUTO_LAUNCH_COOLDOWN_MS = 5e3;
23896
24309
  }
23897
24310
  });
23898
24311
  function normalizeCategories(categories) {
@@ -27011,6 +27424,7 @@ ${lastSnapshot}`;
27011
27424
  MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
27012
27425
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS2,
27013
27426
  NodePtyTransportFactory: () => NodePtyTransportFactory,
27427
+ P2pRelayFailureError: () => P2pRelayFailureError,
27014
27428
  ProviderCliAdapter: () => ProviderCliAdapter,
27015
27429
  ProviderInstanceManager: () => ProviderInstanceManager,
27016
27430
  ProviderLoader: () => ProviderLoader,
@@ -27027,6 +27441,7 @@ ${lastSnapshot}`;
27027
27441
  buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
27028
27442
  buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
27029
27443
  buildMachineInfo: () => buildMachineInfo2,
27444
+ buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
27030
27445
  buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
27031
27446
  buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
27032
27447
  buildSessionEntries: () => buildSessionEntries,
@@ -27041,6 +27456,7 @@ ${lastSnapshot}`;
27041
27456
  claimNextTask: () => claimNextTask,
27042
27457
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
27043
27458
  classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush2,
27459
+ classifyP2pRelayFailure: () => classifyP2pRelayFailure,
27044
27460
  clearDebugTrace: () => clearDebugTrace,
27045
27461
  compareGitSnapshots: () => compareGitSnapshots,
27046
27462
  configureDebugTraceStore: () => configureDebugTraceStore,
@@ -27108,6 +27524,7 @@ ${lastSnapshot}`;
27108
27524
  isInternalChatMessage: () => isInternalChatMessage,
27109
27525
  isManagedStatusWaiting: () => isManagedStatusWaiting,
27110
27526
  isManagedStatusWorking: () => isManagedStatusWorking,
27527
+ isP2pRelayTransportFailure: () => isP2pRelayTransportFailure,
27111
27528
  isPathInside: () => isPathInside,
27112
27529
  isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
27113
27530
  isSessionHostRecoverySnapshot: () => isSessionHostRecoverySnapshot,
@@ -29036,7 +29453,115 @@ ${lastSnapshot}`;
29036
29453
  init_mesh_ledger();
29037
29454
  init_mesh_work_queue();
29038
29455
  init_mesh_events();
29039
- var import_fs5 = require("fs");
29456
+ var NO_FALLBACK_REASON = "Repo Mesh command/data-plane is P2P-only; WS/REST command fallback is intentionally disabled to preserve the transport boundary.";
29457
+ var P2P_NEXT_ACTION = "Check daemon/P2P health, wait briefly for connection establishment, then do one bounded retry or requeue the mesh task after clearing stale target session metadata.";
29458
+ var NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
29459
+ function messageFromError(error48) {
29460
+ if (error48 instanceof Error) return error48.message;
29461
+ if (typeof error48 === "string") return error48;
29462
+ if (error48 && typeof error48 === "object") {
29463
+ const candidate = error48.error ?? error48.message ?? error48.reason;
29464
+ if (typeof candidate === "string") return candidate;
29465
+ }
29466
+ return String(error48 || "mesh relay command failed");
29467
+ }
29468
+ function classifyP2pRelayFailure(error48, _context = {}) {
29469
+ const message = messageFromError(error48);
29470
+ const lower = message.toLowerCase();
29471
+ const hasP2pSignal = /p2p|datachannel|node-datachannel|webrtc|ice|mesh_relay_command|daemon_mesh_p2p_transport/i.test(message);
29472
+ const hasFailureSignal = /unavailable|missing|failed|failure|timeout|timed out|not connected|closed|disconnected|offline|no route|route unavailable|cannot send|cannot establish/i.test(message);
29473
+ if (/requires targetdaemonid and command|providerpriority|no inference provider|permission denied|read-only|not a member/i.test(message)) {
29474
+ return {
29475
+ code: "mesh_logic_or_provider_failure",
29476
+ reason: "mesh_logic_or_provider_failure",
29477
+ transport: "unknown",
29478
+ recoverable: false,
29479
+ retryRecommended: false,
29480
+ nextAction: NON_P2P_NEXT_ACTION,
29481
+ noFallbackReason: NO_FALLBACK_REASON
29482
+ };
29483
+ }
29484
+ let code = null;
29485
+ let reason = "";
29486
+ if (/timeout|timed out/i.test(message) && (hasP2pSignal || /mesh transport/i.test(message))) {
29487
+ code = "p2p_timeout";
29488
+ reason = "daemon_mesh_p2p_timeout";
29489
+ } else if (/no route|route unavailable/i.test(message)) {
29490
+ code = "p2p_no_route";
29491
+ reason = "daemon_mesh_p2p_no_route";
29492
+ } else if (/offline|not owned|not found|not connected to server/i.test(message) && /daemon|peer|target/i.test(message)) {
29493
+ code = "p2p_daemon_offline";
29494
+ reason = "daemon_mesh_target_offline";
29495
+ } else if (/closed|disconnected/i.test(message) && (hasP2pSignal || /state changed/i.test(message))) {
29496
+ code = "p2p_datachannel_closed";
29497
+ reason = "daemon_mesh_p2p_datachannel_closed";
29498
+ } else if (/not connected|cannot send|cannot establish/i.test(message) && hasP2pSignal) {
29499
+ code = "p2p_not_connected";
29500
+ reason = "daemon_mesh_p2p_not_connected";
29501
+ } else if (hasP2pSignal && hasFailureSignal) {
29502
+ code = "p2p_unavailable";
29503
+ reason = "daemon_mesh_p2p_transport_unavailable";
29504
+ }
29505
+ if (!code) {
29506
+ return {
29507
+ code: "mesh_logic_or_provider_failure",
29508
+ reason: "mesh_logic_or_provider_failure",
29509
+ transport: "unknown",
29510
+ recoverable: false,
29511
+ retryRecommended: false,
29512
+ nextAction: NON_P2P_NEXT_ACTION,
29513
+ noFallbackReason: NO_FALLBACK_REASON
29514
+ };
29515
+ }
29516
+ return {
29517
+ code,
29518
+ reason,
29519
+ transport: "p2p",
29520
+ recoverable: true,
29521
+ retryRecommended: true,
29522
+ nextAction: P2P_NEXT_ACTION,
29523
+ noFallbackReason: NO_FALLBACK_REASON
29524
+ };
29525
+ }
29526
+ function isP2pRelayTransportFailure(error48) {
29527
+ return classifyP2pRelayFailure(error48).recoverable === true;
29528
+ }
29529
+ function buildP2pRelayFailurePayload(error48, context = {}) {
29530
+ const classification = classifyP2pRelayFailure(error48, context);
29531
+ return {
29532
+ success: false,
29533
+ ...classification,
29534
+ error: messageFromError(error48),
29535
+ ...context.command ? { command: context.command } : {},
29536
+ ...context.targetDaemonId ? { targetDaemonId: context.targetDaemonId } : {}
29537
+ };
29538
+ }
29539
+ var P2pRelayFailureError = class extends Error {
29540
+ code;
29541
+ reason;
29542
+ transport;
29543
+ recoverable;
29544
+ retryRecommended;
29545
+ nextAction;
29546
+ noFallbackReason;
29547
+ command;
29548
+ targetDaemonId;
29549
+ constructor(message, context = {}) {
29550
+ super(message);
29551
+ this.name = "P2pRelayFailureError";
29552
+ const payload = buildP2pRelayFailurePayload(message, context);
29553
+ this.code = payload.code;
29554
+ this.reason = payload.reason;
29555
+ this.transport = payload.transport;
29556
+ this.recoverable = payload.recoverable;
29557
+ this.retryRecommended = payload.retryRecommended;
29558
+ this.nextAction = payload.nextAction;
29559
+ this.noFallbackReason = payload.noFallbackReason;
29560
+ this.command = context.command;
29561
+ this.targetDaemonId = context.targetDaemonId;
29562
+ }
29563
+ };
29564
+ var import_fs6 = require("fs");
29040
29565
  var import_path5 = require("path");
29041
29566
  init_config();
29042
29567
  var DEFAULT_STATE = {
@@ -29087,11 +29612,11 @@ ${lastSnapshot}`;
29087
29612
  }
29088
29613
  function loadState() {
29089
29614
  const statePath = getStatePath();
29090
- if (!(0, import_fs5.existsSync)(statePath)) {
29615
+ if (!(0, import_fs6.existsSync)(statePath)) {
29091
29616
  return { ...DEFAULT_STATE };
29092
29617
  }
29093
29618
  try {
29094
- const raw = (0, import_fs5.readFileSync)(statePath, "utf-8");
29619
+ const raw = (0, import_fs6.readFileSync)(statePath, "utf-8");
29095
29620
  return normalizeState(JSON.parse(raw));
29096
29621
  } catch {
29097
29622
  return { ...DEFAULT_STATE };
@@ -29100,15 +29625,15 @@ ${lastSnapshot}`;
29100
29625
  function saveState(state) {
29101
29626
  const statePath = getStatePath();
29102
29627
  const normalized = normalizeState(state);
29103
- (0, import_fs5.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
29628
+ (0, import_fs6.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
29104
29629
  }
29105
29630
  function resetState() {
29106
29631
  saveState({ ...DEFAULT_STATE });
29107
29632
  }
29108
- var import_child_process = require("child_process");
29109
- var import_fs6 = require("fs");
29633
+ var import_child_process2 = require("child_process");
29634
+ var import_fs7 = require("fs");
29110
29635
  var import_os22 = require("os");
29111
- var path9 = __toESM2(require("path"));
29636
+ var path10 = __toESM2(require("path"));
29112
29637
  var BUILTIN_IDE_DEFINITIONS = [];
29113
29638
  var registeredIDEs = /* @__PURE__ */ new Map();
29114
29639
  function registerIDEDefinition(def) {
@@ -29127,13 +29652,13 @@ ${lastSnapshot}`;
29127
29652
  function findCliCommand(command) {
29128
29653
  const trimmed = String(command || "").trim();
29129
29654
  if (!trimmed) return null;
29130
- if (path9.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
29131
- const candidate = trimmed.startsWith("~") ? path9.join((0, import_os22.homedir)(), trimmed.slice(1)) : trimmed;
29132
- const resolved = path9.isAbsolute(candidate) ? candidate : path9.resolve(candidate);
29133
- return (0, import_fs6.existsSync)(resolved) ? resolved : null;
29655
+ if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
29656
+ const candidate = trimmed.startsWith("~") ? path10.join((0, import_os22.homedir)(), trimmed.slice(1)) : trimmed;
29657
+ const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
29658
+ return (0, import_fs7.existsSync)(resolved) ? resolved : null;
29134
29659
  }
29135
29660
  try {
29136
- const result = (0, import_child_process.execSync)(
29661
+ const result = (0, import_child_process2.execSync)(
29137
29662
  (0, import_os22.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
29138
29663
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
29139
29664
  ).trim();
@@ -29144,7 +29669,7 @@ ${lastSnapshot}`;
29144
29669
  }
29145
29670
  function getIdeVersion(cliCommand) {
29146
29671
  try {
29147
- const result = (0, import_child_process.execSync)(`"${cliCommand}" --version`, {
29672
+ const result = (0, import_child_process2.execSync)(`"${cliCommand}" --version`, {
29148
29673
  encoding: "utf-8",
29149
29674
  timeout: 1e4,
29150
29675
  stdio: ["pipe", "pipe", "pipe"]
@@ -29157,13 +29682,13 @@ ${lastSnapshot}`;
29157
29682
  function checkPathExists(paths) {
29158
29683
  const home = (0, import_os22.homedir)();
29159
29684
  for (const p of paths) {
29160
- const normalized = p.startsWith("~") ? path9.join(home, p.slice(1)) : p;
29685
+ const normalized = p.startsWith("~") ? path10.join(home, p.slice(1)) : p;
29161
29686
  if (normalized.includes("*")) {
29162
29687
  const username = home.split(/[\\/]/).pop() || "";
29163
29688
  const resolved = normalized.replace("*", username);
29164
- if ((0, import_fs6.existsSync)(resolved)) return resolved;
29689
+ if ((0, import_fs7.existsSync)(resolved)) return resolved;
29165
29690
  } else {
29166
- if ((0, import_fs6.existsSync)(normalized)) return normalized;
29691
+ if ((0, import_fs7.existsSync)(normalized)) return normalized;
29167
29692
  }
29168
29693
  }
29169
29694
  return null;
@@ -29177,7 +29702,7 @@ ${lastSnapshot}`;
29177
29702
  let resolvedCli = cliPath;
29178
29703
  if (!resolvedCli && appPath && os222 === "darwin") {
29179
29704
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
29180
- if ((0, import_fs6.existsSync)(bundledCli)) resolvedCli = bundledCli;
29705
+ if ((0, import_fs7.existsSync)(bundledCli)) resolvedCli = bundledCli;
29181
29706
  }
29182
29707
  if (!resolvedCli && appPath && os222 === "win32") {
29183
29708
  const { dirname: dirname9 } = await import("path");
@@ -29190,7 +29715,7 @@ ${lastSnapshot}`;
29190
29715
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
29191
29716
  ];
29192
29717
  for (const c of candidates) {
29193
- if ((0, import_fs6.existsSync)(c)) {
29718
+ if ((0, import_fs7.existsSync)(c)) {
29194
29719
  resolvedCli = c;
29195
29720
  break;
29196
29721
  }
@@ -29211,133 +29736,7 @@ ${lastSnapshot}`;
29211
29736
  }
29212
29737
  return results;
29213
29738
  }
29214
- var import_child_process2 = require("child_process");
29215
- var os32 = __toESM2(require("os"));
29216
- var path10 = __toESM2(require("path"));
29217
- var import_fs7 = require("fs");
29218
- function parseVersion(raw) {
29219
- const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
29220
- return match ? match[1] : raw.split("\n")[0].slice(0, 100);
29221
- }
29222
- function shellQuote(value) {
29223
- if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
29224
- return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
29225
- }
29226
- function expandHome(value) {
29227
- const trimmed = value.trim();
29228
- if (!trimmed.startsWith("~")) return trimmed;
29229
- return path10.join(os32.homedir(), trimmed.slice(1));
29230
- }
29231
- function isExplicitCommandPath(command) {
29232
- const trimmed = command.trim();
29233
- return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
29234
- }
29235
- function resolveCommandPath(command) {
29236
- const trimmed = command.trim();
29237
- if (!trimmed) return null;
29238
- if (isExplicitCommandPath(trimmed)) {
29239
- const expanded = expandHome(trimmed);
29240
- const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
29241
- return (0, import_fs7.existsSync)(candidate) ? candidate : null;
29242
- }
29243
- return null;
29244
- }
29245
- function execAsync(cmd, timeoutMs = 5e3) {
29246
- return new Promise((resolve16) => {
29247
- const child = (0, import_child_process2.exec)(cmd, {
29248
- encoding: "utf-8",
29249
- timeout: timeoutMs,
29250
- ...process.platform === "win32" ? { windowsHide: true } : {}
29251
- }, (err, stdout) => {
29252
- if (err || !stdout?.trim()) {
29253
- resolve16(null);
29254
- } else {
29255
- resolve16(stdout.trim());
29256
- }
29257
- });
29258
- child.on("error", () => resolve16(null));
29259
- });
29260
- }
29261
- async function detectCLIs(providerLoader, options) {
29262
- const platform10 = os32.platform();
29263
- const whichCmd = platform10 === "win32" ? "where" : "which";
29264
- const includeVersion = options?.includeVersion !== false;
29265
- const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
29266
- const results = await Promise.all(
29267
- cliList.map(async (cli) => {
29268
- try {
29269
- const explicitPath = resolveCommandPath(cli.command);
29270
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
29271
- if (!pathResult) return { ...cli, installed: false };
29272
- const firstPath = explicitPath || pathResult.split("\n")[0];
29273
- let version2;
29274
- if (includeVersion) {
29275
- const versionCommands = [
29276
- `"${firstPath}" --version`,
29277
- `"${firstPath}" -V`,
29278
- `"${firstPath}" -v`,
29279
- cli.versionCommand
29280
- ].filter((v) => !!v);
29281
- try {
29282
- for (const versionCommand of versionCommands) {
29283
- const versionResult = await execAsync(versionCommand, 3e3);
29284
- if (versionResult) {
29285
- version2 = parseVersion(versionResult);
29286
- break;
29287
- }
29288
- }
29289
- } catch {
29290
- }
29291
- }
29292
- return { ...cli, installed: true, version: version2, path: firstPath };
29293
- } catch {
29294
- return { ...cli, installed: false };
29295
- }
29296
- })
29297
- );
29298
- return results;
29299
- }
29300
- async function detectCLI(cliId, providerLoader, options) {
29301
- const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
29302
- if (providerLoader) {
29303
- const cliList = providerLoader.getCliDetectionList();
29304
- const target = cliList.find((c) => c.id === resolvedId);
29305
- if (target) {
29306
- const platform10 = os32.platform();
29307
- const whichCmd = platform10 === "win32" ? "where" : "which";
29308
- try {
29309
- const explicitPath = resolveCommandPath(target.command);
29310
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
29311
- if (!pathResult) return null;
29312
- const firstPath = explicitPath || pathResult.split("\n")[0];
29313
- let version2;
29314
- if (options?.includeVersion !== false) {
29315
- const versionCommands = [
29316
- `"${firstPath}" --version`,
29317
- `"${firstPath}" -V`,
29318
- `"${firstPath}" -v`,
29319
- target.versionCommand
29320
- ].filter((v) => !!v);
29321
- try {
29322
- for (const versionCommand of versionCommands) {
29323
- const versionResult = await execAsync(versionCommand, 3e3);
29324
- if (versionResult) {
29325
- version2 = parseVersion(versionResult);
29326
- break;
29327
- }
29328
- }
29329
- } catch {
29330
- }
29331
- }
29332
- return { ...target, installed: true, version: version2, path: firstPath };
29333
- } catch {
29334
- return null;
29335
- }
29336
- }
29337
- }
29338
- const all = await detectCLIs(providerLoader, options);
29339
- return all.find((c) => c.id === resolvedId && c.installed) || null;
29340
- }
29739
+ init_cli_detector();
29341
29740
  var os42 = __toESM2(require("os"));
29342
29741
  var import_child_process3 = require("child_process");
29343
29742
  function parseDarwinAvailableBytes(totalMem) {
@@ -38361,6 +38760,7 @@ ${effect.notification.body || ""}`.trim();
38361
38760
  var import_child_process6 = require("child_process");
38362
38761
  var import_chalk = __toESM2((init_source(), __toCommonJS(source_exports)));
38363
38762
  init_provider_cli_adapter();
38763
+ init_cli_detector();
38364
38764
  init_config();
38365
38765
  var os12 = __toESM2(require("os"));
38366
38766
  var path16 = __toESM2(require("path"));
@@ -43914,6 +44314,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
43914
44314
  return getProviderLoader().getAvailableIdeTypes();
43915
44315
  }
43916
44316
  init_config();
44317
+ init_cli_detector();
43917
44318
  init_logger();
43918
44319
  var fs8 = __toESM2(require("fs"));
43919
44320
  var path21 = __toESM2(require("path"));
@@ -45046,6 +45447,209 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
45046
45447
  }
45047
45448
  return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
45048
45449
  }
45450
+ var REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
45451
+ var REFINE_VALIDATION_TIMEOUT_MS = 12e4;
45452
+ var REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
45453
+ var REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
45454
+ var REFINE_VALIDATION_MAX_COMMANDS = 4;
45455
+ function truncateValidationOutput(value) {
45456
+ const text = typeof value === "string" ? value : value == null ? "" : String(value);
45457
+ if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
45458
+ return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
45459
+ [truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
45460
+ }
45461
+ function readPackageScripts(workspace) {
45462
+ try {
45463
+ const packageJsonPath = (0, import_path6.join)(workspace, "package.json");
45464
+ const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
45465
+ return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
45466
+ } catch {
45467
+ return {};
45468
+ }
45469
+ }
45470
+ function tokenizeValidationCommand(command) {
45471
+ const trimmed = command.trim();
45472
+ if (!trimmed) return null;
45473
+ if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
45474
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
45475
+ if (!tokens.length) return null;
45476
+ if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
45477
+ return tokens;
45478
+ }
45479
+ function scriptMatchesValidationCategory(scriptName, category) {
45480
+ return scriptName === category || scriptName.startsWith(`${category}:`);
45481
+ }
45482
+ function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
45483
+ const tokens = tokenizeValidationCommand(rawCommand);
45484
+ if (!tokens) {
45485
+ return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
45486
+ }
45487
+ const [binary, second, third, ...rest] = tokens;
45488
+ let scriptName = "";
45489
+ let command = binary;
45490
+ let args = [];
45491
+ if ((binary === "npm" || binary === "pnpm" || binary === "bun") && second === "run" && third) {
45492
+ scriptName = third;
45493
+ args = ["run", scriptName, ...rest];
45494
+ } else if (binary === "npm" && second === "test" && !third) {
45495
+ scriptName = "test";
45496
+ args = ["test"];
45497
+ } else if (binary === "yarn" && second === "run" && third) {
45498
+ scriptName = third;
45499
+ args = ["run", scriptName, ...rest];
45500
+ } else if (binary === "yarn" && second && !third) {
45501
+ scriptName = second;
45502
+ args = [scriptName];
45503
+ } else {
45504
+ return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
45505
+ }
45506
+ if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
45507
+ return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
45508
+ }
45509
+ if (!scriptMatchesValidationCategory(scriptName, category)) {
45510
+ return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
45511
+ }
45512
+ return {
45513
+ command: {
45514
+ command,
45515
+ args,
45516
+ displayCommand: [command, ...args].join(" "),
45517
+ category,
45518
+ source
45519
+ }
45520
+ };
45521
+ }
45522
+ function collectProjectContextValidationCandidates(mesh) {
45523
+ const commands = mesh?.projectContext?.commands;
45524
+ if (!commands || typeof commands !== "object" || Array.isArray(commands)) return [];
45525
+ const candidates = [];
45526
+ for (const category of REFINE_VALIDATION_CATEGORIES) {
45527
+ const entries = Array.isArray(commands[category]) ? commands[category] : [];
45528
+ for (const entry of entries) {
45529
+ if (typeof entry?.command !== "string") continue;
45530
+ candidates.push({
45531
+ command: entry.command,
45532
+ category,
45533
+ source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
45534
+ confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
45535
+ });
45536
+ }
45537
+ }
45538
+ return candidates.sort((a, b) => {
45539
+ const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
45540
+ return rank(a.confidence) - rank(b.confidence);
45541
+ });
45542
+ }
45543
+ function collectPolicyValidationCandidates(mesh) {
45544
+ const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
45545
+ const configured = Array.isArray(policy.validationCommands) ? policy.validationCommands : Array.isArray(policy.validationGate?.commands) ? policy.validationGate.commands : [];
45546
+ return configured.map((entry) => typeof entry === "string" ? { command: entry, category: "", source: "mesh.policy.validationCommands" } : entry).filter((entry) => entry && typeof entry.command === "string").map((entry) => {
45547
+ const commandText = entry.command.trim();
45548
+ const category = REFINE_VALIDATION_CATEGORIES.find((cat) => commandText.includes(` ${cat}`)) ?? "";
45549
+ return { command: commandText, category, source: "mesh.policy.validationCommands" };
45550
+ }).filter((entry) => !!entry.category);
45551
+ }
45552
+ function selectMeshRefineValidationCommands(mesh, workspace) {
45553
+ const scripts = readPackageScripts(workspace);
45554
+ const rejectedCommands = [];
45555
+ const selected = [];
45556
+ const seen = /* @__PURE__ */ new Set();
45557
+ const candidates = [
45558
+ ...collectPolicyValidationCandidates(mesh),
45559
+ ...collectProjectContextValidationCandidates(mesh)
45560
+ ];
45561
+ for (const candidate of candidates) {
45562
+ const parsed = parsePackageManagerValidationCommand(candidate.command, candidate.category, scripts, candidate.source);
45563
+ if (parsed.rejected) {
45564
+ rejectedCommands.push(parsed.rejected);
45565
+ continue;
45566
+ }
45567
+ if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
45568
+ selected.push(parsed.command);
45569
+ seen.add(parsed.command.displayCommand);
45570
+ if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
45571
+ }
45572
+ if (!selected.length && candidates.length === 0) {
45573
+ for (const category of REFINE_VALIDATION_CATEGORIES) {
45574
+ if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
45575
+ const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
45576
+ if (fallback.command && !seen.has(fallback.command.displayCommand)) {
45577
+ selected.push(fallback.command);
45578
+ seen.add(fallback.command.displayCommand);
45579
+ } else if (fallback.rejected) {
45580
+ rejectedCommands.push(fallback.rejected);
45581
+ }
45582
+ if (selected.length >= 2) break;
45583
+ }
45584
+ }
45585
+ return {
45586
+ commands: selected,
45587
+ rejectedCommands,
45588
+ source: selected.some((command) => command.source === "mesh.policy.validationCommands") ? "mesh_policy" : selected.some((command) => command.source !== "package.json:scripts") ? "project_context" : selected.length ? "package_json_scripts" : "unavailable"
45589
+ };
45590
+ }
45591
+ async function runMeshRefineValidationGate(mesh, workspace) {
45592
+ const { execFile: execFile3 } = await import("child_process");
45593
+ const { promisify: promisify3 } = await import("util");
45594
+ const execFileAsync3 = promisify3(execFile3);
45595
+ const selection = selectMeshRefineValidationCommands(mesh, workspace);
45596
+ const summary = {
45597
+ status: "skipped",
45598
+ required: true,
45599
+ commandsRun: [],
45600
+ rejectedCommands: selection.rejectedCommands,
45601
+ skippedReason: void 0,
45602
+ timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
45603
+ outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
45604
+ };
45605
+ if (!selection.commands.length) {
45606
+ summary.skippedReason = "validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available";
45607
+ return summary;
45608
+ }
45609
+ for (const candidate of selection.commands) {
45610
+ const startedAt = Date.now();
45611
+ try {
45612
+ const result = await execFileAsync3(candidate.command, candidate.args, {
45613
+ cwd: workspace,
45614
+ encoding: "utf8",
45615
+ timeout: REFINE_VALIDATION_TIMEOUT_MS,
45616
+ maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
45617
+ env: { ...process.env, CI: process.env.CI || "1" }
45618
+ });
45619
+ summary.commandsRun.push({
45620
+ command: candidate.command,
45621
+ args: candidate.args,
45622
+ displayCommand: candidate.displayCommand,
45623
+ category: candidate.category,
45624
+ source: candidate.source,
45625
+ passed: true,
45626
+ exitCode: 0,
45627
+ durationMs: Date.now() - startedAt,
45628
+ stdout: truncateValidationOutput(result.stdout),
45629
+ stderr: truncateValidationOutput(result.stderr)
45630
+ });
45631
+ } catch (error48) {
45632
+ summary.commandsRun.push({
45633
+ command: candidate.command,
45634
+ args: candidate.args,
45635
+ displayCommand: candidate.displayCommand,
45636
+ category: candidate.category,
45637
+ source: candidate.source,
45638
+ passed: false,
45639
+ exitCode: typeof error48?.code === "number" ? error48.code : null,
45640
+ signal: typeof error48?.signal === "string" ? error48.signal : null,
45641
+ timedOut: error48?.killed === true || /timed out/i.test(String(error48?.message || "")),
45642
+ durationMs: Date.now() - startedAt,
45643
+ stdout: truncateValidationOutput(error48?.stdout),
45644
+ stderr: truncateValidationOutput(error48?.stderr || error48?.message)
45645
+ });
45646
+ summary.status = "failed";
45647
+ return summary;
45648
+ }
45649
+ }
45650
+ summary.status = "passed";
45651
+ return summary;
45652
+ }
45049
45653
  function loadYamlModule() {
45050
45654
  return yaml;
45051
45655
  }
@@ -45329,20 +45933,98 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
45329
45933
  recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
45330
45934
  };
45331
45935
  }
45936
+ const forceFallbackConvergence = await this.getWorktreeForceCleanupConvergence({
45937
+ repoRoot,
45938
+ workspace,
45939
+ node: args.node
45940
+ });
45332
45941
  try {
45333
- const result = await removeWorktree2(repoRoot, workspace, { requireClean: true });
45334
- return { success: true, removedPath: result.removedPath, repoRoot };
45942
+ const result = await removeWorktree2(repoRoot, workspace, {
45943
+ requireClean: true,
45944
+ allowSubmoduleForceFallback: forceFallbackConvergence.allow
45945
+ });
45946
+ return {
45947
+ success: true,
45948
+ removedPath: result.removedPath,
45949
+ repoRoot,
45950
+ ...result.fallback ? {
45951
+ fallback: result.fallback,
45952
+ forced: result.forced,
45953
+ reason: result.reason,
45954
+ convergence: forceFallbackConvergence
45955
+ } : {}
45956
+ };
45335
45957
  } catch (e) {
45336
45958
  const message = String(e?.message || e || "worktree cleanup failed");
45337
45959
  const dirty = message.includes("dirty worktree") || message.includes("local changes");
45960
+ const submoduleForceBlocked = /working trees containing submodules cannot be moved or removed/i.test(message) && !forceFallbackConvergence.allow;
45338
45961
  return {
45339
45962
  success: false,
45340
- code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
45341
- error: message,
45342
- recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure."
45963
+ code: dirty ? "mesh_worktree_cleanup_dirty" : submoduleForceBlocked ? "mesh_worktree_cleanup_force_fallback_blocked" : "mesh_worktree_cleanup_failed",
45964
+ error: submoduleForceBlocked ? `${message}; refusing --force fallback because convergence could not be verified: ${forceFallbackConvergence.error || "unknown convergence state"}` : message,
45965
+ recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : submoduleForceBlocked ? "Verify the worktree branch is merged/contained in the source default branch (for example origin/main) or mark the node with a safe branchConvergence final state before retrying. The mesh registry entry is preserved." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure.",
45966
+ ...submoduleForceBlocked ? { convergence: forceFallbackConvergence } : {}
45343
45967
  };
45344
45968
  }
45345
45969
  }
45970
+ async getWorktreeForceCleanupConvergence(args) {
45971
+ const metadataStatus = typeof args.node?.branchConvergence?.status === "string" ? args.node.branchConvergence.status : "";
45972
+ if (metadataStatus === "merged_to_main" || metadataStatus === "cleanup_candidate") {
45973
+ return { allow: true, status: metadataStatus, source: "node_branch_convergence" };
45974
+ }
45975
+ const { execFile: execFile3 } = await import("child_process");
45976
+ const { promisify: promisify3 } = await import("util");
45977
+ const execFileAsync3 = promisify3(execFile3);
45978
+ const runGit2 = async (gitArgs, cwd) => {
45979
+ const { stdout } = await execFileAsync3("git", gitArgs, {
45980
+ cwd,
45981
+ encoding: "utf8",
45982
+ timeout: 3e4,
45983
+ maxBuffer: 4 * 1024 * 1024,
45984
+ windowsHide: true
45985
+ });
45986
+ return String(stdout || "").trim();
45987
+ };
45988
+ let head = "";
45989
+ try {
45990
+ head = await runGit2(["rev-parse", "HEAD"], args.workspace);
45991
+ } catch (e) {
45992
+ return { allow: false, error: `could not resolve worktree HEAD: ${e?.message || e}` };
45993
+ }
45994
+ if (!head) return { allow: false, error: "worktree HEAD is empty" };
45995
+ const candidateRefs = [];
45996
+ try {
45997
+ const defaultBranch = await runGit2(["branch", "--show-current"], args.repoRoot);
45998
+ if (defaultBranch) {
45999
+ candidateRefs.push(defaultBranch, `origin/${defaultBranch}`);
46000
+ }
46001
+ } catch {
46002
+ }
46003
+ candidateRefs.push("origin/main", "origin/master", "main", "master");
46004
+ const seen = /* @__PURE__ */ new Set();
46005
+ const checkedRefs = [];
46006
+ for (const ref of candidateRefs) {
46007
+ if (!ref || seen.has(ref)) continue;
46008
+ seen.add(ref);
46009
+ let commit = "";
46010
+ try {
46011
+ commit = await runGit2(["rev-parse", "--verify", `${ref}^{commit}`], args.repoRoot);
46012
+ } catch {
46013
+ continue;
46014
+ }
46015
+ checkedRefs.push(ref);
46016
+ try {
46017
+ await runGit2(["merge-base", "--is-ancestor", head, commit], args.repoRoot);
46018
+ return { allow: true, status: "merged_to_default_ref", source: "git_merge_base", ref };
46019
+ } catch {
46020
+ }
46021
+ }
46022
+ return {
46023
+ allow: false,
46024
+ status: metadataStatus || void 0,
46025
+ error: checkedRefs.length ? `worktree HEAD is not contained in checked refs: ${checkedRefs.join(", ")}` : "no default/main refs were available for convergence verification"
46026
+ };
46027
+ }
45346
46028
  isCompletedHostedSession(record2) {
45347
46029
  return record2?.lifecycle === "stopped" || record2?.lifecycle === "failed" || record2?.lifecycle === "interrupted";
45348
46030
  }
@@ -46302,10 +46984,61 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
46302
46984
  if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
46303
46985
  const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
46304
46986
  const baseBranch = baseBranchStdout.trim();
46987
+ const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
46988
+ if (validationSummary.status === "failed") {
46989
+ return {
46990
+ success: false,
46991
+ code: "validation_failed",
46992
+ convergenceStatus: "blocked_review",
46993
+ error: "Refinery validation gate failed; merge/refine was not attempted.",
46994
+ branch,
46995
+ into: baseBranch,
46996
+ validationSummary,
46997
+ finalBranchConvergenceState: {
46998
+ branch,
46999
+ baseBranch,
47000
+ merged: false,
47001
+ removed: false,
47002
+ validation: "failed",
47003
+ status: "blocked_review"
47004
+ }
47005
+ };
47006
+ }
47007
+ if (validationSummary.status === "skipped") {
47008
+ return {
47009
+ success: false,
47010
+ code: "validation_unavailable",
47011
+ convergenceStatus: "blocked_review",
47012
+ error: "Refinery validation gate is required but no allowlisted validation command was available; merge/refine was not attempted.",
47013
+ branch,
47014
+ into: baseBranch,
47015
+ validationSummary,
47016
+ finalBranchConvergenceState: {
47017
+ branch,
47018
+ baseBranch,
47019
+ merged: false,
47020
+ removed: false,
47021
+ validation: "unavailable",
47022
+ status: "blocked_review"
47023
+ }
47024
+ };
47025
+ }
46305
47026
  try {
46306
47027
  await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
46307
47028
  } catch (e) {
46308
- return { success: false, error: `Merge failed (conflicts?): ${e.message}` };
47029
+ return {
47030
+ success: false,
47031
+ error: `Merge failed (conflicts?): ${e.message}`,
47032
+ validationSummary,
47033
+ finalBranchConvergenceState: {
47034
+ branch,
47035
+ baseBranch,
47036
+ merged: false,
47037
+ removed: false,
47038
+ validation: "passed",
47039
+ status: "not_mergeable"
47040
+ }
47041
+ };
46309
47042
  }
46310
47043
  const removeResult = await this.execute("remove_mesh_node", {
46311
47044
  meshId,
@@ -46318,11 +47051,27 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
46318
47051
  appendLedgerEntry2(meshId, {
46319
47052
  kind: "node_removed",
46320
47053
  nodeId,
46321
- payload: { refined: true, mergedBranch: branch, into: baseBranch }
47054
+ payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
46322
47055
  });
46323
47056
  } catch {
46324
47057
  }
46325
- return { success: true, merged: true, branch, into: baseBranch, removeResult };
47058
+ return {
47059
+ success: true,
47060
+ merged: true,
47061
+ branch,
47062
+ into: baseBranch,
47063
+ removeResult,
47064
+ validationSummary,
47065
+ finalBranchConvergenceState: {
47066
+ branch: baseBranch,
47067
+ mergedBranch: branch,
47068
+ baseBranch,
47069
+ merged: true,
47070
+ removed: removeResult?.success !== false,
47071
+ validation: "passed",
47072
+ status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
47073
+ }
47074
+ };
46326
47075
  } catch (e) {
46327
47076
  return { success: false, error: e.message };
46328
47077
  }
@@ -46377,7 +47126,10 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
46377
47126
  sessionCleanupMode,
46378
47127
  workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
46379
47128
  daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
46380
- worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
47129
+ worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0,
47130
+ worktreeCleanupFallback: typeof worktreeCleanup?.fallback === "string" ? worktreeCleanup.fallback : void 0,
47131
+ forced: worktreeCleanup?.forced === true ? true : void 0,
47132
+ forceFallbackReason: typeof worktreeCleanup?.reason === "string" ? worktreeCleanup.reason : void 0
46381
47133
  }
46382
47134
  });
46383
47135
  } catch {
@@ -54460,6 +55212,7 @@ data: ${JSON.stringify(msg.data)}
54460
55212
  return false;
54461
55213
  }
54462
55214
  }
55215
+ init_cli_detector();
54463
55216
  var SessionRegistry = class {
54464
55217
  bySessionId = /* @__PURE__ */ new Map();
54465
55218
  byManagerKey = /* @__PURE__ */ new Map();