@taskless-app/swarm 0.6.3 → 0.6.4
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/cli.js +122 -26
- package/dist/overmind.js +255 -74
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -104,7 +104,13 @@ var init_types = __esm({
|
|
|
104
104
|
// делать нечего». Находки вроде «в дизайн-системе нет иконки микрофона» раньше либо
|
|
105
105
|
// превращались в такой дорогой вопрос, либо тонули в отчёте о сдаче (дрон LAMPYRIS сдал
|
|
106
106
|
// заглушку «Мик» текстом на кнопке — уехало в прод). note уезжает оператору, дрон работает дальше.
|
|
107
|
-
"note"
|
|
107
|
+
"note",
|
|
108
|
+
// Зависимость прогона снята НЕуспешным финалом блокера (TASWM-361): тот остановлен/упал/
|
|
109
|
+
// переигран, успешного финала не будет — ждать больше нечего. Пишется в ленту ЖДАВШЕГО
|
|
110
|
+
// прогона перед самым спавном: «поехал молча» означало бы, что причина запуска исчезла из
|
|
111
|
+
// истории, и следующий разбор «почему он стартовал поверх незакрытого блокера» упёрся бы в
|
|
112
|
+
// пустоту. Успешное снятие (блокер смержен) события не порождает — это штатный ход.
|
|
113
|
+
"dep_cleared"
|
|
108
114
|
];
|
|
109
115
|
MILESTONE_KINDS = [
|
|
110
116
|
"spawned",
|
|
@@ -125,8 +131,10 @@ var init_types = __esm({
|
|
|
125
131
|
"storm_dead",
|
|
126
132
|
"anomaly",
|
|
127
133
|
// надзор overmind (TASWM-194) — та же нотификация, что улетела в инбокс, вехой в Истории
|
|
128
|
-
"note"
|
|
134
|
+
"note",
|
|
129
135
|
// заметка дрона «замечено по пути» (TASWM-279) — веха, иначе находка тонет в ленте step'ов
|
|
136
|
+
"dep_cleared"
|
|
137
|
+
// зависимость снята неуспешным финалом блокера (TASWM-361) — решение роя, веха как conflict
|
|
130
138
|
];
|
|
131
139
|
MILESTONE_KIND_SET = new Set(MILESTONE_KINDS);
|
|
132
140
|
}
|
|
@@ -355,6 +363,32 @@ var init_status = __esm({
|
|
|
355
363
|
}
|
|
356
364
|
});
|
|
357
365
|
|
|
366
|
+
// ../swarm-core/src/deps.ts
|
|
367
|
+
function judgeDep(runs) {
|
|
368
|
+
const real = runs.filter((r) => !r.service);
|
|
369
|
+
if (real.length === 0) return { ...PENDING };
|
|
370
|
+
if (real.some((r) => !isTerminalStatus(r.status))) return { ...PENDING };
|
|
371
|
+
if (real.some((r) => r.status === "done")) {
|
|
372
|
+
return { outcome: "done", met: true, status: "done", name: null, stopReason: null };
|
|
373
|
+
}
|
|
374
|
+
const last = real[real.length - 1];
|
|
375
|
+
return {
|
|
376
|
+
outcome: "exhausted",
|
|
377
|
+
met: true,
|
|
378
|
+
status: last.status,
|
|
379
|
+
name: last.name ?? null,
|
|
380
|
+
stopReason: last.stopReason ?? null
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
var PENDING;
|
|
384
|
+
var init_deps = __esm({
|
|
385
|
+
"../swarm-core/src/deps.ts"() {
|
|
386
|
+
"use strict";
|
|
387
|
+
init_status();
|
|
388
|
+
PENDING = { outcome: "pending", met: false, status: null, name: null, stopReason: null };
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
358
392
|
// ../swarm-core/src/time.ts
|
|
359
393
|
function stamp(d = /* @__PURE__ */ new Date()) {
|
|
360
394
|
return d.toISOString().slice(0, 19).replace("T", " ");
|
|
@@ -1061,6 +1095,7 @@ var init_src = __esm({
|
|
|
1061
1095
|
init_timeline();
|
|
1062
1096
|
init_reviewPhase();
|
|
1063
1097
|
init_status();
|
|
1098
|
+
init_deps();
|
|
1064
1099
|
init_time();
|
|
1065
1100
|
init_windows();
|
|
1066
1101
|
init_slug();
|
|
@@ -1849,7 +1884,7 @@ var init_package = __esm({
|
|
|
1849
1884
|
"package.json"() {
|
|
1850
1885
|
package_default = {
|
|
1851
1886
|
name: "@taskless-app/swarm",
|
|
1852
|
-
version: "0.6.
|
|
1887
|
+
version: "0.6.4",
|
|
1853
1888
|
type: "module",
|
|
1854
1889
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
1855
1890
|
keywords: [
|
|
@@ -1899,7 +1934,7 @@ var init_package = __esm({
|
|
|
1899
1934
|
execa: "^10.0.0",
|
|
1900
1935
|
zod: "3.25.76"
|
|
1901
1936
|
},
|
|
1902
|
-
gitHead: "
|
|
1937
|
+
gitHead: "12ecebcf1e60baab07e68cae3e78a29d78cb7fde"
|
|
1903
1938
|
};
|
|
1904
1939
|
}
|
|
1905
1940
|
});
|
|
@@ -2688,18 +2723,40 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
2688
2723
|
return rows.map((r) => r.area);
|
|
2689
2724
|
}
|
|
2690
2725
|
/**
|
|
2691
|
-
* Из depends_on прогона — слаги зависимостей, ещё НЕ
|
|
2692
|
-
*
|
|
2693
|
-
*
|
|
2694
|
-
*
|
|
2695
|
-
*
|
|
2696
|
-
*
|
|
2726
|
+
* Из depends_on прогона — слаги зависимостей, ещё НЕ исчерпанных. Пусто → все выполнены,
|
|
2727
|
+
* дрона можно спавнить. Явная зависимость (spawn --after) отдельна от area-lock: area
|
|
2728
|
+
* освобождается на review (до мержа), а тут ждём финала блокера, чтобы следующий дрон
|
|
2729
|
+
* стартовал от его кода.
|
|
2730
|
+
*
|
|
2731
|
+
* TASWM-361: критерий — «живых прогонов по слагу не осталось», а не «есть done». Раньше
|
|
2732
|
+
* зависимость снимал ТОЛЬКО успех, и остановленный оператором блокер держал ждущих вечно
|
|
2733
|
+
* (VESPA и TYPHON — сутки очереди при пустом рое). Само правило и его обоснование —
|
|
2734
|
+
* `judgeDep` в swarm-core/deps.ts (его же читают зеркало прода и прод-снапшот).
|
|
2735
|
+
*
|
|
2736
|
+
* Несуществующий/зациклённый слаг прогонов не имеет вовсе → `pending` → прогон ждёт, как и
|
|
2737
|
+
* раньше, но очередь не виснет (гейт — пропуск в цикле, не блокировка).
|
|
2697
2738
|
*/
|
|
2698
2739
|
unmetDeps(depends_on) {
|
|
2740
|
+
return this.depVerdicts(depends_on).filter((d) => !d.met).map((d) => d.slug);
|
|
2741
|
+
}
|
|
2742
|
+
/**
|
|
2743
|
+
* TASWM-361: вердикт по каждой зависимости — не только «держит ли», но и КАК закрылась
|
|
2744
|
+
* (успехом блокера или его неуспешным финалом). Разница нужна overmind: снятие по
|
|
2745
|
+
* неуспеху оставляет след в ленге ждавшего прогона, штатное — нет.
|
|
2746
|
+
*/
|
|
2747
|
+
depVerdicts(depends_on) {
|
|
2699
2748
|
if (!depends_on || depends_on.length === 0) return [];
|
|
2700
|
-
const
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2749
|
+
const slugs = [...new Set(depends_on)];
|
|
2750
|
+
const rows = this.db.prepare(
|
|
2751
|
+
`SELECT task_slug, status, name, stop_reason FROM runs WHERE task_slug IN (${slugs.map(() => "?").join(",")}) AND resolves_run_id IS NULL AND reviews_run_id IS NULL ORDER BY id`
|
|
2752
|
+
).all(...slugs);
|
|
2753
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
2754
|
+
for (const r of rows) {
|
|
2755
|
+
const list = bySlug.get(r.task_slug) ?? [];
|
|
2756
|
+
list.push({ status: r.status, name: r.name, stopReason: r.stop_reason });
|
|
2757
|
+
bySlug.set(r.task_slug, list);
|
|
2758
|
+
}
|
|
2759
|
+
return depends_on.map((slug) => ({ slug, ...judgeDep(bySlug.get(slug) ?? []) }));
|
|
2703
2760
|
}
|
|
2704
2761
|
/** Все репо, засветившиеся в прогонах — whitelist для валидации spawn из веба. */
|
|
2705
2762
|
distinctRepos() {
|
|
@@ -4184,10 +4241,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
4184
4241
|
// редко — без этого лог был бы простынёй из одной и той же строки, и в ней потонуло бы
|
|
4185
4242
|
// всё остальное. Меняется набор (эпик закрыли / блокер прошли) → печатаем снова.
|
|
4186
4243
|
lastClaimSkipLog = null;
|
|
4187
|
-
// Зависимости, подтверждённые ПРОДОМ (TASWM-300): слаг →
|
|
4188
|
-
//
|
|
4189
|
-
//
|
|
4190
|
-
|
|
4244
|
+
// Зависимости, подтверждённые ПРОДОМ (TASWM-300): слаг → вердикт по ВСЕМ его прогонам.
|
|
4245
|
+
// `done` монотонен (не откатывается) → положительный ответ не протухает и больше не
|
|
4246
|
+
// спрашивается; прочие вердикты переспрашиваются по TTL — «исчерпана неуспешно»
|
|
4247
|
+
// (TASWM-361) монотонностью НЕ обладает: оператор вправе переигрть остановленный блокер
|
|
4248
|
+
// новым прогоном того же слага, и тогда зависимость снова становится ожиданием.
|
|
4249
|
+
// Живёт в памяти: рестарт демона просто переспросит прод.
|
|
4250
|
+
depsProd = /* @__PURE__ */ new Map();
|
|
4191
4251
|
depsAskedAt = /* @__PURE__ */ new Map();
|
|
4192
4252
|
static DEPS_TTL_MS = 6e4;
|
|
4193
4253
|
// TASWM-327: прод старее демона и не знает аргумент `level` у swarm_notify. Выясняется
|
|
@@ -4614,12 +4674,36 @@ var SwarmClient = class _SwarmClient {
|
|
|
4614
4674
|
return [...seen];
|
|
4615
4675
|
}
|
|
4616
4676
|
unmetDeps(depends_on) {
|
|
4677
|
+
return this.depVerdicts(depends_on).filter((d) => !d.met).map((d) => d.slug);
|
|
4678
|
+
}
|
|
4679
|
+
/**
|
|
4680
|
+
* TASWM-361: вердикт по каждой зависимости — см. докблок SwarmStore.depVerdicts.
|
|
4681
|
+
*
|
|
4682
|
+
* АСИММЕТРИЯ ЗЕРКАЛА И ПРОДА, и почему она здесь намеренная. Зеркало — ЧАСТИЧНАЯ выборка
|
|
4683
|
+
* (только прогоны этой машины, только окно гидрации), и по частичной выборке два вывода
|
|
4684
|
+
* стоят разного:
|
|
4685
|
+
* • «есть смерженный прогон слага» — вывод от НАЛИЧИЯ: чего зеркало не видит, того он не
|
|
4686
|
+
* портит, поэтому успех подтверждаем и локально (так было до TASWM-361);
|
|
4687
|
+
* • «живых прогонов слага не осталось» — вывод от ОТСУТСТВИЯ: невидимый зеркалу прогон
|
|
4688
|
+
* на соседней машине как раз и может быть живым. Поэтому «исчерпана неуспешно»
|
|
4689
|
+
* принимаем ТОЛЬКО из ответа прода (refreshDeps) — он видит всех.
|
|
4690
|
+
* Прод недоступен → вердикта нет → прогон честно ждёт следующего окна: лишняя минута
|
|
4691
|
+
* очереди дешевле дрона, поднятого поверх работающего блокера.
|
|
4692
|
+
*/
|
|
4693
|
+
depVerdicts(depends_on) {
|
|
4617
4694
|
if (!depends_on || depends_on.length === 0) return [];
|
|
4618
|
-
const
|
|
4695
|
+
const doneInMirror = /* @__PURE__ */ new Set();
|
|
4619
4696
|
for (const r of this.runs.values()) {
|
|
4620
|
-
if (r.status === "done" && r.resolves_run_id == null)
|
|
4697
|
+
if (r.status === "done" && r.resolves_run_id == null && r.reviews_run_id == null) doneInMirror.add(r.task_slug);
|
|
4621
4698
|
}
|
|
4622
|
-
return depends_on.
|
|
4699
|
+
return depends_on.map((slug) => {
|
|
4700
|
+
if (doneInMirror.has(slug)) {
|
|
4701
|
+
return { slug, outcome: "done", met: true, status: "done", name: null, stopReason: null };
|
|
4702
|
+
}
|
|
4703
|
+
const fromProd = this.depsProd.get(slug);
|
|
4704
|
+
if (fromProd) return { slug, ...fromProd };
|
|
4705
|
+
return { slug, outcome: "pending", met: false, status: null, name: null, stopReason: null };
|
|
4706
|
+
});
|
|
4623
4707
|
}
|
|
4624
4708
|
/**
|
|
4625
4709
|
* TASWM-300: спросить ПРОД про зависимости — см. докблок SwarmStore.refreshDeps.
|
|
@@ -4630,14 +4714,15 @@ var SwarmClient = class _SwarmClient {
|
|
|
4630
4714
|
* просто позавчера, в зеркало не попадала — и `--after` держал прогон в очереди вечно
|
|
4631
4715
|
* (04.08: снимали руками).
|
|
4632
4716
|
*
|
|
4633
|
-
* Кеш.
|
|
4634
|
-
* положительный ответ помним навсегда и больше не спрашиваем.
|
|
4717
|
+
* Кеш. «Смержено» МОНОТОННО: прогон, ставший done, назад не откатывается, — поэтому
|
|
4718
|
+
* положительный ответ помним навсегда и больше не спрашиваем. Остальные вердикты (включая
|
|
4719
|
+
* «исчерпана неуспешно», TASWM-361 — блокер могут переиграть новым прогоном) переспрашиваем
|
|
4635
4720
|
* не чаще DEPS_TTL_MS: тик идёт раз в ~4с, а ждущих зависимость прогонов может быть
|
|
4636
4721
|
* много — без TTL это был бы поход в прод на каждый слаг каждый тик.
|
|
4637
4722
|
*/
|
|
4638
4723
|
async refreshDeps(slugs, nowMs = Date.now()) {
|
|
4639
4724
|
for (const slug of new Set(slugs)) {
|
|
4640
|
-
if (!slug || this.
|
|
4725
|
+
if (!slug || this.depsProd.get(slug)?.outcome === "done") continue;
|
|
4641
4726
|
const asked = this.depsAskedAt.get(slug);
|
|
4642
4727
|
if (asked !== void 0 && nowMs - asked < _SwarmClient.DEPS_TTL_MS) continue;
|
|
4643
4728
|
this.depsAskedAt.set(slug, nowMs);
|
|
@@ -4650,7 +4735,18 @@ var SwarmClient = class _SwarmClient {
|
|
|
4650
4735
|
}
|
|
4651
4736
|
if (res?.isError) continue;
|
|
4652
4737
|
const runs = res.structuredContent?.runs ?? [];
|
|
4653
|
-
|
|
4738
|
+
const facts = runs.slice().sort((a, b) => String(a.createdAt ?? "").localeCompare(String(b.createdAt ?? ""))).map((r) => ({
|
|
4739
|
+
status: String(r.status ?? ""),
|
|
4740
|
+
// Служебный сабран под тем же слагом (резолвер конфликта / ревьюер): его финал —
|
|
4741
|
+
// не финал задачи. Резолвер вычёркивался и до TASWM-361, ревьюер добавлен вместе
|
|
4742
|
+
// с расширением критерия — иначе остановленный ревьюер закрывал бы зависимость.
|
|
4743
|
+
service: Boolean(r.resolvesRunId) || Boolean(r.reviewsRunId),
|
|
4744
|
+
// В проде имя дрона — колонка droneName (см. hydrateRun); `name` терпим как
|
|
4745
|
+
// запасной вариант ради фикстур и старых ответов.
|
|
4746
|
+
name: r.droneName ?? r.name ?? null,
|
|
4747
|
+
stopReason: r.stopReason ?? null
|
|
4748
|
+
}));
|
|
4749
|
+
this.depsProd.set(slug, judgeDep(facts));
|
|
4654
4750
|
}
|
|
4655
4751
|
}
|
|
4656
4752
|
/**
|
|
@@ -7642,8 +7738,8 @@ var GK = ['claw','spore','eye','hex'];
|
|
|
7642
7738
|
var ST = ${jsonForScript(statusTuples())};
|
|
7643
7739
|
var ORDER = { running:0, blocked:1, paused:2, storm_dead:3, review:4, stalled:5, queued:6, stopped:7, done:8, failed:9, superseded:10 };
|
|
7644
7740
|
var TERM = ${jsonForScript(Object.fromEntries(TERMINAL_STATUSES.map((s) => [s, 1])))};
|
|
7645
|
-
var EVL = { spawned:'\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D', step:'', blocked:'\u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443', answered:'\u043F\u043E\u043B\u0443\u0447\u0435\u043D \u043E\u0442\u0432\u0435\u0442', pr_opened:'PR \u043E\u0442\u043A\u0440\u044B\u0442', merged:'PR \u0441\u043C\u0435\u0440\u0436\u0451\u043D, \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u0430', done:'\u0433\u043E\u0442\u043E\u0432\u043E', failed:'\u0434\u0440\u043E\u043D \u0443\u043F\u0430\u043B', stalled:'\u0434\u0440\u043E\u043D \u0437\u0430\u0432\u0438\u0441/\u043F\u043E\u0433\u0438\u0431', respawned:'\u0430\u0432\u0442\u043E-\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435', paused:'\u0437\u0430\u043C\u0435\u0440 \u2014 overmind \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D', resumed:'overmind \u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u2014 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B', rescued:'worktree \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u0437\u0430\u0431\u0435\u0440\u0438 \u0440\u0443\u043A\u0430\u043C\u0438', conflict:'\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main (merge-main-in)', died:'\u0434\u0440\u043E\u043D \u0443\u043C\u0435\u0440', storm_start:'\u26A1 \u043F\u0441\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0432\u044F\u0437\u044C \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430 \u2014 Anthropic \u0448\u0442\u043E\u0440\u043C\u0438\u0442', storm_end:'\u0441\u0432\u044F\u0437\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044F', storm_dead:'\u0434\u0440\u043E\u043D \u0432 \u043A\u043E\u043A\u043E\u043D\u0435 (\u0448\u0442\u043E\u0440\u043C)', stopped:'\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D', superseded:'\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D', anomaly:'\u26A0\uFE0F \u0430\u043D\u043E\u043C\u0430\u043B\u0438\u044F \u0440\u043E\u044F' };
|
|
7646
|
-
var MDOT = { merged:'--success', done:'--success', pr_opened:'--info', spawned:'--muted-foreground', stalled:'--destructive', failed:'--destructive', respawned:'--warning', paused:'--warning', resumed:'--success', rescued:'--destructive', conflict:'--warning', died:'--destructive', storm_start:'--destructive', storm_end:'--success', storm_dead:'--warning', stopped:'--muted-foreground', superseded:'--muted-foreground', anomaly:'--warning' };
|
|
7741
|
+
var EVL = { spawned:'\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D', step:'', blocked:'\u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443', answered:'\u043F\u043E\u043B\u0443\u0447\u0435\u043D \u043E\u0442\u0432\u0435\u0442', pr_opened:'PR \u043E\u0442\u043A\u0440\u044B\u0442', merged:'PR \u0441\u043C\u0435\u0440\u0436\u0451\u043D, \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u0430', done:'\u0433\u043E\u0442\u043E\u0432\u043E', failed:'\u0434\u0440\u043E\u043D \u0443\u043F\u0430\u043B', stalled:'\u0434\u0440\u043E\u043D \u0437\u0430\u0432\u0438\u0441/\u043F\u043E\u0433\u0438\u0431', respawned:'\u0430\u0432\u0442\u043E-\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435', paused:'\u0437\u0430\u043C\u0435\u0440 \u2014 overmind \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D', resumed:'overmind \u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u2014 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B', rescued:'worktree \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u0437\u0430\u0431\u0435\u0440\u0438 \u0440\u0443\u043A\u0430\u043C\u0438', conflict:'\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main (merge-main-in)', died:'\u0434\u0440\u043E\u043D \u0443\u043C\u0435\u0440', storm_start:'\u26A1 \u043F\u0441\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0432\u044F\u0437\u044C \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430 \u2014 Anthropic \u0448\u0442\u043E\u0440\u043C\u0438\u0442', storm_end:'\u0441\u0432\u044F\u0437\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044F', storm_dead:'\u0434\u0440\u043E\u043D \u0432 \u043A\u043E\u043A\u043E\u043D\u0435 (\u0448\u0442\u043E\u0440\u043C)', stopped:'\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D', superseded:'\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D', anomaly:'\u26A0\uFE0F \u0430\u043D\u043E\u043C\u0430\u043B\u0438\u044F \u0440\u043E\u044F', dep_cleared:'\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \u0441\u043D\u044F\u0442\u0430 \u2014 \u0431\u043B\u043E\u043A\u0435\u0440 \u043D\u0435 \u0434\u043E\u0435\u0434\u0435\u0442' };
|
|
7742
|
+
var MDOT = { merged:'--success', done:'--success', pr_opened:'--info', spawned:'--muted-foreground', stalled:'--destructive', failed:'--destructive', respawned:'--warning', paused:'--warning', resumed:'--success', rescued:'--destructive', conflict:'--warning', died:'--destructive', storm_start:'--destructive', storm_end:'--success', storm_dead:'--warning', stopped:'--muted-foreground', superseded:'--muted-foreground', anomaly:'--warning', dep_cleared:'--warning' };
|
|
7647
7743
|
|
|
7648
7744
|
function E(s){ return String(s==null?"":s).replace(/[&<>"]/g,function(c){return{'&':'&','<':'<','>':'>','"':'"'}[c]}); }
|
|
7649
7745
|
function color(st){ return 'var('+(ST[st]||ST.queued)[2]+')'; }
|
package/dist/overmind.js
CHANGED
|
@@ -69,7 +69,13 @@ var init_types = __esm({
|
|
|
69
69
|
// делать нечего». Находки вроде «в дизайн-системе нет иконки микрофона» раньше либо
|
|
70
70
|
// превращались в такой дорогой вопрос, либо тонули в отчёте о сдаче (дрон LAMPYRIS сдал
|
|
71
71
|
// заглушку «Мик» текстом на кнопке — уехало в прод). note уезжает оператору, дрон работает дальше.
|
|
72
|
-
"note"
|
|
72
|
+
"note",
|
|
73
|
+
// Зависимость прогона снята НЕуспешным финалом блокера (TASWM-361): тот остановлен/упал/
|
|
74
|
+
// переигран, успешного финала не будет — ждать больше нечего. Пишется в ленту ЖДАВШЕГО
|
|
75
|
+
// прогона перед самым спавном: «поехал молча» означало бы, что причина запуска исчезла из
|
|
76
|
+
// истории, и следующий разбор «почему он стартовал поверх незакрытого блокера» упёрся бы в
|
|
77
|
+
// пустоту. Успешное снятие (блокер смержен) события не порождает — это штатный ход.
|
|
78
|
+
"dep_cleared"
|
|
73
79
|
];
|
|
74
80
|
MILESTONE_KINDS = [
|
|
75
81
|
"spawned",
|
|
@@ -90,8 +96,10 @@ var init_types = __esm({
|
|
|
90
96
|
"storm_dead",
|
|
91
97
|
"anomaly",
|
|
92
98
|
// надзор overmind (TASWM-194) — та же нотификация, что улетела в инбокс, вехой в Истории
|
|
93
|
-
"note"
|
|
99
|
+
"note",
|
|
94
100
|
// заметка дрона «замечено по пути» (TASWM-279) — веха, иначе находка тонет в ленте step'ов
|
|
101
|
+
"dep_cleared"
|
|
102
|
+
// зависимость снята неуспешным финалом блокера (TASWM-361) — решение роя, веха как conflict
|
|
95
103
|
];
|
|
96
104
|
MILESTONE_KIND_SET = new Set(MILESTONE_KINDS);
|
|
97
105
|
}
|
|
@@ -356,6 +364,43 @@ var init_status = __esm({
|
|
|
356
364
|
}
|
|
357
365
|
});
|
|
358
366
|
|
|
367
|
+
// ../swarm-core/src/deps.ts
|
|
368
|
+
function judgeDep(runs) {
|
|
369
|
+
const real = runs.filter((r) => !r.service);
|
|
370
|
+
if (real.length === 0) return { ...PENDING };
|
|
371
|
+
if (real.some((r) => !isTerminalStatus(r.status))) return { ...PENDING };
|
|
372
|
+
if (real.some((r) => r.status === "done")) {
|
|
373
|
+
return { outcome: "done", met: true, status: "done", name: null, stopReason: null };
|
|
374
|
+
}
|
|
375
|
+
const last = real[real.length - 1];
|
|
376
|
+
return {
|
|
377
|
+
outcome: "exhausted",
|
|
378
|
+
met: true,
|
|
379
|
+
status: last.status,
|
|
380
|
+
name: last.name ?? null,
|
|
381
|
+
stopReason: last.stopReason ?? null
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function depClearedText(slug, v) {
|
|
385
|
+
const who = v.name ? `${slug} (${v.name})` : slug;
|
|
386
|
+
const how = DEP_END_LABEL[v.status ?? ""] ?? `\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D (${v.status ?? "?"})`;
|
|
387
|
+
const why = v.stopReason ? ` \u2014 ${v.stopReason}` : "";
|
|
388
|
+
return `\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \u0441\u043D\u044F\u0442\u0430: \u0436\u0434\u0430\u043B ${who}, \u0442\u043E\u0442 ${how}${why}. \u0423\u0441\u043F\u0435\u0448\u043D\u043E\u0433\u043E \u0444\u0438\u043D\u0430\u043B\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u2014 \u0435\u0434\u0443.`;
|
|
389
|
+
}
|
|
390
|
+
var PENDING, DEP_END_LABEL;
|
|
391
|
+
var init_deps = __esm({
|
|
392
|
+
"../swarm-core/src/deps.ts"() {
|
|
393
|
+
"use strict";
|
|
394
|
+
init_status();
|
|
395
|
+
PENDING = { outcome: "pending", met: false, status: null, name: null, stopReason: null };
|
|
396
|
+
DEP_END_LABEL = {
|
|
397
|
+
stopped: "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u043E\u043C",
|
|
398
|
+
failed: "\u0443\u043F\u0430\u043B",
|
|
399
|
+
superseded: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D"
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
359
404
|
// ../swarm-core/src/time.ts
|
|
360
405
|
function stamp(d = /* @__PURE__ */ new Date()) {
|
|
361
406
|
return d.toISOString().slice(0, 19).replace("T", " ");
|
|
@@ -1230,6 +1275,7 @@ var init_src = __esm({
|
|
|
1230
1275
|
init_timeline();
|
|
1231
1276
|
init_reviewPhase();
|
|
1232
1277
|
init_status();
|
|
1278
|
+
init_deps();
|
|
1233
1279
|
init_time();
|
|
1234
1280
|
init_windows();
|
|
1235
1281
|
init_slug();
|
|
@@ -4868,18 +4914,40 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4868
4914
|
return rows.map((r) => r.area);
|
|
4869
4915
|
}
|
|
4870
4916
|
/**
|
|
4871
|
-
* Из depends_on прогона — слаги зависимостей, ещё НЕ
|
|
4872
|
-
*
|
|
4873
|
-
*
|
|
4874
|
-
*
|
|
4875
|
-
*
|
|
4876
|
-
*
|
|
4917
|
+
* Из depends_on прогона — слаги зависимостей, ещё НЕ исчерпанных. Пусто → все выполнены,
|
|
4918
|
+
* дрона можно спавнить. Явная зависимость (spawn --after) отдельна от area-lock: area
|
|
4919
|
+
* освобождается на review (до мержа), а тут ждём финала блокера, чтобы следующий дрон
|
|
4920
|
+
* стартовал от его кода.
|
|
4921
|
+
*
|
|
4922
|
+
* TASWM-361: критерий — «живых прогонов по слагу не осталось», а не «есть done». Раньше
|
|
4923
|
+
* зависимость снимал ТОЛЬКО успех, и остановленный оператором блокер держал ждущих вечно
|
|
4924
|
+
* (VESPA и TYPHON — сутки очереди при пустом рое). Само правило и его обоснование —
|
|
4925
|
+
* `judgeDep` в swarm-core/deps.ts (его же читают зеркало прода и прод-снапшот).
|
|
4926
|
+
*
|
|
4927
|
+
* Несуществующий/зациклённый слаг прогонов не имеет вовсе → `pending` → прогон ждёт, как и
|
|
4928
|
+
* раньше, но очередь не виснет (гейт — пропуск в цикле, не блокировка).
|
|
4877
4929
|
*/
|
|
4878
4930
|
unmetDeps(depends_on) {
|
|
4931
|
+
return this.depVerdicts(depends_on).filter((d) => !d.met).map((d) => d.slug);
|
|
4932
|
+
}
|
|
4933
|
+
/**
|
|
4934
|
+
* TASWM-361: вердикт по каждой зависимости — не только «держит ли», но и КАК закрылась
|
|
4935
|
+
* (успехом блокера или его неуспешным финалом). Разница нужна overmind: снятие по
|
|
4936
|
+
* неуспеху оставляет след в ленге ждавшего прогона, штатное — нет.
|
|
4937
|
+
*/
|
|
4938
|
+
depVerdicts(depends_on) {
|
|
4879
4939
|
if (!depends_on || depends_on.length === 0) return [];
|
|
4880
|
-
const
|
|
4881
|
-
const
|
|
4882
|
-
|
|
4940
|
+
const slugs = [...new Set(depends_on)];
|
|
4941
|
+
const rows = this.db.prepare(
|
|
4942
|
+
`SELECT task_slug, status, name, stop_reason FROM runs WHERE task_slug IN (${slugs.map(() => "?").join(",")}) AND resolves_run_id IS NULL AND reviews_run_id IS NULL ORDER BY id`
|
|
4943
|
+
).all(...slugs);
|
|
4944
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
4945
|
+
for (const r of rows) {
|
|
4946
|
+
const list = bySlug.get(r.task_slug) ?? [];
|
|
4947
|
+
list.push({ status: r.status, name: r.name, stopReason: r.stop_reason });
|
|
4948
|
+
bySlug.set(r.task_slug, list);
|
|
4949
|
+
}
|
|
4950
|
+
return depends_on.map((slug) => ({ slug, ...judgeDep(bySlug.get(slug) ?? []) }));
|
|
4883
4951
|
}
|
|
4884
4952
|
/** Все репо, засветившиеся в прогонах — whitelist для валидации spawn из веба. */
|
|
4885
4953
|
distinctRepos() {
|
|
@@ -5568,10 +5636,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
5568
5636
|
// редко — без этого лог был бы простынёй из одной и той же строки, и в ней потонуло бы
|
|
5569
5637
|
// всё остальное. Меняется набор (эпик закрыли / блокер прошли) → печатаем снова.
|
|
5570
5638
|
lastClaimSkipLog = null;
|
|
5571
|
-
// Зависимости, подтверждённые ПРОДОМ (TASWM-300): слаг →
|
|
5572
|
-
//
|
|
5573
|
-
//
|
|
5574
|
-
|
|
5639
|
+
// Зависимости, подтверждённые ПРОДОМ (TASWM-300): слаг → вердикт по ВСЕМ его прогонам.
|
|
5640
|
+
// `done` монотонен (не откатывается) → положительный ответ не протухает и больше не
|
|
5641
|
+
// спрашивается; прочие вердикты переспрашиваются по TTL — «исчерпана неуспешно»
|
|
5642
|
+
// (TASWM-361) монотонностью НЕ обладает: оператор вправе переигрть остановленный блокер
|
|
5643
|
+
// новым прогоном того же слага, и тогда зависимость снова становится ожиданием.
|
|
5644
|
+
// Живёт в памяти: рестарт демона просто переспросит прод.
|
|
5645
|
+
depsProd = /* @__PURE__ */ new Map();
|
|
5575
5646
|
depsAskedAt = /* @__PURE__ */ new Map();
|
|
5576
5647
|
static DEPS_TTL_MS = 6e4;
|
|
5577
5648
|
// TASWM-327: прод старее демона и не знает аргумент `level` у swarm_notify. Выясняется
|
|
@@ -5998,12 +6069,36 @@ var SwarmClient = class _SwarmClient {
|
|
|
5998
6069
|
return [...seen];
|
|
5999
6070
|
}
|
|
6000
6071
|
unmetDeps(depends_on) {
|
|
6072
|
+
return this.depVerdicts(depends_on).filter((d) => !d.met).map((d) => d.slug);
|
|
6073
|
+
}
|
|
6074
|
+
/**
|
|
6075
|
+
* TASWM-361: вердикт по каждой зависимости — см. докблок SwarmStore.depVerdicts.
|
|
6076
|
+
*
|
|
6077
|
+
* АСИММЕТРИЯ ЗЕРКАЛА И ПРОДА, и почему она здесь намеренная. Зеркало — ЧАСТИЧНАЯ выборка
|
|
6078
|
+
* (только прогоны этой машины, только окно гидрации), и по частичной выборке два вывода
|
|
6079
|
+
* стоят разного:
|
|
6080
|
+
* • «есть смерженный прогон слага» — вывод от НАЛИЧИЯ: чего зеркало не видит, того он не
|
|
6081
|
+
* портит, поэтому успех подтверждаем и локально (так было до TASWM-361);
|
|
6082
|
+
* • «живых прогонов слага не осталось» — вывод от ОТСУТСТВИЯ: невидимый зеркалу прогон
|
|
6083
|
+
* на соседней машине как раз и может быть живым. Поэтому «исчерпана неуспешно»
|
|
6084
|
+
* принимаем ТОЛЬКО из ответа прода (refreshDeps) — он видит всех.
|
|
6085
|
+
* Прод недоступен → вердикта нет → прогон честно ждёт следующего окна: лишняя минута
|
|
6086
|
+
* очереди дешевле дрона, поднятого поверх работающего блокера.
|
|
6087
|
+
*/
|
|
6088
|
+
depVerdicts(depends_on) {
|
|
6001
6089
|
if (!depends_on || depends_on.length === 0) return [];
|
|
6002
|
-
const
|
|
6090
|
+
const doneInMirror = /* @__PURE__ */ new Set();
|
|
6003
6091
|
for (const r of this.runs.values()) {
|
|
6004
|
-
if (r.status === "done" && r.resolves_run_id == null)
|
|
6092
|
+
if (r.status === "done" && r.resolves_run_id == null && r.reviews_run_id == null) doneInMirror.add(r.task_slug);
|
|
6005
6093
|
}
|
|
6006
|
-
return depends_on.
|
|
6094
|
+
return depends_on.map((slug) => {
|
|
6095
|
+
if (doneInMirror.has(slug)) {
|
|
6096
|
+
return { slug, outcome: "done", met: true, status: "done", name: null, stopReason: null };
|
|
6097
|
+
}
|
|
6098
|
+
const fromProd = this.depsProd.get(slug);
|
|
6099
|
+
if (fromProd) return { slug, ...fromProd };
|
|
6100
|
+
return { slug, outcome: "pending", met: false, status: null, name: null, stopReason: null };
|
|
6101
|
+
});
|
|
6007
6102
|
}
|
|
6008
6103
|
/**
|
|
6009
6104
|
* TASWM-300: спросить ПРОД про зависимости — см. докблок SwarmStore.refreshDeps.
|
|
@@ -6014,14 +6109,15 @@ var SwarmClient = class _SwarmClient {
|
|
|
6014
6109
|
* просто позавчера, в зеркало не попадала — и `--after` держал прогон в очереди вечно
|
|
6015
6110
|
* (04.08: снимали руками).
|
|
6016
6111
|
*
|
|
6017
|
-
* Кеш.
|
|
6018
|
-
* положительный ответ помним навсегда и больше не спрашиваем.
|
|
6112
|
+
* Кеш. «Смержено» МОНОТОННО: прогон, ставший done, назад не откатывается, — поэтому
|
|
6113
|
+
* положительный ответ помним навсегда и больше не спрашиваем. Остальные вердикты (включая
|
|
6114
|
+
* «исчерпана неуспешно», TASWM-361 — блокер могут переиграть новым прогоном) переспрашиваем
|
|
6019
6115
|
* не чаще DEPS_TTL_MS: тик идёт раз в ~4с, а ждущих зависимость прогонов может быть
|
|
6020
6116
|
* много — без TTL это был бы поход в прод на каждый слаг каждый тик.
|
|
6021
6117
|
*/
|
|
6022
6118
|
async refreshDeps(slugs, nowMs = Date.now()) {
|
|
6023
6119
|
for (const slug of new Set(slugs)) {
|
|
6024
|
-
if (!slug || this.
|
|
6120
|
+
if (!slug || this.depsProd.get(slug)?.outcome === "done") continue;
|
|
6025
6121
|
const asked = this.depsAskedAt.get(slug);
|
|
6026
6122
|
if (asked !== void 0 && nowMs - asked < _SwarmClient.DEPS_TTL_MS) continue;
|
|
6027
6123
|
this.depsAskedAt.set(slug, nowMs);
|
|
@@ -6034,7 +6130,18 @@ var SwarmClient = class _SwarmClient {
|
|
|
6034
6130
|
}
|
|
6035
6131
|
if (res?.isError) continue;
|
|
6036
6132
|
const runs = res.structuredContent?.runs ?? [];
|
|
6037
|
-
|
|
6133
|
+
const facts = runs.slice().sort((a, b) => String(a.createdAt ?? "").localeCompare(String(b.createdAt ?? ""))).map((r) => ({
|
|
6134
|
+
status: String(r.status ?? ""),
|
|
6135
|
+
// Служебный сабран под тем же слагом (резолвер конфликта / ревьюер): его финал —
|
|
6136
|
+
// не финал задачи. Резолвер вычёркивался и до TASWM-361, ревьюер добавлен вместе
|
|
6137
|
+
// с расширением критерия — иначе остановленный ревьюер закрывал бы зависимость.
|
|
6138
|
+
service: Boolean(r.resolvesRunId) || Boolean(r.reviewsRunId),
|
|
6139
|
+
// В проде имя дрона — колонка droneName (см. hydrateRun); `name` терпим как
|
|
6140
|
+
// запасной вариант ради фикстур и старых ответов.
|
|
6141
|
+
name: r.droneName ?? r.name ?? null,
|
|
6142
|
+
stopReason: r.stopReason ?? null
|
|
6143
|
+
}));
|
|
6144
|
+
this.depsProd.set(slug, judgeDep(facts));
|
|
6038
6145
|
}
|
|
6039
6146
|
}
|
|
6040
6147
|
/**
|
|
@@ -9480,13 +9587,31 @@ ${notFoundText}` : notFoundText }],
|
|
|
9480
9587
|
isError: true
|
|
9481
9588
|
};
|
|
9482
9589
|
}
|
|
9590
|
+
const reviewerBySpawn = opts.reviewsRunId != null || opts.runKind === "reviewer";
|
|
9591
|
+
const iAmReviewer = run?.reviews_run_id != null || run?.kind === "reviewer" || reviewerBySpawn;
|
|
9592
|
+
if (iAmReviewer && run?.reviews_run_id == null) {
|
|
9593
|
+
return {
|
|
9594
|
+
content: [
|
|
9595
|
+
{
|
|
9596
|
+
type: "text",
|
|
9597
|
+
text: '\u0422\u044B \u0434\u0440\u043E\u043D-\u0440\u0435\u0432\u044C\u044E\u0435\u0440, \u043D\u043E \u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0441\u0432\u044F\u0437\u044C \u0441 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u043E\u043C \u2014 \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u043A\u0443\u0434\u0430, \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u043B\u044C\u0437\u044F (TASWM-375). \u041E\u0441\u0442\u0430\u0432\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 PR (`gh pr comment <pr> --body "**APPROVE**|**REQUEST CHANGES** \u2026"`) \u2014 \u0440\u043E\u0439 \u043F\u043E\u0434\u0445\u0432\u0430\u0442\u0438\u0442 \u0435\u0433\u043E \u043E\u0442\u0442\u0443\u0434\u0430 \u2014 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438 done() \u0447\u0435\u0440\u0435\u0437 \u043C\u0438\u043D\u0443\u0442\u0443.'
|
|
9598
|
+
}
|
|
9599
|
+
],
|
|
9600
|
+
isError: true
|
|
9601
|
+
};
|
|
9602
|
+
}
|
|
9483
9603
|
if (run?.reviews_run_id != null) {
|
|
9484
9604
|
if (isTerminalStatus(run.status)) {
|
|
9485
9605
|
return { content: [{ type: "text", text: "\u043F\u0440\u043E\u0433\u043E\u043D \u0443\u0436\u0435 \u0437\u0430\u043A\u0440\u044B\u0442 (PR \u0441\u043C\u0435\u0440\u0436\u0435\u043D \u0440\u0430\u043D\u044C\u0448\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D" }] };
|
|
9486
9606
|
}
|
|
9487
9607
|
if (verdict !== "approve" && verdict !== "changes") {
|
|
9488
9608
|
return {
|
|
9489
|
-
content: [
|
|
9609
|
+
content: [
|
|
9610
|
+
{
|
|
9611
|
+
type: "text",
|
|
9612
|
+
text: '\u0422\u044B \u0434\u0440\u043E\u043D-\u0440\u0435\u0432\u044C\u044E\u0435\u0440 (\u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E, TASWM-148) \u2014 done() \u0442\u0440\u0435\u0431\u0443\u0435\u0442 verdict: "approve" | "changes" (\u0438\u0442\u043E\u0433 \u043F\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u043E\u043C\u0443 PR). \u0422\u0440\u0435\u0442\u044C\u0435\u0433\u043E \u0438\u0441\u0445\u043E\u0434\u0430 \u043D\u0435\u0442, \u0438 \u0443\u0441\u043F\u0435\u0445\u043E\u043C \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043F\u0440\u043E\u0433\u043E\u043D \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442\u0441\u044F (TASWM-375): \u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u043D\u0430 \u043D\u0435\u0433\u043E \u0431\u0443\u0434\u0435\u0442 \u043D\u0435\u0447\u0435\u043C, \u0430 \u043F\u0440\u0430\u0432\u0438\u043B\u043E \xAB\u043D\u0435 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u0434\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430\xBB \u0441\u043E\u0431\u043B\u044E\u0434\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C. \u0420\u0435\u0448\u0438\u0442\u044C \u043D\u0435 \u043C\u043E\u0436\u0435\u0448\u044C \u2014 done() \u043D\u0435 \u0437\u043E\u0432\u0438 \u0432\u043E\u0432\u0441\u0435: \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u043E\u0435\u0442\u0441\u044F \u043A\u0430\u043A failed \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 \u0438 \u0443\u0439\u0434\u0451\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443, \u044D\u0442\u043E \u0447\u0435\u0441\u0442\u043D\u0435\u0435 \u0442\u0438\u0445\u043E\u0433\u043E \u0443\u0441\u043F\u0435\u0445\u0430.'
|
|
9613
|
+
}
|
|
9614
|
+
],
|
|
9490
9615
|
isError: true
|
|
9491
9616
|
};
|
|
9492
9617
|
}
|
|
@@ -9496,28 +9621,39 @@ ${notFoundText}` : notFoundText }],
|
|
|
9496
9621
|
await store.addEvent(run.reviews_run_id, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E \u043E\u0442 ${run.name}: ${verdictText}${summary ? ` \u2014 ${summary}` : ""}`);
|
|
9497
9622
|
const reviewedSlug = reportSlug(reviewed?.task_slug);
|
|
9498
9623
|
let shots = { attachments: [], warning: "" };
|
|
9624
|
+
let commentWarning = "";
|
|
9499
9625
|
if (opts.taskless && reviewedSlug) {
|
|
9500
9626
|
shots = await uploadShots();
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9627
|
+
try {
|
|
9628
|
+
await opts.taskless.addComment(
|
|
9629
|
+
reviewedSlug,
|
|
9630
|
+
`${(summary ?? "").trim()}${shots.warning}`,
|
|
9631
|
+
{
|
|
9632
|
+
swarmKind: "review",
|
|
9633
|
+
swarmDroneName: run.name,
|
|
9634
|
+
swarmVerdict: verdict,
|
|
9635
|
+
// TASWM-304: круг — ПРОВЕРЯЕМОГО прогона (у ревьюер-прогона свой
|
|
9636
|
+
// review_cycle всегда 0), иначе вердикты всех кругов схлопнулись
|
|
9637
|
+
// бы в одну плашку.
|
|
9638
|
+
swarmReviewCycle: reviewed?.review_cycle ?? 0
|
|
9639
|
+
},
|
|
9640
|
+
shots.attachments
|
|
9641
|
+
);
|
|
9642
|
+
} catch (e) {
|
|
9643
|
+
commentWarning = e instanceof Error ? e.message : String(e);
|
|
9644
|
+
await store.addEvent(run.reviews_run_id, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 ${reviewedSlug} \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${commentWarning}`);
|
|
9645
|
+
}
|
|
9646
|
+
} else {
|
|
9647
|
+
commentWarning = opts.taskless ? "\u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0435\u0432\u044C\u044E \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430" : "\u043A\u043B\u0438\u0435\u043D\u0442 Taskless \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D";
|
|
9648
|
+
await store.addEvent(run.reviews_run_id, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${commentWarning}`);
|
|
9516
9649
|
}
|
|
9517
9650
|
const trustedPrUrl = reviewed?.pr_url ?? pr_url;
|
|
9518
9651
|
await store.updateRun(null, { status: "review", pr_url: trustedPrUrl });
|
|
9519
9652
|
await store.addEvent(null, "pr_opened", trustedPrUrl);
|
|
9520
|
-
|
|
9653
|
+
const commentNote = commentWarning ? `
|
|
9654
|
+
|
|
9655
|
+
\u26A0\uFE0F \u0412\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u041D\u0415 \u0443\u0435\u0445\u0430\u043B (${commentWarning}) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0435. \u041F\u0440\u043E\u0434\u0443\u0431\u043B\u0438\u0440\u0443\u0439 \u0435\u0433\u043E \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 PR: \`gh pr comment ${trustedPrUrl} --body "${verdictText} \u2026"\`.` : "";
|
|
9656
|
+
return { content: [{ type: "text", text: `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0437\u0430\u043F\u0438\u0441\u0430\u043D, \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E${shotsNote(shots)}${commentNote}` }] };
|
|
9521
9657
|
}
|
|
9522
9658
|
if (isGithubPrUrl(pr_url)) {
|
|
9523
9659
|
const report = (summary ?? "").trim();
|
|
@@ -9739,7 +9875,14 @@ async function runDroneServer(runId) {
|
|
|
9739
9875
|
);
|
|
9740
9876
|
const expectedTaskSlug = process.env.SWARM_TASK_SLUG || void 0;
|
|
9741
9877
|
const expectedKind = toRunKind(process.env.SWARM_RUN_KIND);
|
|
9742
|
-
const
|
|
9878
|
+
const spawnReviewsRunId = Number(process.env.SWARM_REVIEWS_RUN_ID);
|
|
9879
|
+
const common = {
|
|
9880
|
+
taskless,
|
|
9881
|
+
runKind: expectedKind,
|
|
9882
|
+
prHeadBranch,
|
|
9883
|
+
...expectedTaskSlug ? { expectedTaskSlug } : {},
|
|
9884
|
+
...Number.isInteger(spawnReviewsRunId) && spawnReviewsRunId > 0 ? { reviewsRunId: spawnReviewsRunId } : {}
|
|
9885
|
+
};
|
|
9743
9886
|
const channelUrl = process.env.SWARM_DRONE_CHANNEL_URL;
|
|
9744
9887
|
const channelToken = process.env.SWARM_DRONE_CHANNEL_TOKEN;
|
|
9745
9888
|
if (channelUrl && channelToken) {
|
|
@@ -10894,7 +11037,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
10894
11037
|
// package.json
|
|
10895
11038
|
var package_default = {
|
|
10896
11039
|
name: "@taskless-app/swarm",
|
|
10897
|
-
version: "0.6.
|
|
11040
|
+
version: "0.6.4",
|
|
10898
11041
|
type: "module",
|
|
10899
11042
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
10900
11043
|
keywords: [
|
|
@@ -10944,7 +11087,7 @@ var package_default = {
|
|
|
10944
11087
|
execa: "^10.0.0",
|
|
10945
11088
|
zod: "3.25.76"
|
|
10946
11089
|
},
|
|
10947
|
-
gitHead: "
|
|
11090
|
+
gitHead: "12ecebcf1e60baab07e68cae3e78a29d78cb7fde"
|
|
10948
11091
|
};
|
|
10949
11092
|
|
|
10950
11093
|
// src/version.ts
|
|
@@ -11671,8 +11814,8 @@ var GK = ['claw','spore','eye','hex'];
|
|
|
11671
11814
|
var ST = ${jsonForScript(statusTuples())};
|
|
11672
11815
|
var ORDER = { running:0, blocked:1, paused:2, storm_dead:3, review:4, stalled:5, queued:6, stopped:7, done:8, failed:9, superseded:10 };
|
|
11673
11816
|
var TERM = ${jsonForScript(Object.fromEntries(TERMINAL_STATUSES.map((s) => [s, 1])))};
|
|
11674
|
-
var EVL = { spawned:'\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D', step:'', blocked:'\u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443', answered:'\u043F\u043E\u043B\u0443\u0447\u0435\u043D \u043E\u0442\u0432\u0435\u0442', pr_opened:'PR \u043E\u0442\u043A\u0440\u044B\u0442', merged:'PR \u0441\u043C\u0435\u0440\u0436\u0451\u043D, \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u0430', done:'\u0433\u043E\u0442\u043E\u0432\u043E', failed:'\u0434\u0440\u043E\u043D \u0443\u043F\u0430\u043B', stalled:'\u0434\u0440\u043E\u043D \u0437\u0430\u0432\u0438\u0441/\u043F\u043E\u0433\u0438\u0431', respawned:'\u0430\u0432\u0442\u043E-\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435', paused:'\u0437\u0430\u043C\u0435\u0440 \u2014 overmind \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D', resumed:'overmind \u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u2014 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B', rescued:'worktree \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u0437\u0430\u0431\u0435\u0440\u0438 \u0440\u0443\u043A\u0430\u043C\u0438', conflict:'\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main (merge-main-in)', died:'\u0434\u0440\u043E\u043D \u0443\u043C\u0435\u0440', storm_start:'\u26A1 \u043F\u0441\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0432\u044F\u0437\u044C \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430 \u2014 Anthropic \u0448\u0442\u043E\u0440\u043C\u0438\u0442', storm_end:'\u0441\u0432\u044F\u0437\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044F', storm_dead:'\u0434\u0440\u043E\u043D \u0432 \u043A\u043E\u043A\u043E\u043D\u0435 (\u0448\u0442\u043E\u0440\u043C)', stopped:'\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D', superseded:'\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D', anomaly:'\u26A0\uFE0F \u0430\u043D\u043E\u043C\u0430\u043B\u0438\u044F \u0440\u043E\u044F' };
|
|
11675
|
-
var MDOT = { merged:'--success', done:'--success', pr_opened:'--info', spawned:'--muted-foreground', stalled:'--destructive', failed:'--destructive', respawned:'--warning', paused:'--warning', resumed:'--success', rescued:'--destructive', conflict:'--warning', died:'--destructive', storm_start:'--destructive', storm_end:'--success', storm_dead:'--warning', stopped:'--muted-foreground', superseded:'--muted-foreground', anomaly:'--warning' };
|
|
11817
|
+
var EVL = { spawned:'\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D', step:'', blocked:'\u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443', answered:'\u043F\u043E\u043B\u0443\u0447\u0435\u043D \u043E\u0442\u0432\u0435\u0442', pr_opened:'PR \u043E\u0442\u043A\u0440\u044B\u0442', merged:'PR \u0441\u043C\u0435\u0440\u0436\u0451\u043D, \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u0430', done:'\u0433\u043E\u0442\u043E\u0432\u043E', failed:'\u0434\u0440\u043E\u043D \u0443\u043F\u0430\u043B', stalled:'\u0434\u0440\u043E\u043D \u0437\u0430\u0432\u0438\u0441/\u043F\u043E\u0433\u0438\u0431', respawned:'\u0430\u0432\u0442\u043E-\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435', paused:'\u0437\u0430\u043C\u0435\u0440 \u2014 overmind \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D', resumed:'overmind \u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u2014 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B', rescued:'worktree \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u0437\u0430\u0431\u0435\u0440\u0438 \u0440\u0443\u043A\u0430\u043C\u0438', conflict:'\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main (merge-main-in)', died:'\u0434\u0440\u043E\u043D \u0443\u043C\u0435\u0440', storm_start:'\u26A1 \u043F\u0441\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0432\u044F\u0437\u044C \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430 \u2014 Anthropic \u0448\u0442\u043E\u0440\u043C\u0438\u0442', storm_end:'\u0441\u0432\u044F\u0437\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044F', storm_dead:'\u0434\u0440\u043E\u043D \u0432 \u043A\u043E\u043A\u043E\u043D\u0435 (\u0448\u0442\u043E\u0440\u043C)', stopped:'\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D', superseded:'\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D', anomaly:'\u26A0\uFE0F \u0430\u043D\u043E\u043C\u0430\u043B\u0438\u044F \u0440\u043E\u044F', dep_cleared:'\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \u0441\u043D\u044F\u0442\u0430 \u2014 \u0431\u043B\u043E\u043A\u0435\u0440 \u043D\u0435 \u0434\u043E\u0435\u0434\u0435\u0442' };
|
|
11818
|
+
var MDOT = { merged:'--success', done:'--success', pr_opened:'--info', spawned:'--muted-foreground', stalled:'--destructive', failed:'--destructive', respawned:'--warning', paused:'--warning', resumed:'--success', rescued:'--destructive', conflict:'--warning', died:'--destructive', storm_start:'--destructive', storm_end:'--success', storm_dead:'--warning', stopped:'--muted-foreground', superseded:'--muted-foreground', anomaly:'--warning', dep_cleared:'--warning' };
|
|
11676
11819
|
|
|
11677
11820
|
function E(s){ return String(s==null?"":s).replace(/[&<>"]/g,function(c){return{'&':'&','<':'<','>':'>','"':'"'}[c]}); }
|
|
11678
11821
|
function color(st){ return 'var('+(ST[st]||ST.queued)[2]+')'; }
|
|
@@ -13434,6 +13577,7 @@ var NEEDS_HUMAN_MARKS = [
|
|
|
13434
13577
|
CHANGES_WAKE_ESCALATION_MARK
|
|
13435
13578
|
];
|
|
13436
13579
|
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";
|
|
13580
|
+
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)";
|
|
13437
13581
|
function verdictCommentMark(createdAt) {
|
|
13438
13582
|
return `\u043A\u043E\u043C\u043C\u0435\u043D\u0442-\u0432\u0435\u0440\u0434\u0438\u043A\u0442@${createdAt}`;
|
|
13439
13583
|
}
|
|
@@ -13764,7 +13908,7 @@ function buildCiReadyNudge(prUrl, over) {
|
|
|
13764
13908
|
head,
|
|
13765
13909
|
"",
|
|
13766
13910
|
"\u0422\u044B \u2014 \u0442\u043E\u0442 \u0436\u0435 \u0434\u0440\u043E\u043D-\u0420\u0415\u0412\u042C\u042E\u0415\u0420, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0435\u0448\u044C \u0422\u0423 \u0416\u0415 \u0440\u0430\u0431\u043E\u0442\u0443: \u0444\u0430\u0439\u043B\u044B \u043D\u0435 \u043F\u0440\u0430\u0432\u0438\u0448\u044C, \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u043D\u0435 \u043F\u0443\u0448\u0438\u0448\u044C. \u0427\u0435\u043A-\u043B\u0438\u0441\u0442 \u0438 \u0434\u0438\u0444\u0444 \u0443\u0436\u0435 \u0432 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0435 \u043F\u0440\u043E\u0448\u043B\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u2014 \u043D\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u0439 \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043D\u043E\u0432\u043E, \u0434\u043E\u0432\u0435\u0434\u0438 \u043D\u0430\u0447\u0430\u0442\u043E\u0435.",
|
|
13767
|
-
`\u0417\u0430\u043A\u043E\u043D\u0447\u0438\u043B \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C (\`gh pr comment ${prUrl} --body "**APPROVE**|**REQUEST CHANGES** \u2026"\`) \u0438 \u041E\u0411\u042F\u0417\u0410\u0422\u0415\u041B\u042C\u041D\u041E \u0432\u044B\u0437\u043E\u0432\u0438 done(${prUrl}, verdict: "approve"|"changes", summary: "\u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u0447\u0442\u043E \u0440\u0435\u0448\u0438\u043B"). \u0411\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \
|
|
13911
|
+
`\u0417\u0430\u043A\u043E\u043D\u0447\u0438\u043B \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C (\`gh pr comment ${prUrl} --body "**APPROVE**|**REQUEST CHANGES** \u2026"\`) \u0438 \u041E\u0411\u042F\u0417\u0410\u0422\u0415\u041B\u042C\u041D\u041E \u0432\u044B\u0437\u043E\u0432\u0438 done(${prUrl}, verdict: "approve"|"changes", summary: "\u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u0447\u0442\u043E \u0440\u0435\u0448\u0438\u043B"). \u0411\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043F\u0440\u043E\u0433\u043E\u043D \u0443\u0441\u043F\u0435\u0445\u043E\u043C \u043D\u0435 \u0437\u0430\u043A\u0440\u043E\u0435\u0442\u0441\u044F: \u0440\u043E\u0439 \u043F\u043E\u043C\u0435\u0442\u0438\u0442 \u0435\u0433\u043E \xAB\u0443\u043F\u0430\u043B\xBB \u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043D\u043E\u0432\u043E (TASWM-375) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442, \u0437\u0430 \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0443\u0436\u0435 \u0437\u0430\u043F\u043B\u0430\u0447\u0435\u043D\u043E, \u043F\u0440\u043E\u043F\u0430\u0434\u0451\u0442.`,
|
|
13768
13912
|
"\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."
|
|
13769
13913
|
].join("\n");
|
|
13770
13914
|
}
|
|
@@ -13989,7 +14133,8 @@ function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments
|
|
|
13989
14133
|
" \u0415\u0441\u043B\u0438 \u0445\u043E\u0442\u044C \u043E\u0434\u0438\u043D \u0447\u0435\u043A \u0435\u0449\u0451 PENDING \u2014 \u043D\u0430 \u044D\u0442\u043E\u043C \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C: \u0448\u0430\u0433\u0438 2 \u0438 3 \u043D\u0438\u0436\u0435 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438 \u0446\u0435\u043B\u0438\u043A\u043E\u043C, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043D\u0435 \u0432\u044B\u043D\u043E\u0441\u0438, \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E (\u0441\u043C. \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0432\u044B\u0448\u0435, \u043F\u043E\u0447\u0435\u043C\u0443 \u044D\u0442\u043E \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E \u0438 \u0447\u0442\u043E \u043F\u0440\u043E\u0438\u0437\u043E\u0439\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435).",
|
|
13990
14134
|
`2. \u0427\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C (\u0437\u0435\u043B\u0451\u043D\u044B\u0435 \u0438\u043B\u0438 \u0441 \u043A\u0440\u0430\u0441\u043D\u044B\u043C) \u0438\u043B\u0438 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0438\u0445 \u0432\u043E\u0432\u0441\u0435 \u043D\u0435\u0442 \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043D\u0430 \u0441\u0430\u043C PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u0435\u0433\u043E \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041D\u0430\u0447\u043D\u0438 \u0441 **APPROVE** \u0438\u043B\u0438 **REQUEST CHANGES** \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u0438\u043A\u0430: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B, \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B; \u0434\u043B\u044F REQUEST CHANGES \u2014 \u0442\u043E\u0447\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B/\u0441\u0442\u0440\u043E\u043A\u0438 \u0438 \u0447\u0442\u043E \u0447\u0438\u043D\u0438\u0442\u044C.`
|
|
13991
14135
|
],
|
|
13992
|
-
`3. \u0412\u044B\u0437\u043E\u0432\u0438 done(${prUrl}, verdict: "approve"|"changes", summary: "\u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u0447\u0442\u043E \u0440\u0435\u0448\u0438\u043B") \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0440\u0430\u0437\u043D\u0435\u0441\u0451\u0442 summary \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 \u0437\u0430\u0434\u0430\u0447\u0443-\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A, \u0430 verdict \u0437\u0430\u043F\u0438\u0448\u0435\u0442 \u043D\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D (\u0431\u0435\u0439\u0434\u0436 \u0432 \u0432\u0435\u0431\u0435). \u0422\u044B \u0441\u0430\u043C \u0440\u0435\u0432\u044C\u044E \u041D\u0415 \u043F\u043E\u0434\u043B\u0435\u0436\u0438\u0448\u044C \u2014 \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0438 \u043D\u0435\u0442
|
|
14136
|
+
`3. \u0412\u044B\u0437\u043E\u0432\u0438 done(${prUrl}, verdict: "approve"|"changes", summary: "\u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u0447\u0442\u043E \u0440\u0435\u0448\u0438\u043B") \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0440\u0430\u0437\u043D\u0435\u0441\u0451\u0442 summary \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 \u0437\u0430\u0434\u0430\u0447\u0443-\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A, \u0430 verdict \u0437\u0430\u043F\u0438\u0448\u0435\u0442 \u043D\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D (\u0431\u0435\u0439\u0434\u0436 \u0432 \u0432\u0435\u0431\u0435). \u0422\u044B \u0441\u0430\u043C \u0440\u0435\u0432\u044C\u044E \u041D\u0415 \u043F\u043E\u0434\u043B\u0435\u0436\u0438\u0448\u044C \u2014 \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0438 \u043D\u0435\u0442.`,
|
|
14137
|
+
'\u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D: \u0442\u0440\u0435\u0442\u044C\u0435\u0433\u043E \u0438\u0441\u0445\u043E\u0434\u0430 \u043D\u0435\u0442, \u0438 \u0443\u0441\u043F\u0435\u0445\u043E\u043C \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442\u0441\u044F (TASWM-375) \u2014 done() \u0431\u0435\u0437 verdict \u043E\u0442\u043A\u0430\u0436\u0435\u0442, \u0430 \u043F\u0440\u043E\u0433\u043E\u043D, \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0432\u0448\u0438\u0439\u0441\u044F \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430, \u0440\u043E\u0439 \u043F\u043E\u043C\u0435\u0442\u0438\u0442 "\u0443\u043F\u0430\u043B" \u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043D\u043E\u0432\u043E. \u041E\u0442\u043A\u0430\u0437\u0430\u043B done() \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0441\u0451 \u0440\u0430\u0432\u043D\u043E \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 PR (\u0448\u0430\u0433 2): \u0434\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u0445\u0432\u0430\u0442\u0438\u0442 \u0435\u0433\u043E \u043E\u0442\u0442\u0443\u0434\u0430.'
|
|
13993
14138
|
].join("\n");
|
|
13994
14139
|
}
|
|
13995
14140
|
function droneLabel(run) {
|
|
@@ -14548,6 +14693,19 @@ async function fillSuggestedArea(deps, run) {
|
|
|
14548
14693
|
`\u0437\u043E\u043D\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u0440\u0435\u043F\u043E: ${guess.zone} (${Math.round(guess.confidence * 100)}% \u2014 ${guess.why}) \u2014 TASWM-348`
|
|
14549
14694
|
);
|
|
14550
14695
|
}
|
|
14696
|
+
function noteExhaustedDeps(deps, run, verdicts) {
|
|
14697
|
+
const exhausted = verdicts.filter((d) => d.outcome === "exhausted");
|
|
14698
|
+
if (exhausted.length === 0) return;
|
|
14699
|
+
const already = new Set(
|
|
14700
|
+
deps.db.events(run.id).filter((e) => e.kind === "dep_cleared").map((e) => e.text ?? "")
|
|
14701
|
+
);
|
|
14702
|
+
for (const d of exhausted) {
|
|
14703
|
+
const text = depClearedText(d.slug, d);
|
|
14704
|
+
if (already.has(text)) continue;
|
|
14705
|
+
deps.db.addEvent(run.id, "dep_cleared", text);
|
|
14706
|
+
narrate(deps, `${droneLabel(run)} \u2014 ${text}`);
|
|
14707
|
+
}
|
|
14708
|
+
}
|
|
14551
14709
|
async function trySpawnQueuedRun(deps, run) {
|
|
14552
14710
|
if (deps.control?.paused) return "skipped";
|
|
14553
14711
|
const mergedUrl = doneMergedPrForSlug(deps.db, run.task_slug) ?? (deps.mergedPrForBranch ? await deps.mergedPrForBranch(run.task_slug, run.repo || "").catch(() => null) : null);
|
|
@@ -14558,11 +14716,13 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
14558
14716
|
narrate(deps, `${droneLabel(run)} \u2014 \u0437\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} \u0443\u0436\u0435 \u0441\u043C\u0435\u0440\u0436\u0435\u043D\u0430 \u0434\u0440\u0443\u0433\u0438\u043C \u0440\u0430\u043D\u043E\u043C, \u043D\u0435 \u0441\u043F\u0430\u0432\u043D\u044E.`);
|
|
14559
14717
|
return "closed";
|
|
14560
14718
|
}
|
|
14561
|
-
|
|
14719
|
+
const depVerdicts = deps.db.depVerdicts(run.depends_on);
|
|
14720
|
+
if (depVerdicts.some((d) => !d.met)) return "skipped";
|
|
14562
14721
|
if (!await envGateAllows(deps, run)) return "skipped";
|
|
14563
14722
|
if (!await projectQuotaGateAllows(deps, run)) return "skipped";
|
|
14564
14723
|
if (!await prodStillAllows(deps, run, ["queued"])) return "skipped";
|
|
14565
14724
|
if (deps.db.takeRun && !await deps.db.takeRun(run.id)) return "skipped";
|
|
14725
|
+
noteExhaustedDeps(deps, run, depVerdicts);
|
|
14566
14726
|
await fillSuggestedArea(deps, run);
|
|
14567
14727
|
try {
|
|
14568
14728
|
await deps.spawn(run);
|
|
@@ -14927,6 +15087,28 @@ async function postScrapedVerdictReport(deps, runId, taskSlug, verdict, droneNam
|
|
|
14927
15087
|
deps.db.addEvent(runId, "step", `\u0434\u0435\u043C\u043E\u043D-\u043E\u0442\u0447\u0451\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 ${taskSlug} \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${e instanceof Error ? e.message : String(e)}`);
|
|
14928
15088
|
});
|
|
14929
15089
|
}
|
|
15090
|
+
async function salvageVerdictFromPrComment(deps, rv, reviewsRunId, since, ctx) {
|
|
15091
|
+
if (!deps.verdictComment || !rv.pr_url) return false;
|
|
15092
|
+
const found = await deps.verdictComment(rv.pr_url, since).catch(() => null);
|
|
15093
|
+
if (!found) return false;
|
|
15094
|
+
const verdictText = verdictLabel(found.verdict);
|
|
15095
|
+
deps.db.updateRun(reviewsRunId, { review_verdict: found.verdict });
|
|
15096
|
+
deps.db.addEvent(reviewsRunId, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D \u0438\u0437 PR-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430 (${ctx.how}): ${verdictText}`);
|
|
15097
|
+
await closeReviewerRun(
|
|
15098
|
+
deps,
|
|
15099
|
+
rv,
|
|
15100
|
+
`\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u044B\u043D\u0435\u0441\u0435\u043D \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C (${verdictText}), ${ctx.how} \u2014 \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043A\u0440\u044B\u0442\u043E \u0431\u0435\u0437 \u0441\u043F\u0430\u0432\u043D\u0430 \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 (TASWM-229)`
|
|
15101
|
+
);
|
|
15102
|
+
narrate(deps, `${droneLabel(rv)} \u043E\u0441\u0442\u0430\u0432\u0438\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442 (${verdictText}) \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u2014 \u0437\u0430\u0441\u0447\u0438\u0442\u0430\u043B, \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E.`);
|
|
15103
|
+
const reviewed = deps.db.getRun(reviewsRunId);
|
|
15104
|
+
if (reviewed) {
|
|
15105
|
+
await postScrapedVerdictReport(deps, rv.id, reviewed.task_slug, found.verdict, rv.name || "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)", reviewed.workspace_id, reviewed.review_cycle ?? 0);
|
|
15106
|
+
if (found.verdict === "changes" && !ctx.stormActive && !deps.db.activeResolverFor(reviewed.id)) {
|
|
15107
|
+
await wakeAuthorOnChanges(deps, reviewed, ctx.now, ctx.droneCap);
|
|
15108
|
+
}
|
|
15109
|
+
}
|
|
15110
|
+
return true;
|
|
15111
|
+
}
|
|
14930
15112
|
function buildEscalationCandidates(db) {
|
|
14931
15113
|
const candidates = [];
|
|
14932
15114
|
for (const run of db.listRuns("review")) {
|
|
@@ -15086,6 +15268,24 @@ async function tickOnce(deps) {
|
|
|
15086
15268
|
continue;
|
|
15087
15269
|
}
|
|
15088
15270
|
if (run.reviews_run_id != null) {
|
|
15271
|
+
const reviewed = deps.db.getRun(run.reviews_run_id);
|
|
15272
|
+
const sameCycle = reviewed != null && (reviewed.review_cycle ?? 0) === (run.review_cycle ?? 0);
|
|
15273
|
+
if (reviewed && sameCycle && reviewed.review_verdict == null) {
|
|
15274
|
+
const salvaged = await salvageVerdictFromPrComment(deps, run, run.reviews_run_id, run.created_at, {
|
|
15275
|
+
how: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u0437\u0432\u0430\u043B done(), \u043D\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u0441\u044F",
|
|
15276
|
+
now,
|
|
15277
|
+
droneCap,
|
|
15278
|
+
stormActive
|
|
15279
|
+
});
|
|
15280
|
+
if (salvaged) continue;
|
|
15281
|
+
if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
|
|
15282
|
+
});
|
|
15283
|
+
deps.db.updateRun(run.id, { status: "failed", stop_reason: REVIEW_NO_VERDICT_REASON });
|
|
15284
|
+
deps.db.addEvent(run.id, "failed", REVIEW_NO_VERDICT_REASON);
|
|
15285
|
+
deps.db.addEvent(run.reviews_run_id, "step", `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 ${run.name} (#${run.id}) \u0437\u0430\u043A\u0440\u044B\u043B\u0441\u044F \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043B\u043E\u0441\u044C, \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 \u0437\u0430\u0445\u043E\u0434`);
|
|
15286
|
+
narrate(deps, `${droneLabel(run)} \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B, \u043D\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430, \u2014 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u044E \u043F\u0440\u043E\u0433\u043E\u043D \u043A\u0430\u043A \u0443\u043F\u0430\u0432\u0448\u0438\u0439: \u0437\u0430\u0441\u0447\u0438\u0442\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E.`);
|
|
15287
|
+
continue;
|
|
15288
|
+
}
|
|
15089
15289
|
if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
|
|
15090
15290
|
});
|
|
15091
15291
|
deps.db.updateRun(run.id, { status: "done" });
|
|
@@ -15359,33 +15559,13 @@ async function tickOnce(deps) {
|
|
|
15359
15559
|
}
|
|
15360
15560
|
}
|
|
15361
15561
|
if (!alive && run.status === "running" && run.reviews_run_id != null && run.pr_url && deps.verdictComment) {
|
|
15362
|
-
const
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
|
|
15367
|
-
|
|
15368
|
-
|
|
15369
|
-
`\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D \u0438\u0437 PR-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430 (\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0432\u044B\u0448\u0435\u043B, \u043D\u0435 \u043F\u043E\u0437\u0432\u0430\u0432 done()): ${verdictText}`
|
|
15370
|
-
);
|
|
15371
|
-
if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
|
|
15372
|
-
});
|
|
15373
|
-
deps.db.updateRun(run.id, { status: "done" });
|
|
15374
|
-
deps.db.addEvent(
|
|
15375
|
-
run.id,
|
|
15376
|
-
"done",
|
|
15377
|
-
`\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u044B\u043D\u0435\u0441\u0435\u043D \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C (${verdictText}), \u0441\u0435\u0441\u0441\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u2014 \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043A\u0440\u044B\u0442\u043E \u0431\u0435\u0437 \u0441\u043F\u0430\u0432\u043D\u0430 \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 (TASWM-229)`
|
|
15378
|
-
);
|
|
15379
|
-
narrate(deps, `${droneLabel(run)} \u043E\u0441\u0442\u0430\u0432\u0438\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442 (${verdictText}) \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0438 \u0432\u044B\u0448\u0435\u043B \u2014 \u0437\u0430\u0441\u0447\u0438\u0442\u0430\u043B, \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E.`);
|
|
15380
|
-
const reviewed = deps.db.getRun(run.reviews_run_id);
|
|
15381
|
-
if (reviewed) {
|
|
15382
|
-
await postScrapedVerdictReport(deps, run.id, reviewed.task_slug, verdict.verdict, run.name || "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)", reviewed.workspace_id, reviewed.review_cycle ?? 0);
|
|
15383
|
-
if (verdict.verdict === "changes" && !stormActive && !deps.db.activeResolverFor(reviewed.id)) {
|
|
15384
|
-
await wakeAuthorOnChanges(deps, reviewed, now, droneCap);
|
|
15385
|
-
}
|
|
15386
|
-
}
|
|
15387
|
-
continue;
|
|
15388
|
-
}
|
|
15562
|
+
const salvaged = await salvageVerdictFromPrComment(deps, run, run.reviews_run_id, run.created_at, {
|
|
15563
|
+
how: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0432\u044B\u0448\u0435\u043B, \u043D\u0435 \u043F\u043E\u0437\u0432\u0430\u0432 done()",
|
|
15564
|
+
now,
|
|
15565
|
+
droneCap,
|
|
15566
|
+
stormActive
|
|
15567
|
+
});
|
|
15568
|
+
if (salvaged) continue;
|
|
15389
15569
|
}
|
|
15390
15570
|
if (!alive && run.status === "running" && run.reviews_run_id != null && run.pr_url && deps.ci) {
|
|
15391
15571
|
const parent = deps.db.getRun(run.reviews_run_id);
|
|
@@ -16824,6 +17004,7 @@ export {
|
|
|
16824
17004
|
REVIEW_CYCLE_ESCALATION_MARK,
|
|
16825
17005
|
REVIEW_CYCLE_LIMIT,
|
|
16826
17006
|
REVIEW_ESCALATION_MARK,
|
|
17007
|
+
REVIEW_NO_VERDICT_REASON,
|
|
16827
17008
|
REVIEW_PR_UNKNOWN_MARK,
|
|
16828
17009
|
REVIEW_SPAWN_LIMIT,
|
|
16829
17010
|
REVIEW_UNRECOVERABLE_MARK,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"execa": "^10.0.0",
|
|
39
39
|
"zod": "3.25.76"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "12ecebcf1e60baab07e68cae3e78a29d78cb7fde",
|
|
42
42
|
"scripts": {
|
|
43
43
|
"overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
|
|
44
44
|
"swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",
|