@taskless-app/swarm 0.6.31 → 0.6.32

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 +82 -17
  2. package/dist/overmind.js +847 -474
  3. package/package.json +2 -2
package/dist/overmind.js CHANGED
@@ -1447,7 +1447,7 @@ function buildZoneModel(entries, opts = {}) {
1447
1447
  if (!m) labelVotes.set(z2, m = /* @__PURE__ */ new Map());
1448
1448
  bump(m, manual);
1449
1449
  }
1450
- const rename = /* @__PURE__ */ new Map();
1450
+ const rename2 = /* @__PURE__ */ new Map();
1451
1451
  const takenNames = /* @__PURE__ */ new Set([SPINE_ZONE]);
1452
1452
  const clustersByEvidence = [...labelVotes.entries()].sort((a, b) => {
1453
1453
  const sum = (m) => [...m.values()].reduce((x, y) => x + y, 0);
@@ -1456,11 +1456,11 @@ function buildZoneModel(entries, opts = {}) {
1456
1456
  for (const [zone, votes] of clustersByEvidence) {
1457
1457
  const top = topKey(votes);
1458
1458
  if (!top || top.share < 0.6 || takenNames.has(top.key) || top.key === zone) continue;
1459
- rename.set(zone, top.key);
1459
+ rename2.set(zone, top.key);
1460
1460
  takenNames.add(top.key);
1461
1461
  }
1462
- const renamed = (z2) => rename.get(z2) ?? z2;
1463
- if (rename.size) {
1462
+ const renamed = (z2) => rename2.get(z2) ?? z2;
1463
+ if (rename2.size) {
1464
1464
  for (const [f, z2] of fileZone) fileZone.set(f, renamed(z2));
1465
1465
  for (const [s, z2] of taskZone) taskZone.set(s, renamed(z2));
1466
1466
  }
@@ -1566,8 +1566,8 @@ function predictFiles(model, opts) {
1566
1566
  }
1567
1567
  const zone = normalizeZoneName(opts.zone) ?? (opts.text ? suggestZonesFromText(model, opts.text)[0]?.zone ?? null : null);
1568
1568
  if (zone) {
1569
- const stat = model.zones.find((z2) => z2.zone === zone);
1570
- if (stat) stat.files.slice(0, limit).forEach((f, i) => put(f, Math.max(0.12, 0.45 - i * 0.02), `\u0437\u043E\u043D\u0430 ${zone}`));
1569
+ const stat2 = model.zones.find((z2) => z2.zone === zone);
1570
+ if (stat2) stat2.files.slice(0, limit).forEach((f, i) => put(f, Math.max(0.12, 0.45 - i * 0.02), `\u0437\u043E\u043D\u0430 ${zone}`));
1571
1571
  }
1572
1572
  return [...best.values()].sort((a, b) => b.weight - a.weight || a.path.localeCompare(b.path)).slice(0, limit);
1573
1573
  }
@@ -2231,10 +2231,10 @@ function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWAR
2231
2231
  const real = (env.CODEX_HOME ?? "").trim() || join5(realHome, ".codex");
2232
2232
  for (const file of ["auth.json", "config.toml"]) {
2233
2233
  const target = join5(real, file);
2234
- const link = join5(home, file);
2235
- if (!existsSync4(target) || existsSync4(link)) continue;
2234
+ const link2 = join5(home, file);
2235
+ if (!existsSync4(target) || existsSync4(link2)) continue;
2236
2236
  try {
2237
- symlinkSync(target, link);
2237
+ symlinkSync(target, link2);
2238
2238
  } catch {
2239
2239
  }
2240
2240
  }
@@ -2433,6 +2433,9 @@ __export(config_exports, {
2433
2433
  DRONE_HOME: () => DRONE_HOME,
2434
2434
  DRONE_HOME_ISOLATION: () => DRONE_HOME_ISOLATION,
2435
2435
  DRONE_ISOLATION: () => DRONE_ISOLATION,
2436
+ ENV_CACHE_DIR: () => ENV_CACHE_DIR,
2437
+ ENV_CACHE_ENABLED: () => ENV_CACHE_ENABLED,
2438
+ ENV_CACHE_KEEP: () => ENV_CACHE_KEEP,
2436
2439
  EXECUTOR: () => EXECUTOR,
2437
2440
  HEARTBEAT_PATH: () => HEARTBEAT_PATH,
2438
2441
  HEARTBEAT_STALE_MS: () => HEARTBEAT_STALE_MS,
@@ -2602,7 +2605,7 @@ function reviewCutoffAt() {
2602
2605
  }
2603
2606
  return at;
2604
2607
  }
2605
- var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, SWARM_WORKTREE_REAP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, SWARM_REPO_SCAN_TTL_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_REST_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, SWARM_AUTO_VERIFY, SWARM_MERGE_QUEUE, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_ANOMALY_DEP_MISSING_MS, SWARM_WATCHDOG_STALL_MS;
2608
+ var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, ENV_CACHE_DIR, ENV_CACHE_ENABLED, ENV_CACHE_KEEP, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, SWARM_WORKTREE_REAP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, SWARM_REPO_SCAN_TTL_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_REST_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, SWARM_AUTO_VERIFY, SWARM_MERGE_QUEUE, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_ANOMALY_DEP_MISSING_MS, SWARM_WATCHDOG_STALL_MS;
2606
2609
  var init_config = __esm({
2607
2610
  "src/config.ts"() {
2608
2611
  "use strict";
@@ -2628,6 +2631,9 @@ var init_config = __esm({
2628
2631
  SWARM_OUTBOX = process.env.SWARM_OUTBOX || join6(SWARM_HOME, "outbox.jsonl");
2629
2632
  SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
2630
2633
  SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
2634
+ ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join6(SWARM_HOME, "env-cache");
2635
+ ENV_CACHE_ENABLED = process.env.SWARM_ENV_CACHE !== "0";
2636
+ ENV_CACHE_KEEP = Math.trunc(envNum("SWARM_ENV_CACHE_KEEP", 4));
2631
2637
  SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
2632
2638
  SWARM_MIN_FREE_GB = envNum("SWARM_MIN_FREE_GB", 1.5);
2633
2639
  SWARM_MAX_FD_LOAD_FACTOR = envNum("SWARM_MAX_FD_LOAD_FACTOR", 0.9);
@@ -3327,6 +3333,118 @@ var init_logNoise = __esm({
3327
3333
  }
3328
3334
  });
3329
3335
 
3336
+ // src/netTimeout.ts
3337
+ import { basename as basename2 } from "path";
3338
+ function isTimedOut(outcome) {
3339
+ return typeof outcome === "object" && outcome !== null && outcome.timedOut === true;
3340
+ }
3341
+ function noteTimedOut(scope, call, timeoutMs, outcome, warn = console.warn) {
3342
+ const line = `[${scope}] \u23F1 \u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${humanMs(timeoutMs)} \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call} \u2014 \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B \u0438\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435`;
3343
+ if (!isTimedOut(outcome)) {
3344
+ timeoutNoise.forget(noiseKey(scope, call));
3345
+ return false;
3346
+ }
3347
+ const say = timeoutNoise.say(noiseKey(scope, call), line, Date.now());
3348
+ if (say) warn(say);
3349
+ return true;
3350
+ }
3351
+ function noiseKey(scope, call) {
3352
+ return `timeout:${scope}:${call}`;
3353
+ }
3354
+ function callFailureWord(f) {
3355
+ switch (f.reason) {
3356
+ case "timeout":
3357
+ return "\u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 \u043E\u0442\u0432\u0435\u0434\u0451\u043D\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F (\u043F\u0440\u0435\u0440\u0432\u0430\u043D)";
3358
+ case "unreadable":
3359
+ return "\u043E\u0442\u0432\u0435\u0442\u0438\u043B, \u043D\u043E \u0432\u044B\u0432\u043E\u0434 \u043D\u0435 \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u043D";
3360
+ case "crashed":
3361
+ return "\u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0441\u044F";
3362
+ default:
3363
+ return "\u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0431\u0435\u0437 \u0432\u044B\u0432\u043E\u0434\u0430";
3364
+ }
3365
+ }
3366
+ function classifyCallFailure(outcome) {
3367
+ if (isTimedOut(outcome)) return { reason: "timeout", detail: firstLineOf(errText(outcome)) };
3368
+ const code = outcome?.code;
3369
+ if (code === "ENOENT") return { reason: "crashed", detail: firstLineOf(errText(outcome)) };
3370
+ return { reason: "empty", detail: firstLineOf(errText(outcome)) };
3371
+ }
3372
+ function errText(outcome) {
3373
+ const o = outcome;
3374
+ for (const v of [o?.stderr, o?.shortMessage, o?.message]) if (typeof v === "string" && v.trim()) return v;
3375
+ return "";
3376
+ }
3377
+ function firstLineOf(s) {
3378
+ return s.split("\n").map((l) => l.trim()).find((l) => l.length > 0) ?? "";
3379
+ }
3380
+ function noteCallFailure(scope, call, outcome, warn = console.warn) {
3381
+ const f = classifyCallFailure(outcome);
3382
+ if (f.reason === "timeout") return f;
3383
+ const line = `[${scope}] \u2716 \u0432\u044B\u0437\u043E\u0432 ${callFailureWord(f)}: ${call}${f.detail ? ` \u2014 ${f.detail}` : ""} \u2014 \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B \u0438\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435`;
3384
+ const say = timeoutNoise.say(noiseKey(scope, call), line, Date.now());
3385
+ if (say) warn(say);
3386
+ return f;
3387
+ }
3388
+ function humanMs(ms) {
3389
+ return ms >= 1e3 ? `${Math.round(ms / 1e3)}\u0441` : `${ms}\u043C\u0441`;
3390
+ }
3391
+ function destroyPipes(child) {
3392
+ for (const pipe of [child.stdout, child.stderr, child.stdin]) {
3393
+ try {
3394
+ pipe?.destroy?.();
3395
+ } catch {
3396
+ }
3397
+ }
3398
+ }
3399
+ async function withHardTimeout(child, scope, call, timeoutMs, warn = console.warn) {
3400
+ let timer;
3401
+ const capped = new Promise((_, reject) => {
3402
+ timer = setTimeout(() => {
3403
+ child.kill("SIGKILL");
3404
+ destroyPipes(child);
3405
+ reject(new CallTimedOutError(call, timeoutMs));
3406
+ }, timeoutMs + HARD_CAP_GRACE_MS);
3407
+ timer.unref?.();
3408
+ });
3409
+ try {
3410
+ const res = await Promise.race([child, capped]);
3411
+ noteTimedOut(scope, call, timeoutMs, res, warn);
3412
+ return res;
3413
+ } catch (e) {
3414
+ noteTimedOut(scope, call, timeoutMs, e, warn);
3415
+ throw e;
3416
+ } finally {
3417
+ if (timer) clearTimeout(timer);
3418
+ }
3419
+ }
3420
+ function callLabel(cmd, args, head = 4) {
3421
+ const shown = args.slice(0, head).join(" ");
3422
+ return `${cmd} ${shown}${args.length > head ? " \u2026" : ""}`.trim();
3423
+ }
3424
+ function gitCallLabel(repo, args) {
3425
+ return `git -C ${basename2(repo) || repo} ${args.join(" ")}`;
3426
+ }
3427
+ var GH_CALL_TIMEOUT_MS, GIT_NET_TIMEOUT_MS, GIT_LOCAL_TIMEOUT_MS, GIT_HEAVY_NET_TIMEOUT_MS, HARD_CAP_GRACE_MS, CallTimedOutError, timeoutNoise;
3428
+ var init_netTimeout = __esm({
3429
+ "src/netTimeout.ts"() {
3430
+ "use strict";
3431
+ init_logNoise();
3432
+ GH_CALL_TIMEOUT_MS = 15e3;
3433
+ GIT_NET_TIMEOUT_MS = 15e3;
3434
+ GIT_LOCAL_TIMEOUT_MS = 5e3;
3435
+ GIT_HEAVY_NET_TIMEOUT_MS = 6e4;
3436
+ HARD_CAP_GRACE_MS = 2e3;
3437
+ CallTimedOutError = class extends Error {
3438
+ timedOut = true;
3439
+ constructor(call, timeoutMs) {
3440
+ super(`\u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${timeoutMs}\u043C\u0441 \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call}`);
3441
+ this.name = "CallTimedOutError";
3442
+ }
3443
+ };
3444
+ timeoutNoise = makeRepeatFilter();
3445
+ }
3446
+ });
3447
+
3330
3448
  // src/procList.ts
3331
3449
  function parsePsRows(out) {
3332
3450
  const rows = [];
@@ -3645,84 +3763,6 @@ var init_procCleanup = __esm({
3645
3763
  }
3646
3764
  });
3647
3765
 
3648
- // src/netTimeout.ts
3649
- import { basename as basename2 } from "path";
3650
- function isTimedOut(outcome) {
3651
- return typeof outcome === "object" && outcome !== null && outcome.timedOut === true;
3652
- }
3653
- function noteTimedOut(scope, call, timeoutMs, outcome, warn = console.warn) {
3654
- const line = `[${scope}] \u23F1 \u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${humanMs(timeoutMs)} \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call} \u2014 \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B \u0438\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435`;
3655
- if (!isTimedOut(outcome)) {
3656
- timeoutNoise.forget(noiseKey(scope, call));
3657
- return false;
3658
- }
3659
- const say = timeoutNoise.say(noiseKey(scope, call), line, Date.now());
3660
- if (say) warn(say);
3661
- return true;
3662
- }
3663
- function noiseKey(scope, call) {
3664
- return `timeout:${scope}:${call}`;
3665
- }
3666
- function humanMs(ms) {
3667
- return ms >= 1e3 ? `${Math.round(ms / 1e3)}\u0441` : `${ms}\u043C\u0441`;
3668
- }
3669
- function destroyPipes(child) {
3670
- for (const pipe of [child.stdout, child.stderr, child.stdin]) {
3671
- try {
3672
- pipe?.destroy?.();
3673
- } catch {
3674
- }
3675
- }
3676
- }
3677
- async function withHardTimeout(child, scope, call, timeoutMs, warn = console.warn) {
3678
- let timer;
3679
- const capped = new Promise((_, reject) => {
3680
- timer = setTimeout(() => {
3681
- child.kill("SIGKILL");
3682
- destroyPipes(child);
3683
- reject(new CallTimedOutError(call, timeoutMs));
3684
- }, timeoutMs + HARD_CAP_GRACE_MS);
3685
- timer.unref?.();
3686
- });
3687
- try {
3688
- const res = await Promise.race([child, capped]);
3689
- noteTimedOut(scope, call, timeoutMs, res, warn);
3690
- return res;
3691
- } catch (e) {
3692
- noteTimedOut(scope, call, timeoutMs, e, warn);
3693
- throw e;
3694
- } finally {
3695
- if (timer) clearTimeout(timer);
3696
- }
3697
- }
3698
- function callLabel(cmd, args, head = 4) {
3699
- const shown = args.slice(0, head).join(" ");
3700
- return `${cmd} ${shown}${args.length > head ? " \u2026" : ""}`.trim();
3701
- }
3702
- function gitCallLabel(repo, args) {
3703
- return `git -C ${basename2(repo) || repo} ${args.join(" ")}`;
3704
- }
3705
- var GH_CALL_TIMEOUT_MS, GIT_NET_TIMEOUT_MS, GIT_LOCAL_TIMEOUT_MS, GIT_HEAVY_NET_TIMEOUT_MS, HARD_CAP_GRACE_MS, CallTimedOutError, timeoutNoise;
3706
- var init_netTimeout = __esm({
3707
- "src/netTimeout.ts"() {
3708
- "use strict";
3709
- init_logNoise();
3710
- GH_CALL_TIMEOUT_MS = 15e3;
3711
- GIT_NET_TIMEOUT_MS = 15e3;
3712
- GIT_LOCAL_TIMEOUT_MS = 5e3;
3713
- GIT_HEAVY_NET_TIMEOUT_MS = 6e4;
3714
- HARD_CAP_GRACE_MS = 2e3;
3715
- CallTimedOutError = class extends Error {
3716
- timedOut = true;
3717
- constructor(call, timeoutMs) {
3718
- super(`\u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${timeoutMs}\u043C\u0441 \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call}`);
3719
- this.name = "CallTimedOutError";
3720
- }
3721
- };
3722
- timeoutNoise = makeRepeatFilter();
3723
- }
3724
- });
3725
-
3726
3766
  // src/git.ts
3727
3767
  import { existsSync as existsSync11, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
3728
3768
  import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
@@ -4068,12 +4108,12 @@ __export(droneChannel_exports, {
4068
4108
  verifyLocalCliToken: () => verifyLocalCliToken
4069
4109
  });
4070
4110
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
4071
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "fs";
4111
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync9, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
4072
4112
  import { dirname as dirname4 } from "path";
4073
4113
  import { serve } from "@hono/node-server";
4074
4114
  function loadChannelSecret(path) {
4075
4115
  try {
4076
- const s = readFileSync15(path, "utf8").trim();
4116
+ const s = readFileSync17(path, "utf8").trim();
4077
4117
  if (s.length >= 32) return s;
4078
4118
  } catch {
4079
4119
  }
@@ -4436,8 +4476,8 @@ var init_droneChannel = __esm({
4436
4476
  });
4437
4477
 
4438
4478
  // src/attachments.ts
4439
- import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
4440
- import { join as join21 } from "path";
4479
+ import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
4480
+ import { join as join22 } from "path";
4441
4481
  function attachmentsFromDoc(doc, origin) {
4442
4482
  const out = [];
4443
4483
  const walk2 = (node) => {
@@ -4522,13 +4562,13 @@ async function materializeAttachments(opts) {
4522
4562
  const maxTotal = opts.maxTotalBytes ?? ATTACH_MAX_TOTAL_BYTES;
4523
4563
  const maxFile = opts.maxFileBytes ?? ATTACH_MAX_FILE_BYTES;
4524
4564
  const io = opts.io ?? {
4525
- removeDir: async (dir2) => rm2(dir2, { recursive: true, force: true }),
4526
- mkdir: async (dir2) => void await mkdir2(dir2, { recursive: true }),
4527
- writeFile: async (path, bytes) => writeFile2(path, bytes)
4565
+ removeDir: async (dir2) => rm3(dir2, { recursive: true, force: true }),
4566
+ mkdir: async (dir2) => void await mkdir3(dir2, { recursive: true }),
4567
+ writeFile: async (path, bytes) => writeFile3(path, bytes)
4528
4568
  };
4529
4569
  const result = { files: [], skipped: [] };
4530
4570
  if (opts.refs.length === 0) return result;
4531
- const dir = join21(opts.worktree, ATTACHMENTS_DIR);
4571
+ const dir = join22(opts.worktree, ATTACHMENTS_DIR);
4532
4572
  await io.removeDir(dir);
4533
4573
  let dirReady = false;
4534
4574
  let total = 0;
@@ -4553,7 +4593,7 @@ async function materializeAttachments(opts) {
4553
4593
  try {
4554
4594
  file = await opts.fetchFile(ref.url);
4555
4595
  } catch (e) {
4556
- result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u043E\u0441\u044C (${errText(e)})`);
4596
+ result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u043E\u0441\u044C (${errText2(e)})`);
4557
4597
  continue;
4558
4598
  }
4559
4599
  if (!file) {
@@ -4574,9 +4614,9 @@ async function materializeAttachments(opts) {
4574
4614
  await io.mkdir(dir);
4575
4615
  dirReady = true;
4576
4616
  }
4577
- await io.writeFile(join21(dir, name), file.bytes);
4617
+ await io.writeFile(join22(dir, name), file.bytes);
4578
4618
  } catch (e) {
4579
- result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText(e)})`);
4619
+ result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText2(e)})`);
4580
4620
  continue;
4581
4621
  }
4582
4622
  total += file.bytes.byteLength;
@@ -4630,7 +4670,7 @@ async function prepareTaskAttachments(opts) {
4630
4670
  }
4631
4671
  return renderAttachmentsBlock(result);
4632
4672
  } catch (e) {
4633
- opts.onEvent?.(`\u0432\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044B\u043B\u043E\u0436\u0438\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${errText(e)} \u2014 \u0434\u0440\u043E\u043D \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 \u0431\u0435\u0437 \u043D\u0438\u0445`);
4673
+ opts.onEvent?.(`\u0432\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044B\u043B\u043E\u0436\u0438\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${errText2(e)} \u2014 \u0434\u0440\u043E\u043D \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 \u0431\u0435\u0437 \u043D\u0438\u0445`);
4634
4674
  return void 0;
4635
4675
  }
4636
4676
  }
@@ -4653,7 +4693,7 @@ function mb(bytes) {
4653
4693
  if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} \u041C\u0411`;
4654
4694
  return `${bytes === 0 ? 0 : Math.max(1, Math.round(bytes / 1024))} \u041A\u0411`;
4655
4695
  }
4656
- function errText(e) {
4696
+ function errText2(e) {
4657
4697
  return e instanceof Error ? e.message : String(e);
4658
4698
  }
4659
4699
  var ATTACHMENTS_DIR, ATTACH_MAX_FILES, ATTACH_MAX_TOTAL_BYTES, ATTACH_MAX_FILE_BYTES, IMAGE_EXT, DOC_EXT, TEXT_EXT, KNOWN_EXT, TRANSLIT, EXT_BY_MIME;
@@ -4727,7 +4767,7 @@ var init_attachments = __esm({
4727
4767
 
4728
4768
  // src/shotUpload.ts
4729
4769
  import { readFile as fsReadFile } from "fs/promises";
4730
- import { basename as basename4, isAbsolute as isAbsolute3, join as join22 } from "path";
4770
+ import { basename as basename4, isAbsolute as isAbsolute3, join as join23 } from "path";
4731
4771
  function shotMimetype(path) {
4732
4772
  const dot = path.lastIndexOf(".");
4733
4773
  if (dot < 0) return null;
@@ -4753,7 +4793,7 @@ async function readShots(paths, opts) {
4753
4793
  }
4754
4794
  let bytes;
4755
4795
  try {
4756
- bytes = await read(isAbsolute3(path) ? path : join22(opts.worktree, path));
4796
+ bytes = await read(isAbsolute3(path) ? path : join23(opts.worktree, path));
4757
4797
  } catch (e) {
4758
4798
  skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
4759
4799
  continue;
@@ -4833,12 +4873,12 @@ var init_shotUpload = __esm({
4833
4873
  });
4834
4874
 
4835
4875
  // src/oauth.ts
4836
- import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
4837
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4 } from "fs";
4838
- import { join as join23 } from "path";
4876
+ import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
4877
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4 } from "fs";
4878
+ import { join as join24 } from "path";
4839
4879
  function makePkce() {
4840
4880
  const verifier = randomBytes2(48).toString("base64url");
4841
- const challenge = createHash2("sha256").update(verifier).digest("base64url");
4881
+ const challenge = createHash3("sha256").update(verifier).digest("base64url");
4842
4882
  return { verifier, challenge };
4843
4883
  }
4844
4884
  async function registerClient(args, fetchFn = fetch) {
@@ -4919,7 +4959,7 @@ function toStore(clientId, t, now) {
4919
4959
  }
4920
4960
  function loadStore() {
4921
4961
  try {
4922
- return JSON.parse(readFileSync16(STORE_PATH2, "utf8"));
4962
+ return JSON.parse(readFileSync18(STORE_PATH2, "utf8"));
4923
4963
  } catch {
4924
4964
  return null;
4925
4965
  }
@@ -4935,7 +4975,7 @@ function clearStore() {
4935
4975
  }
4936
4976
  function loadClientId() {
4937
4977
  try {
4938
- return JSON.parse(readFileSync16(CLIENT_PATH, "utf8")).clientId;
4978
+ return JSON.parse(readFileSync18(CLIENT_PATH, "utf8")).clientId;
4939
4979
  } catch {
4940
4980
  return null;
4941
4981
  }
@@ -4970,8 +5010,8 @@ var init_oauth = __esm({
4970
5010
  AUTHORIZE = "/api/oauth/authorize";
4971
5011
  TOKEN = "/api/oauth/token";
4972
5012
  SCOPE = "openid profile email taskless:full";
4973
- STORE_PATH2 = join23(SWARM_HOME, "oauth.json");
4974
- CLIENT_PATH = join23(SWARM_HOME, "oauth-client.json");
5013
+ STORE_PATH2 = join24(SWARM_HOME, "oauth.json");
5014
+ CLIENT_PATH = join24(SWARM_HOME, "oauth-client.json");
4975
5015
  base2 = (api) => api.replace(/\/$/, "");
4976
5016
  }
4977
5017
  });
@@ -5017,11 +5057,11 @@ var init_authState = __esm({
5017
5057
  });
5018
5058
 
5019
5059
  // src/workspace.ts
5020
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, unlinkSync as unlinkSync5 } from "fs";
5021
- import { join as join24 } from "path";
5060
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync13, unlinkSync as unlinkSync5 } from "fs";
5061
+ import { join as join25 } from "path";
5022
5062
  function loadWorkspace() {
5023
5063
  try {
5024
- const w = JSON.parse(readFileSync17(WORKSPACE_PATH, "utf8"));
5064
+ const w = JSON.parse(readFileSync19(WORKSPACE_PATH, "utf8"));
5025
5065
  return w && typeof w.id === "string" && w.id ? w : null;
5026
5066
  } catch {
5027
5067
  return null;
@@ -5035,7 +5075,7 @@ var init_workspace = __esm({
5035
5075
  "src/workspace.ts"() {
5036
5076
  "use strict";
5037
5077
  init_config();
5038
- WORKSPACE_PATH = join24(SWARM_HOME, "workspace.json");
5078
+ WORKSPACE_PATH = join25(SWARM_HOME, "workspace.json");
5039
5079
  }
5040
5080
  });
5041
5081
 
@@ -5616,7 +5656,7 @@ __export(db_exports, {
5616
5656
  });
5617
5657
  import { createRequire as createRequire2 } from "module";
5618
5658
  import { randomUUID as randomUUID3 } from "crypto";
5619
- import { existsSync as existsSync13 } from "fs";
5659
+ import { existsSync as existsSync14 } from "fs";
5620
5660
  import { resolve as resolve3 } from "path";
5621
5661
  function rowToQuestion(row) {
5622
5662
  return {
@@ -6239,7 +6279,7 @@ CREATE TABLE IF NOT EXISTS imports (
6239
6279
  const repoN = normalizeRepo(repo);
6240
6280
  const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
6241
6281
  if (already) return { imported: false, runs: already.run_count };
6242
- if (!existsSync13(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
6282
+ if (!existsSync14(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
6243
6283
  const src = new this.databaseCtor(abs);
6244
6284
  try {
6245
6285
  const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
@@ -6608,10 +6648,10 @@ var init_droneStore = __esm({
6608
6648
 
6609
6649
  // src/overmind.ts
6610
6650
  import { homedir as homedir6 } from "os";
6611
- import { existsSync as existsSync18, readFileSync as readFileSync21, readdirSync as readdirSync13, renameSync as renameSync11 } from "fs";
6612
- import { basename as basename6, dirname as dirname5, join as join31 } from "path";
6651
+ import { existsSync as existsSync19, readFileSync as readFileSync23, readdirSync as readdirSync13, renameSync as renameSync11 } from "fs";
6652
+ import { basename as basename6, dirname as dirname5, join as join32 } from "path";
6613
6653
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
6614
- import { spawn as spawn3 } from "child_process";
6654
+ import { spawn as spawn4 } from "child_process";
6615
6655
 
6616
6656
  // src/restart.ts
6617
6657
  init_src();
@@ -6654,11 +6694,11 @@ function savePauseState(home, state, io = {}) {
6654
6694
  remove(path);
6655
6695
  return;
6656
6696
  }
6657
- const writeFile3 = io.writeFile ?? ((p, data) => {
6697
+ const writeFile4 = io.writeFile ?? ((p, data) => {
6658
6698
  writeFileSync(`${p}.tmp`, data);
6659
6699
  renameSync(`${p}.tmp`, p);
6660
6700
  });
6661
- writeFile3(path, JSON.stringify(state, null, 2));
6701
+ writeFile4(path, JSON.stringify(state, null, 2));
6662
6702
  }
6663
6703
  function makePauseState(source, reason, now = Date.now()) {
6664
6704
  return {
@@ -9415,6 +9455,26 @@ function rotateDaemonLog(path, opts, io = realIo) {
9415
9455
  return "rotated";
9416
9456
  }
9417
9457
 
9458
+ // src/daemonLaunch.ts
9459
+ import { openSync as openSync2, closeSync as closeSync2, readFileSync as readFileSync8 } from "fs";
9460
+ import { spawn } from "child_process";
9461
+ var realRespawnIo = {
9462
+ isTty: () => process.stdout.isTTY === true,
9463
+ open: (path) => openSync2(path, "a")
9464
+ };
9465
+ function respawnStdio(logPath, io = realRespawnIo) {
9466
+ if (io.isTty()) return "inherit";
9467
+ try {
9468
+ const fd = io.open(logPath);
9469
+ return ["ignore", fd, fd];
9470
+ } catch {
9471
+ return "inherit";
9472
+ }
9473
+ }
9474
+
9475
+ // src/overmind.ts
9476
+ init_netTimeout();
9477
+
9418
9478
  // src/logStamp.ts
9419
9479
  import { format } from "util";
9420
9480
  function logStamp(at) {
@@ -9496,13 +9556,13 @@ async function buildRepoRegistry(paths) {
9496
9556
  }
9497
9557
 
9498
9558
  // src/repoMemory.ts
9499
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
9559
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
9500
9560
  import { join as join10 } from "path";
9501
9561
  var FILE = "repos.json";
9502
9562
  function loadRememberedRepos(home) {
9503
9563
  let raw;
9504
9564
  try {
9505
- raw = readFileSync8(join10(home, FILE), "utf8");
9565
+ raw = readFileSync9(join10(home, FILE), "utf8");
9506
9566
  } catch {
9507
9567
  return [];
9508
9568
  }
@@ -9532,11 +9592,11 @@ init_src();
9532
9592
  init_config();
9533
9593
  init_linkFailure();
9534
9594
  import { homedir as homedir4 } from "os";
9535
- import { readFileSync as readFileSync10, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "fs";
9595
+ import { readFileSync as readFileSync11, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "fs";
9536
9596
  import { isAbsolute, join as join12, resolve as resolve2 } from "path";
9537
9597
 
9538
9598
  // src/repoScan.ts
9539
- import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
9599
+ import { readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync6 } from "fs";
9540
9600
  import { join as join11 } from "path";
9541
9601
  var SKIP = /* @__PURE__ */ new Set([
9542
9602
  "node_modules",
@@ -9567,7 +9627,7 @@ function parseOriginFromGitConfig(text) {
9567
9627
  }
9568
9628
  function originOf(dir) {
9569
9629
  try {
9570
- return parseOriginFromGitConfig(readFileSync9(join11(dir, ".git", "config"), "utf8"));
9630
+ return parseOriginFromGitConfig(readFileSync10(join11(dir, ".git", "config"), "utf8"));
9571
9631
  } catch {
9572
9632
  return null;
9573
9633
  }
@@ -9852,7 +9912,7 @@ function repoMapRows(state) {
9852
9912
  }
9853
9913
  function loadRepoMapCache(path) {
9854
9914
  try {
9855
- const raw = JSON.parse(readFileSync10(path, "utf8"));
9915
+ const raw = JSON.parse(readFileSync11(path, "utf8"));
9856
9916
  if (!Array.isArray(raw?.entries)) return { scannedAt: null, entries: [] };
9857
9917
  return {
9858
9918
  scannedAt: typeof raw.scannedAt === "string" && raw.scannedAt ? raw.scannedAt : null,
@@ -9875,7 +9935,7 @@ function writeRepoMapCache(path, entries, scannedAt = stamp()) {
9875
9935
 
9876
9936
  // src/hostIdentity.ts
9877
9937
  import { hostname } from "os";
9878
- import { existsSync as existsSync9, readFileSync as readFileSync11, renameSync as renameSync7, writeFileSync as writeFileSync10 } from "fs";
9938
+ import { existsSync as existsSync9, readFileSync as readFileSync12, renameSync as renameSync7, writeFileSync as writeFileSync10 } from "fs";
9879
9939
  import { join as join13 } from "path";
9880
9940
  import { randomUUID as randomUUID2 } from "crypto";
9881
9941
  function hostIdentityPath(home) {
@@ -9883,8 +9943,8 @@ function hostIdentityPath(home) {
9883
9943
  }
9884
9944
  function loadHostIdentity(deps) {
9885
9945
  const exists = deps.exists ?? existsSync9;
9886
- const readFile2 = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
9887
- const writeFile3 = deps.writeFile ?? ((p, data) => {
9946
+ const readFile2 = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
9947
+ const writeFile4 = deps.writeFile ?? ((p, data) => {
9888
9948
  writeFileSync10(`${p}.tmp`, data);
9889
9949
  renameSync7(`${p}.tmp`, p);
9890
9950
  });
@@ -9904,7 +9964,7 @@ function loadHostIdentity(deps) {
9904
9964
  }
9905
9965
  const hostId = newId();
9906
9966
  try {
9907
- writeFile3(path, JSON.stringify({ hostId, hostName, createdAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
9967
+ writeFile4(path, JSON.stringify({ hostId, hostName, createdAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
9908
9968
  } catch {
9909
9969
  console.warn(`[overmind] \u043D\u0435 \u0441\u043C\u043E\u0433 \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C ${path} \u2014 hostId \u0436\u0438\u0432\u0451\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043E \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0430 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430`);
9910
9970
  }
@@ -10067,8 +10127,8 @@ function buildReliefCostCandidates(cost, t, sustainMs, build) {
10067
10127
  }
10068
10128
 
10069
10129
  // src/launcher.ts
10070
- import { spawn } from "child_process";
10071
- import { closeSync as closeSync2, existsSync as existsSync10, mkdirSync as mkdirSync8, openSync as openSync2, readdirSync as readdirSync8 } from "fs";
10130
+ import { spawn as spawn2 } from "child_process";
10131
+ import { closeSync as closeSync3, existsSync as existsSync10, mkdirSync as mkdirSync8, openSync as openSync3, readdirSync as readdirSync8 } from "fs";
10072
10132
  import { createRequire } from "module";
10073
10133
  import { homedir as homedir5 } from "os";
10074
10134
  import { dirname as dirname3, join as join15 } from "path";
@@ -10107,9 +10167,9 @@ function buildDroneHome(opts) {
10107
10167
  }
10108
10168
  }
10109
10169
  for (const name of wanted) {
10110
- const link = join14(droneHome, name);
10170
+ const link2 = join14(droneHome, name);
10111
10171
  try {
10112
- lstatSync(link);
10172
+ lstatSync(link2);
10113
10173
  continue;
10114
10174
  } catch {
10115
10175
  }
@@ -10121,7 +10181,7 @@ function buildDroneHome(opts) {
10121
10181
  continue;
10122
10182
  }
10123
10183
  try {
10124
- symlinkSync2(target, link, isDir ? "junction" : "file");
10184
+ symlinkSync2(target, link2, isDir ? "junction" : "file");
10125
10185
  } catch (e) {
10126
10186
  if (e.code !== "EEXIST") throw e;
10127
10187
  }
@@ -10303,14 +10363,14 @@ function openNextRunLog(runLogsDir, runLogKeys) {
10303
10363
  const path = nextRunLogPath(runLogsDir, runLogKeys);
10304
10364
  let out;
10305
10365
  try {
10306
- out = openSync2(path, "wx");
10366
+ out = openSync3(path, "wx");
10307
10367
  } catch (e) {
10308
10368
  if (e.code !== "EEXIST") throw e;
10309
10369
  continue;
10310
10370
  }
10311
10371
  let err = null;
10312
10372
  try {
10313
- err = openSync2(`${path.slice(0, -".json".length)}.err.log`, "w");
10373
+ err = openSync3(`${path.slice(0, -".json".length)}.err.log`, "w");
10314
10374
  } catch {
10315
10375
  err = null;
10316
10376
  }
@@ -10419,7 +10479,7 @@ function makeLauncher(cfg) {
10419
10479
  errFd = fds.err ?? void 0;
10420
10480
  stdio = ["ignore", outFd, errFd ?? "ignore"];
10421
10481
  }
10422
- const child = spawn(cfg.bin ?? plan.bin, plan.args, {
10482
+ const child = spawn2(cfg.bin ?? plan.bin, plan.args, {
10423
10483
  cwd: worktree,
10424
10484
  detached: true,
10425
10485
  stdio,
@@ -10430,8 +10490,8 @@ function makeLauncher(cfg) {
10430
10490
  // они про его собственное состояние, и перебивать их ройным нечем.
10431
10491
  env: { ...droneEnv(worktree, process.env, home ? { droneHome: home, realHome } : void 0, cfg.droneCap?.() ?? null), ...plan.env }
10432
10492
  });
10433
- if (outFd !== void 0) closeSync2(outFd);
10434
- if (errFd !== void 0) closeSync2(errFd);
10493
+ if (outFd !== void 0) closeSync3(outFd);
10494
+ if (errFd !== void 0) closeSync3(errFd);
10435
10495
  child.unref();
10436
10496
  if (child.pid == null) throw new Error("\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430 (\u043D\u0435\u0442 pid)");
10437
10497
  return child.pid;
@@ -10442,7 +10502,7 @@ function makeLauncher(cfg) {
10442
10502
  init_procCleanup();
10443
10503
 
10444
10504
  // src/cost.ts
10445
- import { readFileSync as readFileSync12, readdirSync as readdirSync9, renameSync as renameSync8 } from "fs";
10505
+ import { readFileSync as readFileSync13, readdirSync as readdirSync9, renameSync as renameSync8 } from "fs";
10446
10506
  import { join as join16 } from "path";
10447
10507
  init_executors();
10448
10508
  var UNPROCESSED_LOG_RE = /^\d+\.json$/;
@@ -10468,7 +10528,7 @@ function accountRunCost(deps, run) {
10468
10528
  const full = join16(dir, f);
10469
10529
  let raw = null;
10470
10530
  try {
10471
- raw = readFileSync12(full, "utf8");
10531
+ raw = readFileSync13(full, "utf8");
10472
10532
  } catch {
10473
10533
  continue;
10474
10534
  }
@@ -10517,7 +10577,7 @@ function makeCostSweeper(deps) {
10517
10577
  }
10518
10578
 
10519
10579
  // src/runOutcome.ts
10520
- import { readdirSync as readdirSync10, readFileSync as readFileSync13 } from "fs";
10580
+ import { readdirSync as readdirSync10, readFileSync as readFileSync14 } from "fs";
10521
10581
  import { join as join17 } from "path";
10522
10582
  init_executors();
10523
10583
  function endedItself(outcome) {
@@ -10545,7 +10605,7 @@ function readLatestResultRaw(runLogsDir, runLogKeys) {
10545
10605
  }
10546
10606
  if (!latest) return null;
10547
10607
  try {
10548
- return readFileSync13(join17(dir, latest.name), "utf8");
10608
+ return readFileSync14(join17(dir, latest.name), "utf8");
10549
10609
  } catch {
10550
10610
  return null;
10551
10611
  }
@@ -10578,13 +10638,13 @@ init_src();
10578
10638
  init_git();
10579
10639
 
10580
10640
  // src/deps.ts
10581
- import { existsSync as existsSync12, readdirSync as readdirSync12, readFileSync as readFileSync14 } from "fs";
10582
- import { join as join20 } from "path";
10641
+ import { existsSync as existsSync13, readdirSync as readdirSync12, readFileSync as readFileSync16 } from "fs";
10642
+ import { join as join21 } from "path";
10583
10643
  import { execa } from "execa";
10584
10644
 
10585
10645
  // src/prepareLock.ts
10586
10646
  import { createHash } from "crypto";
10587
- import { closeSync as closeSync3, openSync as openSync3, rmSync as rmSync2, statSync as statSync9, writeSync } from "fs";
10647
+ import { closeSync as closeSync4, openSync as openSync4, rmSync as rmSync2, statSync as statSync9, writeSync } from "fs";
10588
10648
  import { join as join19 } from "path";
10589
10649
  import { tmpdir } from "os";
10590
10650
  var WAIT_TIMEOUT_MS = 5 * 6e4;
@@ -10595,11 +10655,11 @@ function machineLockPath(key, dir = tmpdir()) {
10595
10655
  }
10596
10656
  function defaultTryCreate(path, content) {
10597
10657
  try {
10598
- const fd = openSync3(path, "wx");
10658
+ const fd = openSync4(path, "wx");
10599
10659
  try {
10600
10660
  writeSync(fd, content);
10601
10661
  } finally {
10602
- closeSync3(fd);
10662
+ closeSync4(fd);
10603
10663
  }
10604
10664
  return true;
10605
10665
  } catch {
@@ -10645,6 +10705,153 @@ async function withMachineLock(key, fn, io = {}) {
10645
10705
  }
10646
10706
  }
10647
10707
 
10708
+ // src/deps.ts
10709
+ init_config();
10710
+
10711
+ // src/envCache.ts
10712
+ init_config();
10713
+ import { createHash as createHash2 } from "crypto";
10714
+ import { cp, link, lstat, mkdir as mkdir2, readdir, readlink, rename, rm as rm2, stat, symlink, utimes, writeFile as writeFile2 } from "fs/promises";
10715
+ import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
10716
+ import { join as join20 } from "path";
10717
+ function fingerprint(parts) {
10718
+ const h = createHash2("sha256");
10719
+ for (const p of parts) {
10720
+ h.update(p);
10721
+ h.update("\0");
10722
+ }
10723
+ return h.digest("hex").slice(0, 32);
10724
+ }
10725
+ async function cloneTree(src, dst) {
10726
+ await mkdir2(dst, { recursive: true });
10727
+ const entries = await readdir(src, { withFileTypes: true });
10728
+ await Promise.all(
10729
+ entries.map(async (e) => {
10730
+ const from = join20(src, e.name);
10731
+ const to = join20(dst, e.name);
10732
+ if (e.isDirectory()) return cloneTree(from, to);
10733
+ if (e.isSymbolicLink()) {
10734
+ return symlink(await readlink(from), to).catch(() => cp(from, to, { recursive: true, verbatimSymlinks: true }));
10735
+ }
10736
+ return link(from, to).catch(() => cp(from, to));
10737
+ })
10738
+ );
10739
+ }
10740
+ function cacheRoot(io) {
10741
+ return io.root ?? ENV_CACHE_DIR;
10742
+ }
10743
+ function snapshotDir(io, key) {
10744
+ return join20(cacheRoot(io), key);
10745
+ }
10746
+ function readManifest(dir) {
10747
+ try {
10748
+ const parsed = JSON.parse(readFileSync15(join20(dir, "manifest.json"), "utf8"));
10749
+ if (!parsed || typeof parsed !== "object") return null;
10750
+ const m = parsed;
10751
+ if (typeof m.key !== "string" || !Array.isArray(m.dirs)) return null;
10752
+ const dirs = m.dirs.filter((d) => typeof d === "string" && d.length > 0 && !d.startsWith("/") && !d.includes(".."));
10753
+ if (!dirs.length) return null;
10754
+ return { key: m.key, dirs, createdAt: typeof m.createdAt === "string" ? m.createdAt : "" };
10755
+ } catch {
10756
+ return null;
10757
+ }
10758
+ }
10759
+ function hasEnvSnapshot(key, io = {}) {
10760
+ const exists = io.exists ?? existsSync12;
10761
+ return exists(join20(snapshotDir(io, key), "manifest.json"));
10762
+ }
10763
+ async function restoreEnvSnapshot(worktree, key, io = {}) {
10764
+ const exists = io.exists ?? existsSync12;
10765
+ const clone = io.clone ?? cloneTree;
10766
+ const dir = snapshotDir(io, key);
10767
+ const manifest = readManifest(dir);
10768
+ if (!manifest) return null;
10769
+ const made = [];
10770
+ try {
10771
+ for (let i = 0; i < manifest.dirs.length; i++) {
10772
+ const rel = manifest.dirs[i] ?? "";
10773
+ const target = join20(worktree, rel);
10774
+ if (exists(target)) continue;
10775
+ await clone(join20(dir, "trees", String(i)), target);
10776
+ made.push(rel);
10777
+ }
10778
+ } catch {
10779
+ for (const rel of made) await rm2(join20(worktree, rel), { recursive: true, force: true }).catch(() => {
10780
+ });
10781
+ return null;
10782
+ }
10783
+ await touch(dir);
10784
+ return made;
10785
+ }
10786
+ async function touch(dir) {
10787
+ const now = /* @__PURE__ */ new Date();
10788
+ await utimes(dir, now, now).catch(() => {
10789
+ });
10790
+ }
10791
+ async function saveEnvSnapshot(worktree, key, dirs, io = {}) {
10792
+ const exists = io.exists ?? existsSync12;
10793
+ const clone = io.clone ?? cloneTree;
10794
+ if (hasEnvSnapshot(key, io)) return true;
10795
+ const present = dirs.filter((rel) => exists(join20(worktree, rel)));
10796
+ if (!present.length) return false;
10797
+ const root = cacheRoot(io);
10798
+ const tmp = join20(root, `.tmp-${key}-${process.pid}`);
10799
+ try {
10800
+ await rm2(tmp, { recursive: true, force: true });
10801
+ await mkdir2(join20(tmp, "trees"), { recursive: true });
10802
+ for (let i = 0; i < present.length; i++) await clone(join20(worktree, present[i] ?? ""), join20(tmp, "trees", String(i)));
10803
+ const manifest = { key, dirs: present, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
10804
+ await writeFile2(join20(tmp, "manifest.json"), JSON.stringify(manifest, null, 2));
10805
+ await rename(tmp, snapshotDir(io, key));
10806
+ return true;
10807
+ } catch {
10808
+ await rm2(tmp, { recursive: true, force: true }).catch(() => {
10809
+ });
10810
+ return hasEnvSnapshot(key, io);
10811
+ }
10812
+ }
10813
+ async function pruneEnvSnapshots(io = {}) {
10814
+ const root = cacheRoot(io);
10815
+ const keep = io.keep ?? ENV_CACHE_KEEP;
10816
+ let names;
10817
+ try {
10818
+ names = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
10819
+ } catch {
10820
+ return 0;
10821
+ }
10822
+ const withTime = [];
10823
+ for (const name of names) {
10824
+ const s = await stat(join20(root, name)).catch(() => null);
10825
+ if (s) withTime.push({ name, mtime: s.mtimeMs });
10826
+ }
10827
+ withTime.sort((a, b) => b.mtime - a.mtime);
10828
+ let removed = 0;
10829
+ for (const { name } of withTime.slice(Math.max(0, keep))) {
10830
+ await rm2(join20(root, name), { recursive: true, force: true }).catch(() => {
10831
+ });
10832
+ removed++;
10833
+ }
10834
+ return removed;
10835
+ }
10836
+ async function sweepEnvCacheTemp(io = {}) {
10837
+ const root = cacheRoot(io);
10838
+ let names;
10839
+ try {
10840
+ names = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory() && e.name.startsWith(".tmp-")).map((e) => e.name);
10841
+ } catch {
10842
+ return 0;
10843
+ }
10844
+ let removed = 0;
10845
+ for (const name of names) {
10846
+ const s = await lstat(join20(root, name)).catch(() => null);
10847
+ if (!s || Date.now() - s.mtimeMs < 60 * 6e4) continue;
10848
+ await rm2(join20(root, name), { recursive: true, force: true }).catch(() => {
10849
+ });
10850
+ removed++;
10851
+ }
10852
+ return removed;
10853
+ }
10854
+
10648
10855
  // src/deps.ts
10649
10856
  var PREPARE_TIMEOUT_MS = 15 * 6e4;
10650
10857
  var MAX_PLAN_COMMANDS = 12;
@@ -10665,7 +10872,7 @@ async function runCommand(cmd, worktree) {
10665
10872
  }
10666
10873
  function safeReadText(p) {
10667
10874
  try {
10668
- return readFileSync14(p, "utf8");
10875
+ return readFileSync16(p, "utf8");
10669
10876
  } catch {
10670
10877
  return null;
10671
10878
  }
@@ -10705,21 +10912,21 @@ function overridePlan(override) {
10705
10912
  };
10706
10913
  }
10707
10914
  function detectPackageManager(worktree, io = {}) {
10708
- const exists = io.exists ?? existsSync12;
10915
+ const exists = io.exists ?? existsSync13;
10709
10916
  const readText = io.readText ?? safeReadText;
10710
- const hasPkgJson = exists(join20(worktree, "package.json"));
10917
+ const hasPkgJson = exists(join21(worktree, "package.json"));
10711
10918
  if (!hasPkgJson) {
10712
- if (exists(join20(worktree, "deno.json")) || exists(join20(worktree, "deno.jsonc")) || exists(join20(worktree, "deno.lock"))) return "deno";
10919
+ if (exists(join21(worktree, "deno.json")) || exists(join21(worktree, "deno.jsonc")) || exists(join21(worktree, "deno.lock"))) return "deno";
10713
10920
  return null;
10714
10921
  }
10715
- const pkg = readJson(readText, join20(worktree, "package.json"));
10922
+ const pkg = readJson(readText, join21(worktree, "package.json"));
10716
10923
  const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
10717
10924
  if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
10718
- if (exists(join20(worktree, "bun.lock")) || exists(join20(worktree, "bun.lockb"))) return "bun";
10719
- if (exists(join20(worktree, "pnpm-lock.yaml"))) return "pnpm";
10720
- if (exists(join20(worktree, "yarn.lock"))) return "yarn";
10721
- if (exists(join20(worktree, "package-lock.json"))) return "npm";
10722
- if (exists(join20(worktree, "deno.lock"))) return "deno";
10925
+ if (exists(join21(worktree, "bun.lock")) || exists(join21(worktree, "bun.lockb"))) return "bun";
10926
+ if (exists(join21(worktree, "pnpm-lock.yaml"))) return "pnpm";
10927
+ if (exists(join21(worktree, "yarn.lock"))) return "yarn";
10928
+ if (exists(join21(worktree, "package-lock.json"))) return "npm";
10929
+ if (exists(join21(worktree, "deno.lock"))) return "deno";
10723
10930
  return "npm";
10724
10931
  }
10725
10932
  function jsInstallCommand(pm) {
@@ -10775,7 +10982,7 @@ var ECOSYSTEM_MARKERS = [
10775
10982
  // конфигурация — дело сборки, не подготовки
10776
10983
  ];
10777
10984
  function detectPlan(worktree, io) {
10778
- const exists = io.exists ?? existsSync12;
10985
+ const exists = io.exists ?? existsSync13;
10779
10986
  const listFiles = io.listFiles ?? safeListFiles;
10780
10987
  const pm = detectPackageManager(worktree, io);
10781
10988
  if (pm) {
@@ -10783,7 +10990,7 @@ function detectPlan(worktree, io) {
10783
10990
  // node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
10784
10991
  // У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
10785
10992
  // заново, но он дешёвый: тянет из того же общего кэша.
10786
- { label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join20(worktree, "node_modules"), source: "detect" }
10993
+ { label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join21(worktree, "node_modules"), source: "detect" }
10787
10994
  ];
10788
10995
  for (const target of workspaceBuildTargets(worktree, io)) {
10789
10996
  commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
@@ -10792,12 +10999,12 @@ function detectPlan(worktree, io) {
10792
10999
  return { source: "detect", ecosystem: `node/${pm}`, commands };
10793
11000
  }
10794
11001
  for (const marker of ECOSYSTEM_MARKERS) {
10795
- const hit = marker.files.some((f) => exists(join20(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
11002
+ const hit = marker.files.some((f) => exists(join21(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
10796
11003
  if (!hit) continue;
10797
11004
  let ecosystem = marker.ecosystem;
10798
11005
  let command = marker.command;
10799
11006
  for (const refine of marker.refine ?? []) {
10800
- if (!exists(join20(worktree, refine.file))) continue;
11007
+ if (!exists(join21(worktree, refine.file))) continue;
10801
11008
  ecosystem = refine.ecosystem;
10802
11009
  command = refine.command;
10803
11010
  break;
@@ -10854,12 +11061,12 @@ var CI_INSTALL_PATTERNS = [
10854
11061
  function ciWorkflowPlan(worktree, io) {
10855
11062
  const readText = io.readText ?? safeReadText;
10856
11063
  const listFiles = io.listFiles ?? safeListFiles;
10857
- const dir = join20(worktree, ".github", "workflows");
11064
+ const dir = join21(worktree, ".github", "workflows");
10858
11065
  const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
10859
11066
  const seen = /* @__PURE__ */ new Set();
10860
11067
  const commands = [];
10861
11068
  for (const name of files.sort()) {
10862
- const yaml = readText(join20(dir, name));
11069
+ const yaml = readText(join21(dir, name));
10863
11070
  if (yaml === null) continue;
10864
11071
  for (const raw of workflowRunLines(yaml)) {
10865
11072
  const line = raw.replace(/\s+/g, " ").trim();
@@ -10874,7 +11081,7 @@ function ciWorkflowPlan(worktree, io) {
10874
11081
  return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
10875
11082
  }
10876
11083
  function workspaceGlobs(worktree, readText) {
10877
- const yaml = readText(join20(worktree, "pnpm-workspace.yaml"));
11084
+ const yaml = readText(join21(worktree, "pnpm-workspace.yaml"));
10878
11085
  if (yaml !== null) {
10879
11086
  const globs = [];
10880
11087
  let inPackages = false;
@@ -10894,7 +11101,7 @@ function workspaceGlobs(worktree, readText) {
10894
11101
  }
10895
11102
  if (globs.length) return globs;
10896
11103
  }
10897
- const pkg = readJson(readText, join20(worktree, "package.json"));
11104
+ const pkg = readJson(readText, join21(worktree, "package.json"));
10898
11105
  const ws = pkg?.workspaces;
10899
11106
  const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
10900
11107
  return list.filter((g) => typeof g === "string");
@@ -10905,14 +11112,14 @@ function workspaceDirs(worktree, globs, io) {
10905
11112
  if (glob.startsWith("!")) continue;
10906
11113
  const star = glob.indexOf("*");
10907
11114
  if (star === -1) {
10908
- const dir = join20(worktree, glob);
10909
- if (io.exists(join20(dir, "package.json"))) dirs.add(dir);
11115
+ const dir = join21(worktree, glob);
11116
+ if (io.exists(join21(dir, "package.json"))) dirs.add(dir);
10910
11117
  continue;
10911
11118
  }
10912
- const base3 = join20(worktree, glob.slice(0, star).replace(/\/$/, ""));
11119
+ const base3 = join21(worktree, glob.slice(0, star).replace(/\/$/, ""));
10913
11120
  for (const name of io.listDirs(base3)) {
10914
- const dir = join20(base3, name);
10915
- if (io.exists(join20(dir, "package.json"))) dirs.add(dir);
11121
+ const dir = join21(base3, name);
11122
+ if (io.exists(join21(dir, "package.json"))) dirs.add(dir);
10916
11123
  }
10917
11124
  }
10918
11125
  return [...dirs];
@@ -10933,18 +11140,18 @@ function entryPaths(pkg) {
10933
11140
  return out;
10934
11141
  }
10935
11142
  function workspaceBuildTargets(worktree, io = {}) {
10936
- const exists = io.exists ?? existsSync12;
11143
+ const exists = io.exists ?? existsSync13;
10937
11144
  const readText = io.readText ?? safeReadText;
10938
11145
  const listDirs = io.listDirs ?? safeListDirs;
10939
11146
  const globs = workspaceGlobs(worktree, readText);
10940
11147
  if (!globs.length) return [];
10941
11148
  const pkgs = [];
10942
11149
  for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
10943
- const pkg = readJson(readText, join20(dir, "package.json"));
11150
+ const pkg = readJson(readText, join21(dir, "package.json"));
10944
11151
  if (!pkg || typeof pkg.name !== "string") continue;
10945
11152
  const scripts = pkg.scripts ?? {};
10946
11153
  const entries = entryPaths(pkg);
10947
- const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join20(dir, e)));
11154
+ const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join21(dir, e)));
10948
11155
  const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
10949
11156
  pkgs.push({ name: pkg.name, dir, deps, needsBuild });
10950
11157
  }
@@ -10972,7 +11179,7 @@ function devDatabaseUrl(worktree, io = {}) {
10972
11179
  const fromEnv = (io.env ?? process.env).DATABASE_URL;
10973
11180
  if (fromEnv && fromEnv.trim()) return fromEnv.trim();
10974
11181
  for (const file of [".env", ".env.example"]) {
10975
- const raw = readText(join20(worktree, file));
11182
+ const raw = readText(join21(worktree, file));
10976
11183
  if (raw === null) continue;
10977
11184
  const m = /^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/m.exec(raw);
10978
11185
  const value = (m?.[1] ?? "").trim().replace(/^['"]|['"]$/g, "");
@@ -10988,11 +11195,11 @@ function isLocalDatabase(url) {
10988
11195
  }
10989
11196
  }
10990
11197
  function prismaPackages(worktree, io) {
10991
- const exists = io.exists ?? existsSync12;
11198
+ const exists = io.exists ?? existsSync13;
10992
11199
  const readText = io.readText ?? safeReadText;
10993
11200
  const listDirs = io.listDirs ?? safeListDirs;
10994
11201
  const dirs = [worktree, ...workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs })];
10995
- return dirs.filter((dir) => exists(join20(dir, "prisma", "schema.prisma")) && listDirs(join20(dir, "prisma", "migrations")).length > 0);
11202
+ return dirs.filter((dir) => exists(join21(dir, "prisma", "schema.prisma")) && listDirs(join21(dir, "prisma", "migrations")).length > 0);
10996
11203
  }
10997
11204
  function jsExecBin(pm, command) {
10998
11205
  switch (pm) {
@@ -11019,26 +11226,59 @@ function migrateCommands(worktree, pm, io) {
11019
11226
  source: "detect"
11020
11227
  }));
11021
11228
  }
11229
+ var ENV_DEFINING_FILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock", "bun.lockb", "deno.lock", "pnpm-workspace.yaml"];
11230
+ function envCachePlan(worktree, io = {}) {
11231
+ if (!ENV_CACHE_ENABLED) return null;
11232
+ const exists = io.exists ?? existsSync13;
11233
+ const readText = io.readText ?? safeReadText;
11234
+ const listDirs = io.listDirs ?? safeListDirs;
11235
+ const pm = detectPackageManager(worktree, io);
11236
+ if (!pm) return null;
11237
+ const parts = [`pm:${pm}`];
11238
+ for (const name of ENV_DEFINING_FILES) {
11239
+ const raw = readText(join21(worktree, name));
11240
+ if (raw !== null) parts.push(`${name}
11241
+ ${raw}`);
11242
+ }
11243
+ const pkgDirs = workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs });
11244
+ const rels = pkgDirs.map((dir) => dir.slice(worktree.length + 1)).sort();
11245
+ for (const rel of ["", ...rels]) {
11246
+ const raw = readText(join21(worktree, rel, "package.json"));
11247
+ if (raw !== null) parts.push(`${rel}/package.json
11248
+ ${raw}`);
11249
+ }
11250
+ return { key: fingerprint(parts), dirs: ["node_modules", ...rels.map((rel) => join21(rel, "node_modules"))] };
11251
+ }
11022
11252
  function planPrepare(worktree, io = {}) {
11023
11253
  const plan = overridePlan(io.override) ?? detectPlan(worktree, io) ?? ciWorkflowPlan(worktree, io);
11024
11254
  if (!plan) return { source: "none", ecosystem: null, commands: [] };
11025
11255
  return { ...plan, commands: plan.commands.slice(0, MAX_PLAN_COMMANDS) };
11026
11256
  }
11027
11257
  async function ensureWorktreeDeps(worktree, io = {}) {
11028
- const exists = io.exists ?? existsSync12;
11258
+ const exists = io.exists ?? existsSync13;
11029
11259
  const exec = io.exec ?? ((cmd) => runCommand(cmd, worktree));
11030
11260
  const approve = io.approve ?? (() => true);
11031
- const plan = planPrepare(worktree, io);
11261
+ const cachePlan = envCachePlan(worktree, io);
11262
+ const cacheIO = io.envCache ?? {};
11032
11263
  const report = {
11033
- ecosystem: plan.ecosystem,
11034
- source: plan.source,
11035
- outcome: plan.source === "none" ? "not-detected" : "nothing-to-do",
11264
+ ecosystem: null,
11265
+ source: "none",
11266
+ outcome: "not-detected",
11036
11267
  ran: [],
11037
11268
  failed: [],
11038
11269
  skipped: [],
11039
11270
  blocked: [],
11040
- warned: []
11271
+ warned: [],
11272
+ restored: [],
11273
+ cached: false
11041
11274
  };
11275
+ if (cachePlan && !exists(join21(worktree, "node_modules"))) {
11276
+ report.restored = await restoreEnvSnapshot(worktree, cachePlan.key, cacheIO) ?? [];
11277
+ }
11278
+ const plan = planPrepare(worktree, io);
11279
+ report.ecosystem = plan.ecosystem;
11280
+ report.source = plan.source;
11281
+ report.outcome = plan.source === "none" ? "not-detected" : "nothing-to-do";
11042
11282
  for (const cmd of plan.commands) {
11043
11283
  if (cmd.skipIfExists && exists(cmd.skipIfExists)) {
11044
11284
  report.skipped.push(cmd.label);
@@ -11060,11 +11300,20 @@ async function ensureWorktreeDeps(worktree, io = {}) {
11060
11300
  break;
11061
11301
  }
11062
11302
  }
11303
+ if (cachePlan && !report.failed.length && !report.blocked.length) {
11304
+ report.cached = await saveEnvSnapshot(worktree, cachePlan.key, cachePlan.dirs, cacheIO);
11305
+ if (report.cached) {
11306
+ await pruneEnvSnapshots(cacheIO);
11307
+ await sweepEnvCacheTemp(cacheIO);
11308
+ }
11309
+ }
11063
11310
  return report;
11064
11311
  }
11065
11312
  function depsEvents(report) {
11066
11313
  const events = [];
11067
11314
  const where = report.source === "none" ? "" : ` (${[report.ecosystem, `\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A: ${sourceLabel(report.source)}`].filter(Boolean).join(", ")})`;
11315
+ if (report.restored.length)
11316
+ events.push(`\u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0432\u0437\u044F\u0442\u043E \u0438\u0437 \u043C\u0430\u0448\u0438\u043D\u043D\u043E\u0433\u043E \u043A\u044D\u0448\u0430, \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043D\u0435 \u043F\u043E\u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043B\u0430\u0441\u044C: ${report.restored.length} \u043A\u0430\u0442. node_modules \u2014 TASWM-519`);
11068
11317
  if (report.ran.length) events.push(`\u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 worktree${where}: ${report.ran.join(", ")} \u2014 TASWM-312`);
11069
11318
  if (report.failed.length)
11070
11319
  events.push(
@@ -11267,46 +11516,266 @@ function newestFirst(a, b) {
11267
11516
  if (Number.isFinite(at) && Number.isFinite(bt) && at !== bt) return bt - at;
11268
11517
  return b.id - a.id;
11269
11518
  }
11270
- function preStartAttempts(runs, taskSlug, opts = {}) {
11271
- const windowMs = opts.windowMs ?? SPAWN_ATTEMPT_WINDOW_MS;
11272
- const now = opts.now ?? Date.now();
11273
- const own = ownRuns(runs, taskSlug).sort(newestFirst);
11274
- let n = 0;
11275
- let newestCreatedAt = Number.NEGATIVE_INFINITY;
11276
- let newestSeenId = Number.NEGATIVE_INFINITY;
11277
- for (const run of own) {
11278
- const applied = parkApplied(run);
11279
- if (applied) {
11280
- const returned = applied.upToRunId != null ? newestSeenId > applied.upToRunId : applied.at != null && newestCreatedAt > applied.at;
11281
- if (returned) break;
11282
- }
11283
- newestSeenId = Math.max(newestSeenId, run.id);
11284
- const startedAt = run.created_at ? stampMs(run.created_at) : Number.NaN;
11285
- if (Number.isFinite(startedAt)) newestCreatedAt = Math.max(newestCreatedAt, startedAt);
11286
- if (run.status === "queued" || run.status === "running") continue;
11287
- if (isSpawnParked(run) && run.status !== "failed") continue;
11288
- if (!isPreStartFailure(run)) break;
11289
- if (Number.isFinite(startedAt) && now - startedAt >= windowMs) break;
11290
- if (opts.since != null && Number.isFinite(startedAt) && startedAt < opts.since) break;
11291
- n += 1;
11292
- if (isUnretriableParked(run)) return Math.max(n, PRE_START_ATTEMPT_LIMIT);
11293
- }
11294
- return n;
11519
+ function preStartAttempts(runs, taskSlug, opts = {}) {
11520
+ const windowMs = opts.windowMs ?? SPAWN_ATTEMPT_WINDOW_MS;
11521
+ const now = opts.now ?? Date.now();
11522
+ const own = ownRuns(runs, taskSlug).sort(newestFirst);
11523
+ let n = 0;
11524
+ let newestCreatedAt = Number.NEGATIVE_INFINITY;
11525
+ let newestSeenId = Number.NEGATIVE_INFINITY;
11526
+ for (const run of own) {
11527
+ const applied = parkApplied(run);
11528
+ if (applied) {
11529
+ const returned = applied.upToRunId != null ? newestSeenId > applied.upToRunId : applied.at != null && newestCreatedAt > applied.at;
11530
+ if (returned) break;
11531
+ }
11532
+ newestSeenId = Math.max(newestSeenId, run.id);
11533
+ const startedAt = run.created_at ? stampMs(run.created_at) : Number.NaN;
11534
+ if (Number.isFinite(startedAt)) newestCreatedAt = Math.max(newestCreatedAt, startedAt);
11535
+ if (run.status === "queued" || run.status === "running") continue;
11536
+ if (isSpawnParked(run) && run.status !== "failed") continue;
11537
+ if (!isPreStartFailure(run)) break;
11538
+ if (Number.isFinite(startedAt) && now - startedAt >= windowMs) break;
11539
+ if (opts.since != null && Number.isFinite(startedAt) && startedAt < opts.since) break;
11540
+ n += 1;
11541
+ if (isUnretriableParked(run)) return Math.max(n, PRE_START_ATTEMPT_LIMIT);
11542
+ }
11543
+ return n;
11544
+ }
11545
+ function lastPreStartAttempt(runs, taskSlug) {
11546
+ return runs.filter((r) => r.task_slug === taskSlug && isPreStartFailure(r)).sort(newestFirst)[0] ?? null;
11547
+ }
11548
+ function unresolvedRepoReason(repo, served) {
11549
+ const raw = repo.trim();
11550
+ if (!raw || raw.startsWith("/") || raw.startsWith("~") || /^[a-z]:[\\/]/i.test(raw)) return null;
11551
+ const list = served.length ? served.join(", ") : "\u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E (\u0440\u0435\u0435\u0441\u0442\u0440 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u043F\u0443\u0441\u0442)";
11552
+ return `\u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 ${raw} \u043D\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u0432 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0443\u0442\u044C: \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0435\u0433\u043E \u043A\u043B\u043E\u043D\u0430 \u043D\u0435\u0442. \u041C\u0430\u0448\u0438\u043D\u0430 \u043E\u0431\u0441\u043B\u0443\u0436\u0438\u0432\u0430\u0435\u0442: ${list}. \u041A\u043B\u043E\u043D\u0438\u0440\u0443\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0438 \u0434\u043E\u0431\u0430\u0432\u044C \u0435\u0433\u043E \u0432 SWARM_REPO_PATHS (\u0438\u043B\u0438 swarm start --repo <\u043F\u0443\u0442\u044C>), \u043B\u0438\u0431\u043E \u043E\u0442\u0434\u0430\u0439 \u0437\u0430\u0434\u0430\u0447\u0443 \u043C\u0430\u0448\u0438\u043D\u0435, \u0433\u0434\u0435 \u043E\u043D \u0435\u0441\u0442\u044C.`;
11553
+ }
11554
+ function parkReason(attempts, reason) {
11555
+ return `${PRE_START_MARK} ${attempts} \u0440\u0430\u0437\u0430 \u043F\u043E\u0434\u0440\u044F\u0434 \u2014 ${SPAWN_PARK_MARK}, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u0441\u043F\u0430\u0432\u043D\u0438\u0442\u044C \u043F\u043E \u043A\u0440\u0443\u0433\u0443. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
11556
+ }
11557
+ function parkReasonUnretriable(reason) {
11558
+ return `${PRE_START_MARK}, \u0438 ${UNRETRIABLE_PARK_MARK} \u2014 ${SPAWN_PARK_MARK} \u0441\u0440\u0430\u0437\u0443, \u043D\u0435 \u0442\u0440\u0430\u0442\u044F ${PRE_START_ATTEMPT_LIMIT} \u043F\u043E\u043F\u044B\u0442\u043A\u0438. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
11559
+ }
11560
+
11561
+ // package.json
11562
+ var package_default = {
11563
+ name: "@taskless-app/swarm",
11564
+ version: "0.6.32",
11565
+ type: "module",
11566
+ description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
11567
+ keywords: [
11568
+ "ai-agents",
11569
+ "coding-agents",
11570
+ "swarm",
11571
+ "orchestration",
11572
+ "autonomous-agents",
11573
+ "mcp",
11574
+ "taskless",
11575
+ "cli"
11576
+ ],
11577
+ license: "SEE LICENSE IN LICENSE",
11578
+ author: "Ilya Smurov <ilya@smurov.com>",
11579
+ homepage: "https://github.com/ilyasmurov/taskless/tree/main/packages/swarm-daemon#readme",
11580
+ repository: {
11581
+ type: "git",
11582
+ url: "git+https://github.com/ilyasmurov/taskless.git",
11583
+ directory: "packages/swarm-daemon"
11584
+ },
11585
+ engines: {
11586
+ node: ">=22"
11587
+ },
11588
+ bin: {
11589
+ swarm: "dist/cli.js"
11590
+ },
11591
+ files: [
11592
+ "dist",
11593
+ "docs/HERO.md",
11594
+ "hooks",
11595
+ "roles"
11596
+ ],
11597
+ scripts: {
11598
+ overmind: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/overmind.ts',
11599
+ swarm: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/cli.ts',
11600
+ build: "tsup",
11601
+ start: "node dist/overmind.js",
11602
+ prepublishOnly: "pnpm run build",
11603
+ test: "vitest run && SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
11604
+ "test:client": "SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
11605
+ "test:watch": "vitest",
11606
+ typecheck: "tsc -p tsconfig.json --noEmit",
11607
+ lint: "echo 'no lint in swarm-daemon yet'"
11608
+ },
11609
+ dependencies: {
11610
+ "@hono/node-server": "^2.0.12",
11611
+ "@modelcontextprotocol/sdk": "^1.29.0",
11612
+ execa: "^10.0.0",
11613
+ zod: "3.25.76"
11614
+ },
11615
+ gitHead: "f40f0fb4d2f96809f88b7d6ae4f086c5f4f004da"
11616
+ };
11617
+
11618
+ // src/version.ts
11619
+ var PACKAGE_VERSION = package_default.version;
11620
+ var MIN_NODE_MAJOR = Number(/(\d+)/.exec(package_default.engines?.node ?? "")?.[1] ?? 0);
11621
+
11622
+ // src/release.ts
11623
+ init_src();
11624
+ init_claudeBin();
11625
+ var SWARM_PACKAGE = SWARM_PACKAGE_NAME;
11626
+ var LATEST_TIMEOUT_MS = 4e3;
11627
+ async function fetchLatestVersion(opts = {}) {
11628
+ const fetchFn = opts.fetchFn ?? fetch;
11629
+ const timeoutMs = opts.timeoutMs ?? LATEST_TIMEOUT_MS;
11630
+ const pkg = opts.pkg ?? SWARM_PACKAGE;
11631
+ const ctrl = new AbortController();
11632
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
11633
+ try {
11634
+ const res = await fetchFn(`https://registry.npmjs.org/${pkg}/latest`, {
11635
+ signal: ctrl.signal,
11636
+ headers: { accept: "application/vnd.npm.install-v1+json, application/json" }
11637
+ });
11638
+ if (!res.ok) return null;
11639
+ const body = await res.json();
11640
+ return typeof body.version === "string" && body.version ? body.version : null;
11641
+ } catch {
11642
+ return null;
11643
+ } finally {
11644
+ clearTimeout(timer);
11645
+ }
11646
+ }
11647
+ function isOutdated(current, latest) {
11648
+ if (!latest || !current) return false;
11649
+ return compareVersionDirs(latest, current) > 0;
11650
+ }
11651
+ function outdatedLine(current, latest, updateCommand = "swarm update") {
11652
+ if (!isOutdated(current, latest)) return null;
11653
+ return `\u26A0 \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u043E\u044F \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430: \u0443 \u0442\u0435\u0431\u044F v${current}, \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430 v${latest} \u2014 \u043E\u0431\u043D\u043E\u0432\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: ${updateCommand}`;
11654
+ }
11655
+
11656
+ // src/daemonVersion.ts
11657
+ function captureDaemonStart(now = () => /* @__PURE__ */ new Date()) {
11658
+ return { startedAt: now().toISOString(), version: PACKAGE_VERSION };
11659
+ }
11660
+ var DAEMON_CODE_PATHS = ["packages/swarm-daemon", "packages/swarm-core"];
11661
+ async function checkDaemonVersion(start, opts = {}) {
11662
+ const now = opts.now ?? (() => /* @__PURE__ */ new Date());
11663
+ const fetchLatest = opts.latest ?? (() => fetchLatestVersion());
11664
+ const latestVersion = await fetchLatest().catch(() => null);
11665
+ return {
11666
+ startedAt: start.startedAt,
11667
+ version: start.version,
11668
+ latestVersion,
11669
+ stale: isOutdated(start.version, latestVersion),
11670
+ checkedAt: now().toISOString()
11671
+ };
11672
+ }
11673
+
11674
+ // src/escalations.ts
11675
+ var RESOLVE_ESCALATION_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D";
11676
+ var REVIEW_ESCALATION_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0441\u043C\u043E\u0433\u043B\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C\u0441\u044F";
11677
+ var REVIEW_UNRECOVERABLE_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u044C";
11678
+ function reviewUnrecoverableMark(branch) {
11679
+ return `${REVIEW_UNRECOVERABLE_MARK} (\u0432\u0435\u0442\u043A\u0430 ${branch})`;
11680
+ }
11681
+ var REVIEW_CYCLE_ESCALATION_MARK = "\u0440\u0435-\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u0446\u0438\u043A\u043B\u0438\u043B\u043E\u0441\u044C";
11682
+ var CI_WAKE_ESCALATION_MARK = "CI-\u0430\u0432\u0442\u043E\u043F\u043E\u0447\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043F\u0440\u0430\u0432\u0438\u043B\u0430\u0441\u044C";
11683
+ var CHANGES_WAKE_ESCALATION_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C";
11684
+ var REVIEW_BUDGET_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u043E\u0441\u044C \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442";
11685
+ function reviewBudgetMark(cycle) {
11686
+ return `${REVIEW_BUDGET_MARK} (\u043A\u0440\u0443\u0433 ${cycle})`;
11295
11687
  }
11296
- function lastPreStartAttempt(runs, taskSlug) {
11297
- return runs.filter((r) => r.task_slug === taskSlug && isPreStartFailure(r)).sort(newestFirst)[0] ?? null;
11688
+ var SELF_REFERENCE_MARK = "PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430";
11689
+ var VERIFY_ESCALATION_MARK = "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F: \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B";
11690
+ var CI_WAIT_ESCALATION_MARK = "\u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 CI \u0437\u0430\u0442\u044F\u043D\u0443\u043B\u043E\u0441\u044C";
11691
+ var ESCALATIONS = [
11692
+ {
11693
+ mark: SELF_REFERENCE_MARK,
11694
+ // Надзора тут нет намеренно: у self-reference свой канал доставки (watchdog-нотификация
11695
+ // на месте записи), и вход в NEEDS_HUMAN_MARKS задвоил бы сигнал. Ровно на этом маркере
11696
+ // wakeRun и запирал дверь: под self-reference попадает ЛЮБАЯ задача роя, трогающая
11697
+ // packages/swarm-daemon или packages/swarm-core, — вердикта не будет никогда.
11698
+ sides: ["reviewer", "author"],
11699
+ why: "PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430: \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E \u043D\u0430\u043C\u0435\u0440\u0435\u043D\u043D\u043E, \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442",
11700
+ stopReason: () => `PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 (${DAEMON_CODE_PATHS.join(", ")}) \u2014 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E \u043D\u0430\u043C\u0435\u0440\u0435\u043D\u043D\u043E (TASWM-270): \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0438\u0439 \u0434\u0435\u043C\u043E\u043D \u043A\u0440\u0443\u0442\u0438\u0442\u0441\u044F \u043D\u0430 \u0441\u0442\u0430\u0440\u043E\u043C \u043A\u043E\u0434\u0435 \u0438 \u0441\u0432\u043E\u044E \u0436\u0435 \u043F\u0440\u0430\u0432\u043A\u0443 \u043D\u0435 \u0430\u0442\u0442\u0435\u0441\u0442\u0443\u0435\u0442. \u041D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
11701
+ },
11702
+ {
11703
+ mark: REVIEW_ESCALATION_MARK,
11704
+ sides: ["reviewer", "author", "supervision"],
11705
+ why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E \u0440\u043E\u0439 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442",
11706
+ stopReason: (_run, limits) => `\u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B (${limits.reviewSpawn}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
11707
+ },
11708
+ {
11709
+ mark: REVIEW_CYCLE_ESCALATION_MARK,
11710
+ sides: ["reviewer", "author", "supervision"],
11711
+ why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0446\u0438\u043A\u043B\u043E\u0432 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0430\u0432\u0442\u043E\u0441\u043F\u0430\u0432\u043D\u043E\u0432 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442",
11712
+ stopReason: (_run, limits) => `\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0446\u0438\u043A\u043B\u043E\u0432 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (${limits.reviewCycle}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
11713
+ },
11714
+ {
11715
+ mark: REVIEW_BUDGET_MARK,
11716
+ sides: ["reviewer", "author", "supervision"],
11717
+ why: "\u0440\u0435\u0432\u044C\u044E \u044D\u0442\u043E\u0433\u043E \u043A\u0440\u0443\u0433\u0430 \u0441\u0433\u043E\u0440\u0435\u043B\u043E \u043D\u0430 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 \u0442\u0440\u0430\u0442 \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0430\u044F \u043F\u043E\u043F\u044B\u0442\u043A\u0430 \u0443\u043F\u0440\u0451\u0442\u0441\u044F \u0432 \u0442\u043E\u0442 \u0436\u0435",
11718
+ scoped: (run) => reviewBudgetMark(run.review_cycle ?? 0),
11719
+ stopReason: () => "\u0440\u0435\u0432\u044C\u044E \u044D\u0442\u043E\u0433\u043E \u043A\u0440\u0443\u0433\u0430 \u0441\u0433\u043E\u0440\u0435\u043B\u043E \u043D\u0430 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 \u0442\u0440\u0430\u0442 \u2014 \u043F\u043E\u0432\u0442\u043E\u0440 \u0443\u043F\u0440\u0451\u0442\u0441\u044F \u0432 \u0442\u043E\u0442 \u0436\u0435, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
11720
+ },
11721
+ {
11722
+ mark: REVIEW_UNRECOVERABLE_MARK,
11723
+ sides: ["reviewer", "author", "supervision"],
11724
+ why: "\u0432\u0435\u0442\u043A\u0438 PR \u043D\u0435\u0442 \u043D\u0430 origin \u2014 \u043F\u043E\u0432\u0442\u043E\u0440 \u0441\u0434\u0435\u043B\u0430\u0435\u0442 \u0442\u043E\u0442 \u0436\u0435 git-\u0432\u044B\u0437\u043E\u0432 \u0441 \u0442\u0435\u043C \u0436\u0435 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u043C",
11725
+ scoped: (run) => reviewUnrecoverableMark(run.branch || run.task_slug),
11726
+ stopReason: () => "\u0432\u0435\u0442\u043A\u0438 PR \u043D\u0435\u0442 \u043D\u0430 origin \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u043E\u043C \u043D\u0435 \u043B\u0435\u0447\u0438\u0442\u0441\u044F, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
11727
+ },
11728
+ {
11729
+ mark: CHANGES_WAKE_ESCALATION_MARK,
11730
+ // Проверяющего этот маркер не касается: вердикт-то есть, не поднимается АВТОР.
11731
+ sides: ["author", "supervision"],
11732
+ why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0431\u0435\u0441\u043F\u043B\u043E\u0434\u043D\u044B\u0445 \u0444\u0438\u043A\u0441-\u043F\u043E\u0431\u0443\u0434\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0430\u0432\u0442\u043E\u0440\u0430 \u0440\u043E\u0439 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0431\u0443\u0434\u0438\u0442",
11733
+ scoped: (run) => `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run.review_cycle ?? 0})`
11734
+ },
11735
+ {
11736
+ mark: RESOLVE_ESCALATION_MARK,
11737
+ sides: ["author", "supervision"],
11738
+ why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0430\u0432\u0442\u043E-\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440\u043E\u0432 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0434\u0430\u043B\u044C\u0448\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0440\u0430\u0437\u0432\u043E\u0434\u0438\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
11739
+ },
11740
+ {
11741
+ mark: CI_WAKE_ESCALATION_MARK,
11742
+ sides: ["author", "supervision"],
11743
+ why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u043F\u043E\u0431\u0443\u0434\u043E\u043A \u043F\u043E \u043A\u0440\u0430\u0441\u043D\u043E\u043C\u0443 CI \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0440\u043E\u0439 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u043B \u0447\u0438\u043D\u0438\u0442\u044C CI \u0441\u0430\u043C"
11744
+ },
11745
+ {
11746
+ mark: VERIFY_ESCALATION_MARK,
11747
+ // Надзора нет: верификация — не ревью, её отсутствие не держит PR (TASWM-344), и в
11748
+ // NEEDS_HUMAN_MARKS этот маркер никогда не входил. Автору дверь открывает: вердикта
11749
+ // верификации у прогона не будет, поднять его может только оператор.
11750
+ sides: ["author"],
11751
+ why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0443 PR \u043D\u0435 \u0431\u0443\u0434\u0435\u0442"
11752
+ },
11753
+ {
11754
+ mark: CI_WAIT_ESCALATION_MARK,
11755
+ // Сторон НЕТ — и это решение, а не пропуск. Маркер лежит на прогоне РЕВЬЮЕРА (paused,
11756
+ // AWAITING_CI_REASON), а не на прогоне автора, и человека зовёт сам:
11757
+ // escalateStaleCiWaits пишет аномалию и дёргает watchdog.notify на месте. Вписать его в
11758
+ // author/supervision значило бы задвоить сигнал и поменять поведение — чего задача
11759
+ // прямо просила не делать.
11760
+ sides: [],
11761
+ why: "\u0440\u043E\u0439 \u0417\u041E\u0412\u0401\u0422 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430, \u043D\u043E \u0441\u0430\u043C \u0435\u0449\u0451 \u0436\u0434\u0451\u0442: \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043D\u0435 \u0441\u0434\u0430\u043B\u0441\u044F, \u0430 \u043D\u043E\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E \u0448\u043B\u0451\u0442 \u0441\u0432\u043E\u0438\u043C \u043A\u0430\u043D\u0430\u043B\u043E\u043C"
11762
+ }
11763
+ ];
11764
+ function escalationText(entry, run, side) {
11765
+ return side === "supervision" ? entry.mark : entry.scoped?.(run) ?? entry.mark;
11298
11766
  }
11299
- function unresolvedRepoReason(repo, served) {
11300
- const raw = repo.trim();
11301
- if (!raw || raw.startsWith("/") || raw.startsWith("~") || /^[a-z]:[\\/]/i.test(raw)) return null;
11302
- const list = served.length ? served.join(", ") : "\u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E (\u0440\u0435\u0435\u0441\u0442\u0440 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u043F\u0443\u0441\u0442)";
11303
- return `\u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 ${raw} \u043D\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u0432 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0443\u0442\u044C: \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0435\u0433\u043E \u043A\u043B\u043E\u043D\u0430 \u043D\u0435\u0442. \u041C\u0430\u0448\u0438\u043D\u0430 \u043E\u0431\u0441\u043B\u0443\u0436\u0438\u0432\u0430\u0435\u0442: ${list}. \u041A\u043B\u043E\u043D\u0438\u0440\u0443\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0438 \u0434\u043E\u0431\u0430\u0432\u044C \u0435\u0433\u043E \u0432 SWARM_REPO_PATHS (\u0438\u043B\u0438 swarm start --repo <\u043F\u0443\u0442\u044C>), \u043B\u0438\u0431\u043E \u043E\u0442\u0434\u0430\u0439 \u0437\u0430\u0434\u0430\u0447\u0443 \u043C\u0430\u0448\u0438\u043D\u0435, \u0433\u0434\u0435 \u043E\u043D \u0435\u0441\u0442\u044C.`;
11767
+ function escalationsFor(side) {
11768
+ return ESCALATIONS.filter((e) => e.sides.includes(side));
11304
11769
  }
11305
- function parkReason(attempts, reason) {
11306
- return `${PRE_START_MARK} ${attempts} \u0440\u0430\u0437\u0430 \u043F\u043E\u0434\u0440\u044F\u0434 \u2014 ${SPAWN_PARK_MARK}, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u0441\u043F\u0430\u0432\u043D\u0438\u0442\u044C \u043F\u043E \u043A\u0440\u0443\u0433\u0443. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
11770
+ function isEscalationText(text, run, side) {
11771
+ return escalationsFor(side).some((e) => text.includes(escalationText(e, run, side)));
11307
11772
  }
11308
- function parkReasonUnretriable(reason) {
11309
- return `${PRE_START_MARK}, \u0438 ${UNRETRIABLE_PARK_MARK} \u2014 ${SPAWN_PARK_MARK} \u0441\u0440\u0430\u0437\u0443, \u043D\u0435 \u0442\u0440\u0430\u0442\u044F ${PRE_START_ATTEMPT_LIMIT} \u043F\u043E\u043F\u044B\u0442\u043A\u0438. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
11773
+ function findEscalation(texts, run, side) {
11774
+ for (const entry of escalationsFor(side)) {
11775
+ const needle = escalationText(entry, run, side);
11776
+ if (texts.some((t) => t.includes(needle))) return entry;
11777
+ }
11778
+ return null;
11310
11779
  }
11311
11780
 
11312
11781
  // src/worktreeReap.ts
@@ -11384,7 +11853,12 @@ import { basename as basename3 } from "path";
11384
11853
  async function runGh(args, timeoutMs, cwd) {
11385
11854
  const label2 = callLabel("gh", args) + (cwd ? ` (repo ${basename3(cwd)})` : "");
11386
11855
  const child = cwd ? $3({ cwd, timeout: timeoutMs })`gh ${args}` : $3({ timeout: timeoutMs })`gh ${args}`;
11387
- return (await withHardTimeout(child, "gh", label2, timeoutMs)).stdout;
11856
+ try {
11857
+ return (await withHardTimeout(child, "gh", label2, timeoutMs)).stdout;
11858
+ } catch (e) {
11859
+ noteCallFailure("gh", label2, e);
11860
+ throw e;
11861
+ }
11388
11862
  }
11389
11863
  function ghRunner(timeoutMs = GH_CALL_TIMEOUT_MS) {
11390
11864
  return (args) => runGh(args, timeoutMs);
@@ -11598,36 +12072,59 @@ var DENIED_STDERR = /(?:^|\D)403(?:\D|$)|not accessible by integration|must have
11598
12072
  function firstLine(s) {
11599
12073
  return s.split("\n").map((l) => l.trim()).find((l) => l.length > 0) ?? "gh \u0432\u0435\u0440\u043D\u0443\u043B 403";
11600
12074
  }
12075
+ var GhCallFailedError = class extends Error {
12076
+ failure;
12077
+ constructor(call, failure) {
12078
+ super(`${call}: ${failure.reason}${failure.detail ? ` \u2014 ${failure.detail}` : ""}`);
12079
+ this.name = "GhCallFailedError";
12080
+ this.failure = failure;
12081
+ }
12082
+ };
11601
12083
  var realChecksRunner = async (pr) => {
11602
- const r = await withHardTimeout(
11603
- $3({ reject: false, timeout: 15e3 })`gh pr checks ${pr} --json state,name,bucket,link`,
11604
- "gh",
11605
- callLabel("gh", ["pr", "checks", pr]),
11606
- 15e3
11607
- );
12084
+ const label2 = callLabel("gh", ["pr", "checks", pr]);
12085
+ const r = await withHardTimeout($3({ reject: false, timeout: 15e3 })`gh pr checks ${pr} --json state,name,bucket,link`, "gh", label2, 15e3);
11608
12086
  const stdout = r.stdout ?? "";
11609
12087
  const stderr = r.stderr ?? "";
12088
+ if (isTimedOut(r)) throw new GhCallFailedError(label2, classifyCallFailure(r));
11610
12089
  if (!stdout.trim() && NO_CHECKS_STDERR.test(stderr)) return "[]";
11611
12090
  if (!stdout.trim() && DENIED_STDERR.test(stderr)) throw new CiAccessDeniedError(firstLine(stderr));
12091
+ if (!stdout.trim()) throw new GhCallFailedError(label2, noteCallFailure("gh", label2, r));
11612
12092
  return stdout;
11613
12093
  };
11614
12094
  async function prCheckRows(pr, run) {
12095
+ let out;
11615
12096
  try {
11616
- const out = await run(pr);
11617
- const rows = JSON.parse(out);
11618
- if (!Array.isArray(rows)) return null;
11619
- return rows.map((r) => ({ name: r.name ?? "?", bucket: (r.bucket ?? "").toLowerCase(), link: r.link ?? "" }));
12097
+ out = await run(pr);
11620
12098
  } catch (e) {
11621
12099
  if (e instanceof CiAccessDeniedError) return { denied: e.detail };
11622
- return null;
12100
+ return { unavailable: e instanceof GhCallFailedError ? e.failure : classifyCallFailure(e) };
12101
+ }
12102
+ let parsed;
12103
+ try {
12104
+ parsed = JSON.parse(out);
12105
+ } catch {
12106
+ return { unavailable: { reason: "unreadable", detail: headOfOutput(out) } };
11623
12107
  }
12108
+ if (!Array.isArray(parsed)) return { unavailable: { reason: "unreadable", detail: "\u043E\u0442\u0432\u0435\u0442 gh \u2014 \u043D\u0435 \u0441\u043F\u0438\u0441\u043E\u043A \u0447\u0435\u043A\u043E\u0432" } };
12109
+ return parsed.map((r) => ({
12110
+ name: r.name ?? "?",
12111
+ bucket: (r.bucket ?? "").toLowerCase(),
12112
+ link: r.link ?? ""
12113
+ }));
12114
+ }
12115
+ function headOfOutput(out) {
12116
+ const head = firstLine(out).slice(0, 120);
12117
+ return head === "gh \u0432\u0435\u0440\u043D\u0443\u043B 403" ? "\u043F\u0443\u0441\u0442\u043E" : head;
11624
12118
  }
11625
12119
  function isDenied(rows) {
11626
- return rows != null && !Array.isArray(rows);
12120
+ return !Array.isArray(rows) && "denied" in rows;
12121
+ }
12122
+ function isUnavailable(rows) {
12123
+ return !Array.isArray(rows) && "unavailable" in rows;
11627
12124
  }
11628
12125
  async function ghChecks(pr, run = realChecksRunner) {
11629
12126
  const rows = await prCheckRows(pr, run);
11630
- if (!rows || isDenied(rows) || rows.length === 0) return null;
12127
+ if (isDenied(rows) || isUnavailable(rows) || rows.length === 0) return null;
11631
12128
  let pass = 0, fail = 0, pending2 = 0;
11632
12129
  for (const r of rows) {
11633
12130
  if (r.bucket === "pass") pass++;
@@ -11651,13 +12148,16 @@ async function ghChecks(pr, run = realChecksRunner) {
11651
12148
  }
11652
12149
  return { bucket, text, tone, total, pass, fail, pending: pending2 };
11653
12150
  }
11654
- function runIdFromLink(link) {
11655
- const m = link.match(/\/actions\/runs\/(\d+)/);
12151
+ function runIdFromLink(link2) {
12152
+ const m = link2.match(/\/actions\/runs\/(\d+)/);
11656
12153
  return m ? m[1] : null;
11657
12154
  }
11658
12155
  async function prCiStatus(pr, run = realChecksRunner) {
11659
12156
  const rows = await prCheckRows(pr, run);
11660
- if (!rows) return null;
12157
+ if (isUnavailable(rows)) {
12158
+ const f = rows.unavailable;
12159
+ return { bucket: "unavailable", reason: f.reason, failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0, ...f.detail ? { detail: f.detail } : {} };
12160
+ }
11661
12161
  if (isDenied(rows)) return { bucket: "denied", failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0, detail: rows.denied };
11662
12162
  if (rows.length === 0) return { bucket: "none", failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0 };
11663
12163
  const total = rows.length;
@@ -11713,7 +12213,7 @@ init_git();
11713
12213
 
11714
12214
  // src/diff.ts
11715
12215
  import { statSync as statSync10 } from "fs";
11716
- import { join as join25 } from "path";
12216
+ import { join as join26 } from "path";
11717
12217
  import { $ as $4 } from "execa";
11718
12218
  var MAX_FILES = 200;
11719
12219
  var MAX_LINES_PER_FILE = 1500;
@@ -11881,7 +12381,7 @@ async function collectRunDiff(worktree, opts = {}) {
11881
12381
  files.push(pf);
11882
12382
  }
11883
12383
  }
11884
- files.sort((a, b) => mtime(join25(worktree, b.path)) - mtime(join25(worktree, a.path)));
12384
+ files.sort((a, b) => mtime(join26(worktree, b.path)) - mtime(join26(worktree, a.path)));
11885
12385
  const truncatedFiles = files.length > MAX_FILES;
11886
12386
  if (truncatedFiles) files = files.slice(0, MAX_FILES);
11887
12387
  const added = files.reduce((s, f) => s + f.added, 0);
@@ -12714,7 +13214,7 @@ ${report}${shots.warning}`,
12714
13214
  const myKind = opts.runKind ?? (await store.myRun().catch(() => null))?.kind;
12715
13215
  if (myKind === "hero") {
12716
13216
  const taskless = opts.taskless ?? null;
12717
- const errText2 = (text) => ({ content: [{ type: "text", text }], isError: true });
13217
+ const errText3 = (text) => ({ content: [{ type: "text", text }], isError: true });
12718
13218
  let heroCache = null;
12719
13219
  const heroRun = async () => heroCache ??= await store.myRun();
12720
13220
  let epicCache;
@@ -12732,14 +13232,14 @@ ${report}${shots.warning}`,
12732
13232
  estimate_hours: z.number().positive().describe("\u043E\u0446\u0435\u043D\u043A\u0430 \u0432 \u0447\u0430\u0441\u0430\u0445 (\u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E)")
12733
13233
  },
12734
13234
  guard(async ({ title, description_md, estimate_hours }) => {
12735
- if (!taskless) return errText2("Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0442\u044C.");
13235
+ if (!taskless) return errText3("Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0442\u044C.");
12736
13236
  const hero = await heroRun();
12737
- if (!hero) return errText2("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0442\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
13237
+ if (!hero) return errText3("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0442\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
12738
13238
  const ep = await epic(hero.task_slug);
12739
- if (!ep) return errText2(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u044D\u043F\u0438\u043A ${hero.task_slug} \u0438\u0437 Taskless \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0430.`);
12740
- if (!ep.projectId) return errText2(`\u044D\u043F\u0438\u043A ${hero.task_slug} \u0431\u0435\u0437 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F (\u043D\u0443\u0436\u0435\u043D projectId).`);
13239
+ if (!ep) return errText3(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u044D\u043F\u0438\u043A ${hero.task_slug} \u0438\u0437 Taskless \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0430.`);
13240
+ if (!ep.projectId) return errText3(`\u044D\u043F\u0438\u043A ${hero.task_slug} \u0431\u0435\u0437 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F (\u043D\u0443\u0436\u0435\u043D projectId).`);
12741
13241
  const statusId = await taskless.backlogStatusId();
12742
- if (!statusId) return errText2("\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0441\u0442\u0430\u0442\u0443\u0441 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438 backlog \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430 \u2014 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u044E (\u0440\u0438\u0441\u043A \u043F\u043E\u043F\u0430\u0441\u0442\u044C \u0441\u0440\u0430\u0437\u0443 \u0432 \xAB\u0421\u0434\u0435\u043B\u0430\u0442\u044C\xBB).");
13242
+ if (!statusId) return errText3("\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0441\u0442\u0430\u0442\u0443\u0441 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438 backlog \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430 \u2014 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u044E (\u0440\u0438\u0441\u043A \u043F\u043E\u043F\u0430\u0441\u0442\u044C \u0441\u0440\u0430\u0437\u0443 \u0432 \xAB\u0421\u0434\u0435\u043B\u0430\u0442\u044C\xBB).");
12743
13243
  const footer = `
12744
13244
 
12745
13245
  ---
@@ -12753,7 +13253,7 @@ ${report}${shots.warning}`,
12753
13253
  projectId: ep.projectId
12754
13254
  // TASWM-138: наследуем проект эпика — иначе сирота не claimable
12755
13255
  });
12756
- if (!created) return errText2("Taskless \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043B \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 (\u0441\u043C. \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430).");
13256
+ if (!created) return errText3("Taskless \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043B \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 (\u0441\u043C. \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430).");
12757
13257
  await store.addEvent(null, "step", `\u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u043E\u0437\u0434\u0430\u043D\u0430: ${created.slug}`);
12758
13258
  return { content: [{ type: "text", text: created.slug }] };
12759
13259
  })
@@ -12767,18 +13267,18 @@ ${report}${shots.warning}`,
12767
13267
  after: z.array(z.string()).optional().describe("\u0441\u043B\u0430\u0433\u0438 \u0437\u0430\u0434\u0430\u0447, \u043F\u043E\u0441\u043B\u0435 done \u043A\u043E\u0442\u043E\u0440\u044B\u0445 \u0441\u043F\u0430\u0432\u043D\u0438\u0442\u044C (\u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E)")
12768
13268
  },
12769
13269
  guard(async ({ slug, area, after }) => {
12770
- if (!taskless) return errText2("Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D.");
13270
+ if (!taskless) return errText3("Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D.");
12771
13271
  const hero = await heroRun();
12772
- if (!hero) return errText2("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
13272
+ if (!hero) return errText3("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
12773
13273
  const read = await taskless.getTaskDetailed(slug);
12774
- if (!read.ok) return errText2(`\u043D\u0435 \u0441\u043C\u043E\u0433 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 ${slug}: ${describeToolFailure(read.failure)}.`);
13274
+ if (!read.ok) return errText3(`\u043D\u0435 \u0441\u043C\u043E\u0433 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 ${slug}: ${describeToolFailure(read.failure)}.`);
12775
13275
  const task = read.task;
12776
13276
  const ep = await epic(hero.task_slug);
12777
13277
  if (!ep || task.parentId !== ep.id) {
12778
- return errText2(`${slug} \u2014 \u043D\u0435 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0432\u043E\u0435\u0433\u043E \u044D\u043F\u0438\u043A\u0430 ${hero.task_slug}. \u0414\u0438\u0441\u043F\u0430\u0442\u0447\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u0432\u043E\u0438 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438.`);
13278
+ return errText3(`${slug} \u2014 \u043D\u0435 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0432\u043E\u0435\u0433\u043E \u044D\u043F\u0438\u043A\u0430 ${hero.task_slug}. \u0414\u0438\u0441\u043F\u0430\u0442\u0447\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u0432\u043E\u0438 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438.`);
12779
13279
  }
12780
13280
  if (task.statusCategory !== "unstarted") {
12781
- return errText2(
13281
+ return errText3(
12782
13282
  `\u0436\u0434\u0438 \u0430\u043F\u043F\u0440\u0443\u0432\u0430 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430: \u043F\u0435\u0440\u0435\u043D\u043E\u0441 \u0432 \xAB\u0421\u0434\u0435\u043B\u0430\u0442\u044C\xBB. ${slug} \u0441\u0435\u0439\u0447\u0430\u0441 \u0432 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \xAB${task.statusCategory ?? "?"}\xBB, \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F unstarted (\u043E\u0434\u043E\u0431\u0440\u0435\u043D\u043D\u044B\u0445).`
12783
13283
  );
12784
13284
  }
@@ -12800,7 +13300,7 @@ ${report}${shots.warning}`,
12800
13300
  {},
12801
13301
  guard(async () => {
12802
13302
  const hero = await heroRun();
12803
- if (!hero) return errText2("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043E\u0442\u0440\u044F\u0434\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435 \u0441\u043E\u0431\u0440\u0430\u0442\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
13303
+ if (!hero) return errText3("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043E\u0442\u0440\u044F\u0434\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435 \u0441\u043E\u0431\u0440\u0430\u0442\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
12804
13304
  const squad = await store.squadRuns();
12805
13305
  const qByRun = /* @__PURE__ */ new Map();
12806
13306
  for (const q of await store.openQuestionsFor()) {
@@ -12830,9 +13330,9 @@ ${report}${shots.warning}`,
12830
13330
  },
12831
13331
  guard(async ({ question_id, text }) => {
12832
13332
  const mine = (await store.openQuestionsFor()).some((q) => q.id === question_id);
12833
- if (!mine) return errText2(`\u0432\u043E\u043F\u0440\u043E\u0441 #${question_id} \u043D\u0435 \u0438\u0437 \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0445 \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0442\u0432\u043E\u0435\u0433\u043E \u043E\u0442\u0440\u044F\u0434\u0430 \u2014 \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F.`);
13333
+ if (!mine) return errText3(`\u0432\u043E\u043F\u0440\u043E\u0441 #${question_id} \u043D\u0435 \u0438\u0437 \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0445 \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0442\u0432\u043E\u0435\u0433\u043E \u043E\u0442\u0440\u044F\u0434\u0430 \u2014 \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F.`);
12834
13334
  const answered = await store.answer(question_id, text, "hero");
12835
- if (!answered || answered.status !== "answered") return errText2(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 #${question_id}.`);
13335
+ if (!answered || answered.status !== "answered") return errText3(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 #${question_id}.`);
12836
13336
  return { content: [{ type: "text", text: `\u043E\u0442\u0432\u0435\u0442 \u0434\u0440\u043E\u043D\u0443 (\u0432\u043E\u043F\u0440\u043E\u0441 #${question_id}) \u0437\u0430\u043F\u0438\u0441\u0430\u043D, \u0434\u0440\u043E\u043D \u0440\u0430\u0437\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D` }] };
12837
13337
  })
12838
13338
  );
@@ -12906,7 +13406,7 @@ async function runDroneServer(runId) {
12906
13406
 
12907
13407
  // src/droneBridge.ts
12908
13408
  init_src();
12909
- import { createHash as createHash3 } from "crypto";
13409
+ import { createHash as createHash4 } from "crypto";
12910
13410
 
12911
13411
  // src/anomalies.ts
12912
13412
  init_src();
@@ -13437,7 +13937,7 @@ function questionKey(runId, text) {
13437
13937
  return `${runId} ${text.trim().replace(/\s+/g, " ")}`;
13438
13938
  }
13439
13939
  function noteKey(runId, text) {
13440
- return `note:${runId}:${createHash3("sha1").update(questionKey(runId, text)).digest("hex").slice(0, 16)}`;
13940
+ return `note:${runId}:${createHash4("sha1").update(questionKey(runId, text)).digest("hex").slice(0, 16)}`;
13441
13941
  }
13442
13942
  async function syncDroneChannel(opts) {
13443
13943
  const result = { imported: [], answered: [], notes: [] };
@@ -13663,42 +14163,6 @@ init_authState();
13663
14163
 
13664
14164
  // src/swarmStatus.ts
13665
14165
  init_linkFailure();
13666
-
13667
- // src/release.ts
13668
- init_src();
13669
- init_claudeBin();
13670
- var SWARM_PACKAGE = SWARM_PACKAGE_NAME;
13671
- var LATEST_TIMEOUT_MS = 4e3;
13672
- async function fetchLatestVersion(opts = {}) {
13673
- const fetchFn = opts.fetchFn ?? fetch;
13674
- const timeoutMs = opts.timeoutMs ?? LATEST_TIMEOUT_MS;
13675
- const pkg = opts.pkg ?? SWARM_PACKAGE;
13676
- const ctrl = new AbortController();
13677
- const timer = setTimeout(() => ctrl.abort(), timeoutMs);
13678
- try {
13679
- const res = await fetchFn(`https://registry.npmjs.org/${pkg}/latest`, {
13680
- signal: ctrl.signal,
13681
- headers: { accept: "application/vnd.npm.install-v1+json, application/json" }
13682
- });
13683
- if (!res.ok) return null;
13684
- const body = await res.json();
13685
- return typeof body.version === "string" && body.version ? body.version : null;
13686
- } catch {
13687
- return null;
13688
- } finally {
13689
- clearTimeout(timer);
13690
- }
13691
- }
13692
- function isOutdated(current, latest) {
13693
- if (!latest || !current) return false;
13694
- return compareVersionDirs(latest, current) > 0;
13695
- }
13696
- function outdatedLine(current, latest, updateCommand = "swarm update") {
13697
- if (!isOutdated(current, latest)) return null;
13698
- return `\u26A0 \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u043E\u044F \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430: \u0443 \u0442\u0435\u0431\u044F v${current}, \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430 v${latest} \u2014 \u043E\u0431\u043D\u043E\u0432\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: ${updateCommand}`;
13699
- }
13700
-
13701
- // src/swarmStatus.ts
13702
14166
  init_src();
13703
14167
  function classifyLinkError(e) {
13704
14168
  const { kind, detail } = explainLinkFailure(e);
@@ -14000,22 +14464,22 @@ function makeMergeGateParts(deps) {
14000
14464
  }
14001
14465
 
14002
14466
  // src/envGate.ts
14003
- import { existsSync as existsSync15 } from "fs";
14004
- import { join as join27 } from "path";
14467
+ import { existsSync as existsSync16 } from "fs";
14468
+ import { join as join28 } from "path";
14005
14469
  import { createConnection } from "net";
14006
14470
  import { execa as execa2 } from "execa";
14007
14471
 
14008
14472
  // src/platform.ts
14009
- import { existsSync as existsSync14, readFileSync as readFileSync18 } from "fs";
14010
- import { delimiter, join as join26 } from "path";
14473
+ import { existsSync as existsSync15, readFileSync as readFileSync20 } from "fs";
14474
+ import { delimiter, join as join27 } from "path";
14011
14475
  function resolveCommandPath(name, env = process.env, platform = process.platform) {
14012
- if (name.includes("/") || name.includes("\\")) return existsSync14(name) ? name : null;
14476
+ if (name.includes("/") || name.includes("\\")) return existsSync15(name) ? name : null;
14013
14477
  const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
14014
14478
  const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
14015
14479
  for (const dir of dirs) {
14016
14480
  for (const ext of exts) {
14017
- const candidate = join26(dir, name + ext);
14018
- if (existsSync14(candidate)) return candidate;
14481
+ const candidate = join27(dir, name + ext);
14482
+ if (existsSync15(candidate)) return candidate;
14019
14483
  }
14020
14484
  }
14021
14485
  return null;
@@ -14098,8 +14562,8 @@ var COMPOSE_MARKERS = [
14098
14562
  "compose.yaml"
14099
14563
  ];
14100
14564
  function detectRequirements(repo, io = {}) {
14101
- const exists = io.exists ?? existsSync15;
14102
- const marker = COMPOSE_MARKERS.find((f) => exists(join27(repo, f)));
14565
+ const exists = io.exists ?? existsSync16;
14566
+ const marker = COMPOSE_MARKERS.find((f) => exists(join28(repo, f)));
14103
14567
  return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
14104
14568
  }
14105
14569
  function resolveRequirements(repo, declared, io = {}) {
@@ -14233,69 +14697,6 @@ init_src();
14233
14697
  import { randomBytes as randomBytes3 } from "crypto";
14234
14698
  import { basename as basename5 } from "path";
14235
14699
  import { serve as serve2 } from "@hono/node-server";
14236
-
14237
- // package.json
14238
- var package_default = {
14239
- name: "@taskless-app/swarm",
14240
- version: "0.6.31",
14241
- type: "module",
14242
- description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
14243
- keywords: [
14244
- "ai-agents",
14245
- "coding-agents",
14246
- "swarm",
14247
- "orchestration",
14248
- "autonomous-agents",
14249
- "mcp",
14250
- "taskless",
14251
- "cli"
14252
- ],
14253
- license: "SEE LICENSE IN LICENSE",
14254
- author: "Ilya Smurov <ilya@smurov.com>",
14255
- homepage: "https://github.com/ilyasmurov/taskless/tree/main/packages/swarm-daemon#readme",
14256
- repository: {
14257
- type: "git",
14258
- url: "git+https://github.com/ilyasmurov/taskless.git",
14259
- directory: "packages/swarm-daemon"
14260
- },
14261
- engines: {
14262
- node: ">=22"
14263
- },
14264
- bin: {
14265
- swarm: "dist/cli.js"
14266
- },
14267
- files: [
14268
- "dist",
14269
- "docs/HERO.md",
14270
- "hooks",
14271
- "roles"
14272
- ],
14273
- scripts: {
14274
- overmind: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/overmind.ts',
14275
- swarm: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/cli.ts',
14276
- build: "tsup",
14277
- start: "node dist/overmind.js",
14278
- prepublishOnly: "pnpm run build",
14279
- test: "vitest run && SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
14280
- "test:client": "SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
14281
- "test:watch": "vitest",
14282
- typecheck: "tsc -p tsconfig.json --noEmit",
14283
- lint: "echo 'no lint in swarm-daemon yet'"
14284
- },
14285
- dependencies: {
14286
- "@hono/node-server": "^2.0.12",
14287
- "@modelcontextprotocol/sdk": "^1.29.0",
14288
- execa: "^10.0.0",
14289
- zod: "3.25.76"
14290
- },
14291
- gitHead: "82950eae7a6ad09be4193708257798c811bb6fae"
14292
- };
14293
-
14294
- // src/version.ts
14295
- var PACKAGE_VERSION = package_default.version;
14296
- var MIN_NODE_MAJOR = Number(/(\d+)/.exec(package_default.engines?.node ?? "")?.[1] ?? 0);
14297
-
14298
- // src/web.ts
14299
14700
  init_storm();
14300
14701
  init_resources();
14301
14702
  init_config();
@@ -15903,34 +16304,16 @@ function closeWebServer(server) {
15903
16304
  });
15904
16305
  }
15905
16306
 
15906
- // src/daemonVersion.ts
15907
- function captureDaemonStart(now = () => /* @__PURE__ */ new Date()) {
15908
- return { startedAt: now().toISOString(), version: PACKAGE_VERSION };
15909
- }
15910
- var DAEMON_CODE_PATHS = ["packages/swarm-daemon", "packages/swarm-core"];
15911
- async function checkDaemonVersion(start, opts = {}) {
15912
- const now = opts.now ?? (() => /* @__PURE__ */ new Date());
15913
- const fetchLatest = opts.latest ?? (() => fetchLatestVersion());
15914
- const latestVersion = await fetchLatest().catch(() => null);
15915
- return {
15916
- startedAt: start.startedAt,
15917
- version: start.version,
15918
- latestVersion,
15919
- stale: isOutdated(start.version, latestVersion),
15920
- checkedAt: now().toISOString()
15921
- };
15922
- }
15923
-
15924
16307
  // src/hostCommands.ts
15925
16308
  init_src();
15926
- import { readFileSync as readFileSync19, renameSync as renameSync9, writeFileSync as writeFileSync14 } from "fs";
15927
- import { join as join28 } from "path";
16309
+ import { readFileSync as readFileSync21, renameSync as renameSync9, writeFileSync as writeFileSync14 } from "fs";
16310
+ import { join as join29 } from "path";
15928
16311
  function hostCommandStatePath(home) {
15929
- return join28(home, "host-command.json");
16312
+ return join29(home, "host-command.json");
15930
16313
  }
15931
16314
  function readState(path) {
15932
16315
  try {
15933
- return JSON.parse(readFileSync19(path, "utf8"));
16316
+ return JSON.parse(readFileSync21(path, "utf8"));
15934
16317
  } catch {
15935
16318
  return {};
15936
16319
  }
@@ -16100,8 +16483,8 @@ async function doRestart(cmd, opts, note) {
16100
16483
 
16101
16484
  // src/selfUpdate.ts
16102
16485
  init_git();
16103
- import { existsSync as existsSync16 } from "fs";
16104
- import { join as join29 } from "path";
16486
+ import { existsSync as existsSync17 } from "fs";
16487
+ import { join as join30 } from "path";
16105
16488
  import { $ as $5 } from "execa";
16106
16489
  var DAEMON_PACKAGE = "@taskless-app/swarm";
16107
16490
  var defaultRun = async (cmd, args, cwd) => {
@@ -16114,7 +16497,7 @@ function tail(text, max = 400) {
16114
16497
  }
16115
16498
  async function runSelfUpdate(io) {
16116
16499
  const run = io.run ?? defaultRun;
16117
- const exists = io.exists ?? existsSync16;
16500
+ const exists = io.exists ?? existsSync17;
16118
16501
  const repo = io.repo;
16119
16502
  await io.onStep("fetch", "\u0442\u044F\u043D\u0443 \u0441\u0432\u0435\u0436\u0438\u0439 \u043A\u043E\u0434");
16120
16503
  const fetched = await run("git", ["-C", repo, "fetch", "--quiet", "origin"], repo);
@@ -16143,7 +16526,7 @@ async function runSelfUpdate(io) {
16143
16526
  };
16144
16527
  }
16145
16528
  await io.onStep("install", "\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438");
16146
- if (!exists(join29(repo, "pnpm-lock.yaml"))) {
16529
+ if (!exists(join30(repo, "pnpm-lock.yaml"))) {
16147
16530
  return { ok: false, step: "install", message: "\u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442 pnpm-lock.yaml \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0435\u0433\u043E \u044D\u0442\u0438\u043C \u043F\u0443\u0442\u0451\u043C \u043D\u0435 \u0431\u0435\u0440\u0443\u0441\u044C" };
16148
16531
  }
16149
16532
  const installed2 = await run("pnpm", ["install", "--frozen-lockfile"], repo);
@@ -16244,7 +16627,7 @@ function orphanNoteText(run, q) {
16244
16627
 
16245
16628
  // src/probe.ts
16246
16629
  init_executors();
16247
- import { spawn as spawn2 } from "child_process";
16630
+ import { spawn as spawn3 } from "child_process";
16248
16631
  var RESET_RE = /resets[^\n]*/i;
16249
16632
  function executorHealthy(opts = {}) {
16250
16633
  const executor = opts.executor ?? resolveExecutor();
@@ -16263,7 +16646,7 @@ function executorHealthy(opts = {}) {
16263
16646
  }
16264
16647
  resolve4(r);
16265
16648
  };
16266
- const child = spawn2(bin, [...executor.probe.args], { stdio: ["ignore", "pipe", "pipe"] });
16649
+ const child = spawn3(bin, [...executor.probe.args], { stdio: ["ignore", "pipe", "pipe"] });
16267
16650
  child.stdout?.on("data", (d) => {
16268
16651
  out += d.toString();
16269
16652
  });
@@ -16302,19 +16685,19 @@ async function fetchExternalStorm(url, opts = {}) {
16302
16685
  }
16303
16686
 
16304
16687
  // src/anomalyState.ts
16305
- import { existsSync as existsSync17, readFileSync as readFileSync20, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "fs";
16306
- import { join as join30 } from "path";
16688
+ import { existsSync as existsSync18, readFileSync as readFileSync22, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "fs";
16689
+ import { join as join31 } from "path";
16307
16690
  var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
16308
16691
  var MAX_RESOLVED = 200;
16309
16692
  function emptyState() {
16310
16693
  return { since: {}, lastFiredAt: {}, fires: {}, resolved: [], stopQuiet: null };
16311
16694
  }
16312
16695
  function anomalyStatePath(home) {
16313
- return join30(home, "anomaly-state.json");
16696
+ return join31(home, "anomaly-state.json");
16314
16697
  }
16315
16698
  function loadAnomalyState(home, now = Date.now(), io = {}) {
16316
- const exists = io.exists ?? existsSync17;
16317
- const readFile2 = io.readFile ?? ((p) => readFileSync20(p, "utf8"));
16699
+ const exists = io.exists ?? existsSync18;
16700
+ const readFile2 = io.readFile ?? ((p) => readFileSync22(p, "utf8"));
16318
16701
  const path = anomalyStatePath(home);
16319
16702
  if (!exists(path)) return emptyState();
16320
16703
  try {
@@ -16343,11 +16726,11 @@ function loadAnomalyState(home, now = Date.now(), io = {}) {
16343
16726
  }
16344
16727
  function saveAnomalyState(home, state, io = {}) {
16345
16728
  const path = anomalyStatePath(home);
16346
- const writeFile3 = io.writeFile ?? ((p, data) => {
16729
+ const writeFile4 = io.writeFile ?? ((p, data) => {
16347
16730
  writeFileSync15(`${p}.tmp`, data);
16348
16731
  renameSync10(`${p}.tmp`, p);
16349
16732
  });
16350
- writeFile3(path, JSON.stringify(state, null, 2));
16733
+ writeFile4(path, JSON.stringify(state, null, 2));
16351
16734
  }
16352
16735
  var AnomalyStateStore = class {
16353
16736
  constructor(home, now = Date.now(), io = {}) {
@@ -16814,19 +17197,11 @@ var EXITED_CONTINUE_NUDGE = "\u0422\u0432\u043E\u044F \u043F\u0440\u043E\u0448\u
16814
17197
  var STORM_DEAD_LIMIT = Number(process.env.SWARM_STORM_DEAD_LIMIT) || 3;
16815
17198
  var CONFLICT_RESOLVE_COOLDOWN_MS = Number(process.env.SWARM_CONFLICT_COOLDOWN_MS) || 3 * 60 * 1e3;
16816
17199
  var RESOLVE_LIMIT = Number(process.env.SWARM_RESOLVE_LIMIT) || 3;
16817
- var RESOLVE_ESCALATION_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D";
16818
17200
  var REVIEW_SPAWN_LIMIT = Number(process.env.SWARM_REVIEW_SPAWN_LIMIT) || 3;
16819
- var REVIEW_ESCALATION_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0441\u043C\u043E\u0433\u043B\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C\u0441\u044F";
16820
- var REVIEW_UNRECOVERABLE_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u044C";
16821
- function reviewUnrecoverableMark(branch) {
16822
- return `${REVIEW_UNRECOVERABLE_MARK} (\u0432\u0435\u0442\u043A\u0430 ${branch})`;
16823
- }
16824
17201
  var REVIEW_CYCLE_LIMIT = Number(process.env.SWARM_REVIEW_CYCLE_LIMIT) || 3;
16825
- var REVIEW_CYCLE_ESCALATION_MARK = "\u0440\u0435-\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u0446\u0438\u043A\u043B\u0438\u043B\u043E\u0441\u044C";
16826
17202
  var CI_WAKE_LIMIT = Number(process.env.SWARM_CI_WAKE_LIMIT) || 3;
16827
17203
  var CI_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CI_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
16828
17204
  var CI_CHECK_COOLDOWN_MS = Number(process.env.SWARM_CI_CHECK_COOLDOWN_MS) || 60 * 1e3;
16829
- var CI_WAKE_ESCALATION_MARK = "CI-\u0430\u0432\u0442\u043E\u043F\u043E\u0447\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043F\u0440\u0430\u0432\u0438\u043B\u0430\u0441\u044C";
16830
17205
  var CI_NO_CHECKS_MARK = "\u0443 PR \u043D\u0435\u0442 \u0447\u0435\u043A\u043E\u0432 \u0438 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442";
16831
17206
  var CI_WAIT_POLL_MARK = "\u0436\u0434\u0443 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A";
16832
17207
  var CI_POLL_LIMIT_MARK = "\u043E\u043F\u0440\u043E\u0441 CI \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u043F\u043E \u043F\u043E\u0442\u043E\u043B\u043A\u0443";
@@ -16842,29 +17217,13 @@ var CI_POLL_WINDOW_MS = Math.max(
16842
17217
  Number(process.env.SWARM_CI_POLL_WINDOW_MS) || 90 * 60 * 1e3,
16843
17218
  CI_WAIT_LIMIT_MS + CI_CHECK_COOLDOWN_MS
16844
17219
  );
16845
- var CI_WAIT_ESCALATION_MARK = "\u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 CI \u0437\u0430\u0442\u044F\u043D\u0443\u043B\u043E\u0441\u044C";
16846
17220
  var CHANGES_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CHANGES_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
16847
17221
  var CHANGES_WAKE_LIMIT = Number(process.env.SWARM_CHANGES_WAKE_LIMIT) || 2;
16848
17222
  var CHANGES_FRESH_WAKE_AFTER_MS = Number(process.env.SWARM_CHANGES_FRESH_WAKE_AFTER_MS) || SWARM_ANOMALY_PR_NO_REVIEWER_MS;
16849
17223
  var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB";
16850
- var CHANGES_WAKE_ESCALATION_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C";
16851
17224
  var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
16852
17225
  var BUDGET_STOP_REASON = "\u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442: \u0441\u0435\u0441\u0441\u0438\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B \u0431\u044E\u0434\u0436\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430";
16853
17226
  var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON, ABORTED_STOP_REASON];
16854
- var REVIEW_BUDGET_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u043E\u0441\u044C \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442";
16855
- function reviewBudgetMark(cycle) {
16856
- return `${REVIEW_BUDGET_MARK} (\u043A\u0440\u0443\u0433 ${cycle})`;
16857
- }
16858
- var NEEDS_HUMAN_MARKS = [
16859
- RESOLVE_ESCALATION_MARK,
16860
- REVIEW_ESCALATION_MARK,
16861
- REVIEW_UNRECOVERABLE_MARK,
16862
- REVIEW_CYCLE_ESCALATION_MARK,
16863
- CI_WAKE_ESCALATION_MARK,
16864
- CHANGES_WAKE_ESCALATION_MARK,
16865
- REVIEW_BUDGET_MARK
16866
- ];
16867
- var SELF_REFERENCE_MARK = "PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430";
16868
17227
  var REVIEW_NO_VERDICT_REASON = "\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u043E\u0441\u044C \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430: \u043D\u0438 approve, \u043D\u0438 changes \u2014 \u0437\u0430\u0441\u0447\u0438\u0442\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E, \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 \u0437\u0430\u0445\u043E\u0434 (TASWM-375)";
16869
17228
  function verdictCommentMark(createdAt) {
16870
17229
  return `\u043A\u043E\u043C\u043C\u0435\u043D\u0442-\u0432\u0435\u0440\u0434\u0438\u043A\u0442@${createdAt}`;
@@ -17120,11 +17479,17 @@ async function prGateForReviewer(deps, run, prUrl, when) {
17120
17479
  await finalizeClosedPrReview(deps, run, `PR \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430 (${when}) \u2014 \u0440\u0435\u0432\u044C\u044E \u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E`);
17121
17480
  narrate(deps, `PR ${droneLabel(run)} \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430, ${when} \u2014 \u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043D\u0443\u0436\u043D\u043E, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442.`);
17122
17481
  return { kind: "finalized" };
17123
- case "unknown":
17482
+ case "unknown": {
17483
+ const notPr = !isGithubPrUrl(prUrl);
17124
17484
  if (!deps.db.events(run.id).some((e) => (e.text ?? "").includes(REVIEW_PR_UNKNOWN_MARK))) {
17125
- deps.db.addEvent(run.id, "step", `${REVIEW_PR_UNKNOWN_MARK} (gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0438\u043B\u0438 \u0432 pr_url \u043D\u0435 PR) \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C, \u0441\u043F\u0440\u043E\u0441\u0438\u043C \u043D\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0442\u0438\u043A\u0435`);
17485
+ deps.db.addEvent(
17486
+ run.id,
17487
+ "step",
17488
+ notPr ? `${REVIEW_PR_UNKNOWN_MARK}: \u0432 pr_url \u043D\u0435 GitHub-PR (${prUrl}) \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C. \u0421\u0430\u043C\u043E \u043D\u0435 \u043F\u043E\u0447\u0438\u043D\u0438\u0442\u0441\u044F: \u0434\u0440\u043E\u043D \u0441\u0434\u0430\u043B \u0441\u0441\u044B\u043B\u043A\u043E\u0439 \u043D\u0435 \u043D\u0430 PR.` : `${REVIEW_PR_UNKNOWN_MARK}: gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E PR (\u0441\u0435\u0442\u044C/\u043B\u0438\u043C\u0438\u0442) \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C, \u0441\u043F\u0440\u043E\u0441\u0438\u043C \u043D\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0442\u0438\u043A\u0435`
17489
+ );
17126
17490
  }
17127
17491
  return { kind: "wait" };
17492
+ }
17128
17493
  default:
17129
17494
  if (st.conflicting || st.isDraft) {
17130
17495
  deps.db.addEvent(run.id, "step", `PR \u0441\u0442\u0430\u043B \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u044E\u0449\u0438\u043C/\u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u043E\u043C, ${when} \u2014 \u0441\u043F\u0430\u0432\u043D \u043E\u0442\u043B\u043E\u0436\u0435\u043D \u0434\u043E \u0441\u043B\u0435\u0434. \u0442\u0438\u043A\u0430`);
@@ -17238,17 +17603,11 @@ function relinkUnboundReviewers(deps) {
17238
17603
  narrate(deps, `${droneLabel(rv)} \u0440\u0430\u0431\u043E\u0442\u0430\u043B \u0431\u0435\u0437 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043A \u043F\u0440\u043E\u0433\u043E\u043D\u0443 \u2014 \u0441\u0432\u044F\u0437\u0430\u043B \u043F\u043E PR-\u0441\u0441\u044B\u043B\u043A\u0435, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0442\u0435\u043F\u0435\u0440\u044C \u043B\u044F\u0436\u0435\u0442 \u0432 ${parent.task_slug}, \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u043A\u043E\u043C\u043C\u0435\u043D\u0442 GitHub.`);
17239
17604
  }
17240
17605
  }
17606
+ var ESCALATION_LIMITS = { reviewSpawn: REVIEW_SPAWN_LIMIT, reviewCycle: REVIEW_CYCLE_LIMIT };
17241
17607
  function deliberateReviewStop(db, run) {
17242
17608
  const texts = db.events(run.id).map((e) => e.text ?? "");
17243
- const has = (mark) => texts.some((t) => t.includes(mark));
17244
- if (has(SELF_REFERENCE_MARK)) {
17245
- return `PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 (${DAEMON_CODE_PATHS.join(", ")}) \u2014 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E \u043D\u0430\u043C\u0435\u0440\u0435\u043D\u043D\u043E (TASWM-270): \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0438\u0439 \u0434\u0435\u043C\u043E\u043D \u043A\u0440\u0443\u0442\u0438\u0442\u0441\u044F \u043D\u0430 \u0441\u0442\u0430\u0440\u043E\u043C \u043A\u043E\u0434\u0435 \u0438 \u0441\u0432\u043E\u044E \u0436\u0435 \u043F\u0440\u0430\u0432\u043A\u0443 \u043D\u0435 \u0430\u0442\u0442\u0435\u0441\u0442\u0443\u0435\u0442. \u041D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
17246
- }
17247
- if (has(REVIEW_ESCALATION_MARK)) return `\u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B (${REVIEW_SPAWN_LIMIT}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
17248
- if (has(REVIEW_CYCLE_ESCALATION_MARK)) return `\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0446\u0438\u043A\u043B\u043E\u0432 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (${REVIEW_CYCLE_LIMIT}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
17249
- if (has(reviewBudgetMark(run.review_cycle ?? 0))) return "\u0440\u0435\u0432\u044C\u044E \u044D\u0442\u043E\u0433\u043E \u043A\u0440\u0443\u0433\u0430 \u0441\u0433\u043E\u0440\u0435\u043B\u043E \u043D\u0430 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 \u0442\u0440\u0430\u0442 \u2014 \u043F\u043E\u0432\u0442\u043E\u0440 \u0443\u043F\u0440\u0451\u0442\u0441\u044F \u0432 \u0442\u043E\u0442 \u0436\u0435, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A";
17250
- if (has(reviewUnrecoverableMark(run.branch || run.task_slug))) return "\u0432\u0435\u0442\u043A\u0438 PR \u043D\u0435\u0442 \u043D\u0430 origin \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u043E\u043C \u043D\u0435 \u043B\u0435\u0447\u0438\u0442\u0441\u044F, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A";
17251
- return null;
17609
+ const hit = findEscalation(texts, run, "reviewer");
17610
+ return hit?.stopReason?.(run, ESCALATION_LIMITS) ?? null;
17252
17611
  }
17253
17612
  function noteReviewBlocked(deps, run, reason) {
17254
17613
  if (!shouldNoteReason(deps.db, run.id, reason)) return;
@@ -17322,7 +17681,6 @@ async function closeOrphanedReviewers(deps) {
17322
17681
  }
17323
17682
  }
17324
17683
  var VERIFY_SPAWN_LIMIT = Number(process.env.SWARM_VERIFY_SPAWN_LIMIT) || 2;
17325
- var VERIFY_ESCALATION_MARK = "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F: \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B";
17326
17684
  function countActiveVerifiers(db) {
17327
17685
  return db.listRuns().filter((r) => r.kind === "verifier" && ACTIVE_DRONE_STATUSES.has(r.status)).length;
17328
17686
  }
@@ -17496,10 +17854,10 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
17496
17854
  }
17497
17855
  return parts.join("\n");
17498
17856
  }
17499
- var HERO_CHARTER_PATH = join31(dirname5(THIS_FILE), "..", "docs", "HERO.md");
17857
+ var HERO_CHARTER_PATH = join32(dirname5(THIS_FILE), "..", "docs", "HERO.md");
17500
17858
  function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
17501
17859
  const name = run.name || `#${run.id}`;
17502
- const charter = readFileSync21(HERO_CHARTER_PATH, "utf8");
17860
+ const charter = readFileSync23(HERO_CHARTER_PATH, "utf8");
17503
17861
  const head = task ? `\u042D\u041F\u0418\u041A ${run.task_slug}: ${task.title}
17504
17862
 
17505
17863
  ${task.description}` : `\u042D\u041F\u0418\u041A ${run.task_slug} (\u0442\u0435\u043B\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u043F\u043E slug \u0438 \u0437\u0434\u0440\u0430\u0432\u043E\u043C\u0443 \u0441\u043C\u044B\u0441\u043B\u0443).`;
@@ -17803,17 +18161,11 @@ async function wakeRun(deps, runId) {
17803
18161
  if (!run) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0432 \u0437\u0435\u0440\u043A\u0430\u043B\u0435 \u0440\u043E\u044F` };
17804
18162
  if (run.pid != null && deps.isAlive(run.pid)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 (pid ${run.pid}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
17805
18163
  if (isTerminalStatus(run.status)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435\u043D (${run.status}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
17806
- const authorSideStopped = deps.db.events(run.id).some(
17807
- (e) => [RESOLVE_ESCALATION_MARK, CI_WAKE_ESCALATION_MARK, VERIFY_ESCALATION_MARK].some(
17808
- (m) => (e.text ?? "").includes(m)
17809
- )
17810
- );
17811
- const changesEscalated = deps.db.events(run.id).some((e) => (e.text ?? "").includes(`${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run.review_cycle ?? 0})`));
17812
- const stoppedForHuman = deliberateReviewStop(deps.db, run) != null || changesEscalated || authorSideStopped;
18164
+ const stoppedForHuman = deps.db.events(run.id).some((e) => isEscalationText(e.text ?? "", run, "author"));
17813
18165
  if (run.status === "review" && run.pr_url && !needsFixIteration(run) && !stoppedForHuman) {
17814
18166
  return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0441\u0434\u0430\u043D \u043D\u0430 \u0440\u0435\u0432\u044C\u044E (${run.pr_url}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E; \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u043E\u0439 \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443` };
17815
18167
  }
17816
- const worktreeExists = deps.worktreeExists ?? existsSync18;
18168
+ const worktreeExists = deps.worktreeExists ?? existsSync19;
17817
18169
  const hasTranscript = deps.hasTranscript ?? executorHasSession;
17818
18170
  const wt = run.worktree_path;
17819
18171
  const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
@@ -17907,11 +18259,25 @@ async function handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap)
17907
18259
  }
17908
18260
  if (ci?.bucket === "pass" && await wakeReviewerOnCiReady(deps, run, { kind: "pass", ci })) return;
17909
18261
  const counts = ci && ci.total > 0 ? ` (${ci.pass}/${ci.total})` : "";
17910
- const note = ci == null ? "\u0436\u0434\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u043F\u0440\u043E\u0441\u0430" : ci.bucket === "pending" ? "\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0435\u0449\u0451 \u0438\u0434\u0443\u0442" : ci.bucket === "none" ? "\u0447\u0435\u043A-\u0440\u0430\u043D\u043E\u0432 \u0435\u0449\u0451 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E, workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C \u2014 \u0436\u0434\u0443" : "\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0433\u043E\u0442\u043E\u0432\u044B, \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u043A\u043E\u0433\u043E";
17911
- const waiting = ci == null || ci.bucket === "pending" || ci.bucket === "none";
18262
+ const why = ci == null ? "\u043E\u043F\u0440\u043E\u0441 CI \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F \u0446\u0435\u043B\u0438\u043A\u043E\u043C (checks \u0431\u0440\u043E\u0441\u0438\u043B) \u2014 \u0436\u0434\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u043F\u0440\u043E\u0441\u0430" : ciUnavailableNote(ci);
18263
+ const note = ci == null || ci.bucket === "unavailable" ? why : ci.bucket === "pending" ? "\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0435\u0449\u0451 \u0438\u0434\u0443\u0442" : ci.bucket === "none" ? "\u0447\u0435\u043A-\u0440\u0430\u043D\u043E\u0432 \u0435\u0449\u0451 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E, workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C \u2014 \u0436\u0434\u0443" : "\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0433\u043E\u0442\u043E\u0432\u044B, \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u043A\u043E\u0433\u043E";
18264
+ const waiting = ci == null || ci.bucket === "unavailable" || ci.bucket === "pending" || ci.bucket === "none";
17912
18265
  const text = `CI ${ci?.bucket ?? "\u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D"}${counts} \u2014 ${note}`;
17913
18266
  deps.db.addEvent(run.id, "ci_checked", waiting ? `${text} [${CI_WAIT_POLL_MARK}]` : text);
17914
18267
  }
18268
+ function ciUnavailableNote(ci) {
18269
+ const detail = ci.detail ? ` \u2014 ${ci.detail}` : "";
18270
+ switch (ci.reason) {
18271
+ case "timeout":
18272
+ return `\u0422\u0410\u0419\u041C\u0410\u0423\u0422: gh pr checks \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${Math.round(GH_CALL_TIMEOUT_MS / 1e3)} \u0441 \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D${detail}. \u0421\u0435\u0442\u044C \u0443 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043C\u0435\u0434\u043B\u0435\u043D\u043D\u0435\u0435, \u0447\u0435\u043C \u0443 \u0448\u0435\u043B\u043B\u0430 (\u043F\u0440\u043E\u043A\u0441\u0438/DNS) \u2014 \u0436\u0434\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u043F\u0440\u043E\u0441\u0430`;
18273
+ case "crashed":
18274
+ return `gh \u041D\u0415 \u0417\u0410\u041F\u0423\u0421\u0422\u0418\u041B\u0421\u042F${detail}. \u042D\u0442\u043E \u043D\u0435 \u0441\u0435\u0442\u044C: \u0443 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442 gh \u0432 PATH \u2014 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435\u043C \u043D\u0435 \u043B\u0435\u0447\u0438\u0442\u0441\u044F, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
18275
+ case "unreadable":
18276
+ return `\u041D\u0415\u0427\u0418\u0422\u0410\u0415\u041C\u042B\u0419 \u041E\u0422\u0412\u0415\u0422 gh${detail}. \u0412\u044B\u0432\u043E\u0434 \u0435\u0441\u0442\u044C, \u043D\u043E \u044D\u0442\u043E \u043D\u0435 \u0441\u043F\u0438\u0441\u043E\u043A \u0447\u0435\u043A\u043E\u0432 \u2014 \u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C, \u0447\u0442\u043E \u0438\u043C\u0435\u043D\u043D\u043E \u043F\u0435\u0447\u0430\u0442\u0430\u0435\u0442 gh, \u0430 \u043D\u0435 \u0436\u0434\u0430\u0442\u044C`;
18277
+ default:
18278
+ return `\u041F\u0423\u0421\u0422\u041E\u0419 \u041E\u0422\u0412\u0415\u0422: gh \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F, \u043D\u0435 \u0441\u043A\u0430\u0437\u0430\u0432 \u043D\u0438\u0447\u0435\u0433\u043E${detail}. \u0421\u043C\u043E\u0442\u0440\u0435\u0442\u044C stderr \u0432\u044B\u0437\u043E\u0432\u0430 \u0432 \u043B\u043E\u0433\u0435 \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0442\u0430\u043C DNS/\u043F\u0440\u043E\u043A\u0441\u0438/\u043B\u0438\u043C\u0438\u0442`;
18279
+ }
18280
+ }
17915
18281
  function repoHasCi(deps, run) {
17916
18282
  if (deps.repoHasCi) return deps.repoHasCi(run);
17917
18283
  return repoHasCiWorkflows(run.worktree_path, run.repo);
@@ -17919,7 +18285,7 @@ function repoHasCi(deps, run) {
17919
18285
  function repoHasCiWorkflows(worktreePath, repo) {
17920
18286
  for (const base3 of [worktreePath, repo]) {
17921
18287
  if (!base3) continue;
17922
- const dir = join31(base3, ".github", "workflows");
18288
+ const dir = join32(base3, ".github", "workflows");
17923
18289
  try {
17924
18290
  if (readdirSync13(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
17925
18291
  } catch {
@@ -18105,7 +18471,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
18105
18471
  return true;
18106
18472
  }
18107
18473
  function worktreeAndTranscriptAlive(deps, worktree) {
18108
- const exists = deps.worktreeExists ?? existsSync18;
18474
+ const exists = deps.worktreeExists ?? existsSync19;
18109
18475
  const transcript = deps.hasTranscript ?? executorHasSession;
18110
18476
  return exists(worktree) && transcript(worktree);
18111
18477
  }
@@ -18614,7 +18980,7 @@ async function resumeWatchdogParked(deps, droneCap) {
18614
18980
  }
18615
18981
  if (!candidate) return;
18616
18982
  if (candidate.pid != null && deps.isAlive(candidate.pid)) return;
18617
- const worktreeExists = deps.worktreeExists ?? existsSync18;
18983
+ const worktreeExists = deps.worktreeExists ?? existsSync19;
18618
18984
  const hasTranscript = deps.hasTranscript ?? executorHasSession;
18619
18985
  const wt = candidate.worktree_path;
18620
18986
  const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
@@ -18783,7 +19149,7 @@ async function manageRelief(deps, now, droneCap, stormActive) {
18783
19149
  if (resumeCandidate.pid != null && deps.isAlive(resumeCandidate.pid)) return;
18784
19150
  if (!deps.respawn) return;
18785
19151
  const wt = resumeCandidate.worktree_path;
18786
- const worktreeExists = deps.worktreeExists ?? existsSync18;
19152
+ const worktreeExists = deps.worktreeExists ?? existsSync19;
18787
19153
  const hasTranscript = deps.hasTranscript ?? executorHasSession;
18788
19154
  const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
18789
19155
  const mode = canContinue ? "continue" : "fresh";
@@ -19043,7 +19409,7 @@ async function handleBudgetExhausted(deps, run, ctx) {
19043
19409
  function buildEscalationCandidates(db) {
19044
19410
  const candidates = [];
19045
19411
  for (const run of db.listRuns("review")) {
19046
- const hit = db.events(run.id).findLast((e) => NEEDS_HUMAN_MARKS.some((mark) => (e.text ?? "").includes(mark)));
19412
+ const hit = db.events(run.id).findLast((e) => isEscalationText(e.text ?? "", run, "supervision"));
19047
19413
  if (!hit) continue;
19048
19414
  candidates.push({
19049
19415
  key: `needs_human:${run.id}`,
@@ -19061,7 +19427,7 @@ function buildEscalationCandidates(db) {
19061
19427
  }
19062
19428
  async function tickOnce(deps) {
19063
19429
  const now = deps.now ? deps.now() : Date.now();
19064
- const worktreeExists = deps.worktreeExists ?? existsSync18;
19430
+ const worktreeExists = deps.worktreeExists ?? existsSync19;
19065
19431
  const hasTranscript = deps.hasTranscript ?? executorHasSession;
19066
19432
  deps.sampleHost?.();
19067
19433
  if (deps.rehydrate) {
@@ -19107,12 +19473,12 @@ async function tickOnce(deps) {
19107
19473
  }
19108
19474
  }
19109
19475
  if (deps.repoMap) {
19110
- const rm3 = deps.repoMap;
19111
- const sinceLast = rm3.lastAt == null ? Infinity : now - rm3.lastAt;
19112
- if (sinceLast >= rm3.intervalMs) {
19113
- rm3.lastAt = now;
19476
+ const rm4 = deps.repoMap;
19477
+ const sinceLast = rm4.lastAt == null ? Infinity : now - rm4.lastAt;
19478
+ if (sinceLast >= rm4.intervalMs) {
19479
+ rm4.lastAt = now;
19114
19480
  try {
19115
- await rm3.run();
19481
+ await rm4.run();
19116
19482
  } catch (e) {
19117
19483
  console.error("[overmind] \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043A\u0430\u0440\u0442\u044B \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u0441\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C:", e);
19118
19484
  }
@@ -19578,8 +19944,13 @@ async function tickOnce(deps) {
19578
19944
  const ciWatchOff = parent != null && ciWatchClosed(deps.db, parent);
19579
19945
  const ci = ciWatchOff ? null : await deps.ci.checks(run.pr_url).catch(() => null);
19580
19946
  const noCiRepo = ci?.bucket === "none" && !repoHasCi(deps, run);
19581
- if (!ciWatchOff && !noCiRepo && (ci == null || ci.bucket === "pending" || ci.bucket === "none" || ci.bucket === "denied")) {
19582
- const what = ci == null ? "gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E \u0447\u0435\u043A\u0438 \u2014 \u0436\u0434\u0443, \u043F\u043E\u043A\u0430 \u043F\u0440\u043E\u044F\u0441\u043D\u0438\u0442\u0441\u044F" : ci.bucket === "none" ? "\u0447\u0435\u043A\u043E\u0432 \u043D\u0430 PR \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u2014 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0447\u0435\u043A-\u0440\u0430\u043D\u0430, \u043D\u043E workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C" : ci.bucket === "denied" ? `\u0447\u0435\u043A\u0438 \u0440\u043E\u044E \u043D\u0435 \u0432\u0438\u0434\u043D\u044B: GitHub \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u043F\u043E \u043F\u0440\u0430\u0432\u0430\u043C (403)${ci.detail ? ` \u2014 ${ci.detail}` : ""}` : `CI ${ci.pass}/${ci.total}`;
19947
+ if (!ciWatchOff && !noCiRepo && (ci == null || ci.bucket === "pending" || ci.bucket === "none" || ci.bucket === "denied" || ci.bucket === "unavailable")) {
19948
+ const what = ci == null ? "gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E \u0447\u0435\u043A\u0438 \u2014 \u0436\u0434\u0443, \u043F\u043E\u043A\u0430 \u043F\u0440\u043E\u044F\u0441\u043D\u0438\u0442\u0441\u044F" : ci.bucket === "none" ? "\u0447\u0435\u043A\u043E\u0432 \u043D\u0430 PR \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u2014 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0447\u0435\u043A-\u0440\u0430\u043D\u0430, \u043D\u043E workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C" : ci.bucket === "denied" ? `\u0447\u0435\u043A\u0438 \u0440\u043E\u044E \u043D\u0435 \u0432\u0438\u0434\u043D\u044B: GitHub \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u043F\u043E \u043F\u0440\u0430\u0432\u0430\u043C (403)${ci.detail ? ` \u2014 ${ci.detail}` : ""}` : (
19949
+ // TASWM-497: и здесь причина СЛОВАМИ, а не общее «gh не ответил». Тот же словарь,
19950
+ // что в ленте опроса (callFailureWord), чтобы одно и то же состояние в журнале
19951
+ // прогона и в событии парковки не называлось двумя разными способами.
19952
+ ci.bucket === "unavailable" ? `\u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0447\u0435\u043A\u0438 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: gh ${callFailureWord({ reason: ci.reason ?? "empty", detail: "" })}${ci.detail ? ` \u2014 ${ci.detail}` : ""}` : `CI ${ci.pass}/${ci.total}`
19953
+ );
19583
19954
  deps.db.updateRun(run.id, { status: "paused", stop_reason: AWAITING_CI_REASON });
19584
19955
  deps.db.addEvent(run.id, "paused", `${AWAITING_CI_REASON} (${what}) \u2014 \u043D\u0435 \u0441\u043C\u0435\u0440\u0442\u044C: \u0440\u0430\u0437\u0431\u0443\u0436\u0443 \u0441\u0430\u043C, \u043A\u043E\u0433\u0434\u0430 \u0447\u0435\u043A\u0438 \u0434\u043E\u0439\u0434\u0443\u0442 \u0434\u043E \u043A\u043E\u043D\u0446\u0430`);
19585
19956
  narrate(deps, `${droneLabel(run)} \u0443\u0448\u0451\u043B \u0436\u0434\u0430\u0442\u044C CI (${what}) \u2014 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u044E, \u0440\u0430\u0437\u0431\u0443\u0436\u0443 \u043F\u043E \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A.`);
@@ -19852,7 +20223,7 @@ async function resolveConflict(deps, reviewRun) {
19852
20223
  model: reviewRun.model,
19853
20224
  review_model: reviewRun.review_model
19854
20225
  });
19855
- const wt = join31(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
20226
+ const wt = join32(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
19856
20227
  try {
19857
20228
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
19858
20229
  await deps.git.setWorktreeIdentity(wt, resolver.name);
@@ -19931,7 +20302,7 @@ async function spawnReviewer(deps, reviewRun, files) {
19931
20302
  // (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
19932
20303
  workspace_id: reviewRun.workspace_id
19933
20304
  });
19934
- const wt = join31(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
20305
+ const wt = join32(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
19935
20306
  try {
19936
20307
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
19937
20308
  await deps.git.setWorktreeIdentity(wt, reviewer.name);
@@ -20012,7 +20383,7 @@ async function spawnVerifier(deps, verifiedRun, files) {
20012
20383
  // штамп цикла: фикс-итерацию проверяет свежий верификатор
20013
20384
  workspace_id: verifiedRun.workspace_id
20014
20385
  });
20015
- const wt = join31(deps.worktreesDir, basename6(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
20386
+ const wt = join32(deps.worktreesDir, basename6(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
20016
20387
  try {
20017
20388
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
20018
20389
  await deps.git.setWorktreeIdentity(wt, verifier.name);
@@ -20058,7 +20429,7 @@ async function respawnRun(deps, run, mode, nudge) {
20058
20429
  ]);
20059
20430
  const base3 = effectiveBaseBranch(run.base_branch, projectBase);
20060
20431
  const runToSpawn = base3 && run.base_branch !== base3 ? deps.db.updateRun(run.id, { base_branch: base3 }) ?? run : run;
20061
- if (!existsSync18(wt)) {
20432
+ if (!existsSync19(wt)) {
20062
20433
  const branch = runToSpawn.branch || runToSpawn.task_slug;
20063
20434
  const source = await addWorktree(repo, wt, branch, {
20064
20435
  ...base3 ? { base: await remoteBaseRef(repo, base3) } : {},
@@ -20085,7 +20456,7 @@ async function respawnRun(deps, run, mode, nudge) {
20085
20456
  deps.db.updateRun(run.id, { status: "running", pid });
20086
20457
  }
20087
20458
  async function syncCrashedOnStartup(deps) {
20088
- const worktreeExists = deps.worktreeExists ?? existsSync18;
20459
+ const worktreeExists = deps.worktreeExists ?? existsSync19;
20089
20460
  let orphaned = 0;
20090
20461
  let merged = 0;
20091
20462
  let requeued = 0;
@@ -20239,7 +20610,7 @@ async function migrateWorktreesAtStartup() {
20239
20610
  legacyDir: WORKTREES_LEGACY_DIR,
20240
20611
  targetDir: WORKTREES_DIR,
20241
20612
  envOverride: process.env.SWARM_WORKTREES || "",
20242
- exists: (d) => existsSync18(d),
20613
+ exists: (d) => existsSync19(d),
20243
20614
  isNonEmptyDir: (d) => {
20244
20615
  try {
20245
20616
  return readdirSync13(d).length > 0;
@@ -20254,7 +20625,7 @@ async function migrateWorktreesAtStartup() {
20254
20625
  });
20255
20626
  } catch (e) {
20256
20627
  console.error("[migrate] \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u044F worktrees \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C \u2014 \u0444\u043E\u043B\u0431\u044D\u043A:", e);
20257
- return existsSync18(WORKTREES_DIR) ? WORKTREES_DIR : WORKTREES_LEGACY_DIR;
20628
+ return existsSync19(WORKTREES_DIR) ? WORKTREES_DIR : WORKTREES_LEGACY_DIR;
20258
20629
  }
20259
20630
  }
20260
20631
  async function reportWorkspaceAccess(tl, db) {
@@ -20727,7 +21098,7 @@ async function runDaemon(opts) {
20727
21098
  baseSync: (url) => prBaseSync(url),
20728
21099
  checks: async (url) => {
20729
21100
  const ci = await prCiStatus(url);
20730
- if (ci == null || ci.bucket === "denied") return null;
21101
+ if (ci.bucket === "denied" || ci.bucket === "unavailable") return null;
20731
21102
  return { bucket: ci.bucket };
20732
21103
  }
20733
21104
  } : {}
@@ -20907,7 +21278,7 @@ async function runDaemon(opts) {
20907
21278
  isDirty: (p) => worktreeDirty(p),
20908
21279
  releaseWorktree: (repo, p) => releaseWorktree(repo || opts.repo, p),
20909
21280
  pruneWorktrees: (repo) => pruneWorktrees(repo || opts.repo),
20910
- worktreeExists: existsSync18,
21281
+ worktreeExists: existsSync19,
20911
21282
  liveWorktrees: () => new Set(listDroneProcesses().map((p) => p.worktree)),
20912
21283
  narrate: narrator.narrate
20913
21284
  });
@@ -21030,7 +21401,7 @@ async function runDaemon(opts) {
21030
21401
  mode,
21031
21402
  nudge
21032
21403
  ),
21033
- worktreeExists: (p) => existsSync18(p),
21404
+ worktreeExists: (p) => existsSync19(p),
21034
21405
  // TASWM-514: с этого момента считаем попытки спавна — счётчик живёт ровно одну жизнь
21035
21406
  // процесса демона и рестарт не переживает (см. preStartAttempts).
21036
21407
  startedAt: Date.now(),
@@ -21105,9 +21476,11 @@ async function runDaemon(opts) {
21105
21476
  setTimeout(() => {
21106
21477
  Promise.all([closeWebServer(web.server), droneChannel ? closeDroneChannel(droneChannel.server) : Promise.resolve()]).then(() => {
21107
21478
  if (!stopping) {
21108
- spawn3(process.execPath, respawnArgs, {
21479
+ spawn4(process.execPath, respawnArgs, {
21109
21480
  detached: true,
21110
- stdio: "ignore",
21481
+ // TASWM-497: в ЛОГ, а не в /dev/null. Со `stdio: "ignore"` демон после каждого
21482
+ // `update`/`restart` замолкал навсегда, и разбирать инциденты по логу было нечем.
21483
+ stdio: respawnStdio(DAEMON_LOG_PATH),
21111
21484
  cwd: process.cwd(),
21112
21485
  env: { ...process.env, SWARM_REPO: opts.repo }
21113
21486
  }).unref();