@taskless-app/swarm 0.6.0 → 0.6.2

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.
package/dist/overmind.js CHANGED
@@ -746,10 +746,12 @@ function hostCommandLabel(cmd) {
746
746
  if (isRunScopedCommand(cmd.kind) && cmd.runSlug) return `${base3} ${cmd.runSlug}`;
747
747
  return base3;
748
748
  }
749
- var HOST_COMMAND_KINDS, HOST_COMMAND_STATUSES, TERMINAL, HOST_COMMAND_OPEN_STATUSES, UPDATE_STEP_LABELS, HOST_COMMAND_TTL_MS, HOST_COMMAND_KEEPALIVE_MS, HOST_COMMAND_LABELS, RUN_SCOPED_COMMAND_KINDS, RUN_SCOPED, MAX_DRONES_LIMIT;
749
+ var SWARM_PACKAGE_NAME, MANUAL_UPDATE_COMMAND, HOST_COMMAND_KINDS, HOST_COMMAND_STATUSES, TERMINAL, HOST_COMMAND_OPEN_STATUSES, UPDATE_STEP_LABELS, HOST_COMMAND_TTL_MS, HOST_COMMAND_KEEPALIVE_MS, HOST_COMMAND_ACK_GRACE_MS, HOST_COMMAND_MUTE_REASON, HOST_COMMAND_LABELS, RUN_SCOPED_COMMAND_KINDS, RUN_SCOPED, MAX_DRONES_LIMIT;
750
750
  var init_hostCommands = __esm({
751
751
  "../swarm-core/src/hostCommands.ts"() {
752
752
  "use strict";
753
+ SWARM_PACKAGE_NAME = "@taskless-app/swarm";
754
+ MANUAL_UPDATE_COMMAND = `npm i -g ${SWARM_PACKAGE_NAME}@latest`;
753
755
  HOST_COMMAND_KINDS = ["pause", "resume", "restart", "update", "set_max_drones", "stop_run", "wake_run"];
754
756
  HOST_COMMAND_STATUSES = ["pending", "delivered", "running", "done", "failed", "rejected", "expired"];
755
757
  TERMINAL = /* @__PURE__ */ new Set(["done", "failed", "rejected", "expired"]);
@@ -763,6 +765,8 @@ var init_hostCommands = __esm({
763
765
  };
764
766
  HOST_COMMAND_TTL_MS = 30 * 6e4;
765
767
  HOST_COMMAND_KEEPALIVE_MS = 10 * 6e4;
768
+ HOST_COMMAND_ACK_GRACE_MS = 5 * 6e4;
769
+ HOST_COMMAND_MUTE_REASON = `\u043C\u0430\u0448\u0438\u043D\u0430 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043B\u0430 \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u2014 \u0432\u0435\u0440\u043E\u044F\u0442\u043D\u043E, \u0434\u0435\u043C\u043E\u043D \u0441\u0442\u0430\u0440\u0435\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u044B. \u041E\u0431\u043D\u043E\u0432\u0438 \u0432\u0440\u0443\u0447\u043D\u0443\u044E: ${MANUAL_UPDATE_COMMAND}`;
766
770
  HOST_COMMAND_LABELS = {
767
771
  pause: "\u043F\u0430\u0443\u0437\u0430",
768
772
  resume: "\u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C",
@@ -3263,7 +3267,7 @@ var init_droneChannel = __esm({
3263
3267
 
3264
3268
  // src/attachments.ts
3265
3269
  import { mkdir as mkdir2, rm as rm2, writeFile } from "fs/promises";
3266
- import { join as join17 } from "path";
3270
+ import { join as join18 } from "path";
3267
3271
  function attachmentsFromDoc(doc, origin) {
3268
3272
  const out = [];
3269
3273
  const walk2 = (node) => {
@@ -3354,7 +3358,7 @@ async function materializeAttachments(opts) {
3354
3358
  };
3355
3359
  const result = { files: [], skipped: [] };
3356
3360
  if (opts.refs.length === 0) return result;
3357
- const dir = join17(opts.worktree, ATTACHMENTS_DIR);
3361
+ const dir = join18(opts.worktree, ATTACHMENTS_DIR);
3358
3362
  await io.removeDir(dir);
3359
3363
  let dirReady = false;
3360
3364
  let total = 0;
@@ -3400,7 +3404,7 @@ async function materializeAttachments(opts) {
3400
3404
  await io.mkdir(dir);
3401
3405
  dirReady = true;
3402
3406
  }
3403
- await io.writeFile(join17(dir, name), file.bytes);
3407
+ await io.writeFile(join18(dir, name), file.bytes);
3404
3408
  } catch (e) {
3405
3409
  result.skipped.push(`${label} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText(e)})`);
3406
3410
  continue;
@@ -3553,7 +3557,7 @@ var init_attachments = __esm({
3553
3557
 
3554
3558
  // src/shotUpload.ts
3555
3559
  import { readFile as fsReadFile } from "fs/promises";
3556
- import { basename as basename2, isAbsolute as isAbsolute3, join as join18 } from "path";
3560
+ import { basename as basename2, isAbsolute as isAbsolute3, join as join19 } from "path";
3557
3561
  function shotMimetype(path) {
3558
3562
  const dot = path.lastIndexOf(".");
3559
3563
  if (dot < 0) return null;
@@ -3579,7 +3583,7 @@ async function readShots(paths, opts) {
3579
3583
  }
3580
3584
  let bytes;
3581
3585
  try {
3582
- bytes = await read(isAbsolute3(path) ? path : join18(opts.worktree, path));
3586
+ bytes = await read(isAbsolute3(path) ? path : join19(opts.worktree, path));
3583
3587
  } catch (e) {
3584
3588
  skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
3585
3589
  continue;
@@ -3656,12 +3660,12 @@ var init_shotUpload = __esm({
3656
3660
  });
3657
3661
 
3658
3662
  // src/oauth.ts
3659
- import { createHash, randomBytes as randomBytes2 } from "crypto";
3663
+ import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
3660
3664
  import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, unlinkSync as unlinkSync3 } from "fs";
3661
- import { join as join19 } from "path";
3665
+ import { join as join20 } from "path";
3662
3666
  function makePkce() {
3663
3667
  const verifier = randomBytes2(48).toString("base64url");
3664
- const challenge = createHash("sha256").update(verifier).digest("base64url");
3668
+ const challenge = createHash2("sha256").update(verifier).digest("base64url");
3665
3669
  return { verifier, challenge };
3666
3670
  }
3667
3671
  async function registerClient(args, fetchFn = fetch) {
@@ -3793,15 +3797,15 @@ var init_oauth = __esm({
3793
3797
  AUTHORIZE = "/api/oauth/authorize";
3794
3798
  TOKEN = "/api/oauth/token";
3795
3799
  SCOPE = "openid profile email taskless:full";
3796
- STORE_PATH = join19(SWARM_HOME, "oauth.json");
3797
- CLIENT_PATH = join19(SWARM_HOME, "oauth-client.json");
3800
+ STORE_PATH = join20(SWARM_HOME, "oauth.json");
3801
+ CLIENT_PATH = join20(SWARM_HOME, "oauth-client.json");
3798
3802
  base = (api) => api.replace(/\/$/, "");
3799
3803
  }
3800
3804
  });
3801
3805
 
3802
3806
  // src/agentToken.ts
3803
3807
  import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync4 } from "fs";
3804
- import { join as join20 } from "path";
3808
+ import { join as join21 } from "path";
3805
3809
  function loadAgentToken() {
3806
3810
  try {
3807
3811
  const s = JSON.parse(readFileSync15(STORE_PATH2, "utf8"));
@@ -3821,6 +3825,14 @@ async function probeAgentToken(args, fetchFn = fetch) {
3821
3825
  return "unreachable";
3822
3826
  }
3823
3827
  }
3828
+ function isRevokedTokenError(e) {
3829
+ if (e && typeof e === "object" && e.code === 401) return true;
3830
+ const text = e instanceof Error ? e.message : typeof e === "string" ? e : "";
3831
+ return /invalid_token|(?:http|status|code)[ =:]*401\b|\(401\)|401 unauthorized/i.test(text);
3832
+ }
3833
+ function revokedTokenLine() {
3834
+ return "[taskless] \u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043E\u0442\u043E\u0437\u0432\u0430\u043D (401 invalid_token) \u2014 \u0440\u043E\u0439 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0443. \u041F\u043E\u0447\u0438\u043D\u0438 \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: swarm login";
3835
+ }
3824
3836
  function revokedTokenComplaint() {
3825
3837
  return [
3826
3838
  "",
@@ -3841,14 +3853,14 @@ var init_agentToken = __esm({
3841
3853
  "src/agentToken.ts"() {
3842
3854
  "use strict";
3843
3855
  init_config();
3844
- STORE_PATH2 = join20(SWARM_HOME, "agent.json");
3856
+ STORE_PATH2 = join21(SWARM_HOME, "agent.json");
3845
3857
  base2 = (api) => api.replace(/\/$/, "");
3846
3858
  }
3847
3859
  });
3848
3860
 
3849
3861
  // src/workspace.ts
3850
3862
  import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync5 } from "fs";
3851
- import { join as join21 } from "path";
3863
+ import { join as join22 } from "path";
3852
3864
  function loadWorkspace() {
3853
3865
  try {
3854
3866
  const w = JSON.parse(readFileSync16(WORKSPACE_PATH, "utf8"));
@@ -3865,7 +3877,7 @@ var init_workspace = __esm({
3865
3877
  "src/workspace.ts"() {
3866
3878
  "use strict";
3867
3879
  init_config();
3868
- WORKSPACE_PATH = join21(SWARM_HOME, "workspace.json");
3880
+ WORKSPACE_PATH = join22(SWARM_HOME, "workspace.json");
3869
3881
  }
3870
3882
  });
3871
3883
 
@@ -4220,7 +4232,9 @@ async function connectTaskless(cfg) {
4220
4232
  try {
4221
4233
  await client.connect(transport, { timeout: TASKLESS_CONNECT_TIMEOUT_MS });
4222
4234
  } catch (e) {
4223
- console.warn(`Taskless: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${e instanceof Error ? e.message : e}) \u2192 slug-only. \u0412\u043E\u0439\u0434\u0438 \u0437\u0430\u043D\u043E\u0432\u043E.`);
4235
+ console.warn(
4236
+ isRevokedTokenError(e) ? `${revokedTokenLine()} (\u043F\u043E\u043A\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435\u0442 \u2014 slug-only \u0440\u0435\u0436\u0438\u043C)` : `Taskless: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${e instanceof Error ? e.message : e}) \u2192 slug-only. \u0412\u043E\u0439\u0434\u0438 \u0437\u0430\u043D\u043E\u0432\u043E.`
4237
+ );
4224
4238
  return null;
4225
4239
  }
4226
4240
  const call = async (name, args) => await client.callTool({ name, arguments: args });
@@ -4244,7 +4258,9 @@ async function connectSwarmMcp(cfg) {
4244
4258
  try {
4245
4259
  await client.connect(transport);
4246
4260
  } catch (e) {
4247
- console.warn(`Swarm MCP: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${e instanceof Error ? e.message : e}) \u2192 \u0431\u0443\u0444\u0435\u0440 \u0432 outbox.`);
4261
+ console.warn(
4262
+ isRevokedTokenError(e) ? `${revokedTokenLine()} (\u043F\u043E\u043A\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435\u0442 \u2014 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u043A\u043E\u043F\u044F\u0442\u0441\u044F \u0432 outbox)` : `Swarm MCP: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${e instanceof Error ? e.message : e}) \u2192 \u0431\u0443\u0444\u0435\u0440 \u0432 outbox.`
4263
+ );
4248
4264
  return null;
4249
4265
  }
4250
4266
  return async (name, args) => await client.callTool({ name, arguments: swarmToolArgs(name, args, workspace) });
@@ -5169,7 +5185,7 @@ var init_droneStore = __esm({
5169
5185
  // src/overmind.ts
5170
5186
  import { homedir as homedir5 } from "os";
5171
5187
  import { existsSync as existsSync16, readFileSync as readFileSync20, readdirSync as readdirSync12, renameSync as renameSync10 } from "fs";
5172
- import { basename as basename4, dirname as dirname5, join as join28 } from "path";
5188
+ import { basename as basename4, dirname as dirname4, join as join29 } from "path";
5173
5189
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
5174
5190
  import { spawn as spawn3 } from "child_process";
5175
5191
 
@@ -6694,7 +6710,16 @@ var SwarmClient = class _SwarmClient {
6694
6710
  const data = res?.structuredContent ?? {};
6695
6711
  return {
6696
6712
  commands: Array.isArray(data.commands) ? data.commands.filter(isHostCommandShape) : [],
6697
- logRequests: parseLogRequests(data.logRequests)
6713
+ logRequests: parseLogRequests(data.logRequests),
6714
+ // Предел человека (TASWM-363). Различаем ТРИ случая, и разница не косметическая:
6715
+ // • ключа нет вовсе (старый прод, пульс без hostId) → undefined: прежнее число
6716
+ // не трогаем, иначе обновление прода молча снимало бы предел;
6717
+ // • число >0 → предел назван;
6718
+ // • всё прочее (null, ноль, мусор) → null: предела нет. Выдуманный потолок хуже
6719
+ // отсутствующего — под ним рой встанет молча.
6720
+ ...data.maxDrones === void 0 ? {} : {
6721
+ maxDrones: typeof data.maxDrones === "number" && Number.isFinite(data.maxDrones) && data.maxDrones > 0 ? Math.trunc(data.maxDrones) : null
6722
+ }
6698
6723
  };
6699
6724
  } catch {
6700
6725
  return EMPTY_HEARTBEAT_REPLY;
@@ -8231,19 +8256,89 @@ init_git();
8231
8256
 
8232
8257
  // src/deps.ts
8233
8258
  import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync12 } from "fs";
8234
- import { join as join16 } from "path";
8259
+ import { join as join17 } from "path";
8235
8260
  import { execa } from "execa";
8261
+
8262
+ // src/prepareLock.ts
8263
+ import { createHash } from "crypto";
8264
+ import { closeSync as closeSync3, openSync as openSync3, rmSync as rmSync2, statSync as statSync8, writeSync } from "fs";
8265
+ import { join as join16 } from "path";
8266
+ import { tmpdir } from "os";
8267
+ var WAIT_TIMEOUT_MS = 5 * 6e4;
8268
+ var POLL_MS = 300;
8269
+ var STALE_MS = 20 * 6e4;
8270
+ function machineLockPath(key, dir = tmpdir()) {
8271
+ return join16(dir, `swarm-prepare-${createHash("sha1").update(key).digest("hex").slice(0, 16)}.lock`);
8272
+ }
8273
+ function defaultTryCreate(path, content) {
8274
+ try {
8275
+ const fd = openSync3(path, "wx");
8276
+ try {
8277
+ writeSync(fd, content);
8278
+ } finally {
8279
+ closeSync3(fd);
8280
+ }
8281
+ return true;
8282
+ } catch {
8283
+ return false;
8284
+ }
8285
+ }
8286
+ function defaultAgeMs(path) {
8287
+ try {
8288
+ return Date.now() - statSync8(path).mtimeMs;
8289
+ } catch {
8290
+ return null;
8291
+ }
8292
+ }
8293
+ async function withMachineLock(key, fn, io = {}) {
8294
+ const tryCreate = io.tryCreate ?? defaultTryCreate;
8295
+ const remove = io.remove ?? ((p) => {
8296
+ try {
8297
+ rmSync2(p, { force: true });
8298
+ } catch {
8299
+ }
8300
+ });
8301
+ const ageMs2 = io.ageMs ?? defaultAgeMs;
8302
+ const wait = io.wait ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
8303
+ const now = io.now ?? Date.now;
8304
+ const path = machineLockPath(key, io.dir ?? tmpdir());
8305
+ const deadline = now() + WAIT_TIMEOUT_MS;
8306
+ let locked = false;
8307
+ for (; ; ) {
8308
+ if (tryCreate(path, `${process.pid}
8309
+ `)) {
8310
+ locked = true;
8311
+ break;
8312
+ }
8313
+ const age = ageMs2(path);
8314
+ if (age !== null && age > STALE_MS) remove(path);
8315
+ if (now() >= deadline) break;
8316
+ await wait(POLL_MS);
8317
+ }
8318
+ try {
8319
+ return await fn(locked);
8320
+ } finally {
8321
+ if (locked) remove(path);
8322
+ }
8323
+ }
8324
+
8325
+ // src/deps.ts
8236
8326
  var PREPARE_TIMEOUT_MS = 15 * 6e4;
8237
8327
  var MAX_PLAN_COMMANDS = 12;
8238
8328
  async function runCommand(cmd, worktree) {
8239
- const res = await execa(cmd.run, {
8240
- cwd: cmd.cwd ?? worktree,
8241
- shell: true,
8242
- // команда — строка целиком (у настройки/CI иначе никак), поэтому именно шелл
8243
- reject: false,
8244
- timeout: PREPARE_TIMEOUT_MS
8245
- });
8246
- return res.exitCode === 0;
8329
+ const exec = async () => {
8330
+ const res = await execa(cmd.run, {
8331
+ cwd: cmd.cwd ?? worktree,
8332
+ shell: true,
8333
+ // команда — строка целиком (у настройки/CI иначе никак), поэтому именно шелл
8334
+ reject: false,
8335
+ timeout: PREPARE_TIMEOUT_MS,
8336
+ ...cmd.env ? { env: cmd.env } : {}
8337
+ // execa мержит с окружением процесса, не заменяет его
8338
+ });
8339
+ return res.exitCode === 0;
8340
+ };
8341
+ return cmd.lock ? withMachineLock(cmd.lock, exec) : exec();
8247
8342
  }
8248
8343
  function safeReadText(p) {
8249
8344
  try {
@@ -8289,19 +8384,19 @@ function overridePlan(override) {
8289
8384
  function detectPackageManager(worktree, io = {}) {
8290
8385
  const exists = io.exists ?? existsSync10;
8291
8386
  const readText = io.readText ?? safeReadText;
8292
- const hasPkgJson = exists(join16(worktree, "package.json"));
8387
+ const hasPkgJson = exists(join17(worktree, "package.json"));
8293
8388
  if (!hasPkgJson) {
8294
- if (exists(join16(worktree, "deno.json")) || exists(join16(worktree, "deno.jsonc")) || exists(join16(worktree, "deno.lock"))) return "deno";
8389
+ if (exists(join17(worktree, "deno.json")) || exists(join17(worktree, "deno.jsonc")) || exists(join17(worktree, "deno.lock"))) return "deno";
8295
8390
  return null;
8296
8391
  }
8297
- const pkg = readJson(readText, join16(worktree, "package.json"));
8392
+ const pkg = readJson(readText, join17(worktree, "package.json"));
8298
8393
  const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
8299
8394
  if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
8300
- if (exists(join16(worktree, "bun.lock")) || exists(join16(worktree, "bun.lockb"))) return "bun";
8301
- if (exists(join16(worktree, "pnpm-lock.yaml"))) return "pnpm";
8302
- if (exists(join16(worktree, "yarn.lock"))) return "yarn";
8303
- if (exists(join16(worktree, "package-lock.json"))) return "npm";
8304
- if (exists(join16(worktree, "deno.lock"))) return "deno";
8395
+ if (exists(join17(worktree, "bun.lock")) || exists(join17(worktree, "bun.lockb"))) return "bun";
8396
+ if (exists(join17(worktree, "pnpm-lock.yaml"))) return "pnpm";
8397
+ if (exists(join17(worktree, "yarn.lock"))) return "yarn";
8398
+ if (exists(join17(worktree, "package-lock.json"))) return "npm";
8399
+ if (exists(join17(worktree, "deno.lock"))) return "deno";
8305
8400
  return "npm";
8306
8401
  }
8307
8402
  function jsInstallCommand(pm) {
@@ -8362,20 +8457,21 @@ function detectPlan(worktree, io) {
8362
8457
  // node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
8363
8458
  // У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
8364
8459
  // заново, но он дешёвый: тянет из того же общего кэша.
8365
- { label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join16(worktree, "node_modules"), source: "detect" }
8460
+ { label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join17(worktree, "node_modules"), source: "detect" }
8366
8461
  ];
8367
8462
  for (const target of workspaceBuildTargets(worktree, io)) {
8368
8463
  commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
8369
8464
  }
8465
+ commands.push(...migrateCommands(worktree, pm, io));
8370
8466
  return { source: "detect", ecosystem: `node/${pm}`, commands };
8371
8467
  }
8372
8468
  for (const marker of ECOSYSTEM_MARKERS) {
8373
- const hit = marker.files.some((f) => exists(join16(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
8469
+ const hit = marker.files.some((f) => exists(join17(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
8374
8470
  if (!hit) continue;
8375
8471
  let ecosystem = marker.ecosystem;
8376
8472
  let command = marker.command;
8377
8473
  for (const refine of marker.refine ?? []) {
8378
- if (!exists(join16(worktree, refine.file))) continue;
8474
+ if (!exists(join17(worktree, refine.file))) continue;
8379
8475
  ecosystem = refine.ecosystem;
8380
8476
  command = refine.command;
8381
8477
  break;
@@ -8432,12 +8528,12 @@ var CI_INSTALL_PATTERNS = [
8432
8528
  function ciWorkflowPlan(worktree, io) {
8433
8529
  const readText = io.readText ?? safeReadText;
8434
8530
  const listFiles = io.listFiles ?? safeListFiles;
8435
- const dir = join16(worktree, ".github", "workflows");
8531
+ const dir = join17(worktree, ".github", "workflows");
8436
8532
  const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
8437
8533
  const seen = /* @__PURE__ */ new Set();
8438
8534
  const commands = [];
8439
8535
  for (const name of files.sort()) {
8440
- const yaml = readText(join16(dir, name));
8536
+ const yaml = readText(join17(dir, name));
8441
8537
  if (yaml === null) continue;
8442
8538
  for (const raw of workflowRunLines(yaml)) {
8443
8539
  const line = raw.replace(/\s+/g, " ").trim();
@@ -8452,7 +8548,7 @@ function ciWorkflowPlan(worktree, io) {
8452
8548
  return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
8453
8549
  }
8454
8550
  function workspaceGlobs(worktree, readText) {
8455
- const yaml = readText(join16(worktree, "pnpm-workspace.yaml"));
8551
+ const yaml = readText(join17(worktree, "pnpm-workspace.yaml"));
8456
8552
  if (yaml !== null) {
8457
8553
  const globs = [];
8458
8554
  let inPackages = false;
@@ -8472,7 +8568,7 @@ function workspaceGlobs(worktree, readText) {
8472
8568
  }
8473
8569
  if (globs.length) return globs;
8474
8570
  }
8475
- const pkg = readJson(readText, join16(worktree, "package.json"));
8571
+ const pkg = readJson(readText, join17(worktree, "package.json"));
8476
8572
  const ws = pkg?.workspaces;
8477
8573
  const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
8478
8574
  return list.filter((g) => typeof g === "string");
@@ -8483,14 +8579,14 @@ function workspaceDirs(worktree, globs, io) {
8483
8579
  if (glob.startsWith("!")) continue;
8484
8580
  const star = glob.indexOf("*");
8485
8581
  if (star === -1) {
8486
- const dir = join16(worktree, glob);
8487
- if (io.exists(join16(dir, "package.json"))) dirs.add(dir);
8582
+ const dir = join17(worktree, glob);
8583
+ if (io.exists(join17(dir, "package.json"))) dirs.add(dir);
8488
8584
  continue;
8489
8585
  }
8490
- const base3 = join16(worktree, glob.slice(0, star).replace(/\/$/, ""));
8586
+ const base3 = join17(worktree, glob.slice(0, star).replace(/\/$/, ""));
8491
8587
  for (const name of io.listDirs(base3)) {
8492
- const dir = join16(base3, name);
8493
- if (io.exists(join16(dir, "package.json"))) dirs.add(dir);
8588
+ const dir = join17(base3, name);
8589
+ if (io.exists(join17(dir, "package.json"))) dirs.add(dir);
8494
8590
  }
8495
8591
  }
8496
8592
  return [...dirs];
@@ -8518,11 +8614,11 @@ function workspaceBuildTargets(worktree, io = {}) {
8518
8614
  if (!globs.length) return [];
8519
8615
  const pkgs = [];
8520
8616
  for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
8521
- const pkg = readJson(readText, join16(dir, "package.json"));
8617
+ const pkg = readJson(readText, join17(dir, "package.json"));
8522
8618
  if (!pkg || typeof pkg.name !== "string") continue;
8523
8619
  const scripts = pkg.scripts ?? {};
8524
8620
  const entries = entryPaths(pkg);
8525
- const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join16(dir, e)));
8621
+ const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join17(dir, e)));
8526
8622
  const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
8527
8623
  pkgs.push({ name: pkg.name, dir, deps, needsBuild });
8528
8624
  }
@@ -8544,6 +8640,59 @@ function workspaceBuildTargets(worktree, io = {}) {
8544
8640
  for (const p of targets.values()) visit(p);
8545
8641
  return ordered;
8546
8642
  }
8643
+ var LOCAL_DB_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]", "host.docker.internal"]);
8644
+ function devDatabaseUrl(worktree, io = {}) {
8645
+ const readText = io.readText ?? safeReadText;
8646
+ const fromEnv = (io.env ?? process.env).DATABASE_URL;
8647
+ if (fromEnv && fromEnv.trim()) return fromEnv.trim();
8648
+ for (const file of [".env", ".env.example"]) {
8649
+ const raw = readText(join17(worktree, file));
8650
+ if (raw === null) continue;
8651
+ const m = /^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/m.exec(raw);
8652
+ const value = (m?.[1] ?? "").trim().replace(/^['"]|['"]$/g, "");
8653
+ if (value) return value;
8654
+ }
8655
+ return null;
8656
+ }
8657
+ function isLocalDatabase(url) {
8658
+ try {
8659
+ return LOCAL_DB_HOSTS.has(new URL(url).hostname);
8660
+ } catch {
8661
+ return false;
8662
+ }
8663
+ }
8664
+ function prismaPackages(worktree, io) {
8665
+ const exists = io.exists ?? existsSync10;
8666
+ const readText = io.readText ?? safeReadText;
8667
+ const listDirs = io.listDirs ?? safeListDirs;
8668
+ const dirs = [worktree, ...workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs })];
8669
+ return dirs.filter((dir) => exists(join17(dir, "prisma", "schema.prisma")) && listDirs(join17(dir, "prisma", "migrations")).length > 0);
8670
+ }
8671
+ function jsExecBin(pm, command) {
8672
+ switch (pm) {
8673
+ case "npm":
8674
+ return `npm exec -- ${command}`;
8675
+ case "bun":
8676
+ return `bun x ${command}`;
8677
+ default:
8678
+ return `${pm} exec ${command}`;
8679
+ }
8680
+ }
8681
+ function migrateCommands(worktree, pm, io) {
8682
+ if (pm === "deno") return [];
8683
+ const url = devDatabaseUrl(worktree, io);
8684
+ if (!url || !isLocalDatabase(url)) return [];
8685
+ return prismaPackages(worktree, io).map((dir) => ({
8686
+ label: `\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0411\u0414${dir === worktree ? "" : ` (${dir.slice(worktree.length + 1)})`}`,
8687
+ run: jsExecBin(pm, "prisma migrate deploy"),
8688
+ cwd: dir,
8689
+ env: { DATABASE_URL: url },
8690
+ // Ключ мьютекса — сама база: дроны разных репозиториев с разными базами друг друга не ждут.
8691
+ lock: `migrate:${url}`,
8692
+ optional: true,
8693
+ source: "detect"
8694
+ }));
8695
+ }
8547
8696
  function planPrepare(worktree, io = {}) {
8548
8697
  const plan = overridePlan(io.override) ?? detectPlan(worktree, io) ?? ciWorkflowPlan(worktree, io);
8549
8698
  if (!plan) return { source: "none", ecosystem: null, commands: [] };
@@ -8561,7 +8710,8 @@ async function ensureWorktreeDeps(worktree, io = {}) {
8561
8710
  ran: [],
8562
8711
  failed: [],
8563
8712
  skipped: [],
8564
- blocked: []
8713
+ blocked: [],
8714
+ warned: []
8565
8715
  };
8566
8716
  for (const cmd of plan.commands) {
8567
8717
  if (cmd.skipIfExists && exists(cmd.skipIfExists)) {
@@ -8576,6 +8726,8 @@ async function ensureWorktreeDeps(worktree, io = {}) {
8576
8726
  if (await exec(cmd)) {
8577
8727
  report.ran.push(cmd.label);
8578
8728
  report.outcome = "prepared";
8729
+ } else if (cmd.optional) {
8730
+ report.warned.push(cmd.label);
8579
8731
  } else {
8580
8732
  report.failed.push(cmd.label);
8581
8733
  report.outcome = "failed";
@@ -8592,13 +8744,17 @@ function depsEvents(report) {
8592
8744
  events.push(
8593
8745
  `\u26A0 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 worktree${where} \u0423\u041F\u0410\u041B\u0410 \u043D\u0430 \xAB${report.failed.join(", ")}\xBB \u2014 \u0434\u0440\u043E\u043D \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 \u0411\u0415\u0417 \u0433\u043E\u0442\u043E\u0432\u043E\u0433\u043E \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F: pre-commit \u0445\u0443\u043A \u043C\u043E\u0436\u0435\u0442 \u043C\u043E\u043B\u0447\u0430 \u043D\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C, typecheck/\u0442\u0435\u0441\u0442\u044B \u0431\u0443\u0434\u0443\u0442 \u043A\u0440\u0430\u0441\u043D\u044B\u043C\u0438 \u043D\u0435 \u043F\u043E \u0434\u0435\u043B\u0443 (TASWM-312)`
8594
8746
  );
8747
+ if (report.warned.length)
8748
+ events.push(
8749
+ `\u26A0 \u0448\u0430\u0433 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0438 \xAB${report.warned.join(", ")}\xBB \u043D\u0435 \u043F\u0440\u043E\u0448\u0451\u043B \u2014 worktree \u0433\u043E\u0442\u043E\u0432, \u043D\u043E \u0411\u0410\u0417\u0410 \u041C\u041E\u0413\u041B\u0410 \u041E\u0421\u0422\u0410\u0422\u042C\u0421\u042F \u041D\u0410 \u0421\u0422\u0410\u0420\u041E\u0419 \u0421\u0425\u0415\u041C\u0415 (\u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043E \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u0430\u044F \u0431\u0430\u0437\u0430 \u043F\u0440\u043E\u0441\u0442\u043E \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u0430) \u2014 TASWM-362`
8750
+ );
8595
8751
  if (report.blocked.length)
8596
8752
  events.push(`\u26A0 \u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0438 \u043D\u0435 \u043F\u0443\u0449\u0435\u043D\u0430 \u0433\u0435\u0439\u0442\u043E\u043C \u0434\u043E\u0432\u0435\u0440\u0438\u044F: ${report.blocked.join(", ")} \u2014 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043D\u0435 \u0433\u043E\u0442\u043E\u0432\u0438\u043B\u0438 (TASWM-312)`);
8597
8753
  if (report.outcome === "not-detected")
8598
8754
  events.push(
8599
8755
  `\u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D\u0430: \u043F\u0440\u043E\u0435\u043A\u0442 \u043D\u0435 \u043E\u043F\u043E\u0437\u043D\u0430\u043D (\u043D\u0435\u0442 \u0437\u043D\u0430\u043A\u043E\u043C\u044B\u0445 \u043C\u0430\u0440\u043A\u0435\u0440\u043D\u044B\u0445 \u0444\u0430\u0439\u043B\u043E\u0432, \u0432 .github/workflows \u043A\u043E\u043C\u0430\u043D\u0434 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438 \u043D\u0435 \u043D\u0430\u0448\u043B\u0438) \u2014 \u0434\u0435\u043C\u043E\u043D \u041D\u0418\u0427\u0415\u0413\u041E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u043B; \u0437\u0430\u0434\u0430\u0442\u044C \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u043C\u043E\u0436\u043D\u043E \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 (swarmConfig.prepare) \u2014 TASWM-312`
8600
8756
  );
8601
- if (report.outcome === "nothing-to-do" && !report.skipped.length) {
8757
+ if (report.outcome === "nothing-to-do" && !report.skipped.length && !report.warned.length) {
8602
8758
  if (report.source === "project-config") events.push(`\u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u0430 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u043E\u0439 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 (swarmConfig.prepare \u043F\u0443\u0441\u0442) \u2014 TASWM-312`);
8603
8759
  else if (report.ecosystem) events.push(`\u043F\u0440\u043E\u0435\u043A\u0442${where}: \u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043E\u043C \u043D\u0435\u0447\u0435\u0433\u043E, \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u2014 TASWM-312`);
8604
8760
  }
@@ -8617,6 +8773,9 @@ function depsPromptNotice(report) {
8617
8773
  if (problems.length) {
8618
8774
  return `\u26A0 \u041E\u041A\u0420\u0423\u0416\u0415\u041D\u0418\u0415 WORKTREE \u041D\u0415 \u041F\u041E\u0414\u0413\u041E\u0422\u041E\u0412\u041B\u0415\u041D\u041E (TASWM-312): ${problems.join("; ")}. \u041F\u043E\u043A\u0430 \u044D\u0442\u043E \u0442\u0430\u043A, \u0413\u0415\u0419\u0422 \u041C\u041E\u0416\u0415\u0422 \u041D\u0415 \u0420\u0410\u0411\u041E\u0422\u0410\u0422\u042C: pre-commit \u0445\u0443\u043A \u043C\u043E\u043B\u0447\u0430 \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0441\u044F (\u043F\u0435\u0440\u0432\u044B\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 \u0443\u0439\u0434\u0451\u0442 \u0431\u0435\u0437 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A), \u0430 typecheck/\u0442\u0435\u0441\u0442\u044B \u0431\u0443\u0434\u0443\u0442 \u043A\u0440\u0430\u0441\u043D\u044B\u043C\u0438 \u043D\u0435 \u043F\u043E \u0434\u0435\u043B\u0443. \u041F\u0435\u0440\u0432\u044B\u043C \u0434\u0435\u043B\u043E\u043C \u043F\u043E\u0447\u0438\u043D\u0438 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0441\u0430\u043C (\u043F\u043E\u0441\u0442\u0430\u0432\u044C \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u0442\u0430\u043A, \u043A\u0430\u043A \u044D\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0442 README/CI \u043F\u0440\u043E\u0435\u043A\u0442\u0430) \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0442\u043E\u043C \u0432\u0435\u0440\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0430\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A; \u043D\u0435 \u0441\u0434\u0430\u0432\u0430\u0439 \u0440\u0430\u0431\u043E\u0442\u0443, \u043E\u0431\u044A\u044F\u0441\u043D\u044F\u044F \u043F\u0430\u0434\u0435\u043D\u0438\u044F \xAB\u0441\u043B\u043E\u043C\u0430\u043D\u043D\u044B\u043C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435\u043C\xBB, \u043D\u0435 \u043F\u043E\u043F\u0440\u043E\u0431\u043E\u0432\u0430\u0432 \u044D\u0442\u043E.`;
8619
8775
  }
8776
+ if (report.warned.length) {
8777
+ return `\u2139 \u0411\u0410\u0417\u0410 \u041C\u041E\u0413\u041B\u0410 \u041E\u0421\u0422\u0410\u0422\u042C\u0421\u042F \u041D\u0410 \u0421\u0422\u0410\u0420\u041E\u0419 \u0421\u0425\u0415\u041C\u0415 (TASWM-362): \u0448\u0430\u0433 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0438 \xAB${report.warned.join(", ")}\xBB \u043D\u0435 \u043F\u0440\u043E\u0448\u0451\u043B \u2014 \u043E\u0431\u044B\u0447\u043D\u043E \u043F\u043E\u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u0430\u044F \u0431\u0430\u0437\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u0430. \u0421\u0430\u043C worktree \u0433\u043E\u0442\u043E\u0432, typecheck \u0438 \u044E\u043D\u0438\u0442-\u0442\u0435\u0441\u0442\u044B \u0434\u043E\u0441\u0442\u043E\u0432\u0435\u0440\u043D\u044B. \u0415\u0441\u043B\u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0448\u044C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0438 \u0443\u0432\u0438\u0434\u0438\u0448\u044C \u043E\u0448\u0438\u0431\u043A\u0443 \u043F\u0440\u043E \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0443\u044E \u043A\u043E\u043B\u043E\u043D\u043A\u0443/\u0442\u0430\u0431\u043B\u0438\u0446\u0443 \u2014 \u044D\u0442\u043E \u043E\u043D\u0430: \u043F\u043E\u0434\u043D\u0438\u043C\u0438 \u0431\u0430\u0437\u0443 \u0438 \u043D\u0430\u043A\u0430\u0442\u0438 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0442\u0430\u043A, \u043A\u0430\u043A \u044D\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0442 dev-\u0441\u043A\u0440\u0438\u043F\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430.`;
8778
+ }
8620
8779
  if (report.outcome === "not-detected") {
8621
8780
  return `\u2139 \u0414\u0415\u041C\u041E\u041D \u041D\u0415 \u0413\u041E\u0422\u041E\u0412\u0418\u041B \u041E\u041A\u0420\u0423\u0416\u0415\u041D\u0418\u0415 (TASWM-312): \u043F\u0440\u043E\u0435\u043A\u0442 \u043D\u0435 \u043E\u043F\u043E\u0437\u043D\u0430\u043D \u043F\u043E \u043C\u0430\u0440\u043A\u0435\u0440\u043D\u044B\u043C \u0444\u0430\u0439\u043B\u0430\u043C, \u043A\u043E\u043C\u0430\u043D\u0434 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438 \u0432 .github/workflows \u0442\u043E\u0436\u0435 \u043D\u0435\u0442. \u0417\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438, \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u043D\u0443\u0436\u043D\u044B, \u043D\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u044B \u2014 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438 README/CI \u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u044C \u0438\u0445 \u0441\u0430\u043C \u041F\u0415\u0420\u0415\u0414 \u0442\u0435\u043C, \u043A\u0430\u043A \u0432\u0435\u0440\u0438\u0442\u044C typecheck/\u0442\u0435\u0441\u0442\u0430\u043C.`;
8622
8781
  }
@@ -8976,6 +9135,15 @@ function branchFromPrPatch(currentBranch, headBranch) {
8976
9135
  event: currentBranch ? `\u0432\u0435\u0442\u043A\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0432\u0437\u044F\u0442\u0430 \u0443 PR: ${currentBranch} \u2192 ${headBranch} (\u043F\u043E\u043B\u0435 \u0431\u044B\u043B\u043E \u0441\u043E\u0431\u0440\u0430\u043D\u043E \u0438\u0437 \u0441\u043B\u0430\u0433\u0430 \u2014 TASWM-356)` : `\u0432\u0435\u0442\u043A\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0432\u0437\u044F\u0442\u0430 \u0443 PR: ${headBranch} (TASWM-356)`
8977
9136
  };
8978
9137
  }
9138
+ function prLiveness(st) {
9139
+ if (!st) return "unknown";
9140
+ if (st.merged) return "merged";
9141
+ const state = (st.state ?? "").toUpperCase();
9142
+ if (state === "MERGED") return "merged";
9143
+ if (state === "CLOSED") return "closed";
9144
+ if (state === "UNKNOWN") return "unknown";
9145
+ return "open";
9146
+ }
8979
9147
  function applyReviewTransition(db, run, prUrl) {
8980
9148
  const { patch, ...t } = reviewTransitionPatch(run, prUrl);
8981
9149
  db.updateRun(run.id, patch);
@@ -9486,7 +9654,7 @@ async function runDroneServer(runId) {
9486
9654
 
9487
9655
  // src/droneBridge.ts
9488
9656
  init_src();
9489
- import { createHash as createHash2 } from "crypto";
9657
+ import { createHash as createHash3 } from "crypto";
9490
9658
 
9491
9659
  // src/anomalies.ts
9492
9660
  init_src();
@@ -9881,7 +10049,7 @@ function questionKey(runId, text) {
9881
10049
  return `${runId} ${text.trim().replace(/\s+/g, " ")}`;
9882
10050
  }
9883
10051
  function noteKey(runId, text) {
9884
- return `note:${runId}:${createHash2("sha1").update(questionKey(runId, text)).digest("hex").slice(0, 16)}`;
10052
+ return `note:${runId}:${createHash3("sha1").update(questionKey(runId, text)).digest("hex").slice(0, 16)}`;
9885
10053
  }
9886
10054
  async function syncDroneChannel(opts) {
9887
10055
  const result = { imported: [], answered: [], notes: [] };
@@ -10162,20 +10330,20 @@ function makeExclusiveReader(opts) {
10162
10330
 
10163
10331
  // src/envGate.ts
10164
10332
  import { existsSync as existsSync13 } from "fs";
10165
- import { join as join23 } from "path";
10333
+ import { join as join24 } from "path";
10166
10334
  import { createConnection } from "net";
10167
10335
  import { execa as execa2 } from "execa";
10168
10336
 
10169
10337
  // src/platform.ts
10170
10338
  import { existsSync as existsSync12, readFileSync as readFileSync17 } from "fs";
10171
- import { delimiter, join as join22 } from "path";
10339
+ import { delimiter, join as join23 } from "path";
10172
10340
  function resolveCommandPath(name, env = process.env, platform = process.platform) {
10173
10341
  if (name.includes("/") || name.includes("\\")) return existsSync12(name) ? name : null;
10174
10342
  const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
10175
10343
  const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
10176
10344
  for (const dir of dirs) {
10177
10345
  for (const ext of exts) {
10178
- const candidate = join22(dir, name + ext);
10346
+ const candidate = join23(dir, name + ext);
10179
10347
  if (existsSync12(candidate)) return candidate;
10180
10348
  }
10181
10349
  }
@@ -10184,6 +10352,31 @@ function resolveCommandPath(name, env = process.env, platform = process.platform
10184
10352
  function hasCommandInPath(name, env = process.env, platform = process.platform) {
10185
10353
  return resolveCommandPath(name, env, platform) !== null;
10186
10354
  }
10355
+ function detectGlobalPm(entry, io = {}) {
10356
+ const p = (entry ?? "").replace(/\\/g, "/");
10357
+ if (!p) return "unknown";
10358
+ if (p.endsWith(".ts") || /\/packages\/swarm-daemon\/(src|dist)\//.test(p)) return "sources";
10359
+ if (/\/\.bun\/install\/global\//.test(p) || /\/\.bun\/bin\//.test(p)) return "bun";
10360
+ if (/\/pnpm\//.test(p) || /\/\.pnpm-global\//.test(p) || /\/Library\/pnpm\//.test(p)) return "pnpm";
10361
+ if (/\/\.yarn\//.test(p) || /\/Yarn\/Data\/global\//.test(p) || /\/yarn\/global\//.test(p)) return "yarn";
10362
+ if (/\/node_modules\//.test(p)) return "npm";
10363
+ const env = io.env ?? process.env;
10364
+ const platform = io.platform ?? process.platform;
10365
+ const has = io.hasCommand ?? ((n) => hasCommandInPath(n, env, platform));
10366
+ return has("npm") ? "npm" : "unknown";
10367
+ }
10368
+ function globalInstallCommand(pm, spec) {
10369
+ switch (pm) {
10370
+ case "pnpm":
10371
+ return `pnpm add -g ${spec}`;
10372
+ case "bun":
10373
+ return `bun add -g ${spec}`;
10374
+ case "yarn":
10375
+ return `yarn global add ${spec}`;
10376
+ default:
10377
+ return `npm i -g ${spec}`;
10378
+ }
10379
+ }
10187
10380
 
10188
10381
  // src/envGate.ts
10189
10382
  var PROBE_TIMEOUT_MS = 2500;
@@ -10235,7 +10428,7 @@ var COMPOSE_MARKERS = [
10235
10428
  ];
10236
10429
  function detectRequirements(repo, io = {}) {
10237
10430
  const exists = io.exists ?? existsSync13;
10238
- const marker = COMPOSE_MARKERS.find((f) => exists(join23(repo, f)));
10431
+ const marker = COMPOSE_MARKERS.find((f) => exists(join24(repo, f)));
10239
10432
  return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
10240
10433
  }
10241
10434
  function resolveRequirements(repo, declared, io = {}) {
@@ -10371,8 +10564,8 @@ import { basename as basename3 } from "path";
10371
10564
  import { serve as serve2 } from "@hono/node-server";
10372
10565
 
10373
10566
  // src/diff.ts
10374
- import { statSync as statSync8 } from "fs";
10375
- import { join as join24 } from "path";
10567
+ import { statSync as statSync9 } from "fs";
10568
+ import { join as join25 } from "path";
10376
10569
  import { $ as $4 } from "execa";
10377
10570
  var MAX_FILES = 200;
10378
10571
  var MAX_LINES_PER_FILE = 1500;
@@ -10515,7 +10708,7 @@ async function resolveBase(wt, baseBranch) {
10515
10708
  }
10516
10709
  function mtime(path) {
10517
10710
  try {
10518
- return statSync8(path).mtimeMs;
10711
+ return statSync9(path).mtimeMs;
10519
10712
  } catch {
10520
10713
  return 0;
10521
10714
  }
@@ -10540,7 +10733,7 @@ async function collectRunDiff(worktree, opts = {}) {
10540
10733
  files.push(pf);
10541
10734
  }
10542
10735
  }
10543
- files.sort((a, b) => mtime(join24(worktree, b.path)) - mtime(join24(worktree, a.path)));
10736
+ files.sort((a, b) => mtime(join25(worktree, b.path)) - mtime(join25(worktree, a.path)));
10544
10737
  const truncatedFiles = files.length > MAX_FILES;
10545
10738
  if (truncatedFiles) files = files.slice(0, MAX_FILES);
10546
10739
  const added = files.reduce((s, f) => s + f.added, 0);
@@ -10573,7 +10766,7 @@ async function changedPaths(worktree, opts = {}) {
10573
10766
  // package.json
10574
10767
  var package_default = {
10575
10768
  name: "@taskless-app/swarm",
10576
- version: "0.6.0",
10769
+ version: "0.6.2",
10577
10770
  type: "module",
10578
10771
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
10579
10772
  keywords: [
@@ -10622,7 +10815,8 @@ var package_default = {
10622
10815
  "@modelcontextprotocol/sdk": "^1.29.0",
10623
10816
  execa: "^10.0.0",
10624
10817
  zod: "3.25.76"
10625
- }
10818
+ },
10819
+ gitHead: "e812f35a996b00111f3f535b6b0a45b379e7afb2"
10626
10820
  };
10627
10821
 
10628
10822
  // src/version.ts
@@ -12201,48 +12395,10 @@ function closeWebServer(server) {
12201
12395
  });
12202
12396
  }
12203
12397
 
12204
- // src/daemonVersion.ts
12205
- init_git();
12206
- import { dirname as dirname4 } from "path";
12207
- import { $ as $5 } from "execa";
12208
- async function captureDaemonStart(entryFile, now = () => /* @__PURE__ */ new Date()) {
12209
- try {
12210
- const repo = await repoRoot(dirname4(entryFile));
12211
- const sha = (await $5`git -C ${repo} rev-parse HEAD`).stdout.trim();
12212
- if (!sha) return null;
12213
- return { repo, startedAt: now().toISOString(), startCommit: sha };
12214
- } catch {
12215
- return null;
12216
- }
12217
- }
12218
- var DAEMON_CODE_PATHS = ["packages/swarm-daemon", "packages/swarm-core"];
12219
- async function checkDaemonVersion(start, now = () => /* @__PURE__ */ new Date()) {
12220
- const base3 = { startedAt: start.startedAt, startCommit: start.startCommit, checkedAt: now().toISOString() };
12221
- try {
12222
- await $5({ reject: false })`git -C ${start.repo} fetch --quiet origin`;
12223
- const mainRef = await remoteMainRef(start.repo);
12224
- const mainCommit = (await $5`git -C ${start.repo} rev-parse ${mainRef}`).stdout.trim();
12225
- if (!mainCommit || mainCommit === start.startCommit) {
12226
- return { ...base3, mainCommit: mainCommit || null, stale: false, staleFiles: [] };
12227
- }
12228
- const ancestor = await $5({ reject: false })`git -C ${start.repo} merge-base --is-ancestor ${start.startCommit} ${mainCommit}`;
12229
- if (ancestor.exitCode !== 0) {
12230
- return { ...base3, mainCommit, stale: false, staleFiles: [] };
12231
- }
12232
- const diff = await $5({ reject: false })(
12233
- "git",
12234
- ["-C", start.repo, "diff", "--name-only", start.startCommit, mainCommit, "--", ...DAEMON_CODE_PATHS]
12235
- );
12236
- const staleFiles = diff.stdout.trim() ? diff.stdout.trim().split("\n") : [];
12237
- return { ...base3, mainCommit, stale: staleFiles.length > 0, staleFiles: staleFiles.slice(0, 20) };
12238
- } catch {
12239
- return { ...base3, mainCommit: null, stale: false, staleFiles: [] };
12240
- }
12241
- }
12242
-
12243
12398
  // src/release.ts
12399
+ init_src();
12244
12400
  init_claudeBin();
12245
- var SWARM_PACKAGE = "@taskless-app/swarm";
12401
+ var SWARM_PACKAGE = SWARM_PACKAGE_NAME;
12246
12402
  var LATEST_TIMEOUT_MS = 4e3;
12247
12403
  async function fetchLatestVersion(opts = {}) {
12248
12404
  const fetchFn = opts.fetchFn ?? fetch;
@@ -12273,12 +12429,30 @@ function outdatedLine(current, latest, updateCommand = "swarm update") {
12273
12429
  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}`;
12274
12430
  }
12275
12431
 
12432
+ // src/daemonVersion.ts
12433
+ function captureDaemonStart(now = () => /* @__PURE__ */ new Date()) {
12434
+ return { startedAt: now().toISOString(), version: PACKAGE_VERSION };
12435
+ }
12436
+ var DAEMON_CODE_PATHS = ["packages/swarm-daemon", "packages/swarm-core"];
12437
+ async function checkDaemonVersion(start, opts = {}) {
12438
+ const now = opts.now ?? (() => /* @__PURE__ */ new Date());
12439
+ const fetchLatest = opts.latest ?? (() => fetchLatestVersion());
12440
+ const latestVersion = await fetchLatest().catch(() => null);
12441
+ return {
12442
+ startedAt: start.startedAt,
12443
+ version: start.version,
12444
+ latestVersion,
12445
+ stale: isOutdated(start.version, latestVersion),
12446
+ checkedAt: now().toISOString()
12447
+ };
12448
+ }
12449
+
12276
12450
  // src/hostCommands.ts
12277
12451
  init_src();
12278
12452
  import { readFileSync as readFileSync18, renameSync as renameSync8, writeFileSync as writeFileSync13 } from "fs";
12279
- import { join as join25 } from "path";
12453
+ import { join as join26 } from "path";
12280
12454
  function hostCommandStatePath(home) {
12281
- return join25(home, "host-command.json");
12455
+ return join26(home, "host-command.json");
12282
12456
  }
12283
12457
  function readState(path) {
12284
12458
  try {
@@ -12421,6 +12595,11 @@ async function executeOne(cmd, opts) {
12421
12595
  effects.narrate(`\u26A0\uFE0F \u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0448\u0430\u0433\u0435 \xAB${UPDATE_STEP_LABELS[res.step]}\xBB: ${res.message}`);
12422
12596
  return false;
12423
12597
  }
12598
+ if (res.restart === false) {
12599
+ await ack(cmd.id, "done", { step: res.step, result: res.message });
12600
+ effects.narrate(`\u2705 ${res.message}`);
12601
+ return false;
12602
+ }
12424
12603
  return await doRestart(cmd, opts, res.message);
12425
12604
  }
12426
12605
  }
@@ -12448,11 +12627,11 @@ async function doRestart(cmd, opts, note) {
12448
12627
  // src/selfUpdate.ts
12449
12628
  init_git();
12450
12629
  import { existsSync as existsSync14 } from "fs";
12451
- import { join as join26 } from "path";
12452
- import { $ as $6 } from "execa";
12630
+ import { join as join27 } from "path";
12631
+ import { $ as $5 } from "execa";
12453
12632
  var DAEMON_PACKAGE = "@taskless-app/swarm";
12454
12633
  var defaultRun = async (cmd, args, cwd) => {
12455
- const res = await $6({ reject: false, cwd })(cmd, args);
12634
+ const res = await $5({ reject: false, cwd })(cmd, args);
12456
12635
  return { exitCode: res.exitCode ?? 1, stdout: res.stdout ?? "", stderr: res.stderr ?? "" };
12457
12636
  };
12458
12637
  function tail(text, max = 400) {
@@ -12490,7 +12669,7 @@ async function runSelfUpdate(io) {
12490
12669
  };
12491
12670
  }
12492
12671
  await io.onStep("install", "\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438");
12493
- if (!exists(join26(repo, "pnpm-lock.yaml"))) {
12672
+ if (!exists(join27(repo, "pnpm-lock.yaml"))) {
12494
12673
  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" };
12495
12674
  }
12496
12675
  const installed = await run("pnpm", ["install", "--frozen-lockfile"], repo);
@@ -12509,6 +12688,31 @@ async function runSelfUpdate(io) {
12509
12688
  function defaultSelfUpdateIO(repo, onStep) {
12510
12689
  return { repo, run: defaultRun, mainRef: remoteMainRef, onStep };
12511
12690
  }
12691
+ async function runPackageSelfUpdate(io) {
12692
+ await io.onStep("fetch", "\u0441\u043C\u043E\u0442\u0440\u044E, \u0447\u0442\u043E \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u043E");
12693
+ const latest = await io.latest();
12694
+ if (!latest) {
12695
+ return { ok: false, step: "fetch", message: "\u0434\u043E registry.npmjs.org \u043D\u0435 \u0434\u043E\u0437\u0432\u043E\u043D\u0438\u043B\u0438\u0441\u044C \u2014 \u043A\u0430\u043A\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430, \u0443\u0437\u043D\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C" };
12696
+ }
12697
+ if (!isOutdated(io.currentVersion, latest)) {
12698
+ return { ok: true, restart: false, step: "install", message: `\u0443\u0436\u0435 \u0441\u0432\u0435\u0436\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F: v${io.currentVersion} (\u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430 v${latest})` };
12699
+ }
12700
+ await io.onStep("install", `\u0441\u0442\u0430\u0432\u043B\u044E v${latest}`);
12701
+ const command = io.installCommand(`${SWARM_PACKAGE}@${latest}`);
12702
+ const code = await io.exec(command);
12703
+ if (code !== 0) {
12704
+ return { ok: false, step: "install", message: `\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043D\u0435 \u043F\u0440\u043E\u0448\u043B\u0430 (\u043A\u043E\u0434 ${code}): ${command} \u2014 \u0432\u044B\u043F\u043E\u043B\u043D\u0438 \u044D\u0442\u0443 \u0441\u0442\u0440\u043E\u043A\u0443 \u0440\u0443\u043A\u0430\u043C\u0438 \u0438 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438, \u043D\u0430 \u0447\u0451\u043C \u0441\u043F\u043E\u0442\u044B\u043A\u0430\u0435\u0442\u0441\u044F` };
12705
+ }
12706
+ return { ok: true, step: "install", message: `\u043E\u0431\u043D\u043E\u0432\u0438\u043B\u0441\u044F \u0434\u043E v${latest} (\u0431\u044B\u043B\u043E v${io.currentVersion})` };
12707
+ }
12708
+
12709
+ // src/cliIo.ts
12710
+ import { createInterface } from "readline/promises";
12711
+ import { execa as execa3 } from "execa";
12712
+ async function execLoud(command) {
12713
+ const res = await execa3(command, { shell: true, reject: false, stdio: "inherit" });
12714
+ return res.exitCode ?? 1;
12715
+ }
12512
12716
 
12513
12717
  // src/narration.ts
12514
12718
  var RING_LIMIT = 20;
@@ -12584,14 +12788,14 @@ async function fetchExternalStorm(url, opts = {}) {
12584
12788
 
12585
12789
  // src/anomalyState.ts
12586
12790
  import { existsSync as existsSync15, readFileSync as readFileSync19, renameSync as renameSync9, writeFileSync as writeFileSync14 } from "fs";
12587
- import { join as join27 } from "path";
12791
+ import { join as join28 } from "path";
12588
12792
  var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
12589
12793
  var MAX_RESOLVED = 200;
12590
12794
  function emptyState() {
12591
12795
  return { since: {}, lastFiredAt: {}, fires: {}, resolved: [], stopQuiet: null };
12592
12796
  }
12593
12797
  function anomalyStatePath(home) {
12594
- return join27(home, "anomaly-state.json");
12798
+ return join28(home, "anomaly-state.json");
12595
12799
  }
12596
12800
  function loadAnomalyState(home, now = Date.now(), io = {}) {
12597
12801
  const exists = io.exists ?? existsSync15;
@@ -12871,7 +13075,7 @@ init_src();
12871
13075
  init_src();
12872
13076
 
12873
13077
  // src/gitHistory.ts
12874
- import { $ as $7 } from "execa";
13078
+ import { $ as $6 } from "execa";
12875
13079
  var REC = "";
12876
13080
  var FIELD = "";
12877
13081
  var LOG_FORMAT = `${REC}%H${FIELD}%s`;
@@ -12923,7 +13127,7 @@ function groupTaskHistory(commits, opts = {}) {
12923
13127
  async function readCommitLog(repo, opts = {}) {
12924
13128
  const max = opts.maxCommits ?? 1500;
12925
13129
  try {
12926
- const { stdout } = await $7({ cwd: repo, reject: false })`git log --no-merges --name-only --pretty=format:${LOG_FORMAT} -n ${String(max)} ${opts.ref ?? "HEAD"}`;
13130
+ const { stdout } = await $6({ cwd: repo, reject: false })`git log --no-merges --name-only --pretty=format:${LOG_FORMAT} -n ${String(max)} ${opts.ref ?? "HEAD"}`;
12927
13131
  return parseGitLog(stdout ?? "");
12928
13132
  } catch {
12929
13133
  return [];
@@ -12934,7 +13138,7 @@ async function readTaskHistory(repo, opts = {}) {
12934
13138
  }
12935
13139
  async function headSha(repo) {
12936
13140
  try {
12937
- const { stdout, exitCode } = await $7({ cwd: repo, reject: false })`git rev-parse HEAD`;
13141
+ const { stdout, exitCode } = await $6({ cwd: repo, reject: false })`git rev-parse HEAD`;
12938
13142
  return exitCode === 0 ? stdout.trim() : null;
12939
13143
  } catch {
12940
13144
  return null;
@@ -13217,20 +13421,20 @@ function countActiveDrones(db) {
13217
13421
  function countActiveReviewers(db) {
13218
13422
  return db.listRuns().filter((r) => r.kind === "reviewer" && ACTIVE_DRONE_STATUSES.has(r.status)).length;
13219
13423
  }
13220
- async function closeReviewersOnMerge(deps, reviewRunId) {
13221
- const stale = deps.db.activeReviewersFor(reviewRunId);
13222
- for (const rv of stale) {
13223
- if (rv.pid != null && deps.kill && deps.isAlive(rv.pid)) {
13224
- try {
13225
- deps.kill(rv.pid);
13226
- } catch {
13227
- }
13424
+ async function closeReviewerRun(deps, rv, reason) {
13425
+ if (rv.pid != null && deps.kill && deps.isAlive(rv.pid)) {
13426
+ try {
13427
+ deps.kill(rv.pid);
13428
+ } catch {
13228
13429
  }
13229
- if (rv.worktree_path) await deps.removeWorktree(rv.repo, rv.worktree_path).catch(() => {
13230
- });
13231
- deps.db.updateRun(rv.id, { status: "done" });
13232
- deps.db.addEvent(rv.id, "done", "PR \u0441\u043C\u0435\u0440\u0436\u0435\u043D \u0440\u0430\u043D\u044C\u0448\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0440\u0435\u0432\u044C\u044E \u2014 \u0440\u0435\u0432\u044C\u044E \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043D\u0443\u0436\u043D\u043E");
13233
13430
  }
13431
+ if (rv.worktree_path) await deps.removeWorktree(rv.repo, rv.worktree_path).catch(() => {
13432
+ });
13433
+ deps.db.updateRun(rv.id, { status: "done", stop_reason: null });
13434
+ deps.db.addEvent(rv.id, "done", reason);
13435
+ }
13436
+ async function closeReviewersOnDeadPr(deps, reviewRunId, reason) {
13437
+ for (const rv of deps.db.activeReviewersFor(reviewRunId)) await closeReviewerRun(deps, rv, reason);
13234
13438
  }
13235
13439
  function ciWaitStartedMs(db, runId) {
13236
13440
  const evs = db.events(runId);
@@ -13304,7 +13508,39 @@ async function finalizeMergedReview(deps, run) {
13304
13508
  deps.db.addEvent(run.id, "merged", run.pr_url ?? "");
13305
13509
  deps.db.updateRun(run.id, { status: "done" });
13306
13510
  deps.db.addEvent(run.id, "done");
13307
- await closeReviewersOnMerge(deps, run.id);
13511
+ await closeReviewersOnDeadPr(deps, run.id, "PR \u0441\u043C\u0435\u0440\u0436\u0435\u043D \u0440\u0430\u043D\u044C\u0448\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0440\u0435\u0432\u044C\u044E \u2014 \u0440\u0435\u0432\u044C\u044E \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043D\u0443\u0436\u043D\u043E");
13512
+ }
13513
+ async function finalizeClosedPrReview(deps, run, reason) {
13514
+ await closeReviewersOnDeadPr(deps, run.id, "PR \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430 \u2014 \u0440\u0435\u0432\u044C\u044E\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E");
13515
+ if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
13516
+ });
13517
+ deps.db.updateRun(run.id, { status: "done" });
13518
+ deps.db.addEvent(run.id, "done", reason);
13519
+ }
13520
+ var REVIEW_PR_UNKNOWN_MARK = "\u0441\u0442\u0430\u0442\u0443\u0441 PR \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D";
13521
+ async function prGateForReviewer(deps, run, prUrl, when) {
13522
+ const st = await deps.prStatus(prUrl).catch(() => null);
13523
+ switch (prLiveness(st)) {
13524
+ case "merged":
13525
+ await finalizeMergedReview(deps, run);
13526
+ narrate(deps, `PR ${droneLabel(run)} \u0441\u043C\u0435\u0440\u0436\u0435\u043D, ${when} \u2014 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u044E \u043F\u0440\u043E\u0433\u043E\u043D, \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043D\u0435 \u043D\u0443\u0436\u0435\u043D.`);
13527
+ return { kind: "finalized" };
13528
+ case "closed":
13529
+ 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`);
13530
+ 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.`);
13531
+ return { kind: "finalized" };
13532
+ case "unknown":
13533
+ if (!deps.db.events(run.id).some((e) => (e.text ?? "").includes(REVIEW_PR_UNKNOWN_MARK))) {
13534
+ 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`);
13535
+ }
13536
+ return { kind: "wait" };
13537
+ default:
13538
+ if (st.conflicting || st.isDraft) {
13539
+ 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`);
13540
+ return { kind: "wait" };
13541
+ }
13542
+ return { kind: "spawn", st };
13543
+ }
13308
13544
  }
13309
13545
  function doneMergedPrForSlug(db, slug) {
13310
13546
  const done = db.listRuns("done").find((r) => r.task_slug === slug && r.pr_url && r.kind !== "hero" && r.resolves_run_id == null && r.reviews_run_id == null);
@@ -13367,6 +13603,22 @@ async function closeMergedTaskRuns(deps) {
13367
13603
  }
13368
13604
  }
13369
13605
  }
13606
+ async function closeOrphanedReviewers(deps) {
13607
+ for (const rv of deps.db.listRuns()) {
13608
+ if (rv.kind !== "reviewer" || rv.reviews_run_id == null) continue;
13609
+ if (!REVIEWER_ALIVE_STATUSES.has(rv.status)) continue;
13610
+ const parent = deps.db.getRun(rv.reviews_run_id);
13611
+ if (parent && !isTerminalStatus(parent.status)) continue;
13612
+ try {
13613
+ const why = parent ? `\u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442 (${STATUS_META[parent.status]?.label ?? parent.status})` : "\u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u0438\u0441\u0447\u0435\u0437";
13614
+ await closeReviewerRun(deps, rv, `${why} \u2014 PR \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043D\u0430 \u0440\u0435\u0432\u044C\u044E, \u0440\u0435\u0432\u044C\u044E\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E`);
13615
+ if (parent) deps.db.addEvent(parent.id, "step", `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 ${rv.name} (#${rv.id}) \u0437\u0430\u043A\u0440\u044B\u0442: ${why}`);
13616
+ narrate(deps, `${droneLabel(rv)} \u043E\u0441\u0442\u0430\u043B\u0441\u044F \u043D\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u043E\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u0435 \u2014 \u0433\u0430\u0448\u0443 \u0441\u0430\u043C, \u0441\u043B\u043E\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0451\u043D.`);
13617
+ } catch (e) {
13618
+ console.error(`[overmind] \u0437\u0430\u043A\u0440\u044B\u0442\u0438\u0435 \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 #${rv.id} \u0441\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C:`, e);
13619
+ }
13620
+ }
13621
+ }
13370
13622
  var CONTINUE_NUDGE = "\u0422\u0432\u043E\u044F \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C (\u0441\u0435\u0442\u044C/\u043F\u0440\u043E\u0446\u0435\u0441\u0441) \u2014 \u0442\u044B headless-\u0434\u0440\u043E\u043D, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0435\u0448\u044C \u0422\u0423 \u0416\u0415 \u0437\u0430\u0434\u0430\u0447\u0443. \u041F\u043E\u0441\u043C\u043E\u0442\u0440\u0438 `git status`/`git log` \u0438 \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0441\u0432\u043E\u0438\u0445 \u0448\u0430\u0433\u043E\u0432: \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043D\u043E \u2014 \u041D\u0415 \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u044B\u0432\u0430\u0439, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438 \u0441 \u043C\u0435\u0441\u0442\u0430 \u043E\u0431\u0440\u044B\u0432\u0430. \u0414\u043E\u0432\u0435\u0434\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u0434\u043E \u043A\u043E\u043D\u0446\u0430, \u041A\u041E\u041C\u041C\u0418\u0422\u042C \u0418 \u041F\u0423\u0428\u042C \u043F\u043E\u0441\u043B\u0435 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0448\u0430\u0433\u0430, \u043E\u0442\u043A\u0440\u043E\u0439 PR \u0438 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url). \u0412\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0432 stdout \u043D\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0439 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0435\u0440\u0435\u0437 MCP-\u0442\u0443\u043B ask.";
13371
13623
  function buildCiWakeNudge(summary) {
13372
13624
  return [
@@ -13472,7 +13724,7 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
13472
13724
  }
13473
13725
  return parts.join("\n");
13474
13726
  }
13475
- var HERO_CHARTER_PATH = join28(dirname5(THIS_FILE), "..", "docs", "HERO.md");
13727
+ var HERO_CHARTER_PATH = join29(dirname4(THIS_FILE), "..", "docs", "HERO.md");
13476
13728
  function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
13477
13729
  const name = run.name || `#${run.id}`;
13478
13730
  const charter = readFileSync20(HERO_CHARTER_PATH, "utf8");
@@ -13811,7 +14063,7 @@ function repoHasCi(deps, run) {
13811
14063
  function repoHasCiWorkflows(worktreePath, repo) {
13812
14064
  for (const base3 of [worktreePath, repo]) {
13813
14065
  if (!base3) continue;
13814
- const dir = join28(base3, ".github", "workflows");
14066
+ const dir = join29(base3, ".github", "workflows");
13815
14067
  try {
13816
14068
  if (readdirSync12(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
13817
14069
  } catch {
@@ -13974,10 +14226,9 @@ async function spawnFreshReviewerAfterStall(deps, run, why) {
13974
14226
  if (run.review_cycle >= REVIEW_CYCLE_LIMIT || deps.db.reviewerAttemptCountFor(run.id) >= REVIEW_SPAWN_LIMIT) {
13975
14227
  return;
13976
14228
  }
13977
- const st = await deps.prStatus(run.pr_url).catch(() => null);
13978
- if (!st || st.merged || st.state === "CLOSED" || st.conflicting || st.isDraft) {
13979
- return;
13980
- }
14229
+ const gate = await prGateForReviewer(deps, run, run.pr_url, "\u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B CI");
14230
+ if (gate.kind !== "spawn") return;
14231
+ const st = gate.st;
13981
14232
  try {
13982
14233
  await deps.spawnReviewer(run, st.files ?? []);
13983
14234
  narrate(deps, `\u0420\u0435\u0432\u044C\u044E ${droneLabel(run)} \u043F\u043E\u0442\u0435\u0440\u044F\u043B\u043E \u0441\u0435\u0441\u0441\u0438\u044E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430, \u0436\u0434\u0430\u0432 CI (${why}) \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0441\u0440\u0430\u0437\u0443, \u043D\u0435 \u0436\u0434\u0443 \u043A\u0430\u0440\u0443\u0441\u0435\u043B\u044C.`);
@@ -14710,6 +14961,11 @@ async function tickOnce(deps) {
14710
14961
  narrate(deps, `PR ${droneLabel(run)} \u0441\u043C\u0435\u0440\u0436\u0435\u043D \u2014 ${run.task_slug} \u0437\u0430\u043A\u0440\u044B\u0442\u0430, \u0441\u043B\u043E\u0442 \u0441\u0432\u043E\u0431\u043E\u0434\u0435\u043D.`);
14711
14962
  continue;
14712
14963
  }
14964
+ if (prLiveness(st) === "closed") {
14965
+ await finalizeClosedPrReview(deps, run, "PR \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430 \u2014 \u0440\u0435\u0432\u044C\u044E \u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442");
14966
+ narrate(deps, `PR ${droneLabel(run)} \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430 \u2014 ${run.task_slug} \u0441\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0441 \u0440\u0435\u0432\u044C\u044E, \u0441\u043B\u043E\u0442 \u0441\u0432\u043E\u0431\u043E\u0434\u0435\u043D.`);
14967
+ continue;
14968
+ }
14713
14969
  if (touchesDaemonCode(st.files ?? [])) {
14714
14970
  const already = deps.db.events(run.id).some((e) => (e.text ?? "").includes(SELF_REFERENCE_MARK));
14715
14971
  if (!already) {
@@ -14803,20 +15059,9 @@ async function tickOnce(deps) {
14803
15059
  } else {
14804
15060
  const reviewersCapOk = deps.reviewCap === void 0 || countActiveReviewers(deps.db) < deps.reviewCap;
14805
15061
  if ((!deps.resourcesOk || deps.resourcesOk()) && reviewersCapOk) {
14806
- const freshSt = await deps.prStatus(prUrl);
14807
- if (freshSt.merged) {
14808
- await finalizeMergedReview(deps, run);
14809
- narrate(deps, `PR ${droneLabel(run)} \u0441\u043C\u0435\u0440\u0436\u0435\u043D, \u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u2014 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u044E, \u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043D\u0443\u0436\u043D\u043E.`);
14810
- } else if (freshSt.state === "CLOSED") {
14811
- if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
14812
- });
14813
- deps.db.updateRun(run.id, { status: "done" });
14814
- deps.db.addEvent(run.id, "done", "PR \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430, \u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u2014 \u0440\u0435\u0432\u044C\u044E \u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E");
14815
- narrate(deps, `PR ${droneLabel(run)} \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430, \u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u2014 \u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043D\u0443\u0436\u043D\u043E.`);
14816
- } else if (freshSt.conflicting || freshSt.isDraft) {
14817
- 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, \u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u2014 \u0441\u043F\u0430\u0432\u043D \u043E\u0442\u043B\u043E\u0436\u0435\u043D \u0434\u043E \u0441\u043B\u0435\u0434. \u0442\u0438\u043A\u0430");
14818
- } else {
14819
- run = syncBranchFromPr(deps.db, run, freshSt.headBranch);
15062
+ const gate = await prGateForReviewer(deps, run, prUrl, "\u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430");
15063
+ if (gate.kind === "spawn") {
15064
+ run = syncBranchFromPr(deps.db, run, gate.st.headBranch);
14820
15065
  try {
14821
15066
  await deps.spawnReviewer(run, st.files ?? []);
14822
15067
  narrate(deps, `PR ${droneLabel(run)} \u0433\u043E\u0442\u043E\u0432 \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430.`);
@@ -14841,6 +15086,7 @@ async function tickOnce(deps) {
14841
15086
  }
14842
15087
  }
14843
15088
  await closeMergedTaskRuns(deps);
15089
+ await closeOrphanedReviewers(deps);
14844
15090
  refreshReviewPhases(deps);
14845
15091
  let liveChildPpids;
14846
15092
  try {
@@ -15218,7 +15464,7 @@ async function resolveConflict(deps, reviewRun) {
15218
15464
  workspace_id: reviewRun.workspace_id
15219
15465
  // та же задача, тот же воркспейс (TASWM-288)
15220
15466
  });
15221
- const wt = join28(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
15467
+ const wt = join29(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
15222
15468
  try {
15223
15469
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
15224
15470
  await deps.git.setWorktreeIdentity(wt, resolver.name);
@@ -15270,7 +15516,7 @@ async function spawnReviewer(deps, reviewRun, files) {
15270
15516
  // (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
15271
15517
  workspace_id: reviewRun.workspace_id
15272
15518
  });
15273
- const wt = join28(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
15519
+ const wt = join29(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
15274
15520
  try {
15275
15521
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
15276
15522
  await deps.git.setWorktreeIdentity(wt, reviewer.name);
@@ -15503,7 +15749,7 @@ async function reportWorkspaceAccess(tl, db) {
15503
15749
  }
15504
15750
  async function runDaemon(opts) {
15505
15751
  installCrashGuards();
15506
- const daemonStart = await captureDaemonStart(THIS_FILE);
15752
+ const daemonStart = captureDaemonStart();
15507
15753
  const latestRelease = fetchLatestVersion();
15508
15754
  const worktreesDir = await migrateWorktreesAtStartup();
15509
15755
  if (!opts.repo) {
@@ -15550,8 +15796,10 @@ async function runDaemon(opts) {
15550
15796
  }
15551
15797
  } : void 0;
15552
15798
  let lastDroneCap = null;
15799
+ let lastOwnCapacity = null;
15553
15800
  const hostCommandState = hostCommandStatePath(SWARM_HOME);
15554
15801
  let hostMaxDrones = loadMaxDronesOverride(hostCommandState);
15802
+ let operatorMaxDrones = null;
15555
15803
  const launch = makeLauncher({
15556
15804
  overmindEntry: THIS_FILE,
15557
15805
  dbPath: DB_PATH,
@@ -15589,7 +15837,7 @@ async function runDaemon(opts) {
15589
15837
  repoMap = { run: rebuildRepoMap, intervalMs: SWARM_REPO_MAP_MS, lastAt: Date.now() };
15590
15838
  }
15591
15839
  let daemonVersion;
15592
- if (daemonStart && SWARM_VERSION_CHECK_MS > 0) {
15840
+ if (SWARM_VERSION_CHECK_MS > 0) {
15593
15841
  let cached = null;
15594
15842
  daemonVersion = {
15595
15843
  run: async () => {
@@ -15606,7 +15854,8 @@ async function runDaemon(opts) {
15606
15854
  const client = db;
15607
15855
  heartbeat = {
15608
15856
  push: async () => {
15609
- const reply = await client.heartbeat(daemonVersion?.get() ?? null, control.state ?? null, lastDroneCap);
15857
+ const reply = await client.heartbeat(daemonVersion?.get() ?? null, control.state ?? null, lastOwnCapacity);
15858
+ if (reply.maxDrones !== void 0) operatorMaxDrones = reply.maxDrones;
15610
15859
  if (reply.commands.length) onHostCommands(reply.commands);
15611
15860
  if (reply.logRequests.length) {
15612
15861
  await serveLogRequests(
@@ -15699,7 +15948,10 @@ async function runDaemon(opts) {
15699
15948
  fallback: SWARM_MAX_IN_FLIGHT > 0 ? SWARM_MAX_IN_FLIGHT : 3,
15700
15949
  ttlMs: 1e4
15701
15950
  });
15702
- maxDrones = async () => lastDroneCap = hostMaxDrones ?? await readMaxDrones();
15951
+ maxDrones = async () => {
15952
+ const own = lastOwnCapacity = hostMaxDrones ?? await readMaxDrones();
15953
+ return lastDroneCap = operatorMaxDrones != null ? Math.min(own, operatorMaxDrones) : own;
15954
+ };
15703
15955
  }
15704
15956
  let verifyModeReader;
15705
15957
  let baseBranchReader;
@@ -16200,10 +16452,21 @@ async function runDaemon(opts) {
16200
16452
  return wakeRun({ db, isAlive: pidAlive, respawn: deps.respawn, narrate: narrator.narrate }, run.id);
16201
16453
  },
16202
16454
  update: async (onStep) => {
16203
- if (!daemonStart) {
16204
- return { ok: false, step: "fetch", message: "\u043A\u043E\u0434 \u0434\u0435\u043C\u043E\u043D\u0430 \u043B\u0435\u0436\u0438\u0442 \u043D\u0435 \u0432 git-\u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E" };
16455
+ const pm = detectGlobalPm(process.argv[1]);
16456
+ if (pm !== "sources") {
16457
+ return runPackageSelfUpdate({
16458
+ currentVersion: PACKAGE_VERSION,
16459
+ installCommand: (spec) => globalInstallCommand(pm, spec),
16460
+ latest: () => fetchLatestVersion(),
16461
+ exec: execLoud,
16462
+ onStep
16463
+ });
16464
+ }
16465
+ const repo = await repoRoot(dirname4(THIS_FILE)).catch(() => null);
16466
+ if (!repo) {
16467
+ return { ok: false, step: "fetch", message: "\u0431\u0435\u0433\u0443 \u0438\u0437 \u0438\u0441\u0445\u043E\u0434\u043D\u0438\u043A\u043E\u0432, \u043D\u043E git-\u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u043D\u0430\u0434 \u043D\u0438\u043C\u0438 \u043D\u0435\u0442 \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E" };
16205
16468
  }
16206
- return runSelfUpdate(defaultSelfUpdateIO(daemonStart.repo, onStep));
16469
+ return runSelfUpdate(defaultSelfUpdateIO(repo, onStep));
16207
16470
  },
16208
16471
  narrate: narrator.narrate
16209
16472
  };
@@ -16409,6 +16672,7 @@ export {
16409
16672
  REVIEW_CYCLE_ESCALATION_MARK,
16410
16673
  REVIEW_CYCLE_LIMIT,
16411
16674
  REVIEW_ESCALATION_MARK,
16675
+ REVIEW_PR_UNKNOWN_MARK,
16412
16676
  REVIEW_SPAWN_LIMIT,
16413
16677
  REVIEW_UNRECOVERABLE_MARK,
16414
16678
  SELF_REFERENCE_MARK,