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

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
  }
@@ -25473,7 +25503,8 @@ function ensureMachineId(config2) {
25473
25503
  };
25474
25504
  }
25475
25505
  function getConfigDir() {
25476
- const dir = (0, import_path.join)((0, import_os2.homedir)(), ".adhdev");
25506
+ const override = process.env.ADHDEV_CONFIG_DIR;
25507
+ const dir = override && override.trim() ? override.trim() : (0, import_path.join)((0, import_os2.homedir)(), ".adhdev");
25477
25508
  if (!(0, import_fs4.existsSync)(dir)) {
25478
25509
  (0, import_fs4.mkdirSync)(dir, { recursive: true });
25479
25510
  }
@@ -26092,6 +26123,19 @@ function updateTaskStatus(meshId, taskId, status) {
26092
26123
  writeQueue(meshId, queue);
26093
26124
  return queue[idx];
26094
26125
  }
26126
+ function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
26127
+ const queue = readQueue(meshId);
26128
+ const idx = queue.findIndex((q) => q.id === taskId);
26129
+ if (idx === -1) return null;
26130
+ const now = (/* @__PURE__ */ new Date()).toISOString();
26131
+ queue[idx].autoLaunch = {
26132
+ ...autoLaunch,
26133
+ updatedAt: now
26134
+ };
26135
+ queue[idx].updatedAt = now;
26136
+ writeQueue(meshId, queue);
26137
+ return queue[idx];
26138
+ }
26095
26139
  function cancelTask(meshId, taskId, opts) {
26096
26140
  const queue = readQueue(meshId);
26097
26141
  const idx = queue.findIndex((q) => q.id === taskId);
@@ -26140,12 +26184,20 @@ function updateSessionTaskStatus(meshId, sessionId, status) {
26140
26184
  }
26141
26185
  function getMeshQueueStats(meshId) {
26142
26186
  const queue = readQueue(meshId);
26187
+ const pending = queue.filter((q) => q.status === "pending").length;
26188
+ const assigned = queue.filter((q) => q.status === "assigned").length;
26189
+ const completed = queue.filter((q) => q.status === "completed").length;
26190
+ const failed = queue.filter((q) => q.status === "failed").length;
26191
+ const cancelled = queue.filter((q) => q.status === "cancelled").length;
26143
26192
  return {
26144
- pending: queue.filter((q) => q.status === "pending").length,
26145
- assigned: queue.filter((q) => q.status === "assigned").length,
26146
- completed: queue.filter((q) => q.status === "completed").length,
26147
- failed: queue.filter((q) => q.status === "failed").length,
26148
- cancelled: queue.filter((q) => q.status === "cancelled").length,
26193
+ total: queue.length,
26194
+ active: pending + assigned,
26195
+ historical: completed + failed + cancelled,
26196
+ pending,
26197
+ assigned,
26198
+ completed,
26199
+ failed,
26200
+ cancelled,
26149
26201
  activeAssignments: queue.filter((q) => q.status === "assigned").map((q) => ({
26150
26202
  id: q.id,
26151
26203
  nodeId: q.assignedNodeId,
@@ -26154,6 +26206,129 @@ function getMeshQueueStats(meshId) {
26154
26206
  }))
26155
26207
  };
26156
26208
  }
26209
+ function parseVersion(raw) {
26210
+ const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
26211
+ return match ? match[1] : raw.split("\n")[0].slice(0, 100);
26212
+ }
26213
+ function shellQuote(value) {
26214
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
26215
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
26216
+ }
26217
+ function expandHome(value) {
26218
+ const trimmed = value.trim();
26219
+ if (!trimmed.startsWith("~")) return trimmed;
26220
+ return path8.join(os22.homedir(), trimmed.slice(1));
26221
+ }
26222
+ function isExplicitCommandPath(command) {
26223
+ const trimmed = command.trim();
26224
+ return path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
26225
+ }
26226
+ function resolveCommandPath(command) {
26227
+ const trimmed = command.trim();
26228
+ if (!trimmed) return null;
26229
+ if (isExplicitCommandPath(trimmed)) {
26230
+ const expanded = expandHome(trimmed);
26231
+ const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
26232
+ return (0, import_fs8.existsSync)(candidate) ? candidate : null;
26233
+ }
26234
+ return null;
26235
+ }
26236
+ function execAsync(cmd, timeoutMs = 5e3) {
26237
+ return new Promise((resolve162) => {
26238
+ const child = (0, import_child_process2.exec)(cmd, {
26239
+ encoding: "utf-8",
26240
+ timeout: timeoutMs,
26241
+ ...process.platform === "win32" ? { windowsHide: true } : {}
26242
+ }, (err, stdout) => {
26243
+ if (err || !stdout?.trim()) {
26244
+ resolve162(null);
26245
+ } else {
26246
+ resolve162(stdout.trim());
26247
+ }
26248
+ });
26249
+ child.on("error", () => resolve162(null));
26250
+ });
26251
+ }
26252
+ async function detectCLIs(providerLoader, options) {
26253
+ const platform10 = os22.platform();
26254
+ const whichCmd = platform10 === "win32" ? "where" : "which";
26255
+ const includeVersion = options?.includeVersion !== false;
26256
+ const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
26257
+ const results = await Promise.all(
26258
+ cliList.map(async (cli) => {
26259
+ try {
26260
+ const explicitPath = resolveCommandPath(cli.command);
26261
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
26262
+ if (!pathResult) return { ...cli, installed: false };
26263
+ const firstPath = explicitPath || pathResult.split("\n")[0];
26264
+ let version2;
26265
+ if (includeVersion) {
26266
+ const versionCommands = [
26267
+ `"${firstPath}" --version`,
26268
+ `"${firstPath}" -V`,
26269
+ `"${firstPath}" -v`,
26270
+ cli.versionCommand
26271
+ ].filter((v) => !!v);
26272
+ try {
26273
+ for (const versionCommand of versionCommands) {
26274
+ const versionResult = await execAsync(versionCommand, 3e3);
26275
+ if (versionResult) {
26276
+ version2 = parseVersion(versionResult);
26277
+ break;
26278
+ }
26279
+ }
26280
+ } catch {
26281
+ }
26282
+ }
26283
+ return { ...cli, installed: true, version: version2, path: firstPath };
26284
+ } catch {
26285
+ return { ...cli, installed: false };
26286
+ }
26287
+ })
26288
+ );
26289
+ return results;
26290
+ }
26291
+ async function detectCLI(cliId, providerLoader, options) {
26292
+ const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
26293
+ if (providerLoader) {
26294
+ const cliList = providerLoader.getCliDetectionList();
26295
+ const target = cliList.find((c) => c.id === resolvedId);
26296
+ if (target) {
26297
+ const platform10 = os22.platform();
26298
+ const whichCmd = platform10 === "win32" ? "where" : "which";
26299
+ try {
26300
+ const explicitPath = resolveCommandPath(target.command);
26301
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
26302
+ if (!pathResult) return null;
26303
+ const firstPath = explicitPath || pathResult.split("\n")[0];
26304
+ let version2;
26305
+ if (options?.includeVersion !== false) {
26306
+ const versionCommands = [
26307
+ `"${firstPath}" --version`,
26308
+ `"${firstPath}" -V`,
26309
+ `"${firstPath}" -v`,
26310
+ target.versionCommand
26311
+ ].filter((v) => !!v);
26312
+ try {
26313
+ for (const versionCommand of versionCommands) {
26314
+ const versionResult = await execAsync(versionCommand, 3e3);
26315
+ if (versionResult) {
26316
+ version2 = parseVersion(versionResult);
26317
+ break;
26318
+ }
26319
+ }
26320
+ } catch {
26321
+ }
26322
+ }
26323
+ return { ...target, installed: true, version: version2, path: firstPath };
26324
+ } catch {
26325
+ return null;
26326
+ }
26327
+ }
26328
+ }
26329
+ const all = await detectCLIs(providerLoader, options);
26330
+ return all.find((c) => c.id === resolvedId && c.installed) || null;
26331
+ }
26157
26332
  function setLogLevel(level) {
26158
26333
  currentLevel = level;
26159
26334
  daemonLog("Logger", `Log level set to: ${level}`, "info");
@@ -26168,13 +26343,13 @@ function getDaemonLogDir() {
26168
26343
  return LOG_DIR;
26169
26344
  }
26170
26345
  function getCurrentDaemonLogPath(date5 = /* @__PURE__ */ new Date()) {
26171
- return path8.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
26346
+ return path9.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
26172
26347
  }
26173
26348
  function checkDateRotation() {
26174
26349
  const today = getDateStr();
26175
26350
  if (today !== currentDate) {
26176
26351
  currentDate = today;
26177
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
26352
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
26178
26353
  cleanOldLogs();
26179
26354
  }
26180
26355
  }
@@ -26188,7 +26363,7 @@ function cleanOldLogs() {
26188
26363
  const dateMatch = file2.match(/daemon-(\d{4}-\d{2}-\d{2})/);
26189
26364
  if (dateMatch && dateMatch[1] < cutoffStr) {
26190
26365
  try {
26191
- fs2.unlinkSync(path8.join(LOG_DIR, file2));
26366
+ fs2.unlinkSync(path9.join(LOG_DIR, file2));
26192
26367
  } catch {
26193
26368
  }
26194
26369
  }
@@ -26363,7 +26538,235 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
26363
26538
  });
26364
26539
  return true;
26365
26540
  }
26366
- function triggerMeshQueue(components, meshId) {
26541
+ function normalizeProviderPriority(policy) {
26542
+ const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
26543
+ if (!Array.isArray(raw)) return [];
26544
+ const seen = /* @__PURE__ */ new Set();
26545
+ return raw.map((type2) => typeof type2 === "string" ? type2.trim() : "").filter(Boolean).filter((type2) => {
26546
+ if (seen.has(type2)) return false;
26547
+ seen.add(type2);
26548
+ return true;
26549
+ });
26550
+ }
26551
+ function isTerminalSessionStatus(status) {
26552
+ return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
26553
+ }
26554
+ function isIdleSessionState(state) {
26555
+ const status = readNonEmptyString(state?.status).toLowerCase();
26556
+ if (isTerminalSessionStatus(status)) return false;
26557
+ return status === "idle" || state?.activeChat?.status === "waiting_input";
26558
+ }
26559
+ function isDirtyNode(node) {
26560
+ return node?.health === "dirty" || node?.git?.dirty === true;
26561
+ }
26562
+ function isLaunchableNode(node) {
26563
+ if (!node || node.status === "disabled" || node.status === "removed") return false;
26564
+ const health = readNonEmptyString(node.health).toLowerCase();
26565
+ if (!health) return true;
26566
+ return health === "online" || health === "unknown";
26567
+ }
26568
+ function localAutoLaunchSkipReason(node) {
26569
+ const daemonId = readNonEmptyString(node?.daemonId);
26570
+ const machineId = readNonEmptyString(node?.machineId);
26571
+ const appConfig = loadConfig();
26572
+ const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
26573
+ const cloudDaemonId = localMachineId ? `daemon_${localMachineId}` : "";
26574
+ const standaloneDaemonId = localMachineId ? `standalone_${localMachineId}` : "";
26575
+ const daemonMatchesLocal = !daemonId || daemonId === cloudDaemonId || daemonId === standaloneDaemonId;
26576
+ const machineMatchesLocal = !machineId || localMachineId && machineId === localMachineId;
26577
+ if (node?.isLocalWorktree === true) {
26578
+ return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
26579
+ }
26580
+ if (daemonId || machineId) {
26581
+ return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
26582
+ }
26583
+ return null;
26584
+ }
26585
+ function activeAssignedCount(meshId) {
26586
+ return getQueue(meshId, { status: ["assigned"] }).length;
26587
+ }
26588
+ function nodeHasActiveAssignment(meshId, nodeId) {
26589
+ return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
26590
+ }
26591
+ function liveSessionCountForNode(components, meshId, nodeId) {
26592
+ return components.instanceManager.getByCategory("cli").filter((inst) => {
26593
+ const state = inst.getState();
26594
+ const settings = state.settings || {};
26595
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
26596
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
26597
+ if (instNodeId !== nodeId) return false;
26598
+ const status = readNonEmptyString(state.status).toLowerCase();
26599
+ return !isTerminalSessionStatus(status);
26600
+ }).length;
26601
+ }
26602
+ function recordAutoLaunchEvent(meshId, args) {
26603
+ try {
26604
+ appendLedgerEntry(meshId, {
26605
+ kind: "session_auto_launch",
26606
+ nodeId: args.nodeId,
26607
+ sessionId: args.sessionId,
26608
+ providerType: args.providerType,
26609
+ payload: {
26610
+ phase: args.phase,
26611
+ taskId: args.taskId,
26612
+ reason: args.reason,
26613
+ error: args.error
26614
+ }
26615
+ });
26616
+ } catch (e) {
26617
+ LOG.warn("MeshQueue", `Failed to record auto-launch ledger event: ${e?.message || e}`);
26618
+ }
26619
+ }
26620
+ function markAutoLaunch(meshId, taskId, args) {
26621
+ recordTaskAutoLaunch(meshId, taskId, {
26622
+ status: args.status,
26623
+ reason: args.reason || args.error,
26624
+ nodeId: args.nodeId,
26625
+ providerType: args.providerType,
26626
+ sessionId: args.sessionId
26627
+ });
26628
+ recordAutoLaunchEvent(meshId, {
26629
+ phase: args.status,
26630
+ taskId,
26631
+ nodeId: args.nodeId,
26632
+ providerType: args.providerType,
26633
+ sessionId: args.sessionId,
26634
+ reason: args.reason,
26635
+ error: args.error
26636
+ });
26637
+ }
26638
+ async function resolveUsableProvider(components, nodeId, node) {
26639
+ const providerPriority = normalizeProviderPriority(node?.policy);
26640
+ if (!providerPriority.length) return { reason: "missing_provider_priority" };
26641
+ const providerLoader = components.providerLoader;
26642
+ if (!providerLoader) return { reason: "provider_loader_unavailable" };
26643
+ const failed = [];
26644
+ for (const requestedType of providerPriority) {
26645
+ const normalizedType = typeof providerLoader.resolveAlias === "function" ? providerLoader.resolveAlias(requestedType) : requestedType;
26646
+ if (typeof providerLoader.isMachineProviderEnabled === "function" && !providerLoader.isMachineProviderEnabled(normalizedType)) {
26647
+ failed.push(`${requestedType}: disabled`);
26648
+ continue;
26649
+ }
26650
+ let detected;
26651
+ try {
26652
+ detected = await detectCLI(normalizedType, providerLoader, { includeVersion: false });
26653
+ } catch (e) {
26654
+ failed.push(`${requestedType}: detect failed: ${e?.message || e}`);
26655
+ continue;
26656
+ }
26657
+ if (typeof providerLoader.setCliDetectionResults === "function") {
26658
+ providerLoader.setCliDetectionResults([{
26659
+ id: normalizedType,
26660
+ installed: !!detected,
26661
+ path: detected?.path
26662
+ }], false);
26663
+ }
26664
+ components.onStatusChange?.();
26665
+ if (detected) return { providerType: normalizedType };
26666
+ failed.push(`${requestedType}: not detected`);
26667
+ }
26668
+ return { reason: `provider_priority_unusable: ${failed.join("; ") || nodeId}` };
26669
+ }
26670
+ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
26671
+ const queue = getQueue(meshId);
26672
+ const pending = queue.filter((task) => task.status === "pending");
26673
+ if (!pending.length) return false;
26674
+ const maxParallelTasks = Math.max(1, Math.floor(Number(mesh?.policy?.maxParallelTasks) || 2));
26675
+ for (const task of pending) {
26676
+ if (activeAssignedCount(meshId) >= maxParallelTasks) {
26677
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
26678
+ return false;
26679
+ }
26680
+ if (task.targetSessionId) {
26681
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
26682
+ continue;
26683
+ }
26684
+ const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => task.targetNodeId ? node?.id === task.targetNodeId : true) : [];
26685
+ if (!candidateNodes.length) {
26686
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_matching_node", nodeId: task.targetNodeId });
26687
+ continue;
26688
+ }
26689
+ for (const node of candidateNodes) {
26690
+ const nodeId = readNonEmptyString(node?.id);
26691
+ if (!nodeId) continue;
26692
+ const launchKey = `${meshId}:${nodeId}`;
26693
+ const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
26694
+ if (autoLaunchInProgress.has(launchKey)) {
26695
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_in_progress", nodeId });
26696
+ continue;
26697
+ }
26698
+ if (Date.now() < cooldownUntil) {
26699
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_cooldown", nodeId });
26700
+ continue;
26701
+ }
26702
+ if (isDirtyNode(node)) {
26703
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dirty_workspace", nodeId });
26704
+ continue;
26705
+ }
26706
+ if (!isLaunchableNode(node)) {
26707
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_not_launch_ready", nodeId });
26708
+ continue;
26709
+ }
26710
+ const localSkipReason = localAutoLaunchSkipReason(node);
26711
+ if (localSkipReason) {
26712
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: localSkipReason, nodeId });
26713
+ continue;
26714
+ }
26715
+ if (nodeHasActiveAssignment(meshId, nodeId)) {
26716
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_active_assignment", nodeId });
26717
+ continue;
26718
+ }
26719
+ const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
26720
+ if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
26721
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_concurrent_sessions_reached", nodeId });
26722
+ continue;
26723
+ }
26724
+ autoLaunchInProgress.add(launchKey);
26725
+ try {
26726
+ const resolved = await resolveUsableProvider(components, nodeId, node);
26727
+ if (!resolved.providerType) {
26728
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: resolved.reason || "provider_unusable", nodeId });
26729
+ continue;
26730
+ }
26731
+ markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType });
26732
+ const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
26733
+ cliType: resolved.providerType,
26734
+ dir: node.workspace,
26735
+ settings: {
26736
+ meshNodeFor: meshId,
26737
+ meshNodeId: nodeId,
26738
+ spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
26739
+ launchedByCoordinator: true,
26740
+ autoLaunchedForQueueTaskId: task.id
26741
+ }
26742
+ });
26743
+ if (!launchResult?.success) {
26744
+ const reason = launchResult?.error || "launch_cli_failed";
26745
+ markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
26746
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
26747
+ return false;
26748
+ }
26749
+ const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
26750
+ if (!sessionId) {
26751
+ markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
26752
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
26753
+ return false;
26754
+ }
26755
+ markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId });
26756
+ tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType);
26757
+ return true;
26758
+ } catch (e) {
26759
+ markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
26760
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
26761
+ return false;
26762
+ } finally {
26763
+ autoLaunchInProgress.delete(launchKey);
26764
+ }
26765
+ }
26766
+ }
26767
+ return false;
26768
+ }
26769
+ async function triggerMeshQueue(components, meshId) {
26367
26770
  const mesh = getMeshWithCache(components, meshId);
26368
26771
  if (!mesh) return;
26369
26772
  const cliInstances = components.instanceManager.getByCategory("cli");
@@ -26374,9 +26777,7 @@ function triggerMeshQueue(components, meshId) {
26374
26777
  if (instMeshId !== meshId) continue;
26375
26778
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
26376
26779
  if (!nodeId) continue;
26377
- const status = readNonEmptyString(state.status).toLowerCase();
26378
- if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
26379
- if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
26780
+ if (!isIdleSessionState(state)) continue;
26380
26781
  const sessionId = state.instanceId;
26381
26782
  const providerType = state.type || readNonEmptyString(settings.providerType);
26382
26783
  if (providerType) {
@@ -26392,6 +26793,7 @@ function triggerMeshQueue(components, meshId) {
26392
26793
  }
26393
26794
  }
26394
26795
  }
26796
+ await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
26395
26797
  }
