@adhdev/daemon-standalone 0.9.77-rc.43 → 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
  }
@@ -25798,7 +25828,8 @@ function buildRulesSection(coordinatorCliType) {
25798
25828
  - **Minimize coordinator context.** The coordinator's job is routing, not implementing. Do not read source files, run commands, or analyze code directly \u2014 delegate all of that to node agents. Your context should stay lean.
25799
25829
  - **Delegate analysis too.** If you need to understand a bug or explore the codebase, send that investigation as a task to the queue or a node. Do not do it yourself.
25800
25830
  - **Respect explicit provider requests.** If the user names an agent/provider, pass the matching provider type to \`mesh_launch_session\`: Hermes \u2192 \`hermes-cli\`, Claude Code/Claude \u2192 \`claude-cli\`, Codex \u2192 \`codex-cli\`, Gemini \u2192 \`gemini-cli\`. Never substitute \`claude-cli\` just because the coordinator itself is Claude Code.
25801
- - **Front-load the task message.** When calling \`mesh_enqueue_task\` or \`mesh_send_task\`, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
25831
+ - **Front-load new task messages.** When calling \`mesh_enqueue_task\` or \`mesh_send_task\` for a new task, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
25832
+ - **Avoid context-wasting restarts.** For follow-up, retry, commit/push, preview, or cleanup work on the same issue, prefer the existing idle session and send only the delta from its last verified state. Start a fresh chat/session only for genuinely independent work, explicit provider/user request, unsafe transcript contamination, or required branch/worktree isolation.
25802
25833
  - **Don't inspect code.** Treat delegated agent summaries as self-reports, not verification. Verify side effects via \`mesh_git_status\` (including related repo freshness when configured), not by reading source files.
25803
25834
  - **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed. Never launch a duplicate session or second worker solely because \`mesh_read_chat\` has no final assistant message while the delegated session is still showing tool/terminal activity.
25804
25835
  - **Handle failures with context.** If a task fails, check \`mesh_task_history\` first to see if this task was attempted before and how it failed. Read the chat to understand why, then decide: retry on the same node, reassign to a different node, or escalate to the user.
@@ -26091,6 +26122,19 @@ function updateTaskStatus(meshId, taskId, status) {
26091
26122
  writeQueue(meshId, queue);
26092
26123
  return queue[idx];
26093
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
+ }
26094
26138
  function cancelTask(meshId, taskId, opts) {
26095
26139
  const queue = readQueue(meshId);
26096
26140
  const idx = queue.findIndex((q) => q.id === taskId);
@@ -26153,6 +26197,129 @@ function getMeshQueueStats(meshId) {
26153
26197
  }))
26154
26198
  };
26155
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
+ }
26156
26323
  function setLogLevel(level) {
26157
26324
  currentLevel = level;
26158
26325
  daemonLog("Logger", `Log level set to: ${level}`, "info");
@@ -26167,13 +26334,13 @@ function getDaemonLogDir() {
26167
26334
  return LOG_DIR;
26168
26335
  }
26169
26336
  function getCurrentDaemonLogPath(date5 = /* @__PURE__ */ new Date()) {
26170
- 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`);
26171
26338
  }
26172
26339
  function checkDateRotation() {
26173
26340
  const today = getDateStr();
26174
26341
  if (today !== currentDate) {
26175
26342
  currentDate = today;
26176
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
26343
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
26177
26344
  cleanOldLogs();
26178
26345
  }
26179
26346
  }
@@ -26187,7 +26354,7 @@ function cleanOldLogs() {
26187
26354
  const dateMatch = file2.match(/daemon-(\d{4}-\d{2}-\d{2})/);
26188
26355
  if (dateMatch && dateMatch[1] < cutoffStr) {
26189
26356
  try {
26190
- fs2.unlinkSync(path8.join(LOG_DIR, file2));
26357
+ fs2.unlinkSync(path9.join(LOG_DIR, file2));
26191
26358
  } catch {
26192
26359
  }
26193
26360
  }
@@ -26362,7 +26529,235 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
26362
26529
  });
26363
26530
  return true;
26364
26531
  }
