@taskless-app/swarm 0.6.62 → 0.6.64

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.
Files changed (3) hide show
  1. package/dist/cli.js +465 -206
  2. package/dist/overmind.js +2118 -1803
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -388,8 +388,8 @@ function computeBlocker(status, stopReason, lastStep, pr, ci, deps, openQ) {
388
388
  }
389
389
  }
390
390
  function pickHeadline(lanes) {
391
- const live = lanes.filter((l2) => l2.live && !l2.obsolete);
392
- const by = (pred) => live.find(pred);
391
+ const live2 = lanes.filter((l2) => l2.live && !l2.obsolete);
392
+ const by = (pred) => live2.find(pred);
393
393
  const fmt = (l2, extra) => `${l2.name} \xB7 ${l2.slug} \u2014 ${extra}`;
394
394
  let l = by((x) => x.status === "review" && x.ci?.tone === "fail");
395
395
  if (l) return { icon: "\u274C", text: fmt(l, "CI \u0443\u043F\u0430\u043B \u2014 \u0447\u0438\u043D\u0438\u0442\u044C"), tone: "fail" };
@@ -407,7 +407,7 @@ function pickHeadline(lanes) {
407
407
  l = by((x) => x.status === "review");
408
408
  if (l) return { icon: "\u23F3", text: fmt(l, "review \u2014 CI \u0438\u0434\u0451\u0442"), tone: "review" };
409
409
  l = by((x) => x.status === "running");
410
- if (l) return { icon: "\u25B6", text: `${live.filter((x) => x.status === "running").length} \u0434\u0440\u043E\u043D\u0430 \u0432 \u0440\u0430\u0431\u043E\u0442\u0435 \u2014 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043E\u043A \u043D\u0435\u0442`, tone: "run" };
410
+ if (l) return { icon: "\u25B6", text: `${live2.filter((x) => x.status === "running").length} \u0434\u0440\u043E\u043D\u0430 \u0432 \u0440\u0430\u0431\u043E\u0442\u0435 \u2014 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043E\u043A \u043D\u0435\u0442`, tone: "run" };
411
411
  return null;
412
412
  }
413
413
  function computeCounters(lanes) {
@@ -607,10 +607,10 @@ function pick(rows) {
607
607
  return newest(rows.filter((r) => r.author)) ?? newest(rows);
608
608
  }
609
609
  function pickRunForPr(rows, ref, ctxRepo) {
610
- const num = prNumberOf(ref);
611
- if (num == null) return null;
610
+ const num2 = prNumberOf(ref);
611
+ if (num2 == null) return null;
612
612
  const target = repoKeyOf(ref) ?? repoKeyOf(ctxRepo);
613
- const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num);
613
+ const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num2);
614
614
  if (hits.length === 0) return null;
615
615
  if (target) {
616
616
  const mine = hits.filter((r) => keyOf(r) === target);
@@ -650,6 +650,9 @@ var init_returnReason = __esm({
650
650
  });
651
651
 
652
652
  // ../swarm-core/src/modelPolicy.ts
653
+ function isHarnessDefaultModel(model) {
654
+ return typeof model === "string" && model.trim().toLowerCase() === HARNESS_DEFAULT_MODEL;
655
+ }
653
656
  function findProvider(id) {
654
657
  const key = typeof id === "string" ? id.trim().toLowerCase() : "";
655
658
  return MODEL_PROVIDERS.find((p) => p.id === key) ?? null;
@@ -701,6 +704,19 @@ var init_modelPolicy = __esm({
701
704
  selectable: true,
702
705
  roles: { executor: "glm-5.3", review: "glm-5.3" }
703
706
  },
707
+ // Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
708
+ // имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
709
+ // TASWM-605: обе роли — на свежей модели (решение владельца: рой работает на свежих
710
+ // моделях). Это только фолбэк: демон берёт дефолт живого `grok models` (liveModels.ts).
711
+ // Тарифа нет: цену пишет CLI.
712
+ {
713
+ id: "xai",
714
+ label: "xAI (Grok)",
715
+ executorId: "grok",
716
+ selectable: true,
717
+ roles: { executor: "grok-4.7", review: "grok-4.7" },
718
+ models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
719
+ },
704
720
  // TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
705
721
  // Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
706
722
  // неизвестен, а выдуманное имя хуже маркера «решает Codex».
@@ -1089,18 +1105,18 @@ var init_hosts = __esm({
1089
1105
  function parseHostResources(raw) {
1090
1106
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
1091
1107
  const v = raw;
1092
- const num = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
1093
- const memFreeGb = num(v.memFreeGb);
1094
- const memTotalGb = num(v.memTotalGb);
1108
+ const num2 = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
1109
+ const memFreeGb = num2(v.memFreeGb);
1110
+ const memTotalGb = num2(v.memTotalGb);
1095
1111
  if (memFreeGb == null || memTotalGb == null) return null;
1096
1112
  if (typeof v.measuredAt !== "string" || Number.isNaN(Date.parse(v.measuredAt))) return null;
1097
- const cpu = num(v.cpuBusyPct);
1113
+ const cpu = num2(v.cpuBusyPct);
1098
1114
  return {
1099
1115
  cpuBusyPct: cpu == null ? null : Math.min(100, Math.max(0, cpu)),
1100
1116
  memFreeGb,
1101
1117
  memTotalGb,
1102
- diskFreeGb: num(v.diskFreeGb),
1103
- diskTotalGb: num(v.diskTotalGb),
1118
+ diskFreeGb: num2(v.diskFreeGb),
1119
+ diskTotalGb: num2(v.diskTotalGb),
1104
1120
  measuredAt: v.measuredAt
1105
1121
  };
1106
1122
  }
@@ -1137,6 +1153,7 @@ var init_hostPool = __esm({
1137
1153
  "../swarm-core/src/hostPool.ts"() {
1138
1154
  "use strict";
1139
1155
  init_hosts();
1156
+ init_modelPolicy();
1140
1157
  HOST_REJECTION_TEXT = {
1141
1158
  offline: "\u043D\u0435 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438",
1142
1159
  tick_stalled: `\u0446\u0438\u043A\u043B \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B\u0430 \u0432\u0441\u0442\u0430\u043B \u2014 \u043F\u0443\u043B\u044C\u0441 \u0438\u0434\u0451\u0442, \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442; ${HOST_TICK_STALLED_ACTION}`,
@@ -2109,10 +2126,10 @@ function launchCostUsd(result, model) {
2109
2126
  return costFromUsage(result.usage, model);
2110
2127
  }
2111
2128
  function launchSpend(result) {
2112
- const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2129
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2113
2130
  const sum = (u) => ({
2114
- tokensIn: num(u.input_tokens) + num(u.cache_creation_input_tokens) + num(u.cache_read_input_tokens),
2115
- tokensOut: num(u.output_tokens)
2131
+ tokensIn: num2(u.input_tokens) + num2(u.cache_creation_input_tokens) + num2(u.cache_read_input_tokens),
2132
+ tokensOut: num2(u.output_tokens)
2116
2133
  });
2117
2134
  const spend = sum(result.usage ?? {});
2118
2135
  if (spend.tokensIn === 0 && spend.tokensOut === 0 && result.modelUsage) {
@@ -2122,7 +2139,7 @@ function launchSpend(result) {
2122
2139
  spend.tokensOut += perModel.tokensOut;
2123
2140
  }
2124
2141
  }
2125
- return { ...spend, durationMs: num(result.duration_ms) };
2142
+ return { ...spend, durationMs: num2(result.duration_ms) };
2126
2143
  }
2127
2144
  function asTokenUsage(m) {
2128
2145
  return {
@@ -2214,9 +2231,9 @@ function transcriptSpendInWindow(worktree, fromMs, toMs, opts = {}) {
2214
2231
  unpriced += 1;
2215
2232
  unpricedModel ??= model;
2216
2233
  }
2217
- const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2218
- tokensIn += num(usage2.input_tokens) + num(usage2.cache_creation_input_tokens) + num(usage2.cache_read_input_tokens);
2219
- tokensOut += num(usage2.output_tokens);
2234
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2235
+ tokensIn += num2(usage2.input_tokens) + num2(usage2.cache_creation_input_tokens) + num2(usage2.cache_read_input_tokens);
2236
+ tokensOut += num2(usage2.output_tokens);
2220
2237
  if (firstAt === null || at < firstAt) firstAt = at;
2221
2238
  if (lastAt === null || at > lastAt) lastAt = at;
2222
2239
  replies += 1;
@@ -2444,13 +2461,15 @@ var init_swarmHome = __esm({
2444
2461
  });
2445
2462
 
2446
2463
  // src/executors/codex.ts
2447
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
2464
+ import { copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
2448
2465
  import { join as join5 } from "path";
2449
2466
  function resolveCodexBin(env = process.env) {
2450
2467
  return (env.SWARM_CODEX_BIN ?? "").trim() || "codex";
2451
2468
  }
2452
2469
  function codexHomePath(worktree, swarmHome = SWARM_HOME) {
2453
- return join5(swarmHome, "executor-state", "codex", worktree.replace(/[/.]/g, "-"));
2470
+ const folded = worktree.replace(/[/.]/g, "-");
2471
+ const key = /[:\\]/.test(folded) ? worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree" : folded;
2472
+ return join5(swarmHome, "executor-state", "codex", key);
2454
2473
  }
2455
2474
  function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
2456
2475
  const home = codexHomePath(worktree, swarmHome);
@@ -2463,6 +2482,10 @@ function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWAR
2463
2482
  try {
2464
2483
  symlinkSync(target, link);
2465
2484
  } catch {
2485
+ try {
2486
+ copyFileSync2(target, link);
2487
+ } catch {
2488
+ }
2466
2489
  }
2467
2490
  }
2468
2491
  return home;
@@ -2613,7 +2636,224 @@ ${notice}` : spec.prompt;
2613
2636
  }
2614
2637
  });
2615
2638
 
2639
+ // src/executors/grok.ts
2640
+ import { copyFileSync as copyFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync as statSync4, symlinkSync as symlinkSync2, writeFileSync as writeFileSync3 } from "fs";
2641
+ import { join as join6 } from "path";
2642
+ function resolveGrokBin(env = process.env) {
2643
+ return (env.SWARM_GROK_BIN ?? "").trim() || "grok";
2644
+ }
2645
+ function grokHomePath(worktree, swarmHome = SWARM_HOME) {
2646
+ const key = worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree";
2647
+ return join6(swarmHome, "executor-state", "grok", key);
2648
+ }
2649
+ function grokSessionDir(worktree, swarmHome = SWARM_HOME) {
2650
+ return join6(grokHomePath(worktree, swarmHome), "sessions", encodeURIComponent(worktree));
2651
+ }
2652
+ function tomlBasic(value) {
2653
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
2654
+ return `"${escaped}"`;
2655
+ }
2656
+ function grokMcpToml(spec) {
2657
+ const env = Object.entries(spec.mcp.env).map(([k, v]) => `${k} = ${tomlBasic(v)}`).join(", ");
2658
+ const args = spec.mcp.args.map((a) => tomlBasic(a)).join(", ");
2659
+ return [
2660
+ "[mcp_servers." + spec.mcp.name + "]",
2661
+ "command = " + tomlBasic(spec.mcp.command),
2662
+ "args = [" + args + "]",
2663
+ "env = { " + env + " }",
2664
+ ""
2665
+ ].join("\n");
2666
+ }
2667
+ function ensureGrokHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
2668
+ const home = grokHomePath(worktree, swarmHome);
2669
+ mkdirSync4(home, { recursive: true });
2670
+ const real = (env.GROK_HOME ?? "").trim() || join6(realHome, ".grok");
2671
+ const target = join6(real, "auth.json");
2672
+ const link = join6(home, "auth.json");
2673
+ if (existsSync4(target) && !existsSync4(link)) {
2674
+ try {
2675
+ symlinkSync2(target, link);
2676
+ } catch {
2677
+ try {
2678
+ copyFileSync3(target, link);
2679
+ } catch {
2680
+ }
2681
+ }
2682
+ }
2683
+ return home;
2684
+ }
2685
+ function modelArgs(model) {
2686
+ if (!model || isHarnessDefaultModel(model)) return [];
2687
+ return ["--model", model];
2688
+ }
2689
+ function denyArgs(paths) {
2690
+ return paths.flatMap((p) => ["--deny", `Read(${p}) Edit(${p}) Write(${p})`]);
2691
+ }
2692
+ function newestSessionMtime(dir, depth = 3) {
2693
+ let entries;
2694
+ try {
2695
+ entries = readdirSync5(dir, { withFileTypes: true });
2696
+ } catch {
2697
+ return null;
2698
+ }
2699
+ let latest = null;
2700
+ for (const entry of entries) {
2701
+ const full = join6(dir, entry.name);
2702
+ try {
2703
+ if (entry.isDirectory()) {
2704
+ if (depth <= 0) continue;
2705
+ const nested = newestSessionMtime(full, depth - 1);
2706
+ if (nested != null && (latest == null || nested > latest)) latest = nested;
2707
+ } else if (entry.isFile()) {
2708
+ const mtime2 = statSync4(full).mtimeMs;
2709
+ if (latest == null || mtime2 > latest) latest = mtime2;
2710
+ }
2711
+ } catch {
2712
+ }
2713
+ }
2714
+ return latest;
2715
+ }
2716
+ function num(v) {
2717
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
2718
+ }
2719
+ function grokAbort(rec) {
2720
+ if (rec.type === "error") {
2721
+ const raw = typeof rec.message === "string" && rec.message ? rec.message : "error";
2722
+ const kind = /tool/i.test(raw) ? "tools" : /401|unauthorized|network|fetch|ECONN|api/i.test(raw) ? "link" : "other";
2723
+ return { kind, raw: raw.slice(0, 300) };
2724
+ }
2725
+ const stop = typeof rec.stopReason === "string" ? rec.stopReason : "";
2726
+ if (rec.is_error === true || stop === "error" || stop.startsWith("error_")) {
2727
+ const raw = stop || "is_error";
2728
+ return { kind: /tool/i.test(raw) ? "tools" : "other", raw };
2729
+ }
2730
+ return void 0;
2731
+ }
2732
+ function parseGrokResult(raw) {
2733
+ const trimmed = raw.trim();
2734
+ if (!trimmed) return null;
2735
+ let parsed;
2736
+ try {
2737
+ parsed = JSON.parse(trimmed);
2738
+ } catch {
2739
+ return null;
2740
+ }
2741
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
2742
+ const rec = parsed;
2743
+ const hasShape = typeof rec.text === "string" || typeof rec.stopReason === "string" || typeof rec.sessionId === "string" || rec.type === "error";
2744
+ if (!hasShape) return null;
2745
+ const reported = typeof rec.total_cost_usd === "number" && Number.isFinite(rec.total_cost_usd);
2746
+ const usage2 = rec.usage;
2747
+ let tokensIn;
2748
+ let tokensOut;
2749
+ if (typeof usage2 === "object" && usage2 !== null) {
2750
+ const u = usage2;
2751
+ const hasAny = ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"].some(
2752
+ (k) => typeof u[k] === "number"
2753
+ );
2754
+ if (hasAny) {
2755
+ tokensIn = num(u.input_tokens) + num(u.cache_read_input_tokens) + num(u.cache_creation_input_tokens);
2756
+ tokensOut = num(u.output_tokens);
2757
+ }
2758
+ }
2759
+ const aborted = grokAbort(rec);
2760
+ const result = {
2761
+ costUsd: reported ? rec.total_cost_usd : 0
2762
+ };
2763
+ if (!reported) result.warning = "grok: total_cost_usd \u043D\u0435\u0442 \u2014 \u0441\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0430 (\u044D\u0442\u043E \u043D\u0435 \u043D\u043E\u043B\u044C)";
2764
+ if (tokensIn != null && tokensOut != null) {
2765
+ result.tokensIn = tokensIn;
2766
+ result.tokensOut = tokensOut;
2767
+ }
2768
+ if (aborted) result.aborted = aborted;
2769
+ return result;
2770
+ }
2771
+ var grokExecutor;
2772
+ var init_grok = __esm({
2773
+ "src/executors/grok.ts"() {
2774
+ "use strict";
2775
+ init_src();
2776
+ init_swarmHome();
2777
+ init_swarmFiles();
2778
+ init_codex();
2779
+ grokExecutor = {
2780
+ id: "grok",
2781
+ title: "Grok CLI (grok)",
2782
+ binEnv: "SWARM_GROK_BIN",
2783
+ install: {
2784
+ what: "Grok CLI (xAI). \u0421\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u043E\u0444\u0438\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u043C \u0438\u043D\u0441\u0442\u0430\u043B\u043B\u0435\u0440\u043E\u043C, \u043D\u0435 \u0447\u0435\u0440\u0435\u0437 npm.",
2785
+ pkg: "grok",
2786
+ command: "curl -fsSL https://x.ai/cli/install.sh | bash",
2787
+ windowsCommand: "irm https://x.ai/cli/install.ps1 | iex"
2788
+ },
2789
+ resolveBin: (env) => resolveGrokBin(env ?? process.env),
2790
+ plan: (spec) => {
2791
+ const home = ensureGrokHome(spec.worktree, spec.realHome);
2792
+ writeFileSync3(join6(home, "config.toml"), grokMcpToml(spec));
2793
+ writeFileSync3(
2794
+ join6(spec.worktree, SWARM_MCP_CONFIG_FILE),
2795
+ JSON.stringify(
2796
+ { mcpServers: { [spec.mcp.name]: { command: spec.mcp.command, args: spec.mcp.args, env: spec.mcp.env } } },
2797
+ null,
2798
+ 2
2799
+ )
2800
+ );
2801
+ const notice = skillsNotice(spec.skillDirs);
2802
+ const prompt = notice ? `${spec.prompt}
2803
+
2804
+ ${notice}` : spec.prompt;
2805
+ const args = [
2806
+ // `--cwd` стоит первым: по нему реконсайл узнаёт worktree живого процесса.
2807
+ // Флаг настоящий. Выдуманный `--mcp-config` grok отверг бы и дрон не стартовал.
2808
+ "--cwd",
2809
+ spec.worktree,
2810
+ "--no-auto-update",
2811
+ "--output-format",
2812
+ "json",
2813
+ "--yolo",
2814
+ ...spec.continueSession ? ["--continue"] : [],
2815
+ ...modelArgs(spec.model),
2816
+ ...spec.effort ? ["--effort", spec.effort] : [],
2817
+ ...denyArgs(spec.denyPaths),
2818
+ "-p",
2819
+ prompt
2820
+ ];
2821
+ return { bin: resolveGrokBin(), args, env: { GROK_HOME: home } };
2822
+ },
2823
+ parseResult(raw) {
2824
+ return parseGrokResult(raw);
2825
+ },
2826
+ session: {
2827
+ exists: (worktree) => newestSessionMtime(grokSessionDir(worktree)) !== null,
2828
+ mtimeMs: (worktree) => newestSessionMtime(grokSessionDir(worktree))
2829
+ },
2830
+ probe: {
2831
+ args: ["-p", "ok", "--output-format", "json", "--yolo", "--max-turns", "1", "--no-auto-update"],
2832
+ limitPattern: /usage limit/i,
2833
+ authPattern: /not logged in|unauthorized|\b401\b|authentication required|auth required/i
2834
+ }
2835
+ };
2836
+ }
2837
+ });
2838
+
2839
+ // src/liveModels.ts
2840
+ import { execFile } from "child_process";
2841
+ function liveDefaultModel(providerId) {
2842
+ return live.get(providerId) ?? null;
2843
+ }
2844
+ var live;
2845
+ var init_liveModels = __esm({
2846
+ "src/liveModels.ts"() {
2847
+ "use strict";
2848
+ init_grok();
2849
+ live = /* @__PURE__ */ new Map();
2850
+ }
2851
+ });
2852
+
2616
2853
  // src/swarmModels.ts
2854
+ function roleDefault(role, providerId) {
2855
+ return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
2856
+ }
2617
2857
  function pick2(v) {
2618
2858
  const s = typeof v === "string" ? v.trim() : "";
2619
2859
  return s.length > 0 ? s : null;
@@ -2628,13 +2868,25 @@ function forKind(kind, swarm, norm) {
2628
2868
  if (!swarm) return null;
2629
2869
  return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
2630
2870
  }
2871
+ function providersFromEnv(env = process.env) {
2872
+ const raw = (env.SWARM_PROVIDERS ?? "").trim();
2873
+ if (!raw) return null;
2874
+ const known = new Set(MODEL_PROVIDERS.map((p) => p.id));
2875
+ const list = [...new Set(raw.split(/[\s,]+/).map((p) => p.trim().toLowerCase()).filter((p) => known.has(p)))];
2876
+ return list.length ? list : null;
2877
+ }
2631
2878
  function resolveRunModelRef(run, swarm, env = process.env) {
2632
- const configured = parseModelRef(
2633
- pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
2634
- );
2879
+ const pinned = parseModelRef(pick2(run.model));
2880
+ if (pinned) return pinned;
2881
+ const configured = parseModelRef(forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL));
2882
+ const own = providersFromEnv(env);
2883
+ if (own) {
2884
+ if (configured && own.includes(configured.providerId)) return configured;
2885
+ return { providerId: own[0], model: roleDefault(modelRoleOf(run.kind), own[0]) };
2886
+ }
2635
2887
  if (configured) return configured;
2636
2888
  const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
2637
- return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
2889
+ return { providerId, model: roleDefault(modelRoleOf(run.kind), providerId) };
2638
2890
  }
2639
2891
  function resolveRunEffort(run, swarm, env = process.env) {
2640
2892
  return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
@@ -2643,6 +2895,7 @@ var init_swarmModels = __esm({
2643
2895
  "src/swarmModels.ts"() {
2644
2896
  "use strict";
2645
2897
  init_src();
2898
+ init_liveModels();
2646
2899
  }
2647
2900
  });
2648
2901
 
@@ -2678,21 +2931,24 @@ var init_executors = __esm({
2678
2931
  init_src();
2679
2932
  init_claudeCode();
2680
2933
  init_codex();
2934
+ init_grok();
2681
2935
  init_swarmModels();
2682
2936
  init_types2();
2683
2937
  init_claudeCode();
2684
2938
  init_codex();
2939
+ init_grok();
2685
2940
  DEFAULT_EXECUTOR_ID = "claude-code";
2686
2941
  EXECUTORS = {
2687
2942
  [claudeCodeExecutor.id]: claudeCodeExecutor,
2688
- [codexExecutor.id]: codexExecutor
2943
+ [codexExecutor.id]: codexExecutor,
2944
+ [grokExecutor.id]: grokExecutor
2689
2945
  };
2690
2946
  }
2691
2947
  });
2692
2948
 
2693
2949
  // src/config.ts
2694
- import { basename, join as join6, resolve } from "path";
2695
- import { readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync3 } from "fs";
2950
+ import { basename, join as join7, resolve } from "path";
2951
+ import { readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync4 } from "fs";
2696
2952
  function normalizeRepo(repo) {
2697
2953
  const r = (repo ?? "").trim();
2698
2954
  if (!r) return "";
@@ -2706,10 +2962,10 @@ function effectiveRepoOf(repo, fallback) {
2706
2962
  return normalizeRepo(repo || fallback || "");
2707
2963
  }
2708
2964
  function droneWorktreePath(worktreesDir, repo, slug) {
2709
- return join6(droneWorktreeRoot(worktreesDir, repo), slug);
2965
+ return join7(droneWorktreeRoot(worktreesDir, repo), slug);
2710
2966
  }
2711
2967
  function droneWorktreeRoot(worktreesDir, repo) {
2712
- return join6(worktreesDir, basename(normalizeRepo(repo)));
2968
+ return join7(worktreesDir, basename(normalizeRepo(repo)));
2713
2969
  }
2714
2970
  function resolveEffectiveWorktreesDir(deps) {
2715
2971
  if (deps.envOverride) return deps.envOverride;
@@ -2736,7 +2992,7 @@ function mcpEndpoint(api, path = TASKLESS_MCP_PATH) {
2736
2992
  }
2737
2993
  function tokenFromFile() {
2738
2994
  try {
2739
- return readFileSync3(join6(SWARM_HOME, "token"), "utf8").trim();
2995
+ return readFileSync3(join7(SWARM_HOME, "token"), "utf8").trim();
2740
2996
  } catch {
2741
2997
  return "";
2742
2998
  }
@@ -2749,25 +3005,25 @@ var init_config = __esm({
2749
3005
  init_executors();
2750
3006
  init_swarmHome();
2751
3007
  init_swarmHome();
2752
- DB_PATH = process.env.SWARM_DB || join6(SWARM_HOME, "swarm.db");
3008
+ DB_PATH = process.env.SWARM_DB || join7(SWARM_HOME, "swarm.db");
2753
3009
  EXECUTOR = resolveExecutor(process.env.SWARM_EXECUTOR);
2754
- DRONE_HOME = process.env.SWARM_DRONE_HOME || join6(SWARM_HOME, "drone-home");
3010
+ DRONE_HOME = process.env.SWARM_DRONE_HOME || join7(SWARM_HOME, "drone-home");
2755
3011
  DRONE_HOME_ISOLATION = process.env.SWARM_DRONE_ISOLATE_HOME !== "0" && process.platform !== "win32";
2756
3012
  DRONE_ISOLATION = DRONE_HOME_ISOLATION ? { home: DRONE_HOME, swarmHome: SWARM_HOME } : void 0;
2757
- WORKTREES_NOINDEX_DIR = join6(SWARM_HOME, "worktrees.noindex");
2758
- WORKTREES_LEGACY_DIR = join6(SWARM_HOME, "worktrees");
3013
+ WORKTREES_NOINDEX_DIR = join7(SWARM_HOME, "worktrees.noindex");
3014
+ WORKTREES_LEGACY_DIR = join7(SWARM_HOME, "worktrees");
2759
3015
  WORKTREES_DIR = process.env.SWARM_WORKTREES || WORKTREES_NOINDEX_DIR;
2760
- RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join6(SWARM_HOME, "run-logs");
2761
- DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join6(SWARM_HOME, "overmind.log");
3016
+ RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join7(SWARM_HOME, "run-logs");
3017
+ DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join7(SWARM_HOME, "overmind.log");
2762
3018
  DAEMON_LOG_MAX_BYTES = envNum("SWARM_DAEMON_LOG_MAX_BYTES", 16 * 1024 * 1024);
2763
3019
  DAEMON_LOG_KEEP = envNum("SWARM_DAEMON_LOG_KEEP", 2);
2764
3020
  DAEMON_LOG_CHECK_MS = envNum("SWARM_DAEMON_LOG_CHECK_MS", 6e4);
2765
3021
  DIST_BUILD = true;
2766
3022
  SWARM_BACKEND = process.env.SWARM_BACKEND === "client" ? "client" : process.env.SWARM_BACKEND === "sqlite" ? "sqlite" : DIST_BUILD ? "client" : "sqlite";
2767
- SWARM_OUTBOX = process.env.SWARM_OUTBOX || join6(SWARM_HOME, "outbox.jsonl");
3023
+ SWARM_OUTBOX = process.env.SWARM_OUTBOX || join7(SWARM_HOME, "outbox.jsonl");
2768
3024
  SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
2769
3025
  SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
2770
- ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join6(SWARM_HOME, "env-cache");
3026
+ ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join7(SWARM_HOME, "env-cache");
2771
3027
  ENV_CACHE_ENABLED = process.env.SWARM_ENV_CACHE !== "0";
2772
3028
  ENV_CACHE_KEEP = Math.trunc(envNum("SWARM_ENV_CACHE_KEEP", 4));
2773
3029
  SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
@@ -2797,15 +3053,15 @@ var init_config = __esm({
2797
3053
  DRONE_CHANNEL_ENABLED = process.env.SWARM_DRONE_CHANNEL !== "0";
2798
3054
  DRONE_CHANNEL_PORT = Number(process.env.SWARM_DRONE_CHANNEL_PORT) || 7421;
2799
3055
  DRONE_CHANNEL_URL = process.env.SWARM_DRONE_CHANNEL_URL || `http://127.0.0.1:${DRONE_CHANNEL_PORT}`;
2800
- DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join6(SWARM_HOME, "drone-channel.key");
3056
+ DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join7(SWARM_HOME, "drone-channel.key");
2801
3057
  DRONE_CHANNEL_GRACE_MS = envNum("SWARM_DRONE_CHANNEL_GRACE_MS", 12e4);
2802
- HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join6(SWARM_HOME, "overmind.heartbeat");
3058
+ HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join7(SWARM_HOME, "overmind.heartbeat");
2803
3059
  HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
2804
3060
  SWARM_REPO = process.env.SWARM_REPO || "";
2805
3061
  SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
2806
3062
  SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
2807
3063
  SWARM_SCAN_ROOTS_RAW = process.env.SWARM_SCAN_ROOTS || "";
2808
- REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join6(SWARM_HOME, "repo-map.json");
3064
+ REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join7(SWARM_HOME, "repo-map.json");
2809
3065
  SWARM_REPO_MAP_MS = envNum("SWARM_REPO_MAP_MS", 15 * 6e4);
2810
3066
  SWARM_REPO_SCAN_TTL_MS = envNum("SWARM_REPO_SCAN_TTL_MS", 24 * 60 * 6e4);
2811
3067
  TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
@@ -2820,7 +3076,7 @@ var init_config = __esm({
2820
3076
  SWARM_AUTO_REVIEW = process.env.SWARM_AUTO_REVIEW !== "0";
2821
3077
  SWARM_AUTO_VERIFY = process.env.SWARM_AUTO_VERIFY !== "0";
2822
3078
  SWARM_MERGE_QUEUE = process.env.SWARM_MERGE_QUEUE !== "0";
2823
- REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join6(SWARM_HOME, "review-cutoff.json");
3079
+ REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join7(SWARM_HOME, "review-cutoff.json");
2824
3080
  SWARM_REVIEW_CAP = Math.trunc(envNum("SWARM_REVIEW_CAP", 1));
2825
3081
  SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
2826
3082
  WEB_PORT = Number(process.env.SWARM_WEB_PORT) || 7420;
@@ -3052,11 +3308,11 @@ var init_resources = __esm({
3052
3308
 
3053
3309
  // src/names.ts
3054
3310
  function pickName(rows) {
3055
- const live = /* @__PURE__ */ new Set();
3311
+ const live2 = /* @__PURE__ */ new Set();
3056
3312
  const lastUse = /* @__PURE__ */ new Map();
3057
3313
  for (const r of rows) {
3058
3314
  if (!r.name) continue;
3059
- if (NAME_LIVE.has(r.status)) live.add(r.name);
3315
+ if (NAME_LIVE.has(r.status)) live2.add(r.name);
3060
3316
  const b = baseName(r.name);
3061
3317
  lastUse.set(b, Math.max(lastUse.get(b) ?? 0, r.id));
3062
3318
  }
@@ -3066,7 +3322,7 @@ function pickName(rows) {
3066
3322
  for (let g = 1; ; g++) {
3067
3323
  for (const base3 of ordered) {
3068
3324
  const cand = genName(base3, g);
3069
- if (!live.has(cand)) return cand;
3325
+ if (!live2.has(cand)) return cand;
3070
3326
  }
3071
3327
  }
3072
3328
  }
@@ -3148,8 +3404,8 @@ var init_store = __esm({
3148
3404
  });
3149
3405
 
3150
3406
  // src/agentToken.ts
3151
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, unlinkSync } from "fs";
3152
- import { join as join7 } from "path";
3407
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, unlinkSync } from "fs";
3408
+ import { join as join8 } from "path";
3153
3409
  function loadAgentToken() {
3154
3410
  try {
3155
3411
  const s = JSON.parse(readFileSync5(STORE_PATH, "utf8"));
@@ -3159,7 +3415,7 @@ function loadAgentToken() {
3159
3415
  }
3160
3416
  }
3161
3417
  function saveAgentToken(s) {
3162
- writeFileSync4(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
3418
+ writeFileSync5(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
3163
3419
  }
3164
3420
  function clearAgentToken() {
3165
3421
  try {
@@ -3230,7 +3486,7 @@ var init_agentToken = __esm({
3230
3486
  "src/agentToken.ts"() {
3231
3487
  "use strict";
3232
3488
  init_config();
3233
- STORE_PATH = join7(SWARM_HOME, "agent.json");
3489
+ STORE_PATH = join8(SWARM_HOME, "agent.json");
3234
3490
  base = (api) => api.replace(/\/$/, "");
3235
3491
  TOKEN_PROBE_TIMEOUT_MS = 8e3;
3236
3492
  }
@@ -3616,7 +3872,7 @@ var init_package = __esm({
3616
3872
  "package.json"() {
3617
3873
  package_default = {
3618
3874
  name: "@taskless-app/swarm",
3619
- version: "0.6.62",
3875
+ version: "0.6.64",
3620
3876
  type: "module",
3621
3877
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3622
3878
  keywords: [
@@ -3667,7 +3923,7 @@ var init_package = __esm({
3667
3923
  execa: "^10.0.0",
3668
3924
  zod: "3.25.76"
3669
3925
  },
3670
- gitHead: "e999630218010a25c03a6024e1b71ef34b2359ec"
3926
+ gitHead: "7f27042a00bf1554798cce7b8a233ee6c5686a50"
3671
3927
  };
3672
3928
  }
3673
3929
  });
@@ -3833,7 +4089,7 @@ var init_netTimeout = __esm({
3833
4089
 
3834
4090
  // src/droneChannel.ts
3835
4091
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
3836
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
4092
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
3837
4093
  import { dirname as dirname3 } from "path";
3838
4094
  import { serve } from "@hono/node-server";
3839
4095
  function loadChannelSecret(path) {
@@ -3843,8 +4099,8 @@ function loadChannelSecret(path) {
3843
4099
  } catch {
3844
4100
  }
3845
4101
  const secret = randomBytes(32).toString("hex");
3846
- mkdirSync7(dirname3(path), { recursive: true });
3847
- writeFileSync7(path, secret, { mode: 384 });
4102
+ mkdirSync8(dirname3(path), { recursive: true });
4103
+ writeFileSync8(path, secret, { mode: 384 });
3848
4104
  try {
3849
4105
  chmodSync2(path, 384);
3850
4106
  } catch {
@@ -3921,8 +4177,8 @@ var init_authState = __esm({
3921
4177
 
3922
4178
  // src/oauth.ts
3923
4179
  import { createHash, randomBytes as randomBytes2 } from "crypto";
3924
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3 } from "fs";
3925
- import { join as join12 } from "path";
4180
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3 } from "fs";
4181
+ import { join as join13 } from "path";
3926
4182
  function makePkce() {
3927
4183
  const verifier = randomBytes2(48).toString("base64url");
3928
4184
  const challenge = createHash("sha256").update(verifier).digest("base64url");
@@ -4012,7 +4268,7 @@ function loadStore() {
4012
4268
  }
4013
4269
  }
4014
4270
  function saveStore(s) {
4015
- writeFileSync8(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
4271
+ writeFileSync9(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
4016
4272
  }
4017
4273
  function clearStore() {
4018
4274
  try {
@@ -4031,7 +4287,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
4031
4287
  const existing = loadClientId();
4032
4288
  if (existing) return existing;
4033
4289
  const clientId = await registerClient({ api, redirectUri }, fetchFn);
4034
- writeFileSync8(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
4290
+ writeFileSync9(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
4035
4291
  return clientId;
4036
4292
  }
4037
4293
  async function ensureAccess(store, opts = {}) {
@@ -4057,16 +4313,16 @@ var init_oauth = __esm({
4057
4313
  AUTHORIZE = "/api/oauth/authorize";
4058
4314
  TOKEN = "/api/oauth/token";
4059
4315
  SCOPE = "openid profile email taskless:full";
4060
- STORE_PATH2 = join12(SWARM_HOME, "oauth.json");
4061
- CLIENT_PATH = join12(SWARM_HOME, "oauth-client.json");
4316
+ STORE_PATH2 = join13(SWARM_HOME, "oauth.json");
4317
+ CLIENT_PATH = join13(SWARM_HOME, "oauth-client.json");
4062
4318
  base2 = (api) => api.replace(/\/$/, "");
4063
4319
  }
4064
4320
  });
4065
4321
 
4066
4322
  // src/git.ts
4067
- import { existsSync as existsSync10, readdirSync as readdirSync9, realpathSync as realpathSync2, statSync as statSync9 } from "fs";
4323
+ import { existsSync as existsSync11, readdirSync as readdirSync10, realpathSync as realpathSync2, statSync as statSync10 } from "fs";
4068
4324
  import { appendFile, chmod, mkdir, readFile, rename, rm, writeFile } from "fs/promises";
4069
- import { isAbsolute, join as join15, resolve as resolve2, sep as sep2 } from "path";
4325
+ import { isAbsolute, join as join16, resolve as resolve2, sep as sep2 } from "path";
4070
4326
  import { $ as $5 } from "execa";
4071
4327
  function gitNet(repo, args, timeoutMs) {
4072
4328
  return withHardTimeout(
@@ -4080,14 +4336,14 @@ async function reclaimStaleWorktree(repo, worktree) {
4080
4336
  await detachWorktree(repo, worktree);
4081
4337
  }
4082
4338
  async function inspectWorktreePath(path) {
4083
- if (!existsSync10(path)) return "free";
4339
+ if (!existsSync11(path)) return "free";
4084
4340
  try {
4085
- if (readdirSync9(path).length === 0) return "free";
4341
+ if (readdirSync10(path).length === 0) return "free";
4086
4342
  } catch {
4087
4343
  return "orphan";
4088
4344
  }
4089
4345
  try {
4090
- if (statSync9(join15(path, ".git")).isDirectory()) return "main-checkout";
4346
+ if (statSync10(join16(path, ".git")).isDirectory()) return "main-checkout";
4091
4347
  } catch {
4092
4348
  }
4093
4349
  const top = await $5({ reject: false })`git -C ${path} rev-parse --show-toplevel`;
@@ -4126,7 +4382,7 @@ async function clearWorktreePath(repo, path, branch, opts = {}) {
4126
4382
  await detachWorktree(repo, path);
4127
4383
  opts.onStaleReclaim?.(path);
4128
4384
  }
4129
- if (!existsSync10(path)) return;
4385
+ if (!existsSync11(path)) return;
4130
4386
  await parkOrphanDir(repo, path, branch);
4131
4387
  }
4132
4388
  async function parkOrphanDir(repo, path, branch) {
@@ -4147,7 +4403,7 @@ async function parkOrphanDir(repo, path, branch) {
4147
4403
  }
4148
4404
  function isLinkedWorktree(dir) {
4149
4405
  try {
4150
- return statSync9(join15(dir, ".git")).isFile();
4406
+ return statSync10(join16(dir, ".git")).isFile();
4151
4407
  } catch {
4152
4408
  return false;
4153
4409
  }
@@ -4159,7 +4415,7 @@ async function detachWorktree(repo, worktree) {
4159
4415
  }
4160
4416
  }
4161
4417
  async function ensureWorktreeDirGone(worktree) {
4162
- if (!existsSync10(worktree)) return false;
4418
+ if (!existsSync11(worktree)) return false;
4163
4419
  if (!isLinkedWorktree(worktree)) {
4164
4420
  console.error(
4165
4421
  `[git] ${worktree} \u2014 \u043D\u0435 \u043F\u0440\u0438\u0441\u0442\u0451\u0433\u043D\u0443\u0442\u044B\u0439 worktree (.git \u043D\u0435 \u0444\u0430\u0439\u043B): \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u041D\u0415 \u0442\u0440\u043E\u0433\u0430\u044E. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E worktree \u0434\u0440\u043E\u043D\u0430 \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043E \u0440\u0443\u043A\u0430\u043C\u0438; \u0435\u0441\u043B\u0438 \u044D\u0442\u043E \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0447\u0435\u043A\u0430\u0443\u0442, \u0437\u043D\u0430\u0447\u0438\u0442 \u0432\u0435\u0442\u043A\u0430 \u0441\u043B\u0430\u0433\u0430 \u0447\u0435\u043A\u043D\u0443\u0442\u0430 \u0432 \u043D\u0451\u043C (git checkout <slug>) \u2014 \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0435\u0433\u043E \u043D\u0430 \u0434\u0440\u0443\u0433\u0443\u044E \u0432\u0435\u0442\u043A\u0443.`
@@ -4174,7 +4430,7 @@ async function ensureWorktreeDirGone(worktree) {
4174
4430
  );
4175
4431
  return true;
4176
4432
  }
4177
- if (existsSync10(worktree)) {
4433
+ if (existsSync11(worktree)) {
4178
4434
  console.error(`[git] \u043A\u0430\u0442\u0430\u043B\u043E\u0433 worktree ${worktree} \u043F\u0435\u0440\u0435\u0436\u0438\u043B rm \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0443\u043A\u0430\u043C\u0438, \u0438\u043D\u0430\u0447\u0435 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0441\u043F\u0430\u0432\u043D \u0441\u043B\u0430\u0433\u0430 \u0432\u0441\u0442\u0430\u043D\u0435\u0442.`);
4179
4435
  }
4180
4436
  return true;
@@ -4223,9 +4479,9 @@ async function checkoutWorktree(repo, path, branch, opts) {
4223
4479
  }
4224
4480
  const busyAt = await worktreeForBranch(repo, branch);
4225
4481
  if (busyAt) {
4226
- const exists = existsSync10(busyAt);
4227
- const live = exists && (opts.isWorktreeHeldLive ? await opts.isWorktreeHeldLive(busyAt) : true);
4228
- if (live) throw new WorktreeBusyError(branch, busyAt);
4482
+ const exists = existsSync11(busyAt);
4483
+ const live2 = exists && (opts.isWorktreeHeldLive ? await opts.isWorktreeHeldLive(busyAt) : true);
4484
+ if (live2) throw new WorktreeBusyError(branch, busyAt);
4229
4485
  if (exists && await worktreeDirty(busyAt)) throw new WorktreeBusyError(branch, busyAt);
4230
4486
  await reclaimStaleWorktree(repo, busyAt);
4231
4487
  opts.onStaleReclaim?.(busyAt);
@@ -4254,8 +4510,8 @@ async function ensureGitExcluded(worktree, pattern) {
4254
4510
  try {
4255
4511
  const commonDir = await gitPath(worktree, "--git-common-dir");
4256
4512
  if (!commonDir) return false;
4257
- const infoDir = join15(commonDir, "info");
4258
- const excludeFile = join15(infoDir, "exclude");
4513
+ const infoDir = join16(commonDir, "info");
4514
+ const excludeFile = join16(infoDir, "exclude");
4259
4515
  let current = "";
4260
4516
  try {
4261
4517
  current = await readFile(excludeFile, "utf8");
@@ -4275,7 +4531,7 @@ async function gitPath(worktree, flag) {
4275
4531
  const out = await $5({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
4276
4532
  const raw = out.stdout.trim();
4277
4533
  if (out.exitCode !== 0 || !raw) return null;
4278
- return isAbsolute(raw) ? raw : join15(worktree, raw);
4534
+ return isAbsolute(raw) ? raw : join16(worktree, raw);
4279
4535
  }
4280
4536
  async function ensureWorktreeHygiene(worktree) {
4281
4537
  for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
@@ -4285,12 +4541,12 @@ async function installGuardHooks(worktree) {
4285
4541
  try {
4286
4542
  const gitDir = await gitPath(worktree, "--git-dir");
4287
4543
  if (!gitDir) return false;
4288
- const hooksDir = join15(gitDir, "swarm-hooks");
4544
+ const hooksDir = join16(gitDir, "swarm-hooks");
4289
4545
  const prev = await previousHooksPath(worktree);
4290
4546
  await mkdir(hooksDir, { recursive: true });
4291
4547
  for (const hook of GUARD_HOOKS) {
4292
- await writeFile(join15(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
4293
- await chmod(join15(hooksDir, hook), 493);
4548
+ await writeFile(join16(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
4549
+ await chmod(join16(hooksDir, hook), 493);
4294
4550
  }
4295
4551
  await $5`git -C ${worktree} config extensions.worktreeConfig true`;
4296
4552
  await $5`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
@@ -4309,7 +4565,7 @@ async function previousHooksPath(worktree) {
4309
4565
  if (out.exitCode === 0 && value) return value;
4310
4566
  }
4311
4567
  const commonDir = await gitPath(worktree, "--git-common-dir");
4312
- return commonDir ? join15(commonDir, "hooks") : null;
4568
+ return commonDir ? join16(commonDir, "hooks") : null;
4313
4569
  }
4314
4570
  async function removeWorktree(repo, path) {
4315
4571
  const branch = (await $5({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
@@ -4372,7 +4628,7 @@ var init_git = __esm({
4372
4628
 
4373
4629
  // src/attachments.ts
4374
4630
  import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
4375
- import { join as join16 } from "path";
4631
+ import { join as join17 } from "path";
4376
4632
  var ATTACHMENTS_DIR, ATTACH_MAX_TOTAL_BYTES, ATTACH_MAX_FILE_BYTES, IMAGE_EXT, DOC_EXT, TEXT_EXT, KNOWN_EXT;
4377
4633
  var init_attachments = __esm({
4378
4634
  "src/attachments.ts"() {
@@ -4391,7 +4647,7 @@ var init_attachments = __esm({
4391
4647
 
4392
4648
  // src/shotUpload.ts
4393
4649
  import { readFile as fsReadFile } from "fs/promises";
4394
- import { basename as basename5, isAbsolute as isAbsolute2, join as join17 } from "path";
4650
+ import { basename as basename5, isAbsolute as isAbsolute2, join as join18 } from "path";
4395
4651
  var SHOT_MAX_FILE_BYTES;
4396
4652
  var init_shotUpload = __esm({
4397
4653
  "src/shotUpload.ts"() {
@@ -4401,8 +4657,8 @@ var init_shotUpload = __esm({
4401
4657
  });
4402
4658
 
4403
4659
  // src/workspace.ts
4404
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, unlinkSync as unlinkSync4 } from "fs";
4405
- import { join as join18 } from "path";
4660
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, unlinkSync as unlinkSync4 } from "fs";
4661
+ import { join as join19 } from "path";
4406
4662
  function loadWorkspace() {
4407
4663
  try {
4408
4664
  const w = JSON.parse(readFileSync11(WORKSPACE_PATH, "utf8"));
@@ -4412,7 +4668,7 @@ function loadWorkspace() {
4412
4668
  }
4413
4669
  }
4414
4670
  function saveWorkspace(w) {
4415
- writeFileSync9(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
4671
+ writeFileSync10(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
4416
4672
  }
4417
4673
  function clearWorkspace() {
4418
4674
  try {
@@ -4428,7 +4684,7 @@ var init_workspace = __esm({
4428
4684
  "src/workspace.ts"() {
4429
4685
  "use strict";
4430
4686
  init_config();
4431
- WORKSPACE_PATH = join18(SWARM_HOME, "workspace.json");
4687
+ WORKSPACE_PATH = join19(SWARM_HOME, "workspace.json");
4432
4688
  }
4433
4689
  });
4434
4690
 
@@ -4846,13 +5102,13 @@ var init_daemonLink = __esm({
4846
5102
  });
4847
5103
 
4848
5104
  // src/pauseState.ts
4849
- import { existsSync as existsSync11, readFileSync as readFileSync12, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
4850
- import { join as join19 } from "path";
5105
+ import { existsSync as existsSync12, readFileSync as readFileSync12, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync11 } from "fs";
5106
+ import { join as join20 } from "path";
4851
5107
  function pauseStatePath(home) {
4852
- return join19(home, "pause.json");
5108
+ return join20(home, "pause.json");
4853
5109
  }
4854
5110
  function loadPauseState(home, io = {}) {
4855
- const exists = io.exists ?? existsSync11;
5111
+ const exists = io.exists ?? existsSync12;
4856
5112
  const readFile2 = io.readFile ?? ((p) => readFileSync12(p, "utf8"));
4857
5113
  const path = pauseStatePath(home);
4858
5114
  if (!exists(path)) return ACTIVE;
@@ -4872,12 +5128,12 @@ function loadPauseState(home, io = {}) {
4872
5128
  function savePauseState(home, state, io = {}) {
4873
5129
  const path = pauseStatePath(home);
4874
5130
  if (!state.paused) {
4875
- const remove = io.remove ?? ((p) => existsSync11(p) && unlinkSync5(p));
5131
+ const remove = io.remove ?? ((p) => existsSync12(p) && unlinkSync5(p));
4876
5132
  remove(path);
4877
5133
  return;
4878
5134
  }
4879
5135
  const writeFile3 = io.writeFile ?? ((p, data) => {
4880
- writeFileSync10(`${p}.tmp`, data);
5136
+ writeFileSync11(`${p}.tmp`, data);
4881
5137
  renameSync5(`${p}.tmp`, p);
4882
5138
  });
4883
5139
  writeFile3(path, JSON.stringify(state, null, 2));
@@ -4914,7 +5170,7 @@ var init_pauseState = __esm({
4914
5170
  });
4915
5171
 
4916
5172
  // src/heartbeat.ts
4917
- import { readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync11 } from "fs";
5173
+ import { readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync12 } from "fs";
4918
5174
  function readHeartbeat(path) {
4919
5175
  try {
4920
5176
  const ms = Number(readFileSync13(path, "utf8").trim());
@@ -4936,7 +5192,7 @@ __export(db_exports, {
4936
5192
  });
4937
5193
  import { createRequire as createRequire2 } from "module";
4938
5194
  import { randomUUID as randomUUID3 } from "crypto";
4939
- import { existsSync as existsSync12 } from "fs";
5195
+ import { existsSync as existsSync13 } from "fs";
4940
5196
  import { resolve as resolve3 } from "path";
4941
5197
  function rowToQuestion(row) {
4942
5198
  return {
@@ -5600,7 +5856,7 @@ CREATE TABLE IF NOT EXISTS imports (
5600
5856
  const repoN = normalizeRepo(repo);
5601
5857
  const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
5602
5858
  if (already) return { imported: false, runs: already.run_count };
5603
- if (!existsSync12(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
5859
+ if (!existsSync13(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
5604
5860
  const src = new this.databaseCtor(abs);
5605
5861
  try {
5606
5862
  const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
@@ -5928,8 +6184,8 @@ CREATE TABLE IF NOT EXISTS imports (
5928
6184
  });
5929
6185
 
5930
6186
  // src/repoScan.ts
5931
- import { readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync10 } from "fs";
5932
- import { join as join20 } from "path";
6187
+ import { readdirSync as readdirSync11, readFileSync as readFileSync14, statSync as statSync11 } from "fs";
6188
+ import { join as join21 } from "path";
5933
6189
  var init_repoScan = __esm({
5934
6190
  "src/repoScan.ts"() {
5935
6191
  "use strict";
@@ -5939,8 +6195,8 @@ var init_repoScan = __esm({
5939
6195
 
5940
6196
  // src/repoDiscovery.ts
5941
6197
  import { homedir as homedir6 } from "os";
5942
- import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as writeFileSync12 } from "fs";
5943
- import { isAbsolute as isAbsolute3, join as join21, resolve as resolve4 } from "path";
6198
+ import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as writeFileSync13 } from "fs";
6199
+ import { isAbsolute as isAbsolute3, join as join22, resolve as resolve4 } from "path";
5944
6200
  function repoMapLines(rows) {
5945
6201
  if (!rows?.length) return [];
5946
6202
  const missing = rows.filter((r) => !r.path).length;
@@ -6331,10 +6587,10 @@ __export(statusCli_exports, {
6331
6587
  probeLink: () => probeLink,
6332
6588
  runStatusCli: () => runStatusCli
6333
6589
  });
6334
- import { existsSync as existsSync13, readFileSync as readFileSync16 } from "fs";
6590
+ import { existsSync as existsSync14, readFileSync as readFileSync16 } from "fs";
6335
6591
  function outboxPending(path) {
6336
6592
  try {
6337
- if (!existsSync13(path)) return 0;
6593
+ if (!existsSync14(path)) return 0;
6338
6594
  return readFileSync16(path, "utf8").split("\n").filter((l) => l.trim()).length;
6339
6595
  } catch {
6340
6596
  return null;
@@ -6542,8 +6798,8 @@ var init_prodMergeGate = __esm({
6542
6798
  });
6543
6799
 
6544
6800
  // src/platform.ts
6545
- import { existsSync as existsSync14, readFileSync as readFileSync17 } from "fs";
6546
- import { delimiter, join as join22 } from "path";
6801
+ import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
6802
+ import { delimiter, join as join23 } from "path";
6547
6803
  function safeReadText(path) {
6548
6804
  try {
6549
6805
  return readFileSync17(path, "utf8");
@@ -6552,13 +6808,13 @@ function safeReadText(path) {
6552
6808
  }
6553
6809
  }
6554
6810
  function resolveCommandPath(name, env = process.env, platform = process.platform) {
6555
- if (name.includes("/") || name.includes("\\")) return existsSync14(name) ? name : null;
6811
+ if (name.includes("/") || name.includes("\\")) return existsSync15(name) ? name : null;
6556
6812
  const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
6557
6813
  const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
6558
6814
  for (const dir of dirs) {
6559
6815
  for (const ext of exts) {
6560
- const candidate = join22(dir, name + ext);
6561
- if (existsSync14(candidate)) return candidate;
6816
+ const candidate = join23(dir, name + ext);
6817
+ if (existsSync15(candidate)) return candidate;
6562
6818
  }
6563
6819
  }
6564
6820
  return null;
@@ -6661,13 +6917,13 @@ var init_platform = __esm({
6661
6917
  });
6662
6918
 
6663
6919
  // src/envGate.ts
6664
- import { existsSync as existsSync15 } from "fs";
6665
- import { join as join23 } from "path";
6920
+ import { existsSync as existsSync16 } from "fs";
6921
+ import { join as join24 } from "path";
6666
6922
  import { createConnection } from "net";
6667
6923
  import { execa as execa3 } from "execa";
6668
6924
  function detectRequirements(repo, io = {}) {
6669
- const exists = io.exists ?? existsSync15;
6670
- const marker = COMPOSE_MARKERS.find((f) => exists(join23(repo, f)));
6925
+ const exists = io.exists ?? existsSync16;
6926
+ const marker = COMPOSE_MARKERS.find((f) => exists(join24(repo, f)));
6671
6927
  return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
6672
6928
  }
6673
6929
  async function dockerAlive() {
@@ -6699,7 +6955,7 @@ var init_envGate = __esm({
6699
6955
  });
6700
6956
 
6701
6957
  // src/doctor.ts
6702
- import { isAbsolute as isAbsolute4, join as join24 } from "path";
6958
+ import { isAbsolute as isAbsolute4, join as join25 } from "path";
6703
6959
  function ok(id, title, detail) {
6704
6960
  return { id, title, status: "ok", detail, remedies: [], critical: false };
6705
6961
  }
@@ -6724,12 +6980,14 @@ function checkExecutor(io, executor = resolveExecutor()) {
6724
6980
  const bin = executor.resolveBin();
6725
6981
  const where = /[/\\]/.test(bin) ? `\u043D\u0430\u0439\u0434\u0435\u043D: ${bin}` : "\u043D\u0430\u0439\u0434\u0435\u043D \u0432 PATH";
6726
6982
  if (io.hasCommand(bin)) return ok("claude", executor.title, where);
6983
+ const custom = executor.install.command == null ? null : io.platform.kind === "windows" && executor.install.windowsCommand ? executor.install.windowsCommand : executor.install.command;
6727
6984
  const remedies = [
6728
6985
  {
6729
6986
  what: executor.install.what,
6730
- command: globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
6987
+ command: custom ?? globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
6731
6988
  sudo: false,
6732
- auto: true
6989
+ // Свой инсталлер (не npm-пакет) человек ставит сам: автозапуск скрипта с сайта — нет.
6990
+ auto: custom == null
6733
6991
  }
6734
6992
  ];
6735
6993
  const detail = io.platform.kind === "windows" ? `\u0432 PATH \u043D\u0435\u0442 \`${bin}\`. \u041D\u0430 Windows spawn \u0438\u0449\u0435\u0442 \u0444\u0430\u0439\u043B \u043F\u043E PATHEXT \u2014 bash-\u043E\u0431\u0451\u0440\u0442\u043A\u0430 \u0431\u0435\u0437 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0435\u043C\u0443 \u043D\u0435 \u0432\u0438\u0434\u043D\u0430; \u043D\u0435\u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0434\u0430\u0451\u0442\u0441\u044F ${executor.binEnv}` : `\u0432 PATH \u043D\u0435\u0442 \`${bin}\` \u2014 \u0434\u0440\u043E\u043D\u0443 \u043D\u0435\u0447\u0435\u043C \u043F\u0438\u0441\u0430\u0442\u044C \u043A\u043E\u0434`;
@@ -6906,7 +7164,7 @@ function checkRepos(io, snap) {
6906
7164
  critical: true
6907
7165
  };
6908
7166
  }
6909
- if (env && !io.exists(join24(env, ".git"))) {
7167
+ if (env && !io.exists(join25(env, ".git"))) {
6910
7168
  return {
6911
7169
  id: "repos",
6912
7170
  title,
@@ -6916,7 +7174,7 @@ function checkRepos(io, snap) {
6916
7174
  critical: true
6917
7175
  };
6918
7176
  }
6919
- const broken = snap.paths.filter((p) => !io.exists(join24(p, ".git")));
7177
+ const broken = snap.paths.filter((p) => !io.exists(join25(p, ".git")));
6920
7178
  if (broken.length) {
6921
7179
  return {
6922
7180
  id: "repos",
@@ -7106,12 +7364,12 @@ var init_doctor = __esm({
7106
7364
  });
7107
7365
 
7108
7366
  // src/repoMemory.ts
7109
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
7110
- import { join as join25 } from "path";
7367
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
7368
+ import { join as join26 } from "path";
7111
7369
  function loadRememberedRepos(home) {
7112
7370
  let raw;
7113
7371
  try {
7114
- raw = readFileSync18(join25(home, FILE), "utf8");
7372
+ raw = readFileSync18(join26(home, FILE), "utf8");
7115
7373
  } catch {
7116
7374
  return [];
7117
7375
  }
@@ -7147,7 +7405,7 @@ __export(installCli_exports, {
7147
7405
  runInstallHookCli: () => runInstallHookCli,
7148
7406
  shortenError: () => shortenError
7149
7407
  });
7150
- import { existsSync as existsSync16 } from "fs";
7408
+ import { existsSync as existsSync17 } from "fs";
7151
7409
  function classifyChromiumError(message) {
7152
7410
  if (/playwright-core не установлен|Cannot find (module|package)/i.test(message)) return "no-module";
7153
7411
  if (/error while loading shared libraries|cannot open shared object file|missing dependencies|\blib[\w.+-]*\.so[.\d]*\b/i.test(message)) return "no-libs";
@@ -7202,7 +7460,7 @@ function defaultDoctorIO(packageVersion) {
7202
7460
  packageVersion,
7203
7461
  entry,
7204
7462
  hasCommand: (name) => hasCommandInPath(name),
7205
- exists: existsSync16,
7463
+ exists: existsSync17,
7206
7464
  run: (command) => runQuiet(command),
7207
7465
  latestVersion: () => latestMemo ??= fetchLatestVersion(),
7208
7466
  probeChromium: () => probeChromium(),
@@ -7430,8 +7688,8 @@ var init_updateCli = __esm({
7430
7688
 
7431
7689
  // src/cli.ts
7432
7690
  init_cliIo();
7433
- import { basename as basename6, dirname as dirname5, join as join26 } from "path";
7434
- import { existsSync as existsSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync14, mkdirSync as mkdirSync10, readdirSync as readdirSync11, statSync as statSync11, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
7691
+ import { basename as basename6, dirname as dirname5, join as join27 } from "path";
7692
+ import { existsSync as existsSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync12, statSync as statSync12, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
7435
7693
  import { setTimeout as sleep2 } from "timers/promises";
7436
7694
 
7437
7695
  // src/swarmClient.ts
@@ -7439,7 +7697,7 @@ init_config();
7439
7697
  init_resources();
7440
7698
  init_names2();
7441
7699
  init_storm();
7442
- import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
7700
+ import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
7443
7701
  import { dirname as dirname2 } from "path";
7444
7702
  import { randomUUID } from "crypto";
7445
7703
 
@@ -7472,24 +7730,24 @@ init_store();
7472
7730
  init_toolFailure();
7473
7731
 
7474
7732
  // src/logTail.ts
7475
- import { closeSync, existsSync as existsSync5, openSync, readSync, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
7476
- import { join as join9 } from "path";
7733
+ import { closeSync, existsSync as existsSync6, openSync, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
7734
+ import { join as join10 } from "path";
7477
7735
 
7478
7736
  // src/runLogDir.ts
7479
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync5, renameSync as renameSync2, statSync as statSync4 } from "fs";
7480
- import { join as join8 } from "path";
7737
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync6, renameSync as renameSync2, statSync as statSync5 } from "fs";
7738
+ import { join as join9 } from "path";
7481
7739
  function runLogDirName(runLogKey) {
7482
7740
  const safe = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
7483
7741
  if (!safe) throw new Error(`\u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0433\u043E\u0434\u0438\u0442\u0441\u044F \u0432 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 run-log'\u043E\u0432: ${JSON.stringify(runLogKey)}`);
7484
7742
  return /^\d+$/.test(safe) ? `k-${safe}` : safe;
7485
7743
  }
7486
7744
  function runLogDirPath(runLogsDir, runLogKey) {
7487
- return join8(runLogsDir, runLogDirName(runLogKey));
7745
+ return join9(runLogsDir, runLogDirName(runLogKey));
7488
7746
  }
7489
7747
  function findRunLogDir(runLogsDir, keys) {
7490
7748
  for (const key of keys) {
7491
7749
  const dir = runLogDirPath(runLogsDir, key);
7492
- if (existsSync4(dir)) return dir;
7750
+ if (existsSync5(dir)) return dir;
7493
7751
  }
7494
7752
  return null;
7495
7753
  }
@@ -7695,25 +7953,25 @@ var SwarmClient = class _SwarmClient {
7695
7953
  this.repoFullNames = registry.map((e) => e.repoFullName);
7696
7954
  this.repoPathByFullName = new Map(registry.map((e) => [e.repoFullName, normalizeRepo(e.path)]));
7697
7955
  this.host = opts.host ?? null;
7698
- this.repoExists = opts.repoExists ?? existsSync6;
7956
+ this.repoExists = opts.repoExists ?? existsSync7;
7699
7957
  this.questionSyncMs = opts.questionSyncMs ?? 3e3;
7700
7958
  this.executorId = opts.executorId ?? EXECUTOR.id;
7701
7959
  this.outboxPath = opts.outboxPath;
7702
7960
  this.mapPath = `${opts.outboxPath}.prodmap.json`;
7703
7961
  this.stormPath = `${opts.outboxPath}.storm.jsonl`;
7704
- mkdirSync6(dirname2(this.outboxPath), { recursive: true });
7962
+ mkdirSync7(dirname2(this.outboxPath), { recursive: true });
7705
7963
  this.recover();
7706
7964
  }
7707
7965
  // ── рестарт: поднять outbox (незаписанные операции) и карту runKey→prodId ─────
7708
7966
  recover() {
7709
- if (existsSync6(this.mapPath)) {
7967
+ if (existsSync7(this.mapPath)) {
7710
7968
  try {
7711
7969
  const obj = JSON.parse(readFileSync6(this.mapPath, "utf8"));
7712
7970
  for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
7713
7971
  } catch {
7714
7972
  }
7715
7973
  }
7716
- if (existsSync6(this.outboxPath)) {
7974
+ if (existsSync7(this.outboxPath)) {
7717
7975
  const txt = readFileSync6(this.outboxPath, "utf8");
7718
7976
  for (const line of txt.split("\n")) {
7719
7977
  const s = line.trim();
@@ -7734,7 +7992,7 @@ var SwarmClient = class _SwarmClient {
7734
7992
  // eventsLog ПЕРВЫМИ (до hydrate), в порядке файла (= хронологический); id
7735
7993
  // переприсваиваем локально (id событий — только порядок, не корреляция с продом).
7736
7994
  replayGlobalEvents() {
7737
- if (!existsSync6(this.stormPath)) return;
7995
+ if (!existsSync7(this.stormPath)) return;
7738
7996
  let txt;
7739
7997
  try {
7740
7998
  txt = readFileSync6(this.stormPath, "utf8");
@@ -8734,7 +8992,7 @@ var SwarmClient = class _SwarmClient {
8734
8992
  * транзиентным сбоем ОС, который этот PR должен переживать, а не превращать в мор.
8735
8993
  */
8736
8994
  reconcileRuntime(opts) {
8737
- const wtExists = opts.worktreeExists ?? existsSync6;
8995
+ const wtExists = opts.worktreeExists ?? existsSync7;
8738
8996
  const pidByWorktree = /* @__PURE__ */ new Map();
8739
8997
  try {
8740
8998
  for (const p of opts.listProcesses()) if (p.worktree) pidByWorktree.set(p.worktree, p.pid);
@@ -9354,7 +9612,7 @@ var SwarmClient = class _SwarmClient {
9354
9612
  *
9355
9613
  * Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
9356
9614
  */
9357
- async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors) {
9615
+ async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors, providers) {
9358
9616
  try {
9359
9617
  const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
9360
9618
  if (daemonVersion) args.daemonVersion = daemonVersion;
@@ -9385,6 +9643,7 @@ var SwarmClient = class _SwarmClient {
9385
9643
  args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
9386
9644
  }
9387
9645
  if (executors) args.executorsAvailable = executors;
9646
+ if (providers) args.providersAvailable = providers;
9388
9647
  const res = await this.call("swarm_heartbeat", args);
9389
9648
  if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
9390
9649
  this.heartbeatOkAt = this.now();
@@ -9972,13 +10231,13 @@ var SwarmClient = class _SwarmClient {
9972
10231
  }
9973
10232
  persistPending() {
9974
10233
  try {
9975
- writeFileSync5(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
10234
+ writeFileSync6(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
9976
10235
  } catch {
9977
10236
  }
9978
10237
  }
9979
10238
  persistMap() {
9980
10239
  try {
9981
- writeFileSync5(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
10240
+ writeFileSync6(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
9982
10241
  } catch {
9983
10242
  }
9984
10243
  }
@@ -10178,20 +10437,20 @@ function tailFile(path, lines) {
10178
10437
  }
10179
10438
 
10180
10439
  // src/logRotate.ts
10181
- import { copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync6, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
10440
+ import { copyFileSync as copyFileSync4, renameSync as renameSync3, statSync as statSync7, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync8 } from "fs";
10182
10441
  var realIo = {
10183
10442
  size: (path) => {
10184
10443
  try {
10185
- return statSync6(path).size;
10444
+ return statSync7(path).size;
10186
10445
  } catch {
10187
10446
  return null;
10188
10447
  }
10189
10448
  },
10190
- copy: copyFileSync2,
10449
+ copy: copyFileSync4,
10191
10450
  rename: renameSync3,
10192
10451
  truncate: (path) => truncateSync(path, 0),
10193
10452
  unlink: unlinkSync2,
10194
- exists: existsSync7
10453
+ exists: existsSync8
10195
10454
  };
10196
10455
  var STRAY_GENERATIONS = 8;
10197
10456
  function rotateDaemonLog(path, opts, io = realIo) {
@@ -10219,17 +10478,17 @@ init_repoRegistry();
10219
10478
 
10220
10479
  // src/hostIdentity.ts
10221
10480
  import { hostname } from "os";
10222
- import { existsSync as existsSync8, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
10223
- import { join as join10 } from "path";
10481
+ import { existsSync as existsSync9, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "fs";
10482
+ import { join as join11 } from "path";
10224
10483
  import { randomUUID as randomUUID2 } from "crypto";
10225
10484
  function hostIdentityPath(home) {
10226
- return join10(home, "host.json");
10485
+ return join11(home, "host.json");
10227
10486
  }
10228
10487
  function loadHostIdentity(deps) {
10229
- const exists = deps.exists ?? existsSync8;
10488
+ const exists = deps.exists ?? existsSync9;
10230
10489
  const readFile2 = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
10231
10490
  const writeFile3 = deps.writeFile ?? ((p, data) => {
10232
- writeFileSync6(`${p}.tmp`, data);
10491
+ writeFileSync7(`${p}.tmp`, data);
10233
10492
  renameSync4(`${p}.tmp`, p);
10234
10493
  });
10235
10494
  const host = deps.hostname ?? hostname;
@@ -10344,8 +10603,8 @@ import { basename as basename4 } from "path";
10344
10603
  import { serve as serve2 } from "@hono/node-server";
10345
10604
 
10346
10605
  // src/diff.ts
10347
- import { statSync as statSync7 } from "fs";
10348
- import { join as join11 } from "path";
10606
+ import { statSync as statSync8 } from "fs";
10607
+ import { join as join12 } from "path";
10349
10608
  import { $ as $3 } from "execa";
10350
10609
  var MAX_FILES = 200;
10351
10610
  var MAX_LINES_PER_FILE = 1500;
@@ -10488,7 +10747,7 @@ async function resolveBase(wt, baseBranch) {
10488
10747
  }
10489
10748
  function mtime(path) {
10490
10749
  try {
10491
- return statSync7(path).mtimeMs;
10750
+ return statSync8(path).mtimeMs;
10492
10751
  } catch {
10493
10752
  return 0;
10494
10753
  }
@@ -10513,7 +10772,7 @@ async function collectRunDiff(worktree, opts = {}) {
10513
10772
  files.push(pf);
10514
10773
  }
10515
10774
  }
10516
- files.sort((a, b) => mtime(join11(worktree, b.path)) - mtime(join11(worktree, a.path)));
10775
+ files.sort((a, b) => mtime(join12(worktree, b.path)) - mtime(join12(worktree, a.path)));
10517
10776
  const truncatedFiles = files.length > MAX_FILES;
10518
10777
  if (truncatedFiles) files = files.slice(0, MAX_FILES);
10519
10778
  const added = files.reduce((s, f) => s + f.added, 0);
@@ -10683,9 +10942,9 @@ function mergeQueueCandidates(runs, repo, repoFallback) {
10683
10942
  const byPr = /* @__PURE__ */ new Map();
10684
10943
  for (const r of runs) {
10685
10944
  if (effectiveRepoOf(r.repo, repoFallback) !== repo || !isMergeCandidate(r)) continue;
10686
- const num = prNumberOf(r.pr_url);
10687
- const seen = byPr.get(num);
10688
- if (!seen || r.id > seen.id) byPr.set(num, r);
10945
+ const num2 = prNumberOf(r.pr_url);
10946
+ const seen = byPr.get(num2);
10947
+ if (!seen || r.id > seen.id) byPr.set(num2, r);
10689
10948
  }
10690
10949
  return [...byPr.values()];
10691
10950
  }
@@ -10698,10 +10957,10 @@ function makeMergeQueueResolver(deps) {
10698
10957
  };
10699
10958
  return async (run) => {
10700
10959
  if (!deps.baseSync || !run.pr_url) return null;
10701
- const num = prNumberOf(run.pr_url);
10702
- if (num == null) return null;
10960
+ const num2 = prNumberOf(run.pr_url);
10961
+ if (num2 == null) return null;
10703
10962
  const candidates = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback);
10704
- const mine = candidates.find((c) => prNumberOf(c.pr_url) === num);
10963
+ const mine = candidates.find((c) => prNumberOf(c.pr_url) === num2);
10705
10964
  if (!mine) return null;
10706
10965
  const ready = [];
10707
10966
  const urlOf = /* @__PURE__ */ new Map();
@@ -10799,28 +11058,28 @@ function makeMergeIntentResolver(deps) {
10799
11058
  if (!sha) {
10800
11059
  return { action: "wait", code: "checks_unknown", reason: "gh \u043D\u0435 \u0441\u043A\u0430\u0437\u0430\u043B HEAD PR \u2014 \u043D\u0435 \u043F\u043E\u043D\u044F\u0442\u044C, \u043F\u0440\u043E \u043A\u0430\u043A\u043E\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 \u044D\u0442\u043E\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442; \u0441\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C" };
10801
11060
  }
10802
- const live = deps.baseSync ? await deps.baseSync(prUrl).catch(() => null) : null;
10803
- if (!live || !live.headSha) {
11061
+ const live2 = deps.baseSync ? await deps.baseSync(prUrl).catch(() => null) : null;
11062
+ if (!live2 || !live2.headSha) {
10804
11063
  return { action: "wait", code: "base_unknown", reason: "\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0435\u0440\u0435\u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0436\u0438\u0432\u043E\u0439 HEAD PR \u2014 \u0441\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C" };
10805
11064
  }
10806
- if (verdict.action === "merge" && live.behindBy > 0) {
11065
+ if (verdict.action === "merge" && live2.behindBy > 0) {
10807
11066
  return {
10808
11067
  action: "refresh_base",
10809
11068
  code: "base_stale",
10810
- reason: `\u0431\u0430\u0437\u0430 \u0443\u0448\u043B\u0430 \u0432\u043F\u0435\u0440\u0451\u0434 \u043D\u0430 ${live.behindBy} \u043A\u043E\u043C\u043C\u0438\u0442(\u043E\u0432) \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u0433\u043E \u043C\u0435\u0440\u0436\u0430 \u2014 \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0432\u043B\u0438\u0442\u044C main \u0438 \u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F CI \u043D\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u043E\u0439 \u0431\u0430\u0437\u0435`
11069
+ reason: `\u0431\u0430\u0437\u0430 \u0443\u0448\u043B\u0430 \u0432\u043F\u0435\u0440\u0451\u0434 \u043D\u0430 ${live2.behindBy} \u043A\u043E\u043C\u043C\u0438\u0442(\u043E\u0432) \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u0433\u043E \u043C\u0435\u0440\u0436\u0430 \u2014 \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0432\u043B\u0438\u0442\u044C main \u0438 \u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F CI \u043D\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u043E\u0439 \u0431\u0430\u0437\u0435`
10811
11070
  };
10812
11071
  }
10813
- if (live.headSha !== sha) {
10814
- return { action: "wait", code: "checks_pending", reason: `\u043D\u0430 PR \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 ${shortSha(live.headSha)} \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E \u0437\u0430\u043D\u043E\u0432\u043E` };
11072
+ if (live2.headSha !== sha) {
11073
+ return { action: "wait", code: "checks_pending", reason: `\u043D\u0430 PR \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 ${shortSha(live2.headSha)} \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E \u0437\u0430\u043D\u043E\u0432\u043E` };
10815
11074
  }
10816
11075
  return verdict;
10817
11076
  };
10818
11077
  return async (run) => {
10819
11078
  if (!deps.baseSync || !run.pr_url || !hasMergeIntent(run)) return null;
10820
- const num = prNumberOf(run.pr_url);
10821
- if (num == null) return null;
11079
+ const num2 = prNumberOf(run.pr_url);
11080
+ if (num2 == null) return null;
10822
11081
  const marked = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback).filter(hasMergeIntent);
10823
- const mine = marked.find((c) => prNumberOf(c.pr_url) === num);
11082
+ const mine = marked.find((c) => prNumberOf(c.pr_url) === num2);
10824
11083
  if (!mine) return null;
10825
11084
  const queue = orderMergeQueue(marked.map(entryOf));
10826
11085
  const entry = entryOf(mine);
@@ -11091,13 +11350,13 @@ function timelineState(db2, now = Date.now(), overmindLog = []) {
11091
11350
  const lanes = [];
11092
11351
  const reviewPRs = [];
11093
11352
  for (const r of runs) {
11094
- const live = !isTerminalStatus(r.status);
11095
- const recencyGated = !live || r.status === "stalled";
11353
+ const live2 = !isTerminalStatus(r.status);
11354
+ const recencyGated = !live2 || r.status === "stalled";
11096
11355
  if (recencyGated && sqliteMs(r.updated_at) < cutoff) continue;
11097
11356
  const events = db2.events(r.id).map((e) => ({ kind: e.kind, text: e.text ?? null, at: sqliteIso(e.at) }));
11098
11357
  const steps = events.filter((e) => e.kind === "step" && e.text).map((e) => ({ at: e.at, text: e.text }));
11099
11358
  const lastStep = steps.length ? steps[steps.length - 1].text : null;
11100
- const laneEnd = live ? nowIso : sqliteIso(r.updated_at);
11359
+ const laneEnd = live2 ? nowIso : sqliteIso(r.updated_at);
11101
11360
  const segments = buildSegments(sqliteIso(r.created_at), events, laneEnd);
11102
11361
  const deps = (r.depends_on ?? []).map((ref) => {
11103
11362
  const { slug, unresolved } = resolveDepRef(ref, { slugByCuid: EMPTY_CUID_MAP, knownSlugs });
@@ -11119,7 +11378,7 @@ function timelineState(db2, now = Date.now(), overmindLog = []) {
11119
11378
  ci = ciCache.get(r.pr_url)?.ci ?? null;
11120
11379
  }
11121
11380
  }
11122
- const obsoleteBy = live && doneNameBySlug.has(r.task_slug) ? doneNameBySlug.get(r.task_slug) : null;
11381
+ const obsoleteBy = live2 && doneNameBySlug.has(r.task_slug) ? doneNameBySlug.get(r.task_slug) : null;
11123
11382
  const obsolete = obsoleteBy != null;
11124
11383
  const blocker = obsolete ? { icon: "\u{1F9DF}", text: `\u0443\u0441\u0442\u0430\u0440\u0435\u043B \u2014 \u0437\u0430\u0434\u0430\u0447\u0443 \u0437\u0430\u043A\u0440\u044B\u043B ${obsoleteBy}`, tone: "queued" } : computeBlocker(r.status, r.stop_reason, lastStep, r.pr_url, ci, deps, openQ.get(r.id) ?? null);
11125
11384
  lanes.push({
@@ -11131,7 +11390,7 @@ function timelineState(db2, now = Date.now(), overmindLog = []) {
11131
11390
  area: r.area,
11132
11391
  start: sqliteIso(r.created_at),
11133
11392
  end: laneEnd,
11134
- live,
11393
+ live: live2,
11135
11394
  obsolete,
11136
11395
  obsoleteBy,
11137
11396
  segments,
@@ -12248,7 +12507,7 @@ function setPausedVia(deps, paused, source, reason) {
12248
12507
  function daemonStatus(deps) {
12249
12508
  const counts = {};
12250
12509
  for (const r of deps.db.listRuns()) counts[r.status] = (counts[r.status] ?? 0) + 1;
12251
- const live = deps.liveSessionIds?.() ?? [];
12510
+ const live2 = deps.liveSessionIds?.() ?? [];
12252
12511
  const ver = deps.daemonVersion?.() ?? null;
12253
12512
  const queuedReady = deps.db.listRuns("queued").filter((r) => deps.db.unmetDeps(r.depends_on).length === 0).length;
12254
12513
  return {
@@ -12265,9 +12524,9 @@ function daemonStatus(deps) {
12265
12524
  backend: SWARM_BACKEND,
12266
12525
  paused: deps.control?.paused ?? false,
12267
12526
  pause: deps.control?.state ?? null,
12268
- restart: deps.restart ? { phase: deps.restart.phase, intent: deps.restart.intent, sessions: live } : null,
12527
+ restart: deps.restart ? { phase: deps.restart.phase, intent: deps.restart.intent, sessions: live2 } : null,
12269
12528
  counts,
12270
- live,
12529
+ live: live2,
12271
12530
  version: PACKAGE_VERSION,
12272
12531
  repos: deps.repoMap?.() ?? [],
12273
12532
  // TASWM-324: «нас не пускают» — то же состояние процесса, что кричит рамкой в лог.
@@ -12537,15 +12796,15 @@ import { cancelRun, respawnRun, runDaemon, wakeRun, OVERMIND_ENTRY, listDronePro
12537
12796
 
12538
12797
  // src/launcher.ts
12539
12798
  import { spawn as spawn2 } from "child_process";
12540
- import { closeSync as closeSync3, existsSync as existsSync9, mkdirSync as mkdirSync9, openSync as openSync3, readdirSync as readdirSync8 } from "fs";
12799
+ import { closeSync as closeSync3, existsSync as existsSync10, mkdirSync as mkdirSync10, openSync as openSync3, readdirSync as readdirSync9 } from "fs";
12541
12800
  import { createRequire } from "module";
12542
12801
  import { homedir as homedir5 } from "os";
12543
- import { dirname as dirname4, join as join14 } from "path";
12802
+ import { dirname as dirname4, join as join15 } from "path";
12544
12803
  import { fileURLToPath as fileURLToPath2 } from "url";
12545
12804
 
12546
12805
  // src/droneHome.ts
12547
- import { lstatSync, mkdirSync as mkdirSync8, readdirSync as readdirSync7, rmSync, statSync as statSync8, symlinkSync as symlinkSync2 } from "fs";
12548
- import { join as join13, relative, sep } from "path";
12806
+ import { lstatSync, mkdirSync as mkdirSync9, readdirSync as readdirSync8, rmSync, statSync as statSync9, symlinkSync as symlinkSync3 } from "fs";
12807
+ import { join as join14, relative, sep } from "path";
12549
12808
  var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
12550
12809
  function droneHomeDeny(env = process.env) {
12551
12810
  const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
@@ -12563,34 +12822,34 @@ function buildDroneHome(opts) {
12563
12822
  const { realHome, droneHome } = opts;
12564
12823
  const deny = opts.deny ?? droneHomeDeny();
12565
12824
  const platform = opts.platform ?? process.platform;
12566
- mkdirSync8(droneHome, { recursive: true, mode: 448 });
12567
- const wanted = new Set(mirroredEntries(readdirSync7(realHome), deny, platform));
12825
+ mkdirSync9(droneHome, { recursive: true, mode: 448 });
12826
+ const wanted = new Set(mirroredEntries(readdirSync8(realHome), deny, platform));
12568
12827
  const rel = relative(realHome, droneHome);
12569
12828
  if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
12570
- for (const existing of readdirSync7(droneHome)) {
12829
+ for (const existing of readdirSync8(droneHome)) {
12571
12830
  if (wanted.has(existing)) continue;
12572
- const p = join13(droneHome, existing);
12831
+ const p = join14(droneHome, existing);
12573
12832
  try {
12574
12833
  if (lstatSync(p).isSymbolicLink()) rmSync(p);
12575
12834
  } catch {
12576
12835
  }
12577
12836
  }
12578
12837
  for (const name of wanted) {
12579
- const link = join13(droneHome, name);
12838
+ const link = join14(droneHome, name);
12580
12839
  try {
12581
12840
  lstatSync(link);
12582
12841
  continue;
12583
12842
  } catch {
12584
12843
  }
12585
- const target = join13(realHome, name);
12844
+ const target = join14(realHome, name);
12586
12845
  let isDir;
12587
12846
  try {
12588
- isDir = statSync8(target).isDirectory();
12847
+ isDir = statSync9(target).isDirectory();
12589
12848
  } catch {
12590
12849
  continue;
12591
12850
  }
12592
12851
  try {
12593
- symlinkSync2(target, link, isDir ? "junction" : "file");
12852
+ symlinkSync3(target, link, isDir ? "junction" : "file");
12594
12853
  } catch (e) {
12595
12854
  if (e.code !== "EEXIST") throw e;
12596
12855
  }
@@ -12603,7 +12862,7 @@ var REAL_PATH_ENV_PINS = [
12603
12862
  { env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
12604
12863
  ];
12605
12864
  function realPathEnvPins(realHome) {
12606
- return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join13(realHome, ...segments)]));
12865
+ return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join14(realHome, ...segments)]));
12607
12866
  }
12608
12867
  function dronePrivatePaths(realHome, swarmHome) {
12609
12868
  return [
@@ -12686,7 +12945,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
12686
12945
  const env = { ...base3 };
12687
12946
  const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
12688
12947
  if (isolate && !base3.TURBO_CACHE_DIR) {
12689
- env.TURBO_CACHE_DIR = join14(worktree, ".turbo", "cache");
12948
+ env.TURBO_CACHE_DIR = join15(worktree, ".turbo", "cache");
12690
12949
  }
12691
12950
  Object.assign(env, droneTestEnv(droneCap, base3));
12692
12951
  for (const key of DAEMON_STATE_ENV) delete env[key];
@@ -12717,21 +12976,21 @@ var DAEMON_STATE_ENV = [
12717
12976
  ...PROVIDER_TOKEN_ENV
12718
12977
  ];
12719
12978
  function bundledRoleDir(kind) {
12720
- return join14(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
12979
+ return join15(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
12721
12980
  }
12722
12981
  function roleDir(worktree, kind) {
12723
- return join14(worktree, ".claude", "roles", kind);
12982
+ return join15(worktree, ".claude", "roles", kind);
12724
12983
  }
12725
12984
  function roleDirs(worktree, kind) {
12726
- return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync9(dir));
12985
+ return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync10(dir));
12727
12986
  }
12728
12987
  function nextRunLogPath(runLogsDir, runLogKeys) {
12729
12988
  const dir = runLogDirForWrite(runLogsDir, runLogKeys);
12730
12989
  if (!dir) throw new Error("\u043D\u0435\u0447\u0435\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u0432\u0430\u0442\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433 run-log'\u043E\u0432: \u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 (SwarmStore.runLogKeys)");
12731
- mkdirSync9(dir, { recursive: true });
12990
+ mkdirSync10(dir, { recursive: true });
12732
12991
  let files = [];
12733
12992
  try {
12734
- files = readdirSync8(dir);
12993
+ files = readdirSync9(dir);
12735
12994
  } catch {
12736
12995
  }
12737
12996
  let next = 0;
@@ -12739,7 +12998,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
12739
12998
  const m = /^(\d+)\.json/.exec(f);
12740
12999
  if (m) next = Math.max(next, Number(m[1]) + 1);
12741
13000
  }
12742
- return join14(dir, `${next}.json`);
13001
+ return join15(dir, `${next}.json`);
12743
13002
  }
12744
13003
  function openNextRunLog(runLogsDir, runLogKeys) {
12745
13004
  for (let attempt = 0; attempt < 50; attempt++) {
@@ -12825,15 +13084,15 @@ function makeLauncher(cfg) {
12825
13084
  console.error(`[launcher] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0443\u0441\u0438\u043B\u0438\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}: ${String(e)}`);
12826
13085
  }
12827
13086
  try {
12828
- cfg.recordModelRef?.(run.id, modelRef);
13087
+ cfg.recordModelRef?.(run.id, modelRef, run);
12829
13088
  } catch (e) {
12830
13089
  console.error(`[launcher] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u043C\u043E\u0434\u0435\u043B\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}: ${String(e)}`);
12831
13090
  }
12832
13091
  const realHome = cfg.droneIsolation?.realHome ?? homedir5();
12833
13092
  const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
12834
- if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync9(join14(cfg.droneIsolation.swarmHome, "token"))) {
13093
+ if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync10(join15(cfg.droneIsolation.swarmHome, "token"))) {
12835
13094
  console.error(
12836
- `[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join14(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
13095
+ `[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join15(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
12837
13096
  );
12838
13097
  }
12839
13098
  const executor = cfg.executor ?? executorForRun(run, cfg.swarmModels?.() ?? null);
@@ -13732,7 +13991,7 @@ async function runDaemonLogs(argv) {
13732
13991
  console.error(`\u043B\u043E\u0433 \u041F\u0420\u041E\u0413\u041E\u041D\u0410 \u2014 \u044D\u0442\u043E \xABswarm log ${stray}\xBB (\u0431\u0435\u0437 s). \xABswarm logs\xBB \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \xABswarm logs -n 200\xBB.`);
13733
13992
  return 1;
13734
13993
  }
13735
- if (!existsSync17(DAEMON_LOG_PATH)) {
13994
+ if (!existsSync18(DAEMON_LOG_PATH)) {
13736
13995
  console.error(`\u043B\u043E\u0433\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442: ${DAEMON_LOG_PATH}`);
13737
13996
  console.error("\u043E\u043D \u043F\u0438\u0448\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0444\u043E\u043D\u043E\u0432\u043E\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435 (swarm start). \u0414\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u043D\u044F\u0442 \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 (--foreground) \u2014 \u0432\u044B\u0432\u043E\u0434 \u0438\u0434\u0451\u0442 \u0442\u0443\u0434\u0430.");
13738
13997
  return 1;
@@ -13740,12 +13999,12 @@ async function runDaemonLogs(argv) {
13740
13999
  const head = tailFile(DAEMON_LOG_PATH, lines);
13741
14000
  if (head) console.log(head);
13742
14001
  if (!follow) return 0;
13743
- let pos = statSync11(DAEMON_LOG_PATH).size;
14002
+ let pos = statSync12(DAEMON_LOG_PATH).size;
13744
14003
  for (; ; ) {
13745
14004
  await sleep2(500);
13746
14005
  let size;
13747
14006
  try {
13748
- size = statSync11(DAEMON_LOG_PATH).size;
14007
+ size = statSync12(DAEMON_LOG_PATH).size;
13749
14008
  } catch {
13750
14009
  continue;
13751
14010
  }
@@ -14014,10 +14273,10 @@ function effectiveWorktreesDir() {
14014
14273
  legacyDir: WORKTREES_LEGACY_DIR,
14015
14274
  targetDir: WORKTREES_DIR,
14016
14275
  envOverride: process.env.SWARM_WORKTREES || "",
14017
- exists: (d) => existsSync17(d),
14276
+ exists: (d) => existsSync18(d),
14018
14277
  isNonEmptyDir: (d) => {
14019
14278
  try {
14020
- return readdirSync11(d).length > 0;
14279
+ return readdirSync12(d).length > 0;
14021
14280
  } catch {
14022
14281
  return false;
14023
14282
  }
@@ -14073,15 +14332,15 @@ async function scoutRepo(repo) {
14073
14332
  await $6({ reject: false })`git -C ${worktree} reset --hard ${baseRef}`;
14074
14333
  }
14075
14334
  await setWorktreeIdentity(worktree, name);
14076
- const abs = (rel) => join26(worktree, rel);
14335
+ const abs = (rel) => join27(worktree, rel);
14077
14336
  const code = await runRepoScout({
14078
14337
  repoFullName,
14079
14338
  repoPath: worktree,
14080
14339
  name,
14081
- readFile: (rel) => existsSync17(abs(rel)) ? readFileSync19(abs(rel), "utf8") : null,
14340
+ readFile: (rel) => existsSync18(abs(rel)) ? readFileSync19(abs(rel), "utf8") : null,
14082
14341
  writeFile: (rel, text) => {
14083
- mkdirSync10(dirname5(abs(rel)), { recursive: true });
14084
- writeFileSync14(abs(rel), text);
14342
+ mkdirSync11(dirname5(abs(rel)), { recursive: true });
14343
+ writeFileSync15(abs(rel), text);
14085
14344
  },
14086
14345
  runScout: (prompt) => new Promise((resolve5) => {
14087
14346
  const child = spawn3(bin, [...ISOLATION_FLAGS, "--dangerously-skip-permissions", "-p", prompt], {