@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.
@@ -25094,6 +25094,7 @@ __export(dist_exports, {
25094
25094
  MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
25095
25095
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
25096
25096
  NodePtyTransportFactory: () => NodePtyTransportFactory,
25097
+ P2pRelayFailureError: () => P2pRelayFailureError,
25097
25098
  ProviderCliAdapter: () => ProviderCliAdapter,
25098
25099
  ProviderInstanceManager: () => ProviderInstanceManager,
25099
25100
  ProviderLoader: () => ProviderLoader,
@@ -25110,6 +25111,7 @@ __export(dist_exports, {
25110
25111
  buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
25111
25112
  buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
25112
25113
  buildMachineInfo: () => buildMachineInfo,
25114
+ buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
25113
25115
  buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
25114
25116
  buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
25115
25117
  buildSessionEntries: () => buildSessionEntries,
@@ -25124,6 +25126,7 @@ __export(dist_exports, {
25124
25126
  claimNextTask: () => claimNextTask,
25125
25127
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
25126
25128
  classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush,
25129
+ classifyP2pRelayFailure: () => classifyP2pRelayFailure,
25127
25130
  clearDebugTrace: () => clearDebugTrace,
25128
25131
  compareGitSnapshots: () => compareGitSnapshots,
25129
25132
  configureDebugTraceStore: () => configureDebugTraceStore,
@@ -25191,6 +25194,7 @@ __export(dist_exports, {
25191
25194
  isInternalChatMessage: () => isInternalChatMessage,
25192
25195
  isManagedStatusWaiting: () => isManagedStatusWaiting,
25193
25196
  isManagedStatusWorking: () => isManagedStatusWorking,
25197
+ isP2pRelayTransportFailure: () => isP2pRelayTransportFailure,
25194
25198
  isPathInside: () => isPathInside,
25195
25199
  isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
25196
25200
  isSessionHostRecoverySnapshot: () => isSessionHostRecoverySnapshot,
@@ -25331,7 +25335,33 @@ async function removeWorktree(repoRoot, worktreePath, opts = {}) {
25331
25335
  });
25332
25336
  } catch (error48) {
25333
25337
  const stderr = typeof error48.stderr === "string" ? error48.stderr : "";
25334
- throw new Error(`git worktree remove failed: ${stderr.trim() || error48.message}`);
25338
+ const stdout = typeof error48.stdout === "string" ? error48.stdout : "";
25339
+ const detail = `${stderr}
25340
+ ${stdout}
25341
+ ${error48.message || ""}`;
25342
+ if (opts.allowSubmoduleForceFallback && SUBMODULE_WORKTREE_REMOVE_RE.test(detail)) {
25343
+ try {
25344
+ await execFileAsync2("git", ["worktree", "remove", "--force", worktreePath], {
25345
+ cwd: repoRoot,
25346
+ encoding: "utf8",
25347
+ timeout: GIT_TIMEOUT_MS,
25348
+ maxBuffer: GIT_MAX_BUFFER,
25349
+ windowsHide: true
25350
+ });
25351
+ } catch (forceError) {
25352
+ const forceStderr = typeof forceError.stderr === "string" ? forceError.stderr : "";
25353
+ const forceStdout = typeof forceError.stdout === "string" ? forceError.stdout : "";
25354
+ throw new Error(`git worktree remove --force fallback failed: ${forceStderr.trim() || forceStdout.trim() || forceError.message}`);
25355
+ }
25356
+ return {
25357
+ success: true,
25358
+ removedPath: worktreePath,
25359
+ fallback: "git_worktree_remove_force_submodule",
25360
+ forced: true,
25361
+ reason: "working_trees_containing_submodules"
25362
+ };
25363
+ }
25364
+ throw new Error(`git worktree remove failed: ${stderr.trim() || stdout.trim() || error48.message}`);
25335
25365
  }
25336
25366
  return { success: true, removedPath: worktreePath };
25337
25367
  }
@@ -26092,6 +26122,19 @@ function updateTaskStatus(meshId, taskId, status) {
26092
26122
  writeQueue(meshId, queue);
26093
26123
  return queue[idx];
26094
26124
  }
26125
+ function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
26126
+ const queue = readQueue(meshId);
26127
+ const idx = queue.findIndex((q) => q.id === taskId);
26128
+ if (idx === -1) return null;
26129
+ const now = (/* @__PURE__ */ new Date()).toISOString();
26130
+ queue[idx].autoLaunch = {
26131
+ ...autoLaunch,
26132
+ updatedAt: now
26133
+ };
26134
+ queue[idx].updatedAt = now;
26135
+ writeQueue(meshId, queue);
26136
+ return queue[idx];
26137
+ }
26095
26138
  function cancelTask(meshId, taskId, opts) {
26096
26139
  const queue = readQueue(meshId);
26097
26140
  const idx = queue.findIndex((q) => q.id === taskId);
@@ -26154,6 +26197,129 @@ function getMeshQueueStats(meshId) {
26154
26197
  }))
26155
26198
  };
26156
26199
  }
26200
+ function parseVersion(raw) {
26201
+ const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
26202
+ return match ? match[1] : raw.split("\n")[0].slice(0, 100);
26203
+ }
26204
+ function shellQuote(value) {
26205
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
26206
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
26207
+ }
26208
+ function expandHome(value) {
26209
+ const trimmed = value.trim();
26210
+ if (!trimmed.startsWith("~")) return trimmed;
26211
+ return path8.join(os22.homedir(), trimmed.slice(1));
26212
+ }
26213
+ function isExplicitCommandPath(command) {
26214
+ const trimmed = command.trim();
26215
+ return path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
26216
+ }
26217
+ function resolveCommandPath(command) {
26218
+ const trimmed = command.trim();
26219
+ if (!trimmed) return null;
26220
+ if (isExplicitCommandPath(trimmed)) {
26221
+ const expanded = expandHome(trimmed);
26222
+ const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
26223
+ return (0, import_fs8.existsSync)(candidate) ? candidate : null;
26224
+ }
26225
+ return null;
26226
+ }
26227
+ function execAsync(cmd, timeoutMs = 5e3) {
26228
+ return new Promise((resolve162) => {
26229
+ const child = (0, import_child_process2.exec)(cmd, {
26230
+ encoding: "utf-8",
26231
+ timeout: timeoutMs,
26232
+ ...process.platform === "win32" ? { windowsHide: true } : {}
26233
+ }, (err, stdout) => {
26234
+ if (err || !stdout?.trim()) {
26235
+ resolve162(null);
26236
+ } else {
26237
+ resolve162(stdout.trim());
26238
+ }
26239
+ });
26240
+ child.on("error", () => resolve162(null));
26241
+ });
26242
+ }
26243
+ async function detectCLIs(providerLoader, options) {
26244
+ const platform10 = os22.platform();
26245
+ const whichCmd = platform10 === "win32" ? "where" : "which";
26246
+ const includeVersion = options?.includeVersion !== false;
26247
+ const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
26248
+ const results = await Promise.all(
26249
+ cliList.map(async (cli) => {
26250
+ try {
26251
+ const explicitPath = resolveCommandPath(cli.command);
26252
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
26253
+ if (!pathResult) return { ...cli, installed: false };
26254
+ const firstPath = explicitPath || pathResult.split("\n")[0];
26255
+ let version2;
26256
+ if (includeVersion) {
26257
+ const versionCommands = [
26258
+ `"${firstPath}" --version`,
26259
+ `"${firstPath}" -V`,
26260
+ `"${firstPath}" -v`,
26261
+ cli.versionCommand
26262
+ ].filter((v) => !!v);
26263
+ try {
26264
+ for (const versionCommand of versionCommands) {
26265
+ const versionResult = await execAsync(versionCommand, 3e3);
26266
+ if (versionResult) {
26267
+ version2 = parseVersion(versionResult);
26268
+ break;
26269
+ }
26270
+ }
26271
+ } catch {
26272
+ }
26273
+ }
26274
+ return { ...cli, installed: true, version: version2, path: firstPath };
26275
+ } catch {
26276
+ return { ...cli, installed: false };
26277
+ }
26278
+ })
26279
+ );
26280
+ return results;
26281
+ }
26282
+ async function detectCLI(cliId, providerLoader, options) {
26283
+ const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
26284
+ if (providerLoader) {
26285
+ const cliList = providerLoader.getCliDetectionList();
26286
+ const target = cliList.find((c) => c.id === resolvedId);
26287
+ if (target) {
26288
+ const platform10 = os22.platform();
26289
+ const whichCmd = platform10 === "win32" ? "where" : "which";
26290
+ try {
26291
+ const explicitPath = resolveCommandPath(target.command);
26292
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
26293
+ if (!pathResult) return null;
26294
+ const firstPath = explicitPath || pathResult.split("\n")[0];
26295
+ let version2;
26296
+ if (options?.includeVersion !== false) {
26297
+ const versionCommands = [
26298
+ `"${firstPath}" --version`,
26299
+ `"${firstPath}" -V`,
26300
+ `"${firstPath}" -v`,
26301
+ target.versionCommand
26302
+ ].filter((v) => !!v);
26303
+ try {
26304
+ for (const versionCommand of versionCommands) {
26305
+ const versionResult = await execAsync(versionCommand, 3e3);
26306
+ if (versionResult) {
26307
+ version2 = parseVersion(versionResult);
26308
+ break;
26309
+ }
26310
+ }
26311
+ } catch {
26312
+ }
26313
+ }
26314
+ return { ...target, installed: true, version: version2, path: firstPath };
26315
+ } catch {
26316
+ return null;
26317
+ }
26318
+ }
26319
+ }
26320
+ const all = await detectCLIs(providerLoader, options);
26321
+ return all.find((c) => c.id === resolvedId && c.installed) || null;
26322
+ }
26157
26323
  function setLogLevel(level) {
26158
26324
  currentLevel = level;
26159
26325
  daemonLog("Logger", `Log level set to: ${level}`, "info");
@@ -26168,13 +26334,13 @@ function getDaemonLogDir() {
26168
26334
  return LOG_DIR;
26169
26335
  }
26170
26336
  function getCurrentDaemonLogPath(date5 = /* @__PURE__ */ new Date()) {
26171
- return path8.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
26337
+ return path9.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
26172
26338
  }
26173
26339
  function checkDateRotation() {
26174
26340
  const today = getDateStr();
26175
26341
  if (today !== currentDate) {
26176
26342
  currentDate = today;
26177
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
26343
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
26178
26344
  cleanOldLogs();
26179
26345
  }
26180
26346
  }
@@ -26188,7 +26354,7 @@ function cleanOldLogs() {
26188
26354
  const dateMatch = file2.match(/daemon-(\d{4}-\d{2}-\d{2})/);
26189
26355
  if (dateMatch && dateMatch[1] < cutoffStr) {
26190
26356
  try {
26191
- fs2.unlinkSync(path8.join(LOG_DIR, file2));
26357
+ fs2.unlinkSync(path9.join(LOG_DIR, file2));
26192
26358
  } catch {
26193
26359
  }
26194
26360
  }
@@ -26363,7 +26529,235 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
26363
26529
  });
26364
26530
  return true;
26365
26531
  }