26365
- 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) {
26366
26761
  const mesh = getMeshWithCache(components, meshId);
26367
26762
  if (!mesh) return;
26368
26763
  const cliInstances = components.instanceManager.getByCategory("cli");
@@ -26373,9 +26768,7 @@ function triggerMeshQueue(components, meshId) {
26373
26768
  if (instMeshId !== meshId) continue;
26374
26769
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
26375
26770
  if (!nodeId) continue;
26376
- const status = readNonEmptyString(state.status).toLowerCase();
26377
- if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
26378
- if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
26771
+ if (!isIdleSessionState(state)) continue;
26379
26772
  const sessionId = state.instanceId;
26380
26773
  const providerType = state.type || readNonEmptyString(settings.providerType);
26381
26774
  if (providerType) {
@@ -26391,6 +26784,7 @@ function triggerMeshQueue(components, meshId) {
26391
26784
  }
26392
26785
  }
26393
26786
  }
26787
+ await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
26394
26788
  }
26395
26789
  function buildMeshSystemMessage(args) {
26396
26790
  const metadata = formatCompletionMetadata(args.metadataEvent);
@@ -26898,7 +27292,7 @@ function findBinary(name) {
26898
27292
  const isWin = os9.platform() === "win32";
26899
27293
  try {
26900
27294
  const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
26901
- return (0, import_child_process2.execSync)(cmd, {
27295
+ return (0, import_child_process3.execSync)(cmd, {
26902
27296
  encoding: "utf-8",
26903
27297
  timeout: 5e3,
26904
27298
  stdio: ["pipe", "pipe", "pipe"],
@@ -27273,7 +27667,7 @@ async function validateWorkspace(workspace) {
27273
27667
  cwd: normalizedWorkspace
27274
27668
  });
27275
27669
  }
27276
- await (0, import_promises5.access)(normalizedWorkspace, import_fs8.constants.R_OK);
27670
+ await (0, import_promises5.access)(normalizedWorkspace, import_fs9.constants.R_OK);
27277
27671
  } catch (error48) {
27278
27672
  if (error48 instanceof GitCommandError) throw error48;
27279
27673
  throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
@@ -28786,6 +29180,86 @@ async function syncMeshLedger(meshId, transport) {
28786
29180
  appendRemoteLedgerEntries2(meshId, res.missingEntries);
28787
29181
  }
28788
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
+ }
28789
29263
  function isPlainObject22(value) {
28790
29264
  return !!value && typeof value === "object" && !Array.isArray(value);
28791
29265
  }
@@ -28826,11 +29300,11 @@ function normalizeState(raw) {
28826
29300
  }
28827
29301
  function loadState() {
28828
29302
  const statePath = getStatePath();
28829
- if (!(0, import_fs9.existsSync)(statePath)) {
29303
+ if (!(0, import_fs10.existsSync)(statePath)) {
28830
29304
  return { ...DEFAULT_STATE };
28831
29305
  }
28832
29306
  try {
28833
- const raw = (0, import_fs9.readFileSync)(statePath, "utf-8");
29307
+ const raw = (0, import_fs10.readFileSync)(statePath, "utf-8");
28834
29308
  return normalizeState(JSON.parse(raw));
28835
29309
  } catch {
28836
29310
  return { ...DEFAULT_STATE };
@@ -28839,7 +29313,7 @@ function loadState() {
28839
29313
  function saveState(state) {
28840
29314
  const statePath = getStatePath();
28841
29315
  const normalized = normalizeState(state);
28842
- (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 });
28843
29317
  }
28844
29318
  function resetState() {
28845
29319
  saveState({ ...DEFAULT_STATE });
@@ -28860,13 +29334,13 @@ function getMergedDefinitions() {
28860
29334
  function findCliCommand(command) {
28861
29335
  const trimmed = String(command || "").trim();
28862
29336
  if (!trimmed) return null;
28863
- if (path9.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
28864
- const candidate = trimmed.startsWith("~") ? path9.join((0, import_os3.homedir)(), trimmed.slice(1)) : trimmed;
28865
- const resolved = path9.isAbsolute(candidate) ? candidate : path9.resolve(candidate);
28866
- 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;
28867
29341
  }
28868
29342
  try {
28869
- const result = (0, import_child_process4.execSync)(
29343
+ const result = (0, import_child_process5.execSync)(
28870
29344
  (0, import_os3.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
28871
29345
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
28872
29346
  ).trim();
@@ -28877,7 +29351,7 @@ function findCliCommand(command) {
28877
29351
  }
28878
29352
  function getIdeVersion(cliCommand) {
28879
29353
  try {
28880
- const result = (0, import_child_process4.execSync)(`"${cliCommand}" --version`, {
29354
+ const result = (0, import_child_process5.execSync)(`"${cliCommand}" --version`, {
28881
29355
  encoding: "utf-8",
28882
29356
  timeout: 1e4,
28883
29357
  stdio: ["pipe", "pipe", "pipe"]
@@ -28890,13 +29364,13 @@ function getIdeVersion(cliCommand) {
28890
29364
  function checkPathExists(paths) {
28891
29365
  const home = (0, import_os3.homedir)();
28892
29366
  for (const p of paths) {
28893
- const normalized = p.startsWith("~") ? path9.join(home, p.slice(1)) : p;
29367
+ const normalized = p.startsWith("~") ? path10.join(home, p.slice(1)) : p;
28894
29368
  if (normalized.includes("*")) {
28895
29369
  const username = home.split(/[\\/]/).pop() || "";
28896
29370
  const resolved = normalized.replace("*", username);
28897
- if ((0, import_fs10.existsSync)(resolved)) return resolved;
29371
+ if ((0, import_fs11.existsSync)(resolved)) return resolved;
28898
29372
  } else {
28899
- if ((0, import_fs10.existsSync)(normalized)) return normalized;
29373
+ if ((0, import_fs11.existsSync)(normalized)) return normalized;
28900
29374
  }
28901
29375
  }
28902
29376
  return null;
@@ -28910,7 +29384,7 @@ async function detectIDEs(providerLoader) {
28910
29384
  let resolvedCli = cliPath;
28911
29385
  if (!resolvedCli && appPath && os222 === "darwin") {
28912
29386
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
28913
- if ((0, import_fs10.existsSync)(bundledCli)) resolvedCli = bundledCli;
29387
+ if ((0, import_fs11.existsSync)(bundledCli)) resolvedCli = bundledCli;
28914
29388
  }
28915
29389
  if (!resolvedCli && appPath && os222 === "win32") {
28916
29390
  const { dirname: dirname92 } = await import("path");
@@ -28923,7 +29397,7 @@ async function detectIDEs(providerLoader) {
28923
29397
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
28924
29398
  ];
28925
29399
  for (const c of candidates) {
28926
- if ((0, import_fs10.existsSync)(c)) {
29400
+ if ((0, import_fs11.existsSync)(c)) {
28927
29401
  resolvedCli = c;
28928
29402
  break;
28929
29403
  }
@@ -28944,129 +29418,6 @@ async function detectIDEs(providerLoader) {
28944
29418
  }
28945
29419
  return results;
28946
29420
  }
28947
- function parseVersion(raw) {
28948
- const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
28949
- return match ? match[1] : raw.split("\n")[0].slice(0, 100);
28950
- }
28951
- function shellQuote(value) {
28952
- if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
28953
- return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
28954
- }
28955
- function expandHome(value) {
28956
- const trimmed = value.trim();
28957
- if (!trimmed.startsWith("~")) return trimmed;
28958
- return path10.join(os32.homedir(), trimmed.slice(1));
28959
- }
28960
- function isExplicitCommandPath(command) {
28961
- const trimmed = command.trim();
28962
- return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
28963
- }
28964
- function resolveCommandPath(command) {
28965
- const trimmed = command.trim();
28966
- if (!trimmed) return null;
28967
- if (isExplicitCommandPath(trimmed)) {
28968
- const expanded = expandHome(trimmed);
28969
- const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
28970
- return (0, import_fs11.existsSync)(candidate) ? candidate : null;
28971
- }
28972
- return null;
28973
- }
28974
- function execAsync(cmd, timeoutMs = 5e3) {
28975
- return new Promise((resolve162) => {
28976
- const child = (0, import_child_process5.exec)(cmd, {
28977
- encoding: "utf-8",
28978
- timeout: timeoutMs,
28979
- ...process.platform === "win32" ? { windowsHide: true } : {}
28980
- }, (err, stdout) => {
28981
- if (err || !stdout?.trim()) {
28982
- resolve162(null);
28983
- } else {
28984
- resolve162(stdout.trim());
28985
- }
28986
- });
28987
- child.on("error", () => resolve162(null));
28988
- });
28989
- }
28990
- async function detectCLIs(providerLoader, options) {
28991
- const platform10 = os32.platform();
28992
- const whichCmd = platform10 === "win32" ? "where" : "which";
28993
- const includeVersion = options?.includeVersion !== false;
28994
- const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
28995
- const results = await Promise.all(
28996
- cliList.map(async (cli) => {
28997
- try {
28998
- const explicitPath = resolveCommandPath(cli.command);
28999
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
29000
- if (!pathResult) return { ...cli, installed: false };
29001
- const firstPath = explicitPath || pathResult.split("\n")[0];
29002
- let version2;
29003
- if (includeVersion) {
29004
- const versionCommands = [
29005
- `"${firstPath}" --version`,
29006
- `"${firstPath}" -V`,
29007
- `"${firstPath}" -v`,
29008
- cli.versionCommand
29009
- ].filter((v) => !!v);
29010
- try {
29011
- for (const versionCommand of versionCommands) {
29012
- const versionResult = await execAsync(versionCommand, 3e3);
29013
- if (versionResult) {
29014
- version2 = parseVersion(versionResult);
29015
- break;
29016
- }
29017
- }
29018
- } catch {
29019
- }
29020
- }
29021
- return { ...cli, installed: true, version: version2, path: firstPath };
29022
- } catch {
29023
- return { ...cli, installed: false };
29024
- }
29025
- })
29026
- );
29027
- return results;
29028
- }
29029
- async function detectCLI(cliId, providerLoader, options) {
29030
- const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
29031
- if (providerLoader) {
29032
- const cliList = providerLoader.getCliDetectionList();
29033
- const target = cliList.find((c) => c.id === resolvedId);
29034
- if (target) {
29035
- const platform10 = os32.platform();
29036
- const whichCmd = platform10 === "win32" ? "where" : "which";
29037
- try {
29038
- const explicitPath = resolveCommandPath(target.command);
29039
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
29040
- if (!pathResult) return null;
29041
- const firstPath = explicitPath || pathResult.split("\n")[0];
29042
- let version2;
29043
- if (options?.includeVersion !== false) {
29044
- const versionCommands = [
29045
- `"${firstPath}" --version`,
29046
- `"${firstPath}" -V`,
29047
- `"${firstPath}" -v`,
29048
- target.versionCommand
29049
- ].filter((v) => !!v);
29050
- try {
29051
- for (const versionCommand of versionCommands) {
29052
- const versionResult = await execAsync(versionCommand, 3e3);
29053
- if (versionResult) {
29054
- version2 = parseVersion(versionResult);
29055
- break;
29056
- }
29057
- }
29058
- } catch {
29059
- }
29060
- }
29061
- return { ...target, installed: true, version: version2, path: firstPath };
29062
- } catch {
29063
- return null;
29064
- }
29065
- }
29066
- }
29067
- const all = await detectCLIs(providerLoader, options);
29068
- return all.find((c) => c.id === resolvedId && c.installed) || null;
29069
- }
29070
29421
  function parseDarwinAvailableBytes(totalMem) {
29071
29422
  if (os42.platform() !== "darwin") return null;
29072
29423
  try {
@@ -36588,6 +36939,204 @@ async function resolveProviderTypeFromPriority(args) {
36588
36939
  }
36589
36940
  return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
36590
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
+ }
36591
37140
  function loadYamlModule() {
36592
37141
  return js_yaml_exports;
36593
37142
  }
@@ -41135,7 +41684,7 @@ async function shutdownDaemonComponents(components) {
41135
41684
  }
41136
41685
  cdpManagers.clear();
41137
41686
  }
41138
- 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;
41139
41688
  var init_dist2 = __esm({
41140
41689
  "../daemon-core/dist/index.mjs"() {
41141
41690
  "use strict";
@@ -41158,20 +41707,24 @@ var init_dist2 = __esm({
41158
41707
  import_fs7 = require("fs");
41159
41708
  import_path4 = require("path");
41160
41709
  import_crypto5 = require("crypto");
41161
- fs2 = __toESM(require("fs"), 1);
41162
- path8 = __toESM(require("path"), 1);
41710
+ import_child_process2 = require("child_process");
41163
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);
41164
41717
  init_dist();
41165
41718
  os8 = __toESM(require("os"), 1);
41166
41719
  os9 = __toESM(require("os"), 1);
41167
41720
  path14 = __toESM(require("path"), 1);
41168
- import_child_process2 = require("child_process");
41721
+ import_child_process3 = require("child_process");
41169
41722
  os10 = __toESM(require("os"), 1);
41170
41723
  path15 = __toESM(require("path"), 1);
41171
41724
  init_dist();
41172
41725
  os11 = __toESM(require("os"), 1);
41173
- import_child_process3 = require("child_process");
41174
- import_fs8 = require("fs");
41726
+ import_child_process4 = require("child_process");
41727
+ import_fs9 = require("fs");
41175
41728
  import_promises5 = require("fs/promises");
41176
41729
  path = __toESM(require("path"), 1);
41177
41730
  import_util4 = require("util");
@@ -41184,16 +41737,12 @@ var init_dist2 = __esm({
41184
41737
  import_crypto6 = require("crypto");
41185
41738
  path6 = __toESM(require("path"), 1);
41186
41739
  path7 = __toESM(require("path"), 1);
41187
- import_fs9 = require("fs");
41188
- import_path5 = require("path");
41189
- import_child_process4 = require("child_process");
41190
41740
  import_fs10 = require("fs");
41191
- import_os3 = require("os");
41192
- path9 = __toESM(require("path"), 1);
41741
+ import_path5 = require("path");
41193
41742
  import_child_process5 = require("child_process");
41194
- os32 = __toESM(require("os"), 1);
41195
- path10 = __toESM(require("path"), 1);
41196
41743
  import_fs11 = require("fs");
41744
+ import_os3 = require("os");
41745
+ path10 = __toESM(require("path"), 1);
41197
41746
  os42 = __toESM(require("os"), 1);
41198
41747
  import_child_process6 = require("child_process");
41199
41748
  init_wrapper();
@@ -41326,6 +41875,7 @@ var init_dist2 = __esm({
41326
41875
  WORKTREE_DIR_NAME = ".adhdev-worktrees";
41327
41876
  GIT_TIMEOUT_MS = 3e4;
41328
41877
  GIT_MAX_BUFFER = 4 * 1024 * 1024;
41878
+ SUBMODULE_WORKTREE_REMOVE_RE = /working trees containing submodules cannot be moved or removed/i;
41329
41879
  }
41330
41880
  });
41331
41881
  config_exports = {};
@@ -41405,18 +41955,24 @@ var init_dist2 = __esm({
41405
41955
 
41406
41956
  | Tool | Purpose |
41407
41957
  |------|---------|
41408
- | \`mesh_status\` | Check all nodes' health, git state, and active sessions |
41958
+ | \`mesh_status\` | Check all nodes' health, git state, active sessions, and branch convergence |
41409
41959
  | \`mesh_list_nodes\` | List nodes with workspace paths |
41960
+ | \`mesh_enqueue_task\` | Add a task to the pull-based work queue; idle nodes auto-claim |
41961
+ | \`mesh_view_queue\` | View queue status \u2014 pending, assigned, completed, failed, cancelled tasks |
41962
+ | \`mesh_queue_cancel\` | Cancel a queue task without deleting audit history |
41963
+ | \`mesh_queue_requeue\` | Return a task to pending for retry; clears stale session targets |
41964
+ | \`mesh_send_task\` | Legacy push: enqueue a task targeted at a specific node |
41410
41965
  | \`mesh_launch_session\` | Start a new agent session on a node |
41411
- | \`mesh_send_task\` | Send a task (natural language) to a running agent |
41412
- | \`mesh_read_chat\` | Read an agent's recent messages to check progress |
41966
+ | \`mesh_read_chat\` | Read recent chat messages from a delegated agent session |
41967
+ | \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
41413
41968
  | \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
41414
41969
  | \`mesh_git_status\` | Check git status on a specific node |
41415
41970
  | \`mesh_checkpoint\` | Create a git checkpoint on a node |
41416
41971
  | \`mesh_approve\` | Approve/reject a pending agent action |
41417
41972
  | \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
41418
41973
  | \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
41419
- | \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |`;
41974
+ | \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |
41975
+ | \`mesh_cleanup_sessions\` | Manually clean up delegated session records for a node |`;
41420
41976
  TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
41421
41977
 
41422
41978
  Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
@@ -41426,9 +41982,10 @@ Before doing any coordinator work, confirm that the actual callable tool list in
41426
41982
  2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign.
41427
41983
  3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
41428
41984
  a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
41429
- b. **Node Preparation**: Call \`mesh_launch_session\` to ensure enough agent sessions are active to handle the queue. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
41985
+ b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
41430
41986
  c. **Targeted Tasks**: Use \`mesh_send_task\` only when you need to bypass the queue and force a specific node to execute a task immediately.
41431
- d. Always provide a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
41987
+ d. For the first dispatch of a new task, provide a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
41988
+ e. For a continuation of the same issue in an existing session, send a concise **delta instruction**: current verified state, the exact failed/blocked step, the newly approved action, and final reporting requirements. Do not resend the full original task or open a new chat solely to continue the same work; that wastes coordinator and worker context.
41432
41989
  4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Use \`mesh_view_queue\` to see the status of all pending, assigned, completed, and failed tasks. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal. Handle approvals via \`mesh_approve\`.
41433
41990
  5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
41434
41991
  6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
@@ -41444,7 +42001,7 @@ When a node agent stops unexpectedly, the daemon automatically enriches the syst
41444
42001
  - A recommendation: **retry**, **reassign**, or **escalate**
41445
42002
 
41446
42003
  Follow these recovery rules:
41447
- 1. **If "Retry recommended"**: Re-launch the session on the same node (\`mesh_launch_session\`), then resend the original task (\`mesh_send_task\`). The system message includes the original task text.
42004
+ 1. **If "Retry recommended"**: Check \`mesh_view_queue\` first \u2014 the daemon may have auto-requeued. If not, re-launch the session on the same node (\`mesh_launch_session\`), then resend the original task (\`mesh_send_task\`). The system message includes the original task text.
41448
42005
  2. **If "Max retries exceeded"**: Do NOT retry on the same node. Either reassign the task to a different node, or inform the user that the task requires manual intervention.
41449
42006
  3. **If no recovery context**: The stop may be intentional (normal completion). Use \`mesh_read_chat\` once to verify, then move on.
41450
42007
  4. **Always record what happened**: After handling a failure, briefly note the outcome in your report to the user.`;
@@ -41477,6 +42034,7 @@ Follow these recovery rules:
41477
42034
  enqueueTask: () => enqueueTask,
41478
42035
  getMeshQueueStats: () => getMeshQueueStats,
41479
42036
  getQueue: () => getQueue,
42037
+ recordTaskAutoLaunch: () => recordTaskAutoLaunch,
41480
42038
  requeueTask: () => requeueTask,
41481
42039
  updateSessionTaskStatus: () => updateSessionTaskStatus,
41482
42040
  updateTaskStatus: () => updateTaskStatus
@@ -41487,13 +42045,18 @@ Follow these recovery rules:
41487
42045
  init_mesh_ledger();
41488
42046
  }
41489
42047
  });
42048
+ init_cli_detector = __esm2({
42049
+ "src/detection/cli-detector.ts"() {
42050
+ "use strict";
42051
+ }
42052
+ });
41490
42053
  init_logger = __esm2({
41491
42054
  "src/logging/logger.ts"() {
41492
42055
  "use strict";
41493
42056
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
41494
42057
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
41495
42058
  currentLevel = "info";
41496
- 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");
41497
42060
  MAX_LOG_SIZE = 5 * 1024 * 1024;
41498
42061
  MAX_LOG_DAYS = 7;
41499
42062
  try {
@@ -41501,16 +42064,16 @@ Follow these recovery rules:
41501
42064
  } catch {
41502
42065
  }
41503
42066
  currentDate = getDateStr();
41504
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
42067
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
41505
42068
  cleanOldLogs();
41506
42069
  try {
41507
- const oldLog = path8.join(LOG_DIR, "daemon.log");
42070
+ const oldLog = path9.join(LOG_DIR, "daemon.log");
41508
42071
  if (fs2.existsSync(oldLog)) {
41509
42072
  const stat22 = fs2.statSync(oldLog);
41510
42073
  const oldDate = stat22.mtime.toISOString().slice(0, 10);
41511
- fs2.renameSync(oldLog, path8.join(LOG_DIR, `daemon-${oldDate}.log`));
42074
+ fs2.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
41512
42075
  }
41513
- const oldLogBackup = path8.join(LOG_DIR, "daemon.log.old");
42076
+ const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
41514
42077
  if (fs2.existsSync(oldLogBackup)) {
41515
42078
  fs2.unlinkSync(oldLogBackup);
41516
42079
  }
@@ -41542,7 +42105,7 @@ Follow these recovery rules:
41542
42105
  }
41543
42106
  };
41544
42107
  interceptorInstalled = false;
41545
- LOG_PATH = path8.join(LOG_DIR, `daemon-${getDateStr()}.log`);
42108
+ LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
41546
42109
  }
41547
42110
  });
41548
42111
  mesh_events_exports = {};
@@ -41556,7 +42119,9 @@ Follow these recovery rules:
41556
42119
  init_mesh_events = __esm2({
41557
42120
  "src/mesh/mesh-events.ts"() {
41558
42121
  "use strict";
42122
+ init_config();
41559
42123
  init_mesh_config();
42124
+ init_cli_detector();
41560
42125
  init_logger();
41561
42126
  init_mesh_ledger();
41562
42127
  init_mesh_work_queue();
@@ -41577,6 +42142,9 @@ Follow these recovery rules:
41577
42142
  "agent:stopped": "task_failed",
41578
42143
  "monitor:long_generating": "task_stalled"
41579
42144
  };
42145
+ autoLaunchInProgress = /* @__PURE__ */ new Set();
42146
+ autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
42147
+ AUTO_LAUNCH_COOLDOWN_MS = 5e3;
41580
42148
  }
41581
42149
  });
41582
42150
  init_debug_config = __esm2({
@@ -44070,7 +44638,7 @@ ${lastSnapshot}`;
44070
44638
  }
44071
44639
  });
44072
44640
  init_repo_mesh_types();
44073
- execFileAsync = (0, import_util4.promisify)(import_child_process3.execFile);
44641
+ execFileAsync = (0, import_util4.promisify)(import_child_process4.execFile);
44074
44642
  DEFAULT_TIMEOUT_MS = 5e3;
44075
44643
  DEFAULT_MAX_BUFFER = 1024 * 1024;
44076
44644
  GitCommandError = class extends Error {
@@ -44329,6 +44897,34 @@ ${lastSnapshot}`;
44329
44897
  init_mesh_ledger();
44330
44898
  init_mesh_work_queue();
44331
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
+ };
44332
44928
  init_config();
44333
44929
  DEFAULT_STATE = {
44334
44930
  recentActivity: [],
@@ -44340,6 +44936,7 @@ ${lastSnapshot}`;
44340
44936
  };
44341
44937
  BUILTIN_IDE_DEFINITIONS = [];
44342
44938
  registeredIDEs = /* @__PURE__ */ new Map();
44939
+ init_cli_detector();
44343
44940
  LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
44344
44941
  DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
44345
44942
  "generating",
@@ -48071,6 +48668,7 @@ ${effect.notification.body || ""}`.trim();
48071
48668
  }
48072
48669
  };
48073
48670
  init_provider_cli_adapter();
48671
+ init_cli_detector();
48074
48672
  init_config();
48075
48673
  init_provider_cli_adapter();
48076
48674
  init_logger();
@@ -52422,6 +53020,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
52422
53020
  };
52423
53021
  _providerLoader = null;
52424
53022
  init_config();
53023
+ init_cli_detector();
52425
53024
  init_logger();
52426
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");
52427
53026
  MAX_FILE_SIZE = 5 * 1024 * 1024;
@@ -52470,6 +53069,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
52470
53069
  stable: "https://api.adhf.dev",
52471
53070
  preview: "https://api-preview.adhf.dev"
52472
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;
52473
53077
  CHAT_COMMANDS = [
52474
53078
  "send_chat",
52475
53079
  "new_chat",
@@ -52602,20 +53206,98 @@ Run 'adhdev doctor' for detailed diagnostics.`
52602
53206
  recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
52603
53207
  };
52604
53208
  }
53209
+ const forceFallbackConvergence = await this.getWorktreeForceCleanupConvergence({
53210
+ repoRoot,
53211
+ workspace,
53212
+ node: args.node
53213
+ });
52605
53214
  try {
52606
- const result = await removeWorktree2(repoRoot, workspace, { requireClean: true });
52607
- 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
+ };
52608
53230
  } catch (e) {
52609
53231
  const message = String(e?.message || e || "worktree cleanup failed");
52610
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;
52611
53234
  return {
52612
53235
  success: false,
52613
- code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
52614
- error: message,
52615
- 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 } : {}
52616
53240
  };
52617
53241
  }
52618
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
+ }
52619
53301
  isCompletedHostedSession(record2) {
52620
53302
  return record2?.lifecycle === "stopped" || record2?.lifecycle === "failed" || record2?.lifecycle === "interrupted";
52621
53303
  }
@@ -53575,10 +54257,61 @@ Run 'adhdev doctor' for detailed diagnostics.`
53575
54257
  if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
53576
54258
  const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
53577
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
+ }
53578
54299
  try {
53579
54300
  await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
53580
54301
  } catch (e) {
53581
- 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
+ };
53582
54315
  }
53583
54316
  const removeResult = await this.execute("remove_mesh_node", {
53584
54317
  meshId,
@@ -53591,11 +54324,27 @@ Run 'adhdev doctor' for detailed diagnostics.`
53591
54324
  appendLedgerEntry2(meshId, {
53592
54325
  kind: "node_removed",
53593
54326
  nodeId,
53594
- payload: { refined: true, mergedBranch: branch, into: baseBranch }
54327
+ payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
53595
54328
  });
53596
54329
  } catch {
53597
54330
  }
53598
- 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
+ };
53599
54348
  } catch (e) {
53600
54349
  return { success: false, error: e.message };
53601
54350
  }
@@ -53650,7 +54399,10 @@ Run 'adhdev doctor' for detailed diagnostics.`
53650
54399
  sessionCleanupMode,
53651
54400
  workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
53652
54401
  daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
53653
- 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
53654
54406
  }
53655
54407
  });
53656
54408
  } catch {
@@ -57547,6 +58299,7 @@ data: ${JSON.stringify(msg.data)}
57547
58299
  apiKeyName: "OpenAI/Anthropic API key"
57548
58300
  }
57549
58301
  ];
58302
+ init_cli_detector();
57550
58303
  SessionRegistry = class {
57551
58304
  bySessionId = /* @__PURE__ */ new Map();
57552
58305
  byManagerKey = /* @__PURE__ */ new Map();
@@ -58032,16 +58785,38 @@ function extractLaunchPayload(value) {
58032
58785
  function classifyMeshLaunchFailure(error48) {
58033
58786
  const message = error48 instanceof Error ? error48.message : String(error48 || "launch failed");
58034
58787
  const lower = message.toLowerCase();
58035
- if (lower.includes("p2p") || lower.includes("datachannel") || lower.includes("node-datachannel")) {
58036
- 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;
58037
58791
  }
58038
58792
  if (lower.includes("cannot connect to daemon ipc") || lower.includes("daemon ipc command")) {
58039
- 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
+ };
58040
58801
  }
58041
58802
  if (lower.includes("timed out") || lower.includes("timeout")) {
58042
- 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
+ };
58043
58811
  }
58044
- 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
+ };
58045
58820
  }
58046
58821
  function buildWorktreeCleanupHint(node) {
58047
58822
  if (!node.isLocalWorktree) return void 0;
@@ -58057,10 +58832,13 @@ function buildRecoverableLaunchFailure(ctx, node, providerType, error48) {
58057
58832
  const cleanup = buildWorktreeCleanupHint(node);
58058
58833
  return {
58059
58834
  success: false,
58060
- recoverable: true,
58835
+ recoverable: classified.recoverable,
58061
58836
  code: classified.code,
58062
58837
  reason: classified.reason,
58063
58838
  transport: classified.transport,
58839
+ retryRecommended: classified.retryRecommended,
58840
+ nextAction: classified.nextAction,
58841
+ ...classified.noFallbackReason ? { noFallbackReason: classified.noFallbackReason } : {},
58064
58842
  error: message,
58065
58843
  meshId: ctx.mesh.id,
58066
58844
  nodeId: node.id,
@@ -58107,6 +58885,18 @@ function getLatestActiveLaunchFailure(meshId, nodeId) {
58107
58885
  }
58108
58886
  return null;
58109
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
+ }
58110
58900
  async function ipcDispatchToRemoteAgent(ctx, node, args) {
58111
58901
  const transport = ctx.transport;
58112
58902
  const daemonId = node.daemonId;
@@ -58142,11 +58932,32 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
58142
58932
  });
58143
58933
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
58144
58934
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
58145
- 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
+ };
58146
58948
  }
58147
58949
  return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
58148
58950
  } catch (e) {
58149
- 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
+ };
58150
58961
  }
58151
58962
  }
58152
58963
  function resolveCoordinatorNode(ctx) {
@@ -58391,8 +59202,7 @@ async function commandForNode(ctx, node, command, args = {}) {
58391
59202
  throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
58392
59203
  }
58393
59204
  function isP2pTransportUnavailableError(error48) {
58394
- const message = error48 instanceof Error ? error48.message : String(error48 || "");
58395
- 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);
58396
59206
  }
58397
59207
  function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
58398
59208
  return {
@@ -58985,7 +59795,13 @@ async function meshSendTask(ctx, args) {
58985
59795
  }
58986
59796
  return JSON.stringify({ success: true, nodeId: args.node_id, taskId: task.id, status: task.status });
58987
59797
  } catch (e) {
58988
- 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);
58989
59805
  }
58990
59806
  }
58991
59807
  async function meshReadChat(ctx, args) {