26396
26798
  function buildMeshSystemMessage(args) {
26397
26799
  const metadata = formatCompletionMetadata(args.metadataEvent);
@@ -26899,7 +27301,7 @@ function findBinary(name) {
26899
27301
  const isWin = os9.platform() === "win32";
26900
27302
  try {
26901
27303
  const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
26902
- return (0, import_child_process2.execSync)(cmd, {
27304
+ return (0, import_child_process3.execSync)(cmd, {
26903
27305
  encoding: "utf-8",
26904
27306
  timeout: 5e3,
26905
27307
  stdio: ["pipe", "pipe", "pipe"],
@@ -27274,7 +27676,7 @@ async function validateWorkspace(workspace) {
27274
27676
  cwd: normalizedWorkspace
27275
27677
  });
27276
27678
  }
27277
- await (0, import_promises5.access)(normalizedWorkspace, import_fs8.constants.R_OK);
27679
+ await (0, import_promises5.access)(normalizedWorkspace, import_fs9.constants.R_OK);
27278
27680
  } catch (error48) {
27279
27681
  if (error48 instanceof GitCommandError) throw error48;
27280
27682
  throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
@@ -28403,7 +28805,7 @@ function addWorkspaceEntry(config2, rawPath, label, options) {
28403
28805
  }
28404
28806
  }
28405
28807
  const v = validateWorkspacePath(abs);
28406
- if (!v.ok) return { error: v.error };
28808
+ if (v.ok !== true) return { error: v.error };
28407
28809
  const list = [...config2.workspaces || []];
28408
28810
  if (list.some((w) => path5.resolve(w.path) === abs)) {
28409
28811
  return { error: "Workspace already in list" };
@@ -28787,6 +29189,86 @@ async function syncMeshLedger(meshId, transport) {
28787
29189
  appendRemoteLedgerEntries2(meshId, res.missingEntries);
28788
29190
  }
28789
29191
  }
29192
+ function messageFromError(error48) {
29193
+ if (error48 instanceof Error) return error48.message;
29194
+ if (typeof error48 === "string") return error48;
29195
+ if (error48 && typeof error48 === "object") {
29196
+ const candidate = error48.error ?? error48.message ?? error48.reason;
29197
+ if (typeof candidate === "string") return candidate;
29198
+ }
29199
+ return String(error48 || "mesh relay command failed");
29200
+ }
29201
+ function classifyP2pRelayFailure(error48, _context = {}) {
29202
+ const message = messageFromError(error48);
29203
+ const lower = message.toLowerCase();
29204
+ const hasP2pSignal = /p2p|datachannel|node-datachannel|webrtc|ice|mesh_relay_command|daemon_mesh_p2p_transport/i.test(message);
29205
+ 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);
29206
+ if (/requires targetdaemonid and command|providerpriority|no inference provider|permission denied|read-only|not a member/i.test(message)) {
29207
+ return {
29208
+ code: "mesh_logic_or_provider_failure",
29209
+ reason: "mesh_logic_or_provider_failure",
29210
+ transport: "unknown",
29211
+ recoverable: false,
29212
+ retryRecommended: false,
29213
+ nextAction: NON_P2P_NEXT_ACTION,
29214
+ noFallbackReason: NO_FALLBACK_REASON
29215
+ };
29216
+ }
29217
+ let code = null;
29218
+ let reason = "";
29219
+ if (/timeout|timed out/i.test(message) && (hasP2pSignal || /mesh transport/i.test(message))) {
29220
+ code = "p2p_timeout";
29221
+ reason = "daemon_mesh_p2p_timeout";
29222
+ } else if (/no route|route unavailable/i.test(message)) {
29223
+ code = "p2p_no_route";
29224
+ reason = "daemon_mesh_p2p_no_route";
29225
+ } else if (/offline|not owned|not found|not connected to server/i.test(message) && /daemon|peer|target/i.test(message)) {
29226
+ code = "p2p_daemon_offline";
29227
+ reason = "daemon_mesh_target_offline";
29228
+ } else if (/closed|disconnected/i.test(message) && (hasP2pSignal || /state changed/i.test(message))) {
29229
+ code = "p2p_datachannel_closed";
29230
+ reason = "daemon_mesh_p2p_datachannel_closed";
29231
+ } else if (/not connected|cannot send|cannot establish/i.test(message) && hasP2pSignal) {
29232
+ code = "p2p_not_connected";
29233
+ reason = "daemon_mesh_p2p_not_connected";
29234
+ } else if (hasP2pSignal && hasFailureSignal) {
29235
+ code = "p2p_unavailable";
29236
+ reason = "daemon_mesh_p2p_transport_unavailable";
29237
+ }
29238
+ if (!code) {
29239
+ return {
29240
+ code: "mesh_logic_or_provider_failure",
29241
+ reason: "mesh_logic_or_provider_failure",
29242
+ transport: "unknown",
29243
+ recoverable: false,
29244
+ retryRecommended: false,
29245
+ nextAction: NON_P2P_NEXT_ACTION,
29246
+ noFallbackReason: NO_FALLBACK_REASON
29247
+ };
29248
+ }
29249
+ return {
29250
+ code,
29251
+ reason,
29252
+ transport: "p2p",
29253
+ recoverable: true,
29254
+ retryRecommended: true,
29255
+ nextAction: P2P_NEXT_ACTION,
29256
+ noFallbackReason: NO_FALLBACK_REASON
29257
+ };
29258
+ }
29259
+ function isP2pRelayTransportFailure(error48) {
29260
+ return classifyP2pRelayFailure(error48).recoverable === true;
29261
+ }
29262
+ function buildP2pRelayFailurePayload(error48, context = {}) {
29263
+ const classification = classifyP2pRelayFailure(error48, context);
29264
+ return {
29265
+ success: false,
29266
+ ...classification,
29267
+ error: messageFromError(error48),
29268
+ ...context.command ? { command: context.command } : {},
29269
+ ...context.targetDaemonId ? { targetDaemonId: context.targetDaemonId } : {}
29270
+ };
29271
+ }
28790
29272
  function isPlainObject22(value) {
28791
29273
  return !!value && typeof value === "object" && !Array.isArray(value);
28792
29274
  }
@@ -28827,11 +29309,11 @@ function normalizeState(raw) {
28827
29309
  }
28828
29310
  function loadState() {
28829
29311
  const statePath = getStatePath();
28830
- if (!(0, import_fs9.existsSync)(statePath)) {
29312
+ if (!(0, import_fs10.existsSync)(statePath)) {
28831
29313
  return { ...DEFAULT_STATE };
28832
29314
  }
28833
29315
  try {
28834
- const raw = (0, import_fs9.readFileSync)(statePath, "utf-8");
29316
+ const raw = (0, import_fs10.readFileSync)(statePath, "utf-8");
28835
29317
  return normalizeState(JSON.parse(raw));
28836
29318
  } catch {
28837
29319
  return { ...DEFAULT_STATE };
@@ -28840,7 +29322,7 @@ function loadState() {
28840
29322
  function saveState(state) {
28841
29323
  const statePath = getStatePath();
28842
29324
  const normalized = normalizeState(state);
28843
- (0, import_fs9.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
29325
+ (0, import_fs10.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
28844
29326
  }
28845
29327
  function resetState() {
28846
29328
  saveState({ ...DEFAULT_STATE });
@@ -28861,13 +29343,13 @@ function getMergedDefinitions() {
28861
29343
  function findCliCommand(command) {
28862
29344
  const trimmed = String(command || "").trim();
28863
29345
  if (!trimmed) return null;
28864
- if (path9.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
28865
- const candidate = trimmed.startsWith("~") ? path9.join((0, import_os3.homedir)(), trimmed.slice(1)) : trimmed;
28866
- const resolved = path9.isAbsolute(candidate) ? candidate : path9.resolve(candidate);
28867
- return (0, import_fs10.existsSync)(resolved) ? resolved : null;
29346
+ if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
29347
+ const candidate = trimmed.startsWith("~") ? path10.join((0, import_os3.homedir)(), trimmed.slice(1)) : trimmed;
29348
+ const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
29349
+ return (0, import_fs11.existsSync)(resolved) ? resolved : null;
28868
29350
  }
28869
29351
  try {
28870
- const result = (0, import_child_process4.execSync)(
29352
+ const result = (0, import_child_process5.execSync)(
28871
29353
  (0, import_os3.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
28872
29354
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
28873
29355
  ).trim();
@@ -28878,7 +29360,7 @@ function findCliCommand(command) {
28878
29360
  }
28879
29361
  function getIdeVersion(cliCommand) {
28880
29362
  try {
28881
- const result = (0, import_child_process4.execSync)(`"${cliCommand}" --version`, {
29363
+ const result = (0, import_child_process5.execSync)(`"${cliCommand}" --version`, {
28882
29364
  encoding: "utf-8",
28883
29365
  timeout: 1e4,
28884
29366
  stdio: ["pipe", "pipe", "pipe"]
@@ -28891,13 +29373,13 @@ function getIdeVersion(cliCommand) {
28891
29373
  function checkPathExists(paths) {
28892
29374
  const home = (0, import_os3.homedir)();
28893
29375
  for (const p of paths) {
28894
- const normalized = p.startsWith("~") ? path9.join(home, p.slice(1)) : p;
29376
+ const normalized = p.startsWith("~") ? path10.join(home, p.slice(1)) : p;
28895
29377
  if (normalized.includes("*")) {
28896
29378
  const username = home.split(/[\\/]/).pop() || "";
28897
29379
  const resolved = normalized.replace("*", username);
28898
- if ((0, import_fs10.existsSync)(resolved)) return resolved;
29380
+ if ((0, import_fs11.existsSync)(resolved)) return resolved;
28899
29381
  } else {
28900
- if ((0, import_fs10.existsSync)(normalized)) return normalized;
29382
+ if ((0, import_fs11.existsSync)(normalized)) return normalized;
28901
29383
  }
28902
29384
  }
28903
29385
  return null;
@@ -28911,7 +29393,7 @@ async function detectIDEs(providerLoader) {
28911
29393
  let resolvedCli = cliPath;
28912
29394
  if (!resolvedCli && appPath && os222 === "darwin") {
28913
29395
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
28914
- if ((0, import_fs10.existsSync)(bundledCli)) resolvedCli = bundledCli;
29396
+ if ((0, import_fs11.existsSync)(bundledCli)) resolvedCli = bundledCli;
28915
29397
  }
28916
29398
  if (!resolvedCli && appPath && os222 === "win32") {
28917
29399
  const { dirname: dirname92 } = await import("path");
@@ -28924,7 +29406,7 @@ async function detectIDEs(providerLoader) {
28924
29406
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
28925
29407
  ];
28926
29408
  for (const c of candidates) {
28927
- if ((0, import_fs10.existsSync)(c)) {
29409
+ if ((0, import_fs11.existsSync)(c)) {
28928
29410
  resolvedCli = c;
28929
29411
  break;
28930
29412
  }
@@ -28945,129 +29427,6 @@ async function detectIDEs(providerLoader) {
28945
29427
  }
28946
29428
  return results;
28947
29429
  }
28948
- function parseVersion(raw) {
28949
- const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
28950
- return match ? match[1] : raw.split("\n")[0].slice(0, 100);
28951
- }
28952
- function shellQuote(value) {
28953
- if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
28954
- return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
28955
- }
28956
- function expandHome(value) {
28957
- const trimmed = value.trim();
28958
- if (!trimmed.startsWith("~")) return trimmed;
28959
- return path10.join(os32.homedir(), trimmed.slice(1));
28960
- }
28961
- function isExplicitCommandPath(command) {
28962
- const trimmed = command.trim();
28963
- return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
28964
- }
28965
- function resolveCommandPath(command) {
28966
- const trimmed = command.trim();
28967
- if (!trimmed) return null;
28968
- if (isExplicitCommandPath(trimmed)) {
28969
- const expanded = expandHome(trimmed);
28970
- const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
28971
- return (0, import_fs11.existsSync)(candidate) ? candidate : null;
28972
- }
28973
- return null;
28974
- }
28975
- function execAsync(cmd, timeoutMs = 5e3) {
28976
- return new Promise((resolve162) => {
28977
- const child = (0, import_child_process5.exec)(cmd, {
28978
- encoding: "utf-8",
28979
- timeout: timeoutMs,
28980
- ...process.platform === "win32" ? { windowsHide: true } : {}
28981
- }, (err, stdout) => {
28982
- if (err || !stdout?.trim()) {
28983
- resolve162(null);
28984
- } else {
28985
- resolve162(stdout.trim());
28986
- }
28987
- });
28988
- child.on("error", () => resolve162(null));
28989
- });
28990
- }
28991
- async function detectCLIs(providerLoader, options) {
28992
- const platform10 = os32.platform();
28993
- const whichCmd = platform10 === "win32" ? "where" : "which";
28994
- const includeVersion = options?.includeVersion !== false;
28995
- const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
28996
- const results = await Promise.all(
28997
- cliList.map(async (cli) => {
28998
- try {
28999
- const explicitPath = resolveCommandPath(cli.command);
29000
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
29001
- if (!pathResult) return { ...cli, installed: false };
29002
- const firstPath = explicitPath || pathResult.split("\n")[0];
29003
- let version2;
29004
- if (includeVersion) {
29005
- const versionCommands = [
29006
- `"${firstPath}" --version`,
29007
- `"${firstPath}" -V`,
29008
- `"${firstPath}" -v`,
29009
- cli.versionCommand
29010
- ].filter((v) => !!v);
29011
- try {
29012
- for (const versionCommand of versionCommands) {
29013
- const versionResult = await execAsync(versionCommand, 3e3);
29014
- if (versionResult) {
29015
- version2 = parseVersion(versionResult);
29016
- break;
29017
- }
29018
- }
29019
- } catch {
29020
- }
29021
- }
29022
- return { ...cli, installed: true, version: version2, path: firstPath };
29023
- } catch {
29024
- return { ...cli, installed: false };
29025
- }
29026
- })
29027
- );
29028
- return results;
29029
- }
29030
- async function detectCLI(cliId, providerLoader, options) {
29031
- const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
29032
- if (providerLoader) {
29033
- const cliList = providerLoader.getCliDetectionList();
29034
- const target = cliList.find((c) => c.id === resolvedId);
29035
- if (target) {
29036
- const platform10 = os32.platform();
29037
- const whichCmd = platform10 === "win32" ? "where" : "which";
29038
- try {
29039
- const explicitPath = resolveCommandPath(target.command);
29040
- const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
29041
- if (!pathResult) return null;
29042
- const firstPath = explicitPath || pathResult.split("\n")[0];
29043
- let version2;
29044
- if (options?.includeVersion !== false) {
29045
- const versionCommands = [
29046
- `"${firstPath}" --version`,
29047
- `"${firstPath}" -V`,
29048
- `"${firstPath}" -v`,
29049
- target.versionCommand
29050
- ].filter((v) => !!v);
29051
- try {
29052
- for (const versionCommand of versionCommands) {
29053
- const versionResult = await execAsync(versionCommand, 3e3);
29054
- if (versionResult) {
29055
- version2 = parseVersion(versionResult);
29056
- break;
29057
- }
29058
- }
29059
- } catch {
29060
- }
29061
- }
29062
- return { ...target, installed: true, version: version2, path: firstPath };
29063
- } catch {
29064
- return null;
29065
- }
29066
- }
29067
- }
29068
- const all = await detectCLIs(providerLoader, options);
29069
- return all.find((c) => c.id === resolvedId && c.installed) || null;
29070
- }
29071
29430
  function parseDarwinAvailableBytes(totalMem) {
29072
29431
  if (os42.platform() !== "darwin") return null;
29073
29432
  try {
@@ -36589,6 +36948,204 @@ async function resolveProviderTypeFromPriority(args) {
36589
36948
  }
36590
36949
  return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
36591
36950
  }
36951
+ function truncateValidationOutput(value) {
36952
+ const text = typeof value === "string" ? value : value == null ? "" : String(value);
36953
+ if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
36954
+ return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
36955
+ [truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
36956
+ }
36957
+ function readPackageScripts(workspace) {
36958
+ try {
36959
+ const packageJsonPath = (0, import_path7.join)(workspace, "package.json");
36960
+ const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
36961
+ return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
36962
+ } catch {
36963
+ return {};
36964
+ }
36965
+ }
36966
+ function tokenizeValidationCommand(command) {
36967
+ const trimmed = command.trim();
36968
+ if (!trimmed) return null;
36969
+ if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
36970
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
36971
+ if (!tokens.length) return null;
36972
+ if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
36973
+ return tokens;
36974
+ }
36975
+ function scriptMatchesValidationCategory(scriptName, category) {
36976
+ return scriptName === category || scriptName.startsWith(`${category}:`);
36977
+ }
36978
+ function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
36979
+ const tokens = tokenizeValidationCommand(rawCommand);
36980
+ if (!tokens) {
36981
+ return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
36982
+ }
36983
+ const [binary2, second, third, ...rest] = tokens;
36984
+ let scriptName = "";
36985
+ let command = binary2;
36986
+ let args = [];
36987
+ if ((binary2 === "npm" || binary2 === "pnpm" || binary2 === "bun") && second === "run" && third) {
36988
+ scriptName = third;
36989
+ args = ["run", scriptName, ...rest];
36990
+ } else if (binary2 === "npm" && second === "test" && !third) {
36991
+ scriptName = "test";
36992
+ args = ["test"];
36993
+ } else if (binary2 === "yarn" && second === "run" && third) {
36994
+ scriptName = third;
36995
+ args = ["run", scriptName, ...rest];
36996
+ } else if (binary2 === "yarn" && second && !third) {
36997
+ scriptName = second;
36998
+ args = [scriptName];
36999
+ } else {
37000
+ return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
37001
+ }
37002
+ if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
37003
+ return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
37004
+ }
37005
+ if (!scriptMatchesValidationCategory(scriptName, category)) {
37006
+ return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
37007
+ }
37008
+ return {
37009
+ command: {
37010
+ command,
37011
+ args,
37012
+ displayCommand: [command, ...args].join(" "),
37013
+ category,
37014
+ source
37015
+ }
37016
+ };
37017
+ }
37018
+ function collectProjectContextValidationCandidates(mesh) {
37019
+ const commands = mesh?.projectContext?.commands;
37020
+ if (!commands || typeof commands !== "object" || Array.isArray(commands)) return [];
37021
+ const candidates = [];
37022
+ for (const category of REFINE_VALIDATION_CATEGORIES) {
37023
+ const entries = Array.isArray(commands[category]) ? commands[category] : [];
37024
+ for (const entry of entries) {
37025
+ if (typeof entry?.command !== "string") continue;
37026
+ candidates.push({
37027
+ command: entry.command,
37028
+ category,
37029
+ source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
37030
+ confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
37031
+ });
37032
+ }
37033
+ }
37034
+ return candidates.sort((a, b) => {
37035
+ const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
37036
+ return rank(a.confidence) - rank(b.confidence);
37037
+ });
37038
+ }
37039
+ function collectPolicyValidationCandidates(mesh) {
37040
+ const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
37041
+ const configured = Array.isArray(policy.validationCommands) ? policy.validationCommands : Array.isArray(policy.validationGate?.commands) ? policy.validationGate.commands : [];
37042
+ return configured.map((entry) => typeof entry === "string" ? { command: entry, category: "", source: "mesh.policy.validationCommands" } : entry).filter((entry) => entry && typeof entry.command === "string").map((entry) => {
37043
+ const commandText = entry.command.trim();
37044
+ const category = REFINE_VALIDATION_CATEGORIES.find((cat) => commandText.includes(` ${cat}`)) ?? "";
37045
+ return { command: commandText, category, source: "mesh.policy.validationCommands" };
37046
+ }).filter((entry) => !!entry.category);
37047
+ }
37048
+ function selectMeshRefineValidationCommands(mesh, workspace) {
37049
+ const scripts = readPackageScripts(workspace);
37050
+ const rejectedCommands = [];
37051
+ const selected = [];
37052
+ const seen = /* @__PURE__ */ new Set();
37053
+ const candidates = [
37054
+ ...collectPolicyValidationCandidates(mesh),
37055
+ ...collectProjectContextValidationCandidates(mesh)
37056
+ ];
37057
+ for (const candidate of candidates) {
37058
+ const parsed = parsePackageManagerValidationCommand(candidate.command, candidate.category, scripts, candidate.source);
37059
+ if (parsed.rejected) {
37060
+ rejectedCommands.push(parsed.rejected);
37061
+ continue;
37062
+ }
37063
+ if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
37064
+ selected.push(parsed.command);
37065
+ seen.add(parsed.command.displayCommand);
37066
+ if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
37067
+ }
37068
+ if (!selected.length && candidates.length === 0) {
37069
+ for (const category of REFINE_VALIDATION_CATEGORIES) {
37070
+ if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
37071
+ const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
37072
+ if (fallback.command && !seen.has(fallback.command.displayCommand)) {
37073
+ selected.push(fallback.command);
37074
+ seen.add(fallback.command.displayCommand);
37075
+ } else if (fallback.rejected) {
37076
+ rejectedCommands.push(fallback.rejected);
37077
+ }
37078
+ if (selected.length >= 2) break;
37079
+ }
37080
+ }
37081
+ return {
37082
+ commands: selected,
37083
+ rejectedCommands,
37084
+ 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"
37085
+ };
37086
+ }
37087
+ async function runMeshRefineValidationGate(mesh, workspace) {
37088
+ const { execFile: execFile3 } = await import("child_process");
37089
+ const { promisify: promisify3 } = await import("util");
37090
+ const execFileAsync3 = promisify3(execFile3);
37091
+ const selection = selectMeshRefineValidationCommands(mesh, workspace);
37092
+ const summary = {
37093
+ status: "skipped",
37094
+ required: true,
37095
+ commandsRun: [],
37096
+ rejectedCommands: selection.rejectedCommands,
37097
+ skippedReason: void 0,
37098
+ timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
37099
+ outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
37100
+ };
37101
+ if (!selection.commands.length) {
37102
+ summary.skippedReason = "validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available";
37103
+ return summary;
37104
+ }
37105
+ for (const candidate of selection.commands) {
37106
+ const startedAt = Date.now();
37107
+ try {
37108
+ const result = await execFileAsync3(candidate.command, candidate.args, {
37109
+ cwd: workspace,
37110
+ encoding: "utf8",
37111
+ timeout: REFINE_VALIDATION_TIMEOUT_MS,
37112
+ maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
37113
+ env: { ...process.env, CI: process.env.CI || "1" }
37114
+ });
37115
+ summary.commandsRun.push({
37116
+ command: candidate.command,
37117
+ args: candidate.args,
37118
+ displayCommand: candidate.displayCommand,
37119
+ category: candidate.category,
37120
+ source: candidate.source,
37121
+ passed: true,
37122
+ exitCode: 0,
37123
+ durationMs: Date.now() - startedAt,
37124
+ stdout: truncateValidationOutput(result.stdout),
37125
+ stderr: truncateValidationOutput(result.stderr)
37126
+ });
37127
+ } catch (error48) {
37128
+ summary.commandsRun.push({
37129
+ command: candidate.command,
37130
+ args: candidate.args,
37131
+ displayCommand: candidate.displayCommand,
37132
+ category: candidate.category,
37133
+ source: candidate.source,
37134
+ passed: false,
37135
+ exitCode: typeof error48?.code === "number" ? error48.code : null,
37136
+ signal: typeof error48?.signal === "string" ? error48.signal : null,
37137
+ timedOut: error48?.killed === true || /timed out/i.test(String(error48?.message || "")),
37138
+ durationMs: Date.now() - startedAt,
37139
+ stdout: truncateValidationOutput(error48?.stdout),
37140
+ stderr: truncateValidationOutput(error48?.stderr || error48?.message)
37141
+ });
37142
+ summary.status = "failed";
37143
+ return summary;
37144
+ }
37145
+ }
37146
+ summary.status = "passed";
37147
+ return summary;
37148
+ }
36592
37149
  function loadYamlModule() {
36593
37150
  return js_yaml_exports;
36594
37151
  }
@@ -41136,7 +41693,7 @@ async function shutdownDaemonComponents(components) {
41136
41693
  }
41137
41694
  cdpManagers.clear();
41138
41695
  }
41139
- var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, fs2, path8, os22, os8, os9, path14, import_child_process2, os10, path15, os11, import_child_process3, import_fs8, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs9, import_path5, import_child_process4, import_fs10, import_os3, path9, import_child_process5, os32, path10, import_fs11, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, mesh_work_queue_exports, init_mesh_work_queue, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, COMPLETED_FINALIZATION_RETRY_MS, COMPLETED_FINALIZATION_MAX_WAIT_MS, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
41696
+ var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, import_child_process2, os22, path8, import_fs8, fs2, path9, os32, os8, os9, path14, import_child_process3, os10, path15, os11, import_child_process4, import_fs9, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs10, import_path5, import_child_process5, import_fs11, import_os3, path10, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, SUBMODULE_WORKTREE_REMOVE_RE, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, mesh_work_queue_exports, init_mesh_work_queue, init_cli_detector, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, NO_FALLBACK_REASON, P2P_NEXT_ACTION, NON_P2P_NEXT_ACTION, P2pRelayFailureError, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, COMPLETED_FINALIZATION_RETRY_MS, COMPLETED_FINALIZATION_MAX_WAIT_MS, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, REFINE_VALIDATION_CATEGORIES, REFINE_VALIDATION_TIMEOUT_MS, REFINE_VALIDATION_OUTPUT_LIMIT_BYTES, REFINE_VALIDATION_SUMMARY_CHARS, REFINE_VALIDATION_MAX_COMMANDS, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
41140
41697
  var init_dist2 = __esm({
41141
41698
  "../daemon-core/dist/index.mjs"() {
41142
41699
  "use strict";
@@ -41159,20 +41716,24 @@ var init_dist2 = __esm({
41159
41716
  import_fs7 = require("fs");
41160
41717
  import_path4 = require("path");
41161
41718
  import_crypto5 = require("crypto");
41162
- fs2 = __toESM(require("fs"), 1);
41163
- path8 = __toESM(require("path"), 1);
41719
+ import_child_process2 = require("child_process");
41164
41720
  os22 = __toESM(require("os"), 1);
41721
+ path8 = __toESM(require("path"), 1);
41722
+ import_fs8 = require("fs");
41723
+ fs2 = __toESM(require("fs"), 1);
41724
+ path9 = __toESM(require("path"), 1);
41725
+ os32 = __toESM(require("os"), 1);
41165
41726
  init_dist();
41166
41727
  os8 = __toESM(require("os"), 1);
41167
41728
  os9 = __toESM(require("os"), 1);
41168
41729
  path14 = __toESM(require("path"), 1);
41169
- import_child_process2 = require("child_process");
41730
+ import_child_process3 = require("child_process");
41170
41731
  os10 = __toESM(require("os"), 1);
41171
41732
  path15 = __toESM(require("path"), 1);
41172
41733
  init_dist();
41173
41734
  os11 = __toESM(require("os"), 1);
41174
- import_child_process3 = require("child_process");
41175
- import_fs8 = require("fs");
41735
+ import_child_process4 = require("child_process");
41736
+ import_fs9 = require("fs");
41176
41737
  import_promises5 = require("fs/promises");
41177
41738
  path = __toESM(require("path"), 1);
41178
41739
  import_util4 = require("util");
@@ -41185,16 +41746,12 @@ var init_dist2 = __esm({
41185
41746
  import_crypto6 = require("crypto");
41186
41747
  path6 = __toESM(require("path"), 1);
41187
41748
  path7 = __toESM(require("path"), 1);
41188
- import_fs9 = require("fs");
41189
- import_path5 = require("path");
41190
- import_child_process4 = require("child_process");
41191
41749
  import_fs10 = require("fs");
41192
- import_os3 = require("os");
41193
- path9 = __toESM(require("path"), 1);
41750
+ import_path5 = require("path");
41194
41751
  import_child_process5 = require("child_process");
41195
- os32 = __toESM(require("os"), 1);
41196
- path10 = __toESM(require("path"), 1);
41197
41752
  import_fs11 = require("fs");
41753
+ import_os3 = require("os");
41754
+ path10 = __toESM(require("path"), 1);
41198
41755
  os42 = __toESM(require("os"), 1);
41199
41756
  import_child_process6 = require("child_process");
41200
41757
  init_wrapper();
@@ -41327,6 +41884,7 @@ var init_dist2 = __esm({
41327
41884
  WORKTREE_DIR_NAME = ".adhdev-worktrees";
41328
41885
  GIT_TIMEOUT_MS = 3e4;
41329
41886
  GIT_MAX_BUFFER = 4 * 1024 * 1024;
41887
+ SUBMODULE_WORKTREE_REMOVE_RE = /working trees containing submodules cannot be moved or removed/i;
41330
41888
  }
41331
41889
  });
41332
41890
  config_exports = {};
@@ -41485,6 +42043,7 @@ Follow these recovery rules:
41485
42043
  enqueueTask: () => enqueueTask,
41486
42044
  getMeshQueueStats: () => getMeshQueueStats,
41487
42045
  getQueue: () => getQueue,
42046
+ recordTaskAutoLaunch: () => recordTaskAutoLaunch,
41488
42047
  requeueTask: () => requeueTask,
41489
42048
  updateSessionTaskStatus: () => updateSessionTaskStatus,
41490
42049
  updateTaskStatus: () => updateTaskStatus
@@ -41495,13 +42054,18 @@ Follow these recovery rules:
41495
42054
  init_mesh_ledger();
41496
42055
  }
41497
42056
  });
42057
+ init_cli_detector = __esm2({
42058
+ "src/detection/cli-detector.ts"() {
42059
+ "use strict";
42060
+ }
42061
+ });
41498
42062
  init_logger = __esm2({
41499
42063
  "src/logging/logger.ts"() {
41500
42064
  "use strict";
41501
42065
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
41502
42066
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
41503
42067
  currentLevel = "info";
41504
- LOG_DIR = process.platform === "win32" ? path8.join(process.env.LOCALAPPDATA || process.env.APPDATA || path8.join(os22.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path8.join(os22.homedir(), "Library", "Logs", "adhdev") : path8.join(os22.homedir(), ".local", "share", "adhdev", "logs");
42068
+ LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os32.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os32.homedir(), "Library", "Logs", "adhdev") : path9.join(os32.homedir(), ".local", "share", "adhdev", "logs");
41505
42069
  MAX_LOG_SIZE = 5 * 1024 * 1024;
41506
42070
  MAX_LOG_DAYS = 7;
41507
42071
  try {
@@ -41509,16 +42073,16 @@ Follow these recovery rules:
41509
42073
  } catch {
41510
42074
  }
41511
42075
  currentDate = getDateStr();
41512
- currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
42076
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
41513
42077
  cleanOldLogs();
41514
42078
  try {
41515
- const oldLog = path8.join(LOG_DIR, "daemon.log");
42079
+ const oldLog = path9.join(LOG_DIR, "daemon.log");
41516
42080
  if (fs2.existsSync(oldLog)) {
41517
42081
  const stat22 = fs2.statSync(oldLog);
41518
42082
  const oldDate = stat22.mtime.toISOString().slice(0, 10);
41519
- fs2.renameSync(oldLog, path8.join(LOG_DIR, `daemon-${oldDate}.log`));
42083
+ fs2.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
41520
42084
  }
41521
- const oldLogBackup = path8.join(LOG_DIR, "daemon.log.old");
42085
+ const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
41522
42086
  if (fs2.existsSync(oldLogBackup)) {
41523
42087
  fs2.unlinkSync(oldLogBackup);
41524
42088
  }
@@ -41550,7 +42114,7 @@ Follow these recovery rules:
41550
42114
  }
41551
42115
  };
41552
42116
  interceptorInstalled = false;
41553
- LOG_PATH = path8.join(LOG_DIR, `daemon-${getDateStr()}.log`);
42117
+ LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
41554
42118
  }
41555
42119
  });
41556
42120
  mesh_events_exports = {};
@@ -41564,7 +42128,9 @@ Follow these recovery rules:
41564
42128
  init_mesh_events = __esm2({
41565
42129
  "src/mesh/mesh-events.ts"() {
41566
42130
  "use strict";
42131
+ init_config();
41567
42132
  init_mesh_config();
42133
+ init_cli_detector();
41568
42134
  init_logger();
41569
42135
  init_mesh_ledger();
41570
42136
  init_mesh_work_queue();
@@ -41585,6 +42151,9 @@ Follow these recovery rules:
41585
42151
  "agent:stopped": "task_failed",
41586
42152
  "monitor:long_generating": "task_stalled"
41587
42153
  };
42154
+ autoLaunchInProgress = /* @__PURE__ */ new Set();
42155
+ autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
42156
+ AUTO_LAUNCH_COOLDOWN_MS = 5e3;
41588
42157
  }
41589
42158
  });
41590
42159
  init_debug_config = __esm2({
@@ -44078,7 +44647,7 @@ ${lastSnapshot}`;
44078
44647
  }
44079
44648
  });
44080
44649
  init_repo_mesh_types();
44081
- execFileAsync = (0, import_util4.promisify)(import_child_process3.execFile);
44650
+ execFileAsync = (0, import_util4.promisify)(import_child_process4.execFile);
44082
44651
  DEFAULT_TIMEOUT_MS = 5e3;
44083
44652
  DEFAULT_MAX_BUFFER = 1024 * 1024;
44084
44653
  GitCommandError = class extends Error {
@@ -44337,6 +44906,34 @@ ${lastSnapshot}`;
44337
44906
  init_mesh_ledger();
44338
44907
  init_mesh_work_queue();
44339
44908
  init_mesh_events();
44909
+ NO_FALLBACK_REASON = "Repo Mesh command/data-plane is P2P-only; WS/REST command fallback is intentionally disabled to preserve the transport boundary.";
44910
+ 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.";
44911
+ NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
44912
+ P2pRelayFailureError = class extends Error {
44913
+ code;
44914
+ reason;
44915
+ transport;
44916
+ recoverable;
44917
+ retryRecommended;
44918
+ nextAction;
44919
+ noFallbackReason;
44920
+ command;
44921
+ targetDaemonId;
44922
+ constructor(message, context = {}) {
44923
+ super(message);
44924
+ this.name = "P2pRelayFailureError";
44925
+ const payload = buildP2pRelayFailurePayload(message, context);
44926
+ this.code = payload.code;
44927
+ this.reason = payload.reason;
44928
+ this.transport = payload.transport;
44929
+ this.recoverable = payload.recoverable;
44930
+ this.retryRecommended = payload.retryRecommended;
44931
+ this.nextAction = payload.nextAction;
44932
+ this.noFallbackReason = payload.noFallbackReason;
44933
+ this.command = context.command;
44934
+ this.targetDaemonId = context.targetDaemonId;
44935
+ }
44936
+ };
44340
44937
  init_config();
44341
44938
  DEFAULT_STATE = {
44342
44939
  recentActivity: [],
@@ -44348,6 +44945,7 @@ ${lastSnapshot}`;
44348
44945
  };
44349
44946
  BUILTIN_IDE_DEFINITIONS = [];
44350
44947
  registeredIDEs = /* @__PURE__ */ new Map();
44948
+ init_cli_detector();
44351
44949
  LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
44352
44950
  DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
44353
44951
  "generating",
@@ -48079,6 +48677,7 @@ ${effect.notification.body || ""}`.trim();
48079
48677
  }
48080
48678
  };
48081
48679
  init_provider_cli_adapter();
48680
+ init_cli_detector();
48082
48681
  init_config();
48083
48682
  init_provider_cli_adapter();
48084
48683
  init_logger();
@@ -52430,6 +53029,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
52430
53029
  };
52431
53030
  _providerLoader = null;
52432
53031
  init_config();
53032
+ init_cli_detector();
52433
53033
  init_logger();
52434
53034
  LOG_DIR2 = process.platform === "win32" ? path21.join(process.env.LOCALAPPDATA || process.env.APPDATA || path21.join(os16.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path21.join(os16.homedir(), "Library", "Logs", "adhdev") : path21.join(os16.homedir(), ".local", "share", "adhdev", "logs");
52435
53035
  MAX_FILE_SIZE = 5 * 1024 * 1024;
@@ -52478,6 +53078,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
52478
53078
  stable: "https://api.adhf.dev",
52479
53079
  preview: "https://api-preview.adhf.dev"
52480
53080
  };
53081
+ REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
53082
+ REFINE_VALIDATION_TIMEOUT_MS = 12e4;
53083
+ REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
53084
+ REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
53085
+ REFINE_VALIDATION_MAX_COMMANDS = 4;
52481
53086
  CHAT_COMMANDS = [
52482
53087
  "send_chat",
52483
53088
  "new_chat",
@@ -52610,20 +53215,98 @@ Run 'adhdev doctor' for detailed diagnostics.`
52610
53215
  recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
52611
53216
  };
52612
53217
  }
53218
+ const forceFallbackConvergence = await this.getWorktreeForceCleanupConvergence({
53219
+ repoRoot,
53220
+ workspace,
53221
+ node: args.node
53222
+ });
52613
53223
  try {
52614
- const result = await removeWorktree2(repoRoot, workspace, { requireClean: true });
52615
- return { success: true, removedPath: result.removedPath, repoRoot };
53224
+ const result = await removeWorktree2(repoRoot, workspace, {
53225
+ requireClean: true,
53226
+ allowSubmoduleForceFallback: forceFallbackConvergence.allow
53227
+ });
53228
+ return {
53229
+ success: true,
53230
+ removedPath: result.removedPath,
53231
+ repoRoot,
53232
+ ...result.fallback ? {
53233
+ fallback: result.fallback,
53234
+ forced: result.forced,
53235
+ reason: result.reason,
53236
+ convergence: forceFallbackConvergence
53237
+ } : {}
53238
+ };
52616
53239
  } catch (e) {
52617
53240
  const message = String(e?.message || e || "worktree cleanup failed");
52618
53241
  const dirty = message.includes("dirty worktree") || message.includes("local changes");
53242
+ const submoduleForceBlocked = /working trees containing submodules cannot be moved or removed/i.test(message) && !forceFallbackConvergence.allow;
52619
53243
  return {
52620
53244
  success: false,
52621
- code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
52622
- error: message,
52623
- recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure."
53245
+ code: dirty ? "mesh_worktree_cleanup_dirty" : submoduleForceBlocked ? "mesh_worktree_cleanup_force_fallback_blocked" : "mesh_worktree_cleanup_failed",
53246
+ error: submoduleForceBlocked ? `${message}; refusing --force fallback because convergence could not be verified: ${forceFallbackConvergence.error || "unknown convergence state"}` : message,
53247
+ 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.",
53248
+ ...submoduleForceBlocked ? { convergence: forceFallbackConvergence } : {}
52624
53249
  };
52625
53250
  }
52626
53251
  }
53252
+ async getWorktreeForceCleanupConvergence(args) {
53253
+ const metadataStatus = typeof args.node?.branchConvergence?.status === "string" ? args.node.branchConvergence.status : "";
53254
+ if (metadataStatus === "merged_to_main" || metadataStatus === "cleanup_candidate") {
53255
+ return { allow: true, status: metadataStatus, source: "node_branch_convergence" };
53256
+ }
53257
+ const { execFile: execFile3 } = await import("child_process");
53258
+ const { promisify: promisify3 } = await import("util");
53259
+ const execFileAsync3 = promisify3(execFile3);
53260
+ const runGit2 = async (gitArgs, cwd) => {
53261
+ const { stdout } = await execFileAsync3("git", gitArgs, {
53262
+ cwd,
53263
+ encoding: "utf8",
53264
+ timeout: 3e4,
53265
+ maxBuffer: 4 * 1024 * 1024,
53266
+ windowsHide: true
53267
+ });
53268
+ return String(stdout || "").trim();
53269
+ };
53270
+ let head = "";
53271
+ try {
53272
+ head = await runGit2(["rev-parse", "HEAD"], args.workspace);
53273
+ } catch (e) {
53274
+ return { allow: false, error: `could not resolve worktree HEAD: ${e?.message || e}` };
53275
+ }
53276
+ if (!head) return { allow: false, error: "worktree HEAD is empty" };
53277
+ const candidateRefs = [];
53278
+ try {
53279
+ const defaultBranch = await runGit2(["branch", "--show-current"], args.repoRoot);
53280
+ if (defaultBranch) {
53281
+ candidateRefs.push(defaultBranch, `origin/${defaultBranch}`);
53282
+ }
53283
+ } catch {
53284
+ }
53285
+ candidateRefs.push("origin/main", "origin/master", "main", "master");
53286
+ const seen = /* @__PURE__ */ new Set();
53287
+ const checkedRefs = [];
53288
+ for (const ref of candidateRefs) {
53289
+ if (!ref || seen.has(ref)) continue;
53290
+ seen.add(ref);
53291
+ let commit = "";
53292
+ try {
53293
+ commit = await runGit2(["rev-parse", "--verify", `${ref}^{commit}`], args.repoRoot);
53294
+ } catch {
53295
+ continue;
53296
+ }
53297
+ checkedRefs.push(ref);
53298
+ try {
53299
+ await runGit2(["merge-base", "--is-ancestor", head, commit], args.repoRoot);
53300
+ return { allow: true, status: "merged_to_default_ref", source: "git_merge_base", ref };
53301
+ } catch {
53302
+ }
53303
+ }
53304
+ return {
53305
+ allow: false,
53306
+ status: metadataStatus || void 0,
53307
+ error: checkedRefs.length ? `worktree HEAD is not contained in checked refs: ${checkedRefs.join(", ")}` : "no default/main refs were available for convergence verification"
53308
+ };
53309
+ }
52627
53310
  isCompletedHostedSession(record2) {
52628
53311
  return record2?.lifecycle === "stopped" || record2?.lifecycle === "failed" || record2?.lifecycle === "interrupted";
52629
53312
  }
@@ -53583,10 +54266,61 @@ Run 'adhdev doctor' for detailed diagnostics.`
53583
54266
  if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
53584
54267
  const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
53585
54268
  const baseBranch = baseBranchStdout.trim();
54269
+ const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
54270
+ if (validationSummary.status === "failed") {
54271
+ return {
54272
+ success: false,
54273
+ code: "validation_failed",
54274
+ convergenceStatus: "blocked_review",
54275
+ error: "Refinery validation gate failed; merge/refine was not attempted.",
54276
+ branch,
54277
+ into: baseBranch,
54278
+ validationSummary,
54279
+ finalBranchConvergenceState: {
54280
+ branch,
54281
+ baseBranch,
54282
+ merged: false,
54283
+ removed: false,
54284
+ validation: "failed",
54285
+ status: "blocked_review"
54286
+ }
54287
+ };
54288
+ }
54289
+ if (validationSummary.status === "skipped") {
54290
+ return {
54291
+ success: false,
54292
+ code: "validation_unavailable",
54293
+ convergenceStatus: "blocked_review",
54294
+ error: "Refinery validation gate is required but no allowlisted validation command was available; merge/refine was not attempted.",
54295
+ branch,
54296
+ into: baseBranch,
54297
+ validationSummary,
54298
+ finalBranchConvergenceState: {
54299
+ branch,
54300
+ baseBranch,
54301
+ merged: false,
54302
+ removed: false,
54303
+ validation: "unavailable",
54304
+ status: "blocked_review"
54305
+ }
54306
+ };
54307
+ }
53586
54308
  try {
53587
54309
  await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
53588
54310
  } catch (e) {
53589
- return { success: false, error: `Merge failed (conflicts?): ${e.message}` };
54311
+ return {
54312
+ success: false,
54313
+ error: `Merge failed (conflicts?): ${e.message}`,
54314
+ validationSummary,
54315
+ finalBranchConvergenceState: {
54316
+ branch,
54317
+ baseBranch,
54318
+ merged: false,
54319
+ removed: false,
54320
+ validation: "passed",
54321
+ status: "not_mergeable"
54322
+ }
54323
+ };
53590
54324
  }
53591
54325
  const removeResult = await this.execute("remove_mesh_node", {
53592
54326
  meshId,
@@ -53599,11 +54333,27 @@ Run 'adhdev doctor' for detailed diagnostics.`
53599
54333
  appendLedgerEntry2(meshId, {
53600
54334
  kind: "node_removed",
53601
54335
  nodeId,
53602
- payload: { refined: true, mergedBranch: branch, into: baseBranch }
54336
+ payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
53603
54337
  });
53604
54338
  } catch {
53605
54339
  }
53606
- return { success: true, merged: true, branch, into: baseBranch, removeResult };
54340
+ return {
54341
+ success: true,
54342
+ merged: true,
54343
+ branch,
54344
+ into: baseBranch,
54345
+ removeResult,
54346
+ validationSummary,
54347
+ finalBranchConvergenceState: {
54348
+ branch: baseBranch,
54349
+ mergedBranch: branch,
54350
+ baseBranch,
54351
+ merged: true,
54352
+ removed: removeResult?.success !== false,
54353
+ validation: "passed",
54354
+ status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
54355
+ }
54356
+ };
53607
54357
  } catch (e) {
53608
54358
  return { success: false, error: e.message };
53609
54359
  }
@@ -53658,7 +54408,10 @@ Run 'adhdev doctor' for detailed diagnostics.`
53658
54408
  sessionCleanupMode,
53659
54409
  workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
53660
54410
  daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
53661
- worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
54411
+ worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0,
54412
+ worktreeCleanupFallback: typeof worktreeCleanup?.fallback === "string" ? worktreeCleanup.fallback : void 0,
54413
+ forced: worktreeCleanup?.forced === true ? true : void 0,
54414
+ forceFallbackReason: typeof worktreeCleanup?.reason === "string" ? worktreeCleanup.reason : void 0
53662
54415
  }
53663
54416
  });
53664
54417
  } catch {
@@ -57555,6 +58308,7 @@ data: ${JSON.stringify(msg.data)}
57555
58308
  apiKeyName: "OpenAI/Anthropic API key"
57556
58309
  }
57557
58310
  ];
58311
+ init_cli_detector();
57558
58312
  SessionRegistry = class {
57559
58313
  bySessionId = /* @__PURE__ */ new Map();
57560
58314
  byManagerKey = /* @__PURE__ */ new Map();
@@ -57825,6 +58579,8 @@ function readString(value) {
57825
58579
  }
57826
58580
  var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
57827
58581
  var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
58582
+ var ACTIVE_QUEUE_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned"]);
58583
+ var HISTORICAL_QUEUE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
57828
58584
  async function refreshMeshFromDaemon(ctx) {
57829
58585
  if (!(ctx.transport instanceof IpcTransport)) return;
57830
58586
  try {
@@ -57950,30 +58706,126 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
57950
58706
  ]
57951
58707
  };
57952
58708
  }
57953
- function annotateQueueStaleness(queue) {
58709
+ function readSessionRecordId(session) {
58710
+ return readString(session?.id) || readString(session?.sessionId) || readString(session?.session_id) || readString(session?.runtimeSessionId) || readString(session?.runtime_session_id) || readString(session?.instanceId) || readString(session?.instance_id);
58711
+ }
58712
+ function addSessionRecord(target, session) {
58713
+ if (!session || typeof session !== "object" || isTerminalSessionRecord(session)) return;
58714
+ const sessionId = readSessionRecordId(session);
58715
+ if (sessionId) target.add(sessionId);
58716
+ }
58717
+ function collectNodeSessionIds(node) {
58718
+ const sessions = /* @__PURE__ */ new Set();
58719
+ const sessionArrays = [
58720
+ node?.sessions,
58721
+ node?.activeSessions,
58722
+ node?.active_sessions,
58723
+ node?.lastProbe?.sessions,
58724
+ node?.last_probe?.sessions,
58725
+ node?.lastProbe?.status?.sessions,
58726
+ node?.last_probe?.status?.sessions
58727
+ ];
58728
+ for (const value of sessionArrays) {
58729
+ if (Array.isArray(value)) value.forEach((session) => addSessionRecord(sessions, session));
58730
+ }
58731
+ const sessionRecords = [
58732
+ node?.activeSession,
58733
+ node?.active_session,
58734
+ node?.currentSession,
58735
+ node?.current_session,
58736
+ node?.runtimeSession,
58737
+ node?.runtime_session,
58738
+ node?.session,
58739
+ node?.lastProbe?.activeSession,
58740
+ node?.last_probe?.active_session,
58741
+ node?.lastProbe?.currentSession,
58742
+ node?.last_probe?.current_session,
58743
+ node?.lastProbe?.session,
58744
+ node?.last_probe?.session
58745
+ ];
58746
+ sessionRecords.forEach((session) => addSessionRecord(sessions, session));
58747
+ return sessions;
58748
+ }
58749
+ function buildQueueLivenessIndex(mesh) {
58750
+ const nodeIds = /* @__PURE__ */ new Set();
58751
+ const nodeSessionIds = /* @__PURE__ */ new Map();
58752
+ for (const node of Array.isArray(mesh?.nodes) ? mesh.nodes : []) {
58753
+ const nodeId = readString(node.id) || readString(node.nodeId) || readString(node.node_id);
58754
+ if (!nodeId) continue;
58755
+ nodeIds.add(nodeId);
58756
+ const sessions = collectNodeSessionIds(node);
58757
+ if (sessions.size > 0) nodeSessionIds.set(nodeId, sessions);
58758
+ }
58759
+ return { nodeIds, nodeSessionIds };
58760
+ }
58761
+ function queueAssignmentStaleReason(task, liveness) {
58762
+ if (task?.status !== "assigned") return void 0;
58763
+ const nodeId = readString(task.assignedNodeId) || readString(task.nodeId) || readString(task.node_id) || readString(task.targetNodeId);
58764
+ const sessionId = readString(task.assignedSessionId) || readString(task.sessionId) || readString(task.session_id) || readString(task.targetSessionId);
58765
+ if (nodeId && liveness.nodeIds.size > 0 && !liveness.nodeIds.has(nodeId)) {
58766
+ return "assigned node is not present in the current mesh snapshot";
58767
+ }
58768
+ if (nodeId && sessionId && liveness.nodeSessionIds.has(nodeId) && !liveness.nodeSessionIds.get(nodeId).has(sessionId)) {
58769
+ return "assigned session is not live on the assigned node";
58770
+ }
58771
+ const updatedAt = new Date(task.updatedAt).getTime();
58772
+ const ageMs = Number.isFinite(updatedAt) ? Date.now() - updatedAt : null;
58773
+ if (!nodeId && ageMs !== null && ageMs >= STALE_ASSIGNED_QUEUE_MS) {
58774
+ return "assigned task has no assigned node metadata";
58775
+ }
58776
+ return void 0;
58777
+ }
58778
+ function buildQueueStatusSummary(queue) {
58779
+ const counts = { pending: 0, assigned: 0, completed: 0, failed: 0, cancelled: 0 };
58780
+ for (const task of queue) {
58781
+ const status = typeof task?.status === "string" ? task.status : void 0;
58782
+ if (status && Object.prototype.hasOwnProperty.call(counts, status)) {
58783
+ counts[status] += 1;
58784
+ }
58785
+ }
58786
+ return {
58787
+ totalCount: queue.length,
58788
+ activeCount: counts.pending + counts.assigned,
58789
+ historicalCount: counts.completed + counts.failed + counts.cancelled,
58790
+ counts,
58791
+ activeCounts: {
58792
+ pending: counts.pending,
58793
+ assigned: counts.assigned
58794
+ },
58795
+ historicalCounts: {
58796
+ completed: counts.completed,
58797
+ failed: counts.failed,
58798
+ cancelled: counts.cancelled
58799
+ }
58800
+ };
58801
+ }
58802
+ function annotateQueueStaleness(queue, mesh) {
58803
+ const liveness = buildQueueLivenessIndex(mesh);
57954
58804
  const now = Date.now();
57955
58805
  return queue.map((task) => {
57956
58806
  const taskStatus = typeof task?.status === "string" ? task.status : void 0;
57957
58807
  const annotated = {
57958
58808
  ...task,
57959
58809
  taskStatus,
58810
+ isActive: taskStatus ? ACTIVE_QUEUE_STATUSES.has(taskStatus) : false,
58811
+ isHistorical: taskStatus ? HISTORICAL_QUEUE_STATUSES.has(taskStatus) : false,
57960
58812
  dispatchedAt: task?.createdAt,
57961
58813
  ...taskStatus === "assigned" ? { activeTaskId: task.id } : {},
57962
58814
  ...taskStatus === "completed" || taskStatus === "failed" ? {
57963
- isHistorical: true,
57964
58815
  completedAt: task.updatedAt
57965
58816
  } : {}
57966
58817
  };
57967
58818
  if (taskStatus !== "assigned") return annotated;
57968
58819
  const updatedAt = new Date(task.updatedAt).getTime();
57969
58820
  const ageMs = Number.isFinite(updatedAt) ? now - updatedAt : null;
57970
- if (ageMs === null || ageMs < STALE_ASSIGNED_QUEUE_MS) return annotated;
58821
+ const staleReason = queueAssignmentStaleReason(task, liveness);
58822
+ if (!staleReason) return annotated;
57971
58823
  return {
57972
58824
  ...annotated,
57973
58825
  stale: true,
57974
58826
  staleAssigned: true,
57975
- staleReason: "assigned task has not reached a terminal state within 30 minutes",
57976
- assignedAgeMs: ageMs
58827
+ staleReason,
58828
+ ...ageMs !== null ? { assignedAgeMs: ageMs } : {}
57977
58829
  };
57978
58830
  });
57979
58831
  }
@@ -58040,16 +58892,38 @@ function extractLaunchPayload(value) {
58040
58892
  function classifyMeshLaunchFailure(error48) {
58041
58893
  const message = error48 instanceof Error ? error48.message : String(error48 || "launch failed");
58042
58894
  const lower = message.toLowerCase();
58043
- if (lower.includes("p2p") || lower.includes("datachannel") || lower.includes("node-datachannel")) {
58044
- return { code: "p2p_unavailable", reason: "daemon_mesh_p2p_transport_unavailable", transport: "daemon_mesh_p2p" };
58895
+ const p2pClassification = classifyP2pRelayFailure(error48, { command: "launch_cli" });
58896
+ if (p2pClassification.recoverable) {
58897
+ return p2pClassification;
58045
58898
  }
58046
58899
  if (lower.includes("cannot connect to daemon ipc") || lower.includes("daemon ipc command")) {
58047
- return { code: "local_ipc_unavailable", reason: "local_daemon_ipc_unavailable", transport: "local_ipc" };
58900
+ return {
58901
+ code: "local_ipc_unavailable",
58902
+ reason: "local_daemon_ipc_unavailable",
58903
+ transport: "local_ipc",
58904
+ recoverable: true,
58905
+ retryRecommended: true,
58906
+ nextAction: "Check the local daemon IPC connection, then retry mesh_launch_session once after the daemon is reachable."
58907
+ };
58048
58908
  }
58049
58909
  if (lower.includes("timed out") || lower.includes("timeout")) {
58050
- return { code: "mesh_transport_timeout", reason: "mesh_transport_timeout", transport: "mesh_transport" };
58910
+ return {
58911
+ code: "mesh_transport_timeout",
58912
+ reason: "mesh_transport_timeout",
58913
+ transport: "mesh_transport",
58914
+ recoverable: true,
58915
+ retryRecommended: true,
58916
+ nextAction: "Check mesh transport health, then do one bounded retry before requeueing or relaunching the task."
58917
+ };
58051
58918
  }
58052
- return { code: "mesh_launch_failed", reason: "provider_launch_failed", transport: "mesh_transport" };
58919
+ return {
58920
+ code: "mesh_launch_failed",
58921
+ reason: "provider_launch_failed",
58922
+ transport: "mesh_transport",
58923
+ recoverable: false,
58924
+ retryRecommended: false,
58925
+ nextAction: "Inspect the provider launch error and fix the underlying provider/configuration issue before retrying."
58926
+ };
58053
58927
  }
58054
58928
  function buildWorktreeCleanupHint(node) {
58055
58929
  if (!node.isLocalWorktree) return void 0;
@@ -58065,10 +58939,13 @@ function buildRecoverableLaunchFailure(ctx, node, providerType, error48) {
58065
58939
  const cleanup = buildWorktreeCleanupHint(node);
58066
58940
  return {
58067
58941
  success: false,
58068
- recoverable: true,
58942
+ recoverable: classified.recoverable,
58069
58943
  code: classified.code,
58070
58944
  reason: classified.reason,
58071
58945
  transport: classified.transport,
58946
+ retryRecommended: classified.retryRecommended,
58947
+ nextAction: classified.nextAction,
58948
+ ...classified.noFallbackReason ? { noFallbackReason: classified.noFallbackReason } : {},
58072
58949
  error: message,
58073
58950
  meshId: ctx.mesh.id,
58074
58951
  nodeId: node.id,
@@ -58115,6 +58992,18 @@ function getLatestActiveLaunchFailure(meshId, nodeId) {
58115
58992
  }
58116
58993
  return null;
58117
58994
  }
58995
+ function buildCoordinatorP2pRelayFailure(error48, context) {
58996
+ const payload = buildP2pRelayFailurePayload(error48, {
58997
+ command: context.command,
58998
+ targetDaemonId: context.targetDaemonId
58999
+ });
59000
+ return {
59001
+ ...payload,
59002
+ ...context.nodeId ? { nodeId: context.nodeId } : {},
59003
+ ...context.sessionId ? { sessionId: context.sessionId } : {},
59004
+ retryHint: payload.retryRecommended ? payload.nextAction : "Do not retry as a P2P transport recovery; inspect the command/provider error first."
59005
+ };
59006
+ }
58118
59007
  async function ipcDispatchToRemoteAgent(ctx, node, args) {
58119
59008
  const transport = ctx.transport;
58120
59009
  const daemonId = node.daemonId;
@@ -58150,11 +59039,32 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
58150
59039
  });
58151
59040
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
58152
59041
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
58153
- return { success: false, error: `P2P dispatch failed: ${dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"}` };
59042
+ const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
59043
+ const errorMessage = dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task";
59044
+ return {
59045
+ ...buildCoordinatorP2pRelayFailure(source?.error || errorMessage, {
59046
+ command: "agent_command",
59047
+ targetDaemonId: daemonId,
59048
+ nodeId: node.id,
59049
+ sessionId
59050
+ }),
59051
+ ...source && typeof source === "object" ? source : {},
59052
+ success: false,
59053
+ error: `P2P dispatch failed: ${errorMessage}`
59054
+ };
58154
59055
  }
58155
59056
  return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
58156
59057
  } catch (e) {
58157
- return { success: false, error: `P2P dispatch failed: ${e?.message || String(e)}` };
59058
+ const errorMessage = e?.message || String(e);
59059
+ return {
59060
+ ...buildCoordinatorP2pRelayFailure(e, {
59061
+ command: "agent_command",
59062
+ targetDaemonId: daemonId,
59063
+ nodeId: node.id,
59064
+ sessionId
59065
+ }),
59066
+ error: `P2P dispatch failed: ${errorMessage}`
59067
+ };
58158
59068
  }
58159
59069
  }
58160
59070
  function resolveCoordinatorNode(ctx) {
@@ -58399,8 +59309,7 @@ async function commandForNode(ctx, node, command, args = {}) {
58399
59309
  throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
58400
59310
  }
58401
59311
  function isP2pTransportUnavailableError(error48) {
58402
- const message = error48 instanceof Error ? error48.message : String(error48 || "");
58403
- return /p2p|datachannel|mesh_relay_command|daemon_mesh_p2p_transport_unavailable/i.test(message) && /unavailable|failed|timeout|timed out|not connected|closed/i.test(message);
59312
+ return isP2pRelayTransportFailure(error48);
58404
59313
  }
58405
59314
  function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
58406
59315
  return {
@@ -58847,11 +59756,17 @@ async function meshEnqueueTask(ctx, args) {
58847
59756
  }
58848
59757
  async function meshViewQueue(ctx, args) {
58849
59758
  try {
58850
- const queue = annotateQueueStaleness(getQueue(ctx.mesh.id, { status: args.status }));
59759
+ const queue = annotateQueueStaleness(getQueue(ctx.mesh.id, { status: args.status }), ctx.mesh);
58851
59760
  const staleAssignedTasks = queue.filter((task) => task?.status === "assigned" && task?.staleAssigned);
59761
+ const summary = buildQueueStatusSummary(queue);
58852
59762
  return JSON.stringify({
58853
59763
  success: true,
58854
59764
  queue,
59765
+ summary,
59766
+ activeCounts: summary.activeCounts,
59767
+ historicalCounts: summary.historicalCounts,
59768
+ activeCount: summary.activeCount,
59769
+ historicalCount: summary.historicalCount,
58855
59770
  staleAssignedTasks,
58856
59771
  staleAssignedCount: staleAssignedTasks.length,
58857
59772
  // Back-compat alias for callers already reading the first hardening payload.
@@ -58993,7 +59908,13 @@ async function meshSendTask(ctx, args) {
58993
59908
  }
58994
59909
  return JSON.stringify({ success: true, nodeId: args.node_id, taskId: task.id, status: task.status });
58995
59910
  } catch (e) {
58996
- return JSON.stringify({ success: false, error: e.message });
59911
+ const failure2 = buildCoordinatorP2pRelayFailure(e, {
59912
+ command: "mesh_send_task",
59913
+ targetDaemonId: node.daemonId,
59914
+ nodeId: args.node_id,
59915
+ sessionId: args.session_id
59916
+ });
59917
+ return JSON.stringify(failure2);
58997
59918
  }
58998
59919
  }
58999
59920
  async function meshReadChat(ctx, args) {