26366
- function triggerMeshQueue(components, meshId) {
26532
+ function normalizeProviderPriority(policy) {
26533
+ const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
26534
+ if (!Array.isArray(raw)) return [];
26535
+ const seen = /* @__PURE__ */ new Set();
26536
+ return raw.map((type2) => typeof type2 === "string" ? type2.trim() : "").filter(Boolean).filter((type2) => {
26537
+ if (seen.has(type2)) return false;
26538
+ seen.add(type2);
26539
+ return true;
26540
+ });
26541
+ }
26542
+ function isTerminalSessionStatus(status) {
26543
+ return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
26544
+ }
26545
+ function isIdleSessionState(state) {
26546
+ const status = readNonEmptyString(state?.status).toLowerCase();
26547
+ if (isTerminalSessionStatus(status)) return false;
26548
+ return status === "idle" || state?.activeChat?.status === "waiting_input";
26549
+ }
26550
+ function isDirtyNode(node) {
26551
+ return node?.health === "dirty" || node?.git?.dirty === true;
26552
+ }
26553
+ function isLaunchableNode(node) {
26554
+ if (!node || node.status === "disabled" || node.status === "removed") return false;
26555
+ const health = readNonEmptyString(node.health).toLowerCase();
26556
+ if (!health) return true;
26557
+ return health === "online" || health === "unknown";
26558
+ }
26559
+ function localAutoLaunchSkipReason(node) {
26560
+ const daemonId = readNonEmptyString(node?.daemonId);
26561
+ const machineId = readNonEmptyString(node?.machineId);
26562
+ const appConfig = loadConfig();
26563
+ const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
26564
+ const cloudDaemonId = localMachineId ? `daemon_${localMachineId}` : "";
26565
+ const standaloneDaemonId = localMachineId ? `standalone_${localMachineId}` : "";
26566
+ const daemonMatchesLocal = !daemonId || daemonId === cloudDaemonId || daemonId === standaloneDaemonId;
26567
+ const machineMatchesLocal = !machineId || localMachineId && machineId === localMachineId;
26568
+ if (node?.isLocalWorktree === true) {
26569
+ return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
26570
+ }
26571
+ if (daemonId || machineId) {
26572
+ return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
26573
+ }
26574
+ return null;
26575
+ }
26576
+ function activeAssignedCount(meshId) {
26577
+ return getQueue(meshId, { status: ["assigned"] }).length;
26578
+ }
26579
+ function nodeHasActiveAssignment(meshId, nodeId) {
26580
+ return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
26581
+ }
26582
+ function liveSessionCountForNode(components, meshId, nodeId) {
26583
+ return components.instanceManager.getByCategory("cli").filter((inst) => {
26584
+ const state = inst.getState();
26585
+ const settings = state.settings || {};
26586
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
26587
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
26588
+ if (instNodeId !== nodeId) return false;
26589
+ const status = readNonEmptyString(state.status).toLowerCase();
26590
+ return !isTerminalSessionStatus(status);
26591
+ }).length;
26592
+ }
26593
+ function recordAutoLaunchEvent(meshId, args) {
26594
+ try {
26595
+ appendLedgerEntry(meshId, {
26596
+ kind: "session_auto_launch",
26597
+ nodeId: args.nodeId,
26598
+ sessionId: args.sessionId,
26599
+ providerType: args.providerType,
26600
+ payload: {
26601
+ phase: args.phase,
26602
+ taskId: args.taskId,
26603
+ reason: args.reason,
26604
+ error: args.error
26605
+ }
26606
+ });
26607
+ } catch (e) {
26608
+ LOG.warn("MeshQueue", `Failed to record auto-launch ledger event: ${e?.message || e}`);
26609
+ }
26610
+ }
26611
+ function markAutoLaunch(meshId, taskId, args) {
26612
+ recordTaskAutoLaunch(meshId, taskId, {
26613
+ status: args.status,
26614
+ reason: args.reason || args.error,
26615
+ nodeId: args.nodeId,
26616
+ providerType: args.providerType,
26617
+ sessionId: args.sessionId
26618
+ });
26619
+ recordAutoLaunchEvent(meshId, {
26620
+ phase: args.status,
26621
+ taskId,
26622
+ nodeId: args.nodeId,
26623
+ providerType: args.providerType,
26624
+ sessionId: args.sessionId,
26625
+ reason: args.reason,
26626
+ error: args.error
26627
+ });
26628
+ }
26629
+ async function resolveUsableProvider(components, nodeId, node) {
26630
+ const providerPriority = normalizeProviderPriority(node?.policy);
26631
+ if (!providerPriority.length) return { reason: "missing_provider_priority" };
26632
+ const providerLoader = components.providerLoader;
26633
+ if (!providerLoader) return { reason: "provider_loader_unavailable" };
26634
+ const failed = [];
26635
+ for (const requestedType of providerPriority) {
26636
+ const normalizedType = typeof providerLoader.resolveAlias === "function" ? providerLoader.resolveAlias(requestedType) : requestedType;
26637
+ if (typeof providerLoader.isMachineProviderEnabled === "function" && !providerLoader.isMachineProviderEnabled(normalizedType)) {
26638
+ failed.push(`${requestedType}: disabled`);
26639
+ continue;
26640
+ }
26641
+ let detected;
26642
+ try {
26643
+ detected = await detectCLI(normalizedType, providerLoader, { includeVersion: false });
26644
+ } catch (e) {
26645
+ failed.push(`${requestedType}: detect failed: ${e?.message || e}`);
26646
+ continue;
26647
+ }
26648
+ if (typeof providerLoader.setCliDetectionResults === "function") {
26649
+ providerLoader.setCliDetectionResults([{
26650
+ id: normalizedType,
26651
+ installed: !!detected,
26652
+ path: detected?.path
26653
+ }], false);
26654
+ }
26655
+ components.onStatusChange?.();
26656
+ if (detected) return { providerType: normalizedType };
26657
+ failed.push(`${requestedType}: not detected`);
26658
+ }
26659
+ return { reason: `provider_priority_unusable: ${failed.join("; ") || nodeId}` };
26660
+ }
26661
+ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
26662
+ const queue = getQueue(meshId);
26663
+ const pending = queue.filter((task) => task.status === "pending");
26664
+ if (!pending.length) return false;
26665
+ const maxParallelTasks = Math.max(1, Math.floor(Number(mesh?.policy?.maxParallelTasks) || 2));
26666
+ for (const task of pending) {
26667
+ if (activeAssignedCount(meshId) >= maxParallelTasks) {
26668
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
26669
+ return false;
26670
+ }
26671
+ if (task.targetSessionId) {
26672
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
26673
+ continue;
26674
+ }
26675
+ const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => task.targetNodeId ? node?.id === task.targetNodeId : true) : [];
26676
+ if (!candidateNodes.length) {
26677
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_matching_node", nodeId: task.targetNodeId });
26678
+ continue;
26679
+ }
26680
+ for (const node of candidateNodes) {
26681
+ const nodeId = readNonEmptyString(node?.id);
26682
+ if (!nodeId) continue;
26683
+ const launchKey = `${meshId}:${nodeId}`;
26684
+ const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
26685
+ if (autoLaunchInProgress.has(launchKey)) {
26686
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_in_progress", nodeId });
26687
+ continue;
26688
+ }
26689
+ if (Date.now() < cooldownUntil) {
26690
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_cooldown", nodeId });
26691
+ continue;
26692
+ }
26693
+ if (isDirtyNode(node)) {
26694
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dirty_workspace", nodeId });
26695
+ continue;
26696
+ }
26697
+ if (!isLaunchableNode(node)) {
26698
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_not_launch_ready", nodeId });
26699
+ continue;
26700
+ }
26701
+ const localSkipReason = localAutoLaunchSkipReason(node);
26702
+ if (localSkipReason) {
26703
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: localSkipReason, nodeId });
26704
+ continue;
26705
+ }
26706
+ if (nodeHasActiveAssignment(meshId, nodeId)) {
26707
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_active_assignment", nodeId });
26708
+ continue;
26709
+ }
26710
+ const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
26711
+ if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
26712
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_concurrent_sessions_reached", nodeId });
26713
+ continue;
26714
+ }
26715
+ autoLaunchInProgress.add(launchKey);
26716
+ try {
26717
+ const resolved = await resolveUsableProvider(components, nodeId, node);
26718
+ if (!resolved.providerType) {
26719
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: resolved.reason || "provider_unusable", nodeId });
26720
+ continue;
26721
+ }
26722
+ markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType });
26723
+ const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
26724
+ cliType: resolved.providerType,
26725
+ dir: node.workspace,
26726
+ settings: {
26727
+ meshNodeFor: meshId,
26728
+ meshNodeId: nodeId,
26729
+ spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
26730
+ launchedByCoordinator: true,
26731
+ autoLaunchedForQueueTaskId: task.id
26732
+ }
26733
+ });
26734
+ if (!launchResult?.success) {
26735
+ const reason = launchResult?.error || "launch_cli_failed";
26736
+ markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
26737
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
26738
+ return false;
26739
+ }
26740
+ const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
26741
+ if (!sessionId) {
26742
+ markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
26743
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
26744
+ return false;
26745
+ }
26746
+ markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId });
26747
+ tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType);
26748
+ return true;
26749
+ } catch (e) {
26750
+ markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
26751
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
26752
+ return false;
26753
+ } finally {
26754
+ autoLaunchInProgress.delete(launchKey);
26755
+ }
26756
+ }
26757
+ }
26758
+ return false;
26759
+ }
26760
+ async function triggerMeshQueue(components, meshId) {
26367
26761
  const mesh = getMeshWithCache(components, meshId);
26368
26762
  if (!mesh) return;
26369
26763
  const cliInstances = components.instanceManager.getByCategory("cli");
@@ -26374,9 +26768,7 @@ function triggerMeshQueue(components, meshId) {
26374
26768
  if (instMeshId !== meshId) continue;
26375
26769
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
26376
26770
  if (!nodeId) continue;
26377
- const status = readNonEmptyString(state.status).toLowerCase();
26378
- if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
26379
- if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
26771
+ if (!isIdleSessionState(state)) continue;
26380
26772
  const sessionId = state.instanceId;
26381
26773
  const providerType = state.type || readNonEmptyString(settings.providerType);
26382
26774
  if (providerType) {
@@ -26392,6 +26784,7 @@ function triggerMeshQueue(components, meshId) {
26392
26784
  }
26393
26785
  }
26394
26786
  }
26787
+ await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
26395
26788
  }
26396
26789
  function buildMeshSystemMessage(args) {
26397
26790
  const metadata = formatCompletionMetadata(args.metadataEvent);
@@ -26899,7 +27292,7 @@ function findBinary(name) {
26899
27292
  const isWin = os9.platform() === "win32";
26900
27293
  try {
26901
27294
  const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
26902
- return (0, import_child_process2.execSync)(cmd, {
27295
+ return (0, import_child_process3.execSync)(cmd, {
26903
27296
  encoding: "utf-8",
26904
27297
  timeout: 5e3,
26905
27298
  stdio: ["pipe", "pipe", "pipe"],
@@ -27274,7 +27667,7 @@ async function validateWorkspace(workspace) {
27274
27667
  cwd: normalizedWorkspace
27275
27668
  });
27276
27669
  }
27277
- await (0, import_promises5.access)(normalizedWorkspace, import_fs8.constants.R_OK);
27670
+ await (0, import_promises5.access)(normalizedWorkspace, import_fs9.constants.R_OK);
27278
27671
  } catch (error48) {
27279
27672
  if (error48 instanceof GitCommandError) throw error48;
27280
27673
  throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
@@ -28787,6 +29180,86 @@ async function syncMeshLedger(meshId, transport) {
28787
29180
  appendRemoteLedgerEntries2(meshId, res.missingEntries);
28788
29181
  }
28789
29182
  }
29183
+ function messageFromError(error48) {
29184
+ if (error48 instanceof Error) return error48.message;
29185
+ if (typeof error48 === "string") return error48;
29186
+ if (error48 && typeof error48 === "object") {
29187
+ const candidate = error48.error ?? error48.message ?? error48.reason;
29188
+ if (typeof candidate === "string") return candidate;
29189
+ }
29190
+ return String(error48 || "mesh relay command failed");
29191
+ }
29192
+ function classifyP2pRelayFailure(error48, _context = {}) {
29193
+ const message = messageFromError(error48);
29194
+ const lower = message.toLowerCase();
29195
+ const hasP2pSignal = /p2p|datachannel|node-datachannel|webrtc|ice|mesh_relay_command|daemon_mesh_p2p_transport/i.test(message);
29196
+ 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);
29197
+ if (/requires targetdaemonid and command|providerpriority|no inference provider|permission denied|read-only|not a member/i.test(message)) {
29198
+ return {
29199
+ code: "mesh_logic_or_provider_failure",
29200
+ reason: "mesh_logic_or_provider_failure",
29201
+ transport: "unknown",
29202
+ recoverable: false,
29203
+ retryRecommended: false,
29204
+ nextAction: NON_P2P_NEXT_ACTION,
29205
+ noFallbackReason: NO_FALLBACK_REASON
29206
+ };
29207
+ }
29208
+ let code = null;
29209
+ let reason = "";
29210
+ if (/timeout|timed out/i.test(message) && (hasP2pSignal || /mesh transport/i.test(message))) {
29211
+ code = "p2p_timeout";
29212
+ reason = "daemon_mesh_p2p_timeout";
29213
+ } else if (/no route|route unavailable/i.test(message)) {
29214
+ code = "p2p_no_route";
29215
+ reason = "daemon_mesh_p2p_no_route";
29216
+ } else if (/offline|not owned|not found|not connected to server/i.test(message) && /daemon|peer|target/i.test(message)) {
29217
+ code = "p2p_daemon_offline";
29218
+ reason = "daemon_mesh_target_offline";
29219
+ } else if (/closed|disconnected/i.test(message) && (hasP2pSignal || /state changed/i.test(message))) {
29220
+ code = "p2p_datachannel_closed";
29221
+ reason = "daemon_mesh_p2p_datachannel_closed";
29222
+ } else if (/not connected|cannot send|cannot establish/i.test(message) && hasP2pSignal) {
29223
+ code = "p2p_not_connected";
29224
+ reason = "daemon_mesh_p2p_not_connected";
29225
+ } else if (hasP2pSignal && hasFailureSignal) {
29226
+ code = "p2p_unavailable";
29227
+ reason = "daemon_mesh_p2p_transport_unavailable";
29228
+ }
29229
+ if (!code) {
29230
+ return {
29231
+ code: "mesh_logic_or_provider_failure",
29232
+ reason: "mesh_logic_or_provider_failure",
29233
+ transport: "unknown",
29234
+ recoverable: false,
29235
+ retryRecommended: false,
29236
+ nextAction: NON_P2P_NEXT_ACTION,
29237
+ noFallbackReason: NO_FALLBACK_REASON
29238
+ };
29239
+ }
29240
+ return {
29241
+ code,
29242
+ reason,
29243
+ transport: "p2p",
29244
+ recoverable: true,
29245
+ retryRecommended: true,
29246
+ nextAction: P2P_NEXT_ACTION,
29247
+ noFallbackReason: NO_FALLBACK_REASON
29248
+ };
29249
+ }
29250
+ function isP2pRelayTransportFailure(error48) {
29251
+ return classifyP2pRelayFailure(error48).recoverable === true;
29252
+ }
29253
+ function buildP2pRelayFailurePayload(error48, context = {}) {
29254
+ const classification = classifyP2pRelayFailure(error48, context);
29255
+ return {
29256
+ success: false,
29257
+ ...classification,
29258
+ error: messageFromError(error48),
29259
+ ...context.command ? { command: context.command } : {},
29260
+ ...context.targetDaemonId ? { targetDaemonId: context.targetDaemonId } : {}
29261
+ };
29262
+ }
28790
29263
  function isPlainObject22(value) {
28791
29264
  return !!value && typeof value === "object" && !Array.isArray(value);
28792
29265
  }
@@ -28827,11 +29300,11 @@ function normalizeState(raw) {
28827
29300
  }
28828
29301
  function loadState() {
28829
29302
  const statePath = getStatePath();
28830
- if (!(0, import_fs9.existsSync)(statePath)) {
29303
+ if (!(0, import_fs10.existsSync)(statePath)) {
28831
29304
  return { ...DEFAULT_STATE };
28832
29305
  }
28833
29306
  try {
28834
- const raw = (0, import_fs9.readFileSync)(statePath, "utf-8");
29307
+ const raw = (0, import_fs10.readFileSync)(statePath, "utf-8");
28835
29308
  return normalizeState(JSON.parse(raw));
28836
29309
  } catch {
28837
29310
  return { ...DEFAULT_STATE };
@@ -28840,7 +29313,7 @@ function loadState() {
28840
29313
  function saveState(state) {
28841
29314
  const statePath = getStatePath();
28842
29315
  const normalized = normalizeState(state);
28843
- (0, import_fs9.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
29316
+ (0, import_fs10.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
28844
29317
  }
28845
29318
  function resetState() {
28846
29319
  saveState({ ...DEFAULT_STATE });
@@ -28861,13 +29334,13 @@ function getMergedDefinitions() {
28861
29334
  function findCliCommand(command) {
28862
29335
  const trimmed = String(command || "").trim();
28863
29336
  if (!trimmed) return null;
28864
- if (path9.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
28865
- const candidate = trimmed.startsWith("~") ? path9.join((0, import_os3.homedir)(), trimmed.slice(1)) : trimmed;
28866
- const resolved = path9.isAbsolute(candidate) ? candidate : path9.resolve(candidate);
28867
- return (0, import_fs10.existsSync)(resolved) ? resolved : null;
29337
+ if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
29338
+ const candidate = trimmed.startsWith("~") ? path10.join((0, import_os3.homedir)(), trimmed.slice(1)) : trimmed;
29339
+ const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
29340
+ return (0, import_fs11.existsSync)(resolved) ? resolved : null;
28868
29341
  }
28869
29342
  try {
28870
- const result = (0, import_child_process4.execSync)(
29343
+ const result = (0, import_child_process5.execSync)(
28871
29344
  (0, import_os3.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
28872
29345
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
28873
29346
  ).trim();
@@ -28878,7 +29351,7 @@ function findCliCommand(command) {
28878
29351
  }
28879
29352
  function getIdeVersion(cliCommand) {
28880
29353
  try {
28881
- const result = (0, import_child_process4.execSync)(`"${cliCommand}" --version`, {
29354
+ const result = (0, import_child_process5.execSync)(`"${cliCommand}" --version`, {
28882
29355
  encoding: "utf-8",
28883
29356
  timeout: 1e4,
28884
29357
  stdio: ["pipe", "pipe", "pipe"]
@@ -28891,13 +29364,13 @@ function getIdeVersion(cliCommand) {
28891
29364
  function checkPathExists(paths) {
28892
29365
  const home = (0, import_os3.homedir)();
28893
29366
  for (const p of paths) {
28894
- const normalized = p.startsWith("~") ? path9.join(home, p.slice(1)) : p;
29367
+ const normalized = p.startsWith("~") ? path10.join(home, p.slice(1)) : p;
28895
29368
  if (normalized.includes("*")) {
28896
29369
  const username = home.split(/[\\/]/).pop() || "";
28897
29370
  const resolved = normalized.replace("*", username);
28898
- if ((0, import_fs10.existsSync)(resolved)) return resolved;
29371
+ if ((0, import_fs11.existsSync)(resolved)) return resolved;
28899
29372
  } else {
28900
- if ((0, import_fs10.existsSync)(normalized)) return normalized;
29373
+ if ((0, import_fs11.existsSync)(normalized)) return normalized;
28901
29374
  }
28902
29375
  }
28903
29376
  return null;
@@ -28911,7 +29384,7 @@ async function detectIDEs(providerLoader) {
28911
29384
  let resolvedCli = cliPath;
28912
29385
  if (!resolvedCli && appPath && os222 === "darwin") {
28913
29386
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
28914
- if ((0, import_fs10.existsSync)(bundledCli)) resolvedCli = bundledCli;
29387
+ if ((0, import_fs11.existsSync)(bundledCli)) resolvedCli = bundledCli;
28915
29388
  }
28916
29389
  if (!resolvedCli && appPath && os222 === "win32") {
28917
29390
  const { dirname: dirname92 } = await import("path");
@@ -28924,7 +29397,7 @@ async function detectIDEs(providerLoader) {
28924
29397
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
28925
29398
  ];
28926
29399
  for (const c of candidates) {
28927
- if ((0, import_fs10.existsSync)(c)) {
29400
+ if ((0, import_fs11.existsSync)(c)) {
28928
29401
  resolvedCli = c;
28929
29402
  break;
28930
29403
  }
@@ -28945,129 +29418,6 @@ async function detectIDEs(providerLoader) {
28945
29418
  }
28946
29419
  return results;
28947
29420
  }
28948
- function parseVersion(raw) {
28949
- const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
28950
- return match ? match[1] : raw.split("\n")[0].slice(0, 100);
28951
- }
28952
- function shellQuote(value) {
28953
- if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
28954
- return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
28955
- }
28956
- function expandHome(value) {
28957
- const trimmed = value.trim();
28958
- if (!trimmed.startsWith("~")) return trimmed;
28959
- return path10.join(os32.homedir(), trimmed.slice(1));
28960
- }
28961
- function isExplicitCommandPath(command) {
28962
- const trimmed = command.trim();
28963
- return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
28964
- }
28965
- function resolveCommandPath(command) {
28966
- const trimmed = command.trim();
28967
- if (!trimmed) return null;
28968
- if (isExplicitCommandPath(trimmed)) {
28969
- const expanded = expandHome(trimmed);
28970
- const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
28971
- return (0, import_fs11.existsSync)(candidate) ? candidate : null;
28972
- }
28973
- return null;
28974
- }
28975
- function execAsync(cmd, timeoutMs = 5e3) {
28976
- return new Promise((resolve162) => {
28977
- const child = (0, import_child_process5.exec)(cmd, {
28978
- encoding: "utf-8",
28979
- timeout: timeoutMs,
28980
- ...process.platform === "win32" ? { windowsHide: true } : {}
28981
- }, (err, stdout) => {
28982
- if (err || !stdout?.trim()) {
28983
- resolve162(null);
28984
- } else {
28985
- resolve162(stdout.trim());
28986
- }
28987
- });
28988
- child.on("error", () => resolve162(null));
28989
- });
28990
- }
28991
- async function detectCLIs(providerLoader, options) {
28992
- const platform10 = os32.platform();
28993
- const whichCmd = platform10 === "win32" ? "where" : "which";
28994
- const includeVersion = options?.includeVersion !== false;
28995
- const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
28996
- const results = await Promise.all(
28997
- cliList.map(async (cli) => {
28998
- try {
28999
- const explicitPath = resolveCommandPath(cli.command);
29000
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
29001
- if (!pathResult) return { ...cli, installed: false };
29002
- const firstPath = explicitPath || pathResult.split("\n")[0];
29003
- let version2;
29004
- if (includeVersion) {
29005
- const versionCommands = [
29006
- `"${firstPath}" --version`,
29007
- `"${firstPath}" -V`,
29008
- `"${firstPath}" -v`,
29009
- cli.versionCommand
29010
- ].filter((v) => !!v);
29011
- try {
29012
- for (const versionCommand of versionCommands) {
29013
- const versionResult = await execAsync(versionCommand, 3e3);
29014
- if (versionResult) {
29015
- version2 = parseVersion(versionResult);
29016
- break;
29017
- }
29018
- }
29019
- } catch {
29020
- }
29021
- }
29022
- return { ...cli, installed: true, version: version2, path: firstPath };
29023
- } catch {
29024
- return { ...cli, installed: false };
29025
- }
29026
- })
29027
- );
29028
- return results;
29029
- }
29030
- async function detectCLI(cliId, providerLoader, options) {
29031
- const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
29032
- if (providerLoader) {
29033
- const cliList = providerLoader.getCliDetectionList();
29034
- const target = cliList.find((c) => c.id === resolvedId);
29035
- if (target) {
29036
- const platform10 = os32.platform();
29037
- const whichCmd = platform10 === "win32" ? "where" : "which";
29038
- try {
29039
- const explicitPath = resolveCommandPath(target.command);
29040
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
29041
- if (!pathResult) return null;
29042
- const firstPath = explicitPath || pathResult.split("\n")[0];
29043
- let version2;
29044
- if (options?.includeVersion !== false) {
29045
- const versionCommands = [
29046
- `"${firstPath}" --version`,
29047
- `"${firstPath}" -V`,
29048
- `"${firstPath}" -v`,
29049
- target.versionCommand
29050
- ].filter((v) => !!v);
29051
- try {
29052
- for (const versionCommand of versionCommands) {
29053
- const versionResult = await execAsync(versionCommand, 3e3);
29054
- if (versionResult) {
29055
- version2 = parseVersion(versionResult);
29056
- break;
29057
- }
29058
- }
29059
- } catch {
29060
- }
29061
- }
29062
- return { ...target, installed: true, version: version2, path: firstPath };
29063
- } catch {
29064
- return null;
29065
- }
29066
- }
29067
- }
29068
- const all = await detectCLIs(providerLoader, options);
29069
- return all.find((c) => c.id === resolvedId && c.installed) || null;
29070
- }
29071
29421
  function parseDarwinAvailableBytes(totalMem) {
29072
29422
  if (os42.platform() !== "darwin") return null;
29073
29423
  try {
@@ -36589,6 +36939,204 @@ async function resolveProviderTypeFromPriority(args) {
36589
36939
  }
36590
36940
  return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
36591
36941
  }
36942
+ function truncateValidationOutput(value) {
36943
+ const text = typeof value === "string" ? value : value == null ? "" : String(value);
36944
+ if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
36945
+ return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
36946
+ [truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
36947
+ }
36948
+ function readPackageScripts(workspace) {
36949
+ try {
36950
+ const packageJsonPath = (0, import_path7.join)(workspace, "package.json");
36951
+ const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
36952
+ return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
36953
+ } catch {
36954
+ return {};
36955
+ }
36956
+ }
36957
+ function tokenizeValidationCommand(command) {
36958
+ const trimmed = command.trim();
36959
+ if (!trimmed) return null;
36960
+ if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
36961
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
36962
+ if (!tokens.length) return null;
36963
+ if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
36964
+ return tokens;
36965
+ }
36966
+ function scriptMatchesValidationCategory(scriptName, category) {
36967
+ return scriptName === category || scriptName.startsWith(`${category}:`);
36968
+ }
36969
+ function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
36970
+ const tokens = tokenizeValidationCommand(rawCommand);
36971
+ if (!tokens) {
36972
+ return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
36973
+ }
36974
+ const [binary2, second, third, ...rest] = tokens;
36975
+ let scriptName = "";
36976
+ let command = binary2;
36977
+ let args = [];
36978
+ if ((binary2 === "npm" || binary2 === "pnpm" || binary2 === "bun") && second === "run" && third) {
36979
+ scriptName = third;
36980
+ args = ["run", scriptName, ...rest];
36981
+ } else if (binary2 === "npm" && second === "test" && !third) {
36982
+ scriptName = "test";
36983
+ args = ["test"];
36984
+ } else if (binary2 === "yarn" && second === "run" && third) {
36985
+ scriptName = third;
36986
+ args = ["run", scriptName, ...rest];
36987
+ } else if (binary2 === "yarn" && second && !third) {
36988
+ scriptName = second;
36989
+ args = [scriptName];
36990
+ } else {
36991
+ return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
36992
+ }
36993
+ if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
36994
+ return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
36995
+ }
36996
+ if (!scriptMatchesValidationCategory(scriptName, category)) {
36997
+ return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
36998
+ }
36999
+ return {
37000
+ command: {
37001
+ command,
37002
+ args,
37003
+ displayCommand: [command, ...args].join(" "),
37004
+ category,
37005
+ source
37006
+ }
37007
+ };
37008
+ }
37009
+ function collectProjectContextValidationCandidates(mesh) {
37010
+ const commands = mesh?.projectContext?.commands;
37011
+ if (!commands || typeof commands !== "object" || Array.isArray(commands)) return [];
37012
+ const candidates = [];
37013
+ for (const category of REFINE_VALIDATION_CATEGORIES) {
37014
+ const entries = Array.isArray(commands[category]) ? commands[category] : [];
37015
+ for (const entry of entries) {
37016
+ if (typeof entry?.command !== "string") continue;
37017
+ candidates.push({
37018
+ command: entry.command,
37019
+ category,
37020
+ source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
37021
+ confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
37022
+ });
37023
+ }
37024
+ }
37025
+ return candidates.sort((a, b) => {
37026
+ const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
37027
+ return rank(a.confidence) - rank(b.confidence);
37028
+ });
37029
+ }
37030
+ function collectPolicyValidationCandidates(mesh) {
37031
+ const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
37032
+ const configured = Array.isArray(policy.validationCommands) ? policy.validationCommands : Array.isArray(policy.validationGate?.commands) ? policy.validationGate.commands : [];
37033
+ return configured.map((entry) => typeof entry === "string" ? { command: entry, category: "", source: "mesh.policy.validationCommands" } : entry).filter((entry) => entry && typeof entry.command === "string").map((entry) => {
37034
+ const commandText = entry.command.trim();
37035
+ const category = REFINE_VALIDATION_CATEGORIES.find((cat) => commandText.includes(` ${cat}`)) ?? "";
37036
+ return { command: commandText, category, source: "mesh.policy.validationCommands" };
37037
+ }).filter((entry) => !!entry.category);
37038
+ }
37039
+ function selectMeshRefineValidationCommands(mesh, workspace) {
37040
+ const scripts = readPackageScripts(workspace);
37041
+ const rejectedCommands = [];
37042
+ const selected = [];
37043
+ const seen = /* @__PURE__ */ new Set();
37044
+ const candidates = [
37045
+ ...collectPolicyValidationCandidates(mesh),
37046
+ ...collectProjectContextValidationCandidates(mesh)
37047
+ ];
37048
+ for (const candidate of candidates) {
37049
+ const parsed = parsePackageManagerValidationCommand(candidate.command, candidate.category, scripts, candidate.source);
37050
+ if (parsed.rejected) {
37051
+ rejectedCommands.push(parsed.rejected);
37052
+ continue;
37053
+ }
37054
+ if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
37055
+ selected.push(parsed.command);
37056
+ seen.add(parsed.command.displayCommand);
37057
+ if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
37058
+ }
37059
+ if (!selected.length && candidates.length === 0) {
37060
+ for (const category of REFINE_VALIDATION_CATEGORIES) {
37061
+ if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
37062
+ const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
37063
+ if (fallback.command && !seen.has(fallback.command.displayCommand)) {
37064
+ selected.push(fallback.command);
37065
+ seen.add(fallback.command.displayCommand);
37066
+ } else if (fallback.rejected) {
37067
+ rejectedCommands.push(fallback.rejected);
37068
+ }
37069
+ if (selected.length >= 2) break;
37070
+ }
37071
+ }
37072
+ return {
37073
+ commands: selected,
37074
+ rejectedCommands,
37075
+ 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"
37076
+ };
37077
+ }
37078
+ async function runMeshRefineValidationGate(mesh, workspace) {
37079
+ const { execFile: execFile3 } = await import("child_process");
37080
+ const { promisify: promisify3 } = await import("util");
37081
+ const execFileAsync3 = promisify3(execFile3);
37082
+ const selection = selectMeshRefineValidationCommands(mesh, workspace);
37083
+ const summary = {
37084
+ status: "skipped",
37085
+ required: true,
37086
+ commandsRun: [],
37087
+ rejectedCommands: selection.rejectedCommands,
37088
+ skippedReason: void 0,
37089
+ timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
37090
+ outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
37091
+ };
37092
+ if (!selection.commands.length) {
37093
+ summary.skippedReason = "validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available";
37094
+ return summary;
37095
+ }
37096
+ for (const candidate of selection.commands) {
37097
+ const startedAt = Date.now();
37098
+ try {
37099
+ const result = await execFileAsync3(candidate.command, candidate.args, {
37100
+ cwd: workspace,
37101
+ encoding: "utf8",
37102
+ timeout: REFINE_VALIDATION_TIMEOUT_MS,
37103
+ maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
37104
+ env: { ...process.env, CI: process.env.CI || "1" }
37105
+ });
37106
+ summary.commandsRun.push({
37107
+ command: candidate.command,
37108
+ args: candidate.args,
37109
+ displayCommand: candidate.displayCommand,
37110
+ category: candidate.category,
37111
+ source: candidate.source,
37112
+ passed: true,
37113
+ exitCode: 0,
37114
+ durationMs: Date.now() - startedAt,
37115
+ stdout: truncateValidationOutput(result.stdout),
37116
+ stderr: truncateValidationOutput(result.stderr)
37117
+ });
37118
+ } catch (error48) {
37119
+ summary.commandsRun.push({
37120
+ command: candidate.command,
37121
+ args: candidate.args,
37122
+ displayCommand: candidate.displayCommand,
37123
+ category: candidate.category,
37124
+ source: candidate.source,
37125
+ passed: false,
37126
+ exitCode: typeof error48?.code === "number" ? error48.code : null,
37127
+ signal: typeof error48?.signal === "string" ? error48.signal : null,
37128
+ timedOut: error48?.killed === true || /timed out/i.test(String(error48?.message || "")),
37129
+ durationMs: Date.now() - startedAt,
37130
+ stdout: truncateValidationOutput(error48?.stdout),
37131
+ stderr: truncateValidationOutput(error48?.stderr || error48?.message)
37132
+ });
37133
+ summary.status = "failed";
37134
+ return summary;
37135
+ }
37136
+ }
37137
+ summary.status = "passed";
37138
+ return summary;
37139
+ }
36592
37140
  function loadYamlModule() {
36593
37141
  return js_yaml_exports;
36594
37142
  }
@@ -41136,7 +41684,7 @@ async function shutdownDaemonComponents(components) {
41136
41684
  }
41137
41685
  cdpManagers.clear();
41138
41686
  }
41139
- var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, fs2, path8, os22, os8, os9, path14, import_child_process2, os10, path15, os11, import_child_process3, import_fs8, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs9, import_path5, import_child_process4, import_fs10, import_os3, path9, import_child_process5, os32, path10, import_fs11, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, mesh_work_queue_exports, init_mesh_work_queue, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, COMPLETED_FINALIZATION_RETRY_MS, COMPLETED_FINALIZATION_MAX_WAIT_MS, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
41687
+ var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, import_child_process2, os22, path8, import_fs8, fs2, path9, os32, os8, os9, path14, import_child_process3, os10, path15, os11, import_child_process4, import_fs9, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs10, import_path5, import_child_process5, import_fs11, import_os3, path10, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, SUBMODULE_WORKTREE_REMOVE_RE, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, mesh_work_queue_exports, init_mesh_work_queue, init_cli_detector, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, NO_FALLBACK_REASON, P2P_NEXT_ACTION, NON_P2P_NEXT_ACTION, P2pRelayFailureError, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, COMPLETED_FINALIZATION_RETRY_MS, COMPLETED_FINALIZATION_MAX_WAIT_MS, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, REFINE_VALIDATION_CATEGORIES, REFINE_VALIDATION_TIMEOUT_MS, REFINE_VALIDATION_OUTPUT_LIMIT_BYTES, REFINE_VALIDATION_SUMMARY_CHARS, REFINE_VALIDATION_MAX_COMMANDS, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
41140
41688
  var init_dist2 = __esm({
41141
41689
  "../daemon-core/dist/index.mjs"() {
41142
41690
  "use strict";
@@ -41159,20 +41707,24 @@ var init_dist2 = __esm({
41159
41707
  import_fs7 = require("fs");
41160
41708
  import_path4 = require("path");
41161
41709
  import_crypto5 = require("crypto");
41162
- fs2 = __toESM(require("fs"), 1);
41163
- path8 = __toESM(require("path"), 1);
41710
+ import_child_process2 = require("child_process");
41164
41711
  os22 = __toESM(require("os"), 1);
41712
+ path8 = __toESM(require("path"), 1);
41713
+ import_fs8 = require("fs");
41714
+ fs2 = __toESM(require("fs"), 1);
41715
+ path9 = __toESM(require("path"), 1);
41716
+ os32 = __toESM(require("os"), 1);
41165
41717
  init_dist();
41166
41718
  os8 = __toESM(require("os"), 1);
41167
41719
  os9 = __toESM(require("os"), 1);
41168
41720
  path14 = __toESM(require("path"), 1);
41169
- import_child_process2 = require("child_process");
41721
+ import_child_process3 = require("child_process");
41170
41722
  os10 = __toESM(require("os"), 1);
41171
41723
  path15 = __toESM(require("path"), 1);
41172
41724
  init_dist();
41173
41725
  os11 = __toESM(require("os"), 1);
41174
- import_child_process3 = require("child_process");
41175
- import_fs8 = require("fs");
41726
+ import_child_process4 = require("child_process");
41727
+ import_fs9 = require("fs");
41176
41728
  import_promises5 = require("fs/promises");
41177
41729
  path = __toESM(require("path"), 1);
41178
41730
  import_util4 = require("util");
@@ -41185,16 +41737,12 @@ var init_dist2 = __esm({
41185
41737
  import_crypto6 = require("crypto");
41186
41738
  path6 = __toESM(require("path"), 1);
41187
41739
  path7 = __toESM(require("path"), 1);
41188
- import_fs9 = require("fs");
41189
- import_path5 = require("path");
41190
- import_child_process4 = require("child_process");
41191
41740
  import_fs10 = require("fs");
41192
- import_os3 = require("os");
41193
- path9 = __toESM(require("path"), 1);
41741
+ import_path5 = require("path");
41194
41742
  import_child_process5 = require("child_process");
41195
- os32 = __toESM(require("os"), 1);
41196
- path10 = __toESM(require("path"), 1);
41197
41743
  import_fs11 = require("fs");
41744
+ import_os3 = require("os");
41745
+ path10 = __toESM(require("path"), 1);
41198
41746
  os42 = __toESM(require("os"), 1);
41199
41747
  import_child_process6 = require("child_process");
41200
41748
  init_wrapper();
@@ -41327,6 +41875,7 @@ var init_dist2 = __esm({
41327
41875
  WORKTREE_DIR_NAME = ".adhdev-worktrees";
41328
41876
  GIT_TIMEOUT_MS = 3e4;
41329
41877
  GIT_MAX_BUFFER = 4 * 1024 * 1024;
41878
+ SUBMODULE_WORKTREE_REMOVE_RE = /working trees containing submodules cannot be moved or removed/i;
41330
41879
  }
41331
41880
  });
41332
41881
  config_exports = {};
@@ -41485,6 +42034,7 @@ Follow these recovery rules:
41485
42034
  enqueueTask: () => enqueueTask,
41486
42035
  getMeshQueueStats: () => getMeshQueueStats,
41487
42036
  getQueue: () => getQueue,
42037
+ recordTaskAutoLaunch: () => recordTaskAutoLaunch,
41488
42038
  requeueTask: () => requeueTask,
41489
42039
  updateSessionTaskStatus: () => updateSessionTaskStatus,
41490
42040
  updateTaskStatus: () => updateTaskStatus
@@ -41495,13 +42045,18 @@ Follow these recovery rules:
41495
42045
  init_mesh_ledger();
41496
42046
  }
41497
42047
  });
42048
+ init_cli_detector = __esm2({
42049
+ "src/detection/cli-detector.ts"() {
42050
+ "use strict";
42051
+ }
42052
+ });
41498
42053
  init_logger = __esm2({
41499
42054
  "src/logging/logger.ts"() {
41500
42055
  "use strict";
41501
42056
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
41502
42057
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
41503
42058
  currentLevel = "info";
41504
- 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");
42059
+ 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");
41505
42060
  MAX_LOG_SIZE = 5 * 1024 * 1024;
41506
42061
  MAX_LOG_DAYS = 7;
41507
42062
  try {
@@ -41509,16 +42064,16 @@ Follow these recovery rules:
41509
42064
  } catch {
41510
42065
  }
41511
42066
  currentDate = getDateStr();
41512
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
42067
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
41513
42068
  cleanOldLogs();
41514
42069
  try {
41515
- const oldLog = path8.join(LOG_DIR, "daemon.log");
42070
+ const oldLog = path9.join(LOG_DIR, "daemon.log");
41516
42071
  if (fs2.existsSync(oldLog)) {
41517
42072
  const stat22 = fs2.statSync(oldLog);
41518
42073
  const oldDate = stat22.mtime.toISOString().slice(0, 10);
41519
- fs2.renameSync(oldLog, path8.join(LOG_DIR, `daemon-${oldDate}.log`));
42074
+ fs2.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
41520
42075
  }
41521
- const oldLogBackup = path8.join(LOG_DIR, "daemon.log.old");
42076
+ const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
41522
42077
  if (fs2.existsSync(oldLogBackup)) {
41523
42078
  fs2.unlinkSync(oldLogBackup);
41524
42079
  }
@@ -41550,7 +42105,7 @@ Follow these recovery rules:
41550
42105
  }
41551
42106
  };
41552
42107
  interceptorInstalled = false;
41553
- LOG_PATH = path8.join(LOG_DIR, `daemon-${getDateStr()}.log`);
42108
+ LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
41554
42109
  }
41555
42110
  });
41556
42111
  mesh_events_exports = {};
@@ -41564,7 +42119,9 @@ Follow these recovery rules:
41564
42119
  init_mesh_events = __esm2({
41565
42120
  "src/mesh/mesh-events.ts"() {
41566
42121
  "use strict";
42122
+ init_config();
41567
42123
  init_mesh_config();
42124
+ init_cli_detector();
41568
42125
  init_logger();
41569
42126
  init_mesh_ledger();
41570
42127
  init_mesh_work_queue();
@@ -41585,6 +42142,9 @@ Follow these recovery rules:
41585
42142
  "agent:stopped": "task_failed",
41586
42143
  "monitor:long_generating": "task_stalled"
41587
42144
  };
42145
+ autoLaunchInProgress = /* @__PURE__ */ new Set();
42146
+ autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
42147
+ AUTO_LAUNCH_COOLDOWN_MS = 5e3;
41588
42148
  }
41589
42149
  });
41590
42150
  init_debug_config = __esm2({
@@ -44078,7 +44638,7 @@ ${lastSnapshot}`;
44078
44638
  }
44079
44639
  });
44080
44640
  init_repo_mesh_types();
44081
- execFileAsync = (0, import_util4.promisify)(import_child_process3.execFile);
44641
+ execFileAsync = (0, import_util4.promisify)(import_child_process4.execFile);
44082
44642
  DEFAULT_TIMEOUT_MS = 5e3;
44083
44643
  DEFAULT_MAX_BUFFER = 1024 * 1024;
44084
44644
  GitCommandError = class extends Error {
@@ -44337,6 +44897,34 @@ ${lastSnapshot}`;
44337
44897
  init_mesh_ledger();
44338
44898
  init_mesh_work_queue();
44339
44899
  init_mesh_events();
44900
+ NO_FALLBACK_REASON = "Repo Mesh command/data-plane is P2P-only; WS/REST command fallback is intentionally disabled to preserve the transport boundary.";
44901
+ 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.";
44902
+ NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
44903
+ P2pRelayFailureError = class extends Error {
44904
+ code;
44905
+ reason;
44906
+ transport;
44907
+ recoverable;
44908
+ retryRecommended;
44909
+ nextAction;
44910
+ noFallbackReason;
44911
+ command;
44912
+ targetDaemonId;
44913
+ constructor(message, context = {}) {
44914
+ super(message);
44915
+ this.name = "P2pRelayFailureError";
44916
+ const payload = buildP2pRelayFailurePayload(message, context);
44917
+ this.code = payload.code;
44918
+ this.reason = payload.reason;
44919
+ this.transport = payload.transport;
44920
+ this.recoverable = payload.recoverable;
44921
+ this.retryRecommended = payload.retryRecommended;
44922
+ this.nextAction = payload.nextAction;
44923
+ this.noFallbackReason = payload.noFallbackReason;
44924
+ this.command = context.command;
44925
+ this.targetDaemonId = context.targetDaemonId;
44926
+ }
44927
+ };
44340
44928
  init_config();
44341
44929
  DEFAULT_STATE = {
44342
44930
  recentActivity: [],
@@ -44348,6 +44936,7 @@ ${lastSnapshot}`;
44348
44936
  };
44349
44937
  BUILTIN_IDE_DEFINITIONS = [];
44350
44938
  registeredIDEs = /* @__PURE__ */ new Map();
44939
+ init_cli_detector();
44351
44940
  LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
44352
44941
  DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
44353
44942
  "generating",
@@ -48079,6 +48668,7 @@ ${effect.notification.body || ""}`.trim();
48079
48668
  }
48080
48669
  };
48081
48670
  init_provider_cli_adapter();
48671
+ init_cli_detector();
48082
48672
  init_config();
48083
48673
  init_provider_cli_adapter();
48084
48674
  init_logger();
@@ -52430,6 +53020,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
52430
53020
  };
52431
53021
  _providerLoader = null;
52432
53022
  init_config();
53023
+ init_cli_detector();
52433
53024
  init_logger();
52434
53025
  LOG_DIR2 = process.platform === "win32" ? path21.join(process.env.LOCALAPPDATA || process.env.APPDATA || path21.join(os16.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path21.join(os16.homedir(), "Library", "Logs", "adhdev") : path21.join(os16.homedir(), ".local", "share", "adhdev", "logs");
52435
53026
  MAX_FILE_SIZE = 5 * 1024 * 1024;
@@ -52478,6 +53069,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
52478
53069
  stable: "https://api.adhf.dev",
52479
53070
  preview: "https://api-preview.adhf.dev"
52480
53071
  };
53072
+ REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
53073
+ REFINE_VALIDATION_TIMEOUT_MS = 12e4;
53074
+ REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
53075
+ REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
53076
+ REFINE_VALIDATION_MAX_COMMANDS = 4;
52481
53077
  CHAT_COMMANDS = [
52482
53078
  "send_chat",
52483
53079
  "new_chat",
@@ -52610,20 +53206,98 @@ Run 'adhdev doctor' for detailed diagnostics.`
52610
53206
  recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
52611
53207
  };
52612
53208
  }
53209
+ const forceFallbackConvergence = await this.getWorktreeForceCleanupConvergence({
53210
+ repoRoot,
53211
+ workspace,
53212
+ node: args.node
53213
+ });
52613
53214
  try {
52614
- const result = await removeWorktree2(repoRoot, workspace, { requireClean: true });
52615
- return { success: true, removedPath: result.removedPath, repoRoot };
53215
+ const result = await removeWorktree2(repoRoot, workspace, {
53216
+ requireClean: true,
53217
+ allowSubmoduleForceFallback: forceFallbackConvergence.allow
53218
+ });
53219
+ return {
53220
+ success: true,
53221
+ removedPath: result.removedPath,
53222
+ repoRoot,
53223
+ ...result.fallback ? {
53224
+ fallback: result.fallback,
53225
+ forced: result.forced,
53226
+ reason: result.reason,
53227
+ convergence: forceFallbackConvergence
53228
+ } : {}
53229
+ };
52616
53230
  } catch (e) {
52617
53231
  const message = String(e?.message || e || "worktree cleanup failed");
52618
53232
  const dirty = message.includes("dirty worktree") || message.includes("local changes");
53233
+ const submoduleForceBlocked = /working trees containing submodules cannot be moved or removed/i.test(message) && !forceFallbackConvergence.allow;
52619
53234
  return {
52620
53235
  success: false,
52621
- code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
52622
- error: message,
52623
- 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."
53236
+ code: dirty ? "mesh_worktree_cleanup_dirty" : submoduleForceBlocked ? "mesh_worktree_cleanup_force_fallback_blocked" : "mesh_worktree_cleanup_failed",
53237
+ error: submoduleForceBlocked ? `${message}; refusing --force fallback because convergence could not be verified: ${forceFallbackConvergence.error || "unknown convergence state"}` : message,
53238
+ 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.",
53239
+ ...submoduleForceBlocked ? { convergence: forceFallbackConvergence } : {}
52624
53240
  };
52625
53241
  }
52626
53242
  }
53243
+ async getWorktreeForceCleanupConvergence(args) {
53244
+ const metadataStatus = typeof args.node?.branchConvergence?.status === "string" ? args.node.branchConvergence.status : "";
53245
+ if (metadataStatus === "merged_to_main" || metadataStatus === "cleanup_candidate") {
53246
+ return { allow: true, status: metadataStatus, source: "node_branch_convergence" };
53247
+ }
53248
+ const { execFile: execFile3 } = await import("child_process");
53249
+ const { promisify: promisify3 } = await import("util");
53250
+ const execFileAsync3 = promisify3(execFile3);
53251
+ const runGit2 = async (gitArgs, cwd) => {
53252
+ const { stdout } = await execFileAsync3("git", gitArgs, {
53253
+ cwd,
53254
+ encoding: "utf8",
53255
+ timeout: 3e4,
53256
+ maxBuffer: 4 * 1024 * 1024,
53257
+ windowsHide: true
53258
+ });
53259
+ return String(stdout || "").trim();
53260
+ };
53261
+ let head = "";
53262
+ try {
53263
+ head = await runGit2(["rev-parse", "HEAD"], args.workspace);
53264
+ } catch (e) {
53265
+ return { allow: false, error: `could not resolve worktree HEAD: ${e?.message || e}` };
53266
+ }
53267
+ if (!head) return { allow: false, error: "worktree HEAD is empty" };
53268
+ const candidateRefs = [];
53269
+ try {
53270
+ const defaultBranch = await runGit2(["branch", "--show-current"], args.repoRoot);
53271
+ if (defaultBranch) {
53272
+ candidateRefs.push(defaultBranch, `origin/${defaultBranch}`);
53273
+ }
53274
+ } catch {
53275
+ }
53276
+ candidateRefs.push("origin/main", "origin/master", "main", "master");
53277
+ const seen = /* @__PURE__ */ new Set();
53278
+ const checkedRefs = [];
53279
+ for (const ref of candidateRefs) {
53280
+ if (!ref || seen.has(ref)) continue;
53281
+ seen.add(ref);
53282
+ let commit = "";
53283
+ try {
53284
+ commit = await runGit2(["rev-parse", "--verify", `${ref}^{commit}`], args.repoRoot);
53285
+ } catch {
53286
+ continue;
53287
+ }
53288
+ checkedRefs.push(ref);
53289
+ try {
53290
+ await runGit2(["merge-base", "--is-ancestor", head, commit], args.repoRoot);
53291
+ return { allow: true, status: "merged_to_default_ref", source: "git_merge_base", ref };
53292
+ } catch {
53293
+ }
53294
+ }
53295
+ return {
53296
+ allow: false,
53297
+ status: metadataStatus || void 0,
53298
+ error: checkedRefs.length ? `worktree HEAD is not contained in checked refs: ${checkedRefs.join(", ")}` : "no default/main refs were available for convergence verification"
53299
+ };
53300
+ }
52627
53301
  isCompletedHostedSession(record2) {
52628
53302
  return record2?.lifecycle === "stopped" || record2?.lifecycle === "failed" || record2?.lifecycle === "interrupted";
52629
53303
  }
@@ -53583,10 +54257,61 @@ Run 'adhdev doctor' for detailed diagnostics.`
53583
54257
  if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
53584
54258
  const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
53585
54259
  const baseBranch = baseBranchStdout.trim();
54260
+ const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
54261
+ if (validationSummary.status === "failed") {
54262
+ return {
54263
+ success: false,
54264
+ code: "validation_failed",
54265
+ convergenceStatus: "blocked_review",
54266
+ error: "Refinery validation gate failed; merge/refine was not attempted.",
54267
+ branch,
54268
+ into: baseBranch,
54269
+ validationSummary,
54270
+ finalBranchConvergenceState: {
54271
+ branch,
54272
+ baseBranch,
54273
+ merged: false,
54274
+ removed: false,
54275
+ validation: "failed",
54276
+ status: "blocked_review"
54277
+ }
54278
+ };
54279
+ }
54280
+ if (validationSummary.status === "skipped") {
54281
+ return {
54282
+ success: false,
54283
+ code: "validation_unavailable",
54284
+ convergenceStatus: "blocked_review",
54285
+ error: "Refinery validation gate is required but no allowlisted validation command was available; merge/refine was not attempted.",
54286
+ branch,
54287
+ into: baseBranch,
54288
+ validationSummary,
54289
+ finalBranchConvergenceState: {
54290
+ branch,
54291
+ baseBranch,
54292
+ merged: false,
54293
+ removed: false,
54294
+ validation: "unavailable",
54295
+ status: "blocked_review"
54296
+ }
54297
+ };
54298
+ }
53586
54299
  try {
53587
54300
  await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
53588
54301
  } catch (e) {
53589
- return { success: false, error: `Merge failed (conflicts?): ${e.message}` };
54302
+ return {
54303
+ success: false,
54304
+ error: `Merge failed (conflicts?): ${e.message}`,
54305
+ validationSummary,
54306
+ finalBranchConvergenceState: {
54307
+ branch,
54308
+ baseBranch,
54309
+ merged: false,
54310
+ removed: false,
54311
+ validation: "passed",
54312
+ status: "not_mergeable"
54313
+ }
54314
+ };
53590
54315
  }
53591
54316
  const removeResult = await this.execute("remove_mesh_node", {
53592
54317
  meshId,
@@ -53599,11 +54324,27 @@ Run 'adhdev doctor' for detailed diagnostics.`
53599
54324
  appendLedgerEntry2(meshId, {
53600
54325
  kind: "node_removed",
53601
54326
  nodeId,
53602
- payload: { refined: true, mergedBranch: branch, into: baseBranch }
54327
+ payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
53603
54328
  });
53604
54329
  } catch {
53605
54330
  }
53606
- return { success: true, merged: true, branch, into: baseBranch, removeResult };
54331
+ return {
54332
+ success: true,
54333
+ merged: true,
54334
+ branch,
54335
+ into: baseBranch,
54336
+ removeResult,
54337
+ validationSummary,
54338
+ finalBranchConvergenceState: {
54339
+ branch: baseBranch,
54340
+ mergedBranch: branch,
54341
+ baseBranch,
54342
+ merged: true,
54343
+ removed: removeResult?.success !== false,
54344
+ validation: "passed",
54345
+ status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
54346
+ }
54347
+ };
53607
54348
  } catch (e) {
53608
54349
  return { success: false, error: e.message };
53609
54350
  }
@@ -53658,7 +54399,10 @@ Run 'adhdev doctor' for detailed diagnostics.`
53658
54399
  sessionCleanupMode,
53659
54400
  workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
53660
54401
  daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
53661
- worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
54402
+ worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0,
54403
+ worktreeCleanupFallback: typeof worktreeCleanup?.fallback === "string" ? worktreeCleanup.fallback : void 0,
54404
+ forced: worktreeCleanup?.forced === true ? true : void 0,
54405
+ forceFallbackReason: typeof worktreeCleanup?.reason === "string" ? worktreeCleanup.reason : void 0
53662
54406
  }
53663
54407
  });
53664
54408
  } catch {
@@ -57555,6 +58299,7 @@ data: ${JSON.stringify(msg.data)}
57555
58299
  apiKeyName: "OpenAI/Anthropic API key"
57556
58300
  }
57557
58301
  ];
58302
+ init_cli_detector();
57558
58303
  SessionRegistry = class {
57559
58304
  bySessionId = /* @__PURE__ */ new Map();
57560
58305
  byManagerKey = /* @__PURE__ */ new Map();
@@ -58040,16 +58785,38 @@ function extractLaunchPayload(value) {
58040
58785
  function classifyMeshLaunchFailure(error48) {
58041
58786
  const message = error48 instanceof Error ? error48.message : String(error48 || "launch failed");
58042
58787
  const lower = message.toLowerCase();
58043
- if (lower.includes("p2p") || lower.includes("datachannel") || lower.includes("node-datachannel")) {
58044
- return { code: "p2p_unavailable", reason: "daemon_mesh_p2p_transport_unavailable", transport: "daemon_mesh_p2p" };
58788
+ const p2pClassification = classifyP2pRelayFailure(error48, { command: "launch_cli" });
58789
+ if (p2pClassification.recoverable) {
58790
+ return p2pClassification;
58045
58791
  }
58046
58792
  if (lower.includes("cannot connect to daemon ipc") || lower.includes("daemon ipc command")) {
58047
- return { code: "local_ipc_unavailable", reason: "local_daemon_ipc_unavailable", transport: "local_ipc" };
58793
+ return {
58794
+ code: "local_ipc_unavailable",
58795
+ reason: "local_daemon_ipc_unavailable",
58796
+ transport: "local_ipc",
58797
+ recoverable: true,
58798
+ retryRecommended: true,
58799
+ nextAction: "Check the local daemon IPC connection, then retry mesh_launch_session once after the daemon is reachable."
58800
+ };
58048
58801
  }
58049
58802
  if (lower.includes("timed out") || lower.includes("timeout")) {
58050
- return { code: "mesh_transport_timeout", reason: "mesh_transport_timeout", transport: "mesh_transport" };
58803
+ return {
58804
+ code: "mesh_transport_timeout",
58805
+ reason: "mesh_transport_timeout",
58806
+ transport: "mesh_transport",
58807
+ recoverable: true,
58808
+ retryRecommended: true,
58809
+ nextAction: "Check mesh transport health, then do one bounded retry before requeueing or relaunching the task."
58810
+ };
58051
58811
  }
58052
- return { code: "mesh_launch_failed", reason: "provider_launch_failed", transport: "mesh_transport" };
58812
+ return {
58813
+ code: "mesh_launch_failed",
58814
+ reason: "provider_launch_failed",
58815
+ transport: "mesh_transport",
58816
+ recoverable: false,
58817
+ retryRecommended: false,
58818
+ nextAction: "Inspect the provider launch error and fix the underlying provider/configuration issue before retrying."
58819
+ };
58053
58820
  }
58054
58821
  function buildWorktreeCleanupHint(node) {
58055
58822
  if (!node.isLocalWorktree) return void 0;
@@ -58065,10 +58832,13 @@ function buildRecoverableLaunchFailure(ctx, node, providerType, error48) {
58065
58832
  const cleanup = buildWorktreeCleanupHint(node);
58066
58833
  return {
58067
58834
  success: false,
58068
- recoverable: true,
58835
+ recoverable: classified.recoverable,
58069
58836
  code: classified.code,
58070
58837
  reason: classified.reason,
58071
58838
  transport: classified.transport,
58839
+ retryRecommended: classified.retryRecommended,
58840
+ nextAction: classified.nextAction,
58841
+ ...classified.noFallbackReason ? { noFallbackReason: classified.noFallbackReason } : {},
58072
58842
  error: message,
58073
58843
  meshId: ctx.mesh.id,
58074
58844
  nodeId: node.id,
@@ -58115,6 +58885,18 @@ function getLatestActiveLaunchFailure(meshId, nodeId) {
58115
58885
  }
58116
58886
  return null;
58117
58887
  }
58888
+ function buildCoordinatorP2pRelayFailure(error48, context) {
58889
+ const payload = buildP2pRelayFailurePayload(error48, {
58890
+ command: context.command,
58891
+ targetDaemonId: context.targetDaemonId
58892
+ });
58893
+ return {
58894
+ ...payload,
58895
+ ...context.nodeId ? { nodeId: context.nodeId } : {},
58896
+ ...context.sessionId ? { sessionId: context.sessionId } : {},
58897
+ retryHint: payload.retryRecommended ? payload.nextAction : "Do not retry as a P2P transport recovery; inspect the command/provider error first."
58898
+ };
58899
+ }
58118
58900
  async function ipcDispatchToRemoteAgent(ctx, node, args) {
58119
58901
  const transport = ctx.transport;
58120
58902
  const daemonId = node.daemonId;
@@ -58150,11 +58932,32 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
58150
58932
  });
58151
58933
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
58152
58934
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
58153
- return { success: false, error: `P2P dispatch failed: ${dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"}` };
58935
+ const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
58936
+ const errorMessage = dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task";
58937
+ return {
58938
+ ...buildCoordinatorP2pRelayFailure(source?.error || errorMessage, {
58939
+ command: "agent_command",
58940
+ targetDaemonId: daemonId,
58941
+ nodeId: node.id,
58942
+ sessionId
58943
+ }),
58944
+ ...source && typeof source === "object" ? source : {},
58945
+ success: false,
58946
+ error: `P2P dispatch failed: ${errorMessage}`
58947
+ };
58154
58948
  }
58155
58949
  return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
58156
58950
  } catch (e) {
58157
- return { success: false, error: `P2P dispatch failed: ${e?.message || String(e)}` };
58951
+ const errorMessage = e?.message || String(e);
58952
+ return {
58953
+ ...buildCoordinatorP2pRelayFailure(e, {
58954
+ command: "agent_command",
58955
+ targetDaemonId: daemonId,
58956
+ nodeId: node.id,
58957
+ sessionId
58958
+ }),
58959
+ error: `P2P dispatch failed: ${errorMessage}`
58960
+ };
58158
58961
  }
58159
58962
  }
58160
58963
  function resolveCoordinatorNode(ctx) {
@@ -58399,8 +59202,7 @@ async function commandForNode(ctx, node, command, args = {}) {
58399
59202
  throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
58400
59203
  }
58401
59204
  function isP2pTransportUnavailableError(error48) {
58402
- const message = error48 instanceof Error ? error48.message : String(error48 || "");
58403
- return /p2p|datachannel|mesh_relay_command|daemon_mesh_p2p_transport_unavailable/i.test(message) && /unavailable|failed|timeout|timed out|not connected|closed/i.test(message);
59205
+ return isP2pRelayTransportFailure(error48);
58404
59206
  }
58405
59207
  function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
58406
59208
  return {
@@ -58993,7 +59795,13 @@ async function meshSendTask(ctx, args) {
58993
59795
  }
58994
59796
  return JSON.stringify({ success: true, nodeId: args.node_id, taskId: task.id, status: task.status });
58995
59797
  } catch (e) {
58996
- return JSON.stringify({ success: false, error: e.message });
59798
+ const failure2 = buildCoordinatorP2pRelayFailure(e, {
59799
+ command: "mesh_send_task",
59800
+ targetDaemonId: node.daemonId,
59801
+ nodeId: args.node_id,
59802
+ sessionId: args.session_id
59803
+ });
59804
+ return JSON.stringify(failure2);
58997
59805
  }
58998
59806
  }
58999
59807
  async function meshReadChat(ctx, args) {