@taskless-app/swarm 0.6.7 → 0.6.9
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 +147 -5
- package/dist/overmind.js +281 -11
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -110,7 +110,11 @@ var init_types = __esm({
|
|
|
110
110
|
// прогона перед самым спавном: «поехал молча» означало бы, что причина запуска исчезла из
|
|
111
111
|
// истории, и следующий разбор «почему он стартовал поверх незакрытого блокера» упёрся бы в
|
|
112
112
|
// пустоту. Успешное снятие (блокер смержен) события не порождает — это штатный ход.
|
|
113
|
-
"dep_cleared"
|
|
113
|
+
"dep_cleared",
|
|
114
|
+
// TASWM-412: дрон вернул сырую задачу автору (см. RunStatus "returned"). Веха, а не step:
|
|
115
|
+
// прогон на этом кончается, и в истории должно быть видно, что он кончился ВОЗВРАТОМ —
|
|
116
|
+
// иначе возврат читается как брошенная посреди работы задача.
|
|
117
|
+
"returned"
|
|
114
118
|
];
|
|
115
119
|
MILESTONE_KINDS = [
|
|
116
120
|
"spawned",
|
|
@@ -133,8 +137,10 @@ var init_types = __esm({
|
|
|
133
137
|
// надзор overmind (TASWM-194) — та же нотификация, что улетела в инбокс, вехой в Истории
|
|
134
138
|
"note",
|
|
135
139
|
// заметка дрона «замечено по пути» (TASWM-279) — веха, иначе находка тонет в ленте step'ов
|
|
136
|
-
"dep_cleared"
|
|
140
|
+
"dep_cleared",
|
|
137
141
|
// зависимость снята неуспешным финалом блокера (TASWM-361) — решение роя, веха как conflict
|
|
142
|
+
"returned"
|
|
143
|
+
// дрон вернул сырую задачу автору (TASWM-412) — финал прогона, веха как done/failed
|
|
138
144
|
];
|
|
139
145
|
MILESTONE_KIND_SET = new Set(MILESTONE_KINDS);
|
|
140
146
|
}
|
|
@@ -212,6 +218,14 @@ function buildSegments(created, events, laneEnd) {
|
|
|
212
218
|
push(e.at);
|
|
213
219
|
phase = "superseded";
|
|
214
220
|
break;
|
|
221
|
+
// TASWM-412: возврат автору — конец прогона, хвост дорожки не должен дорисовываться
|
|
222
|
+
// «работающим» зелёным (та же готча, что чинил TASWM-166 для stopped/superseded).
|
|
223
|
+
// Фаза — ЧУЖАЯ, "stopped": своя фаза значит свой цвет-токен в обеих темах web, то есть
|
|
224
|
+
// правку дизайн-системы; здесь важнее, что полоса перестаёт врать про живую работу.
|
|
225
|
+
case "returned":
|
|
226
|
+
push(e.at);
|
|
227
|
+
phase = "stopped";
|
|
228
|
+
break;
|
|
215
229
|
}
|
|
216
230
|
}
|
|
217
231
|
push(laneEnd);
|
|
@@ -249,6 +263,8 @@ function computeBlocker(status, stopReason, lastStep, pr, ci, deps, openQ) {
|
|
|
249
263
|
return { icon: "\u23F9", text: stopReason || "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", tone: "stopped" };
|
|
250
264
|
case "superseded":
|
|
251
265
|
return { icon: "\u21BA", text: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442", tone: "superseded" };
|
|
266
|
+
case "returned":
|
|
267
|
+
return { icon: "\u21A9", text: stopReason || "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435", tone: "stopped" };
|
|
252
268
|
case "done":
|
|
253
269
|
return { icon: "\u2705", text: "\u0441\u043C\u0435\u0440\u0436\u0435\u043D\u043E", tone: "done" };
|
|
254
270
|
default:
|
|
@@ -322,6 +338,35 @@ var init_reviewPhase = __esm({
|
|
|
322
338
|
}
|
|
323
339
|
});
|
|
324
340
|
|
|
341
|
+
// ../swarm-core/src/mergeGate.ts
|
|
342
|
+
function computeMergeGate(input) {
|
|
343
|
+
if (!input.hasRun) {
|
|
344
|
+
return { allowed: true, code: "unknown_pr", reason: "PR \u043D\u0435 \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0443 \u0440\u043E\u044F \u2014 \u0433\u0435\u0439\u0442 \u0440\u0435\u0432\u044C\u044E \u043A \u043D\u0435\u043C\u0443 \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F" };
|
|
345
|
+
}
|
|
346
|
+
if (!input.autoReview) {
|
|
347
|
+
return { allowed: true, code: "auto_review_off", reason: "\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F" };
|
|
348
|
+
}
|
|
349
|
+
if (input.reviewVerdict === "approve") {
|
|
350
|
+
return { allowed: true, code: "approved", reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u0430\u043B approve \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E" };
|
|
351
|
+
}
|
|
352
|
+
if (input.reviewVerdict === "changes") {
|
|
353
|
+
return { allowed: false, code: "changes_requested", reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u043B \u043F\u0440\u0430\u0432\u043A\u0438 (REQUEST CHANGES) \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F, \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u0438 \u043D\u043E\u0432\u043E\u0433\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430" };
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
allowed: false,
|
|
357
|
+
code: "no_verdict",
|
|
358
|
+
reason: "\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E, \u0430 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043F\u043E \u044D\u0442\u043E\u043C\u0443 PR \u0435\u0449\u0451 \u043D\u0435\u0442 \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F (TASWM-355)"
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function mergeGateLine(d) {
|
|
362
|
+
return `${d.allowed ? "\u2705 \u043C\u0435\u0440\u0436 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D" : "\u26D4 \u043C\u0435\u0440\u0436 \u0437\u0430\u043F\u0440\u0435\u0449\u0451\u043D"}: ${d.reason}`;
|
|
363
|
+
}
|
|
364
|
+
var init_mergeGate = __esm({
|
|
365
|
+
"../swarm-core/src/mergeGate.ts"() {
|
|
366
|
+
"use strict";
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
325
370
|
// ../swarm-core/src/status.ts
|
|
326
371
|
function statusMeta(status) {
|
|
327
372
|
return STATUS_META[status] ?? FALLBACK;
|
|
@@ -354,7 +399,11 @@ var init_status = __esm({
|
|
|
354
399
|
storm_dead: { pill: "storm", label: "\u0448\u0442\u043E\u0440\u043C \u2014 \u0437\u0430\u043C\u043E\u0440\u043E\u0436\u0435\u043D", color: "--warning", terminal: false },
|
|
355
400
|
// умер в шторм Anthropic, поднимется после отбоя (TASWM-23)
|
|
356
401
|
stopped: { pill: "stopped", label: "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", color: "--muted-foreground", terminal: true },
|
|
357
|
-
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true }
|
|
402
|
+
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true },
|
|
403
|
+
// TASWM-412: дрон вернул сырую задачу автору (вопросы комментом, задача назначена на автора
|
|
404
|
+
// и переведена в работу) и освободил слот. Терминальный — работа этого прогона закончена;
|
|
405
|
+
// цвет предупреждающий, а не destructive: смотреть должен АВТОР задачи, чинить нечего.
|
|
406
|
+
returned: { pill: "returned", label: "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443", color: "--warning", terminal: true }
|
|
358
407
|
};
|
|
359
408
|
FALLBACK = STATUS_META.queued;
|
|
360
409
|
TERMINAL_STATUSES = Object.keys(STATUS_META).filter(
|
|
@@ -1176,6 +1225,7 @@ var init_src = __esm({
|
|
|
1176
1225
|
init_types();
|
|
1177
1226
|
init_timeline();
|
|
1178
1227
|
init_reviewPhase();
|
|
1228
|
+
init_mergeGate();
|
|
1179
1229
|
init_status();
|
|
1180
1230
|
init_deps();
|
|
1181
1231
|
init_time();
|
|
@@ -2005,7 +2055,7 @@ var init_package = __esm({
|
|
|
2005
2055
|
"package.json"() {
|
|
2006
2056
|
package_default = {
|
|
2007
2057
|
name: "@taskless-app/swarm",
|
|
2008
|
-
version: "0.6.
|
|
2058
|
+
version: "0.6.9",
|
|
2009
2059
|
type: "module",
|
|
2010
2060
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
2011
2061
|
keywords: [
|
|
@@ -2055,7 +2105,7 @@ var init_package = __esm({
|
|
|
2055
2105
|
execa: "^10.0.0",
|
|
2056
2106
|
zod: "3.25.76"
|
|
2057
2107
|
},
|
|
2058
|
-
gitHead: "
|
|
2108
|
+
gitHead: "cefe25ffbc0e9791234055c8ba552ff3f8d07a3f"
|
|
2059
2109
|
};
|
|
2060
2110
|
}
|
|
2061
2111
|
});
|
|
@@ -2370,6 +2420,16 @@ async function daemonStatus2() {
|
|
|
2370
2420
|
return null;
|
|
2371
2421
|
}
|
|
2372
2422
|
}
|
|
2423
|
+
async function daemonMergeGate(ref) {
|
|
2424
|
+
try {
|
|
2425
|
+
const res = await fetch(`${BASE}/merge-gate?pr=${encodeURIComponent(ref)}`, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
2426
|
+
if (!res.ok) return null;
|
|
2427
|
+
const body = await res.json();
|
|
2428
|
+
return typeof body?.allowed === "boolean" && typeof body?.reason === "string" ? body : null;
|
|
2429
|
+
} catch {
|
|
2430
|
+
return null;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2373
2433
|
var TIMEOUT_MS, ACTION_TIMEOUT_MS, BASE, NO_DAEMON_NOTE;
|
|
2374
2434
|
var init_daemonLink = __esm({
|
|
2375
2435
|
"src/daemonLink.ts"() {
|
|
@@ -5636,6 +5696,27 @@ var SwarmClient = class _SwarmClient {
|
|
|
5636
5696
|
const v = sc.config?.maxDrones;
|
|
5637
5697
|
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
|
|
5638
5698
|
}
|
|
5699
|
+
/**
|
|
5700
|
+
* Live АВТОРЕВЬЮ проекта из Taskless (TASWM-355): тот же swarm_repo_config по
|
|
5701
|
+
* repoFullName — Project.swarmConfig.autoReview. true → у проекта перед мержем обязан быть
|
|
5702
|
+
* вердикт дрона-ревьюера, и гейт мержа (mergeGate.ts) не пускает `gh pr merge`, пока
|
|
5703
|
+
* вердикта нет. До этой ручки демон знал только ГЛОБАЛЬНЫЙ env SWARM_AUTO_REVIEW
|
|
5704
|
+
* («поднимать ли ревьюеров на этой машине»), а настройка проекта до него не доезжала —
|
|
5705
|
+
* гейту же нужен ответ про КОНКРЕТНЫЙ проект. null (прод недоступен / repo не смэплен /
|
|
5706
|
+
* поля нет) → ридер (makeAutoReviewReader) возьмёт env, то есть прежнее поведение.
|
|
5707
|
+
*/
|
|
5708
|
+
async repoAutoReview(repoFullName) {
|
|
5709
|
+
let res;
|
|
5710
|
+
try {
|
|
5711
|
+
res = await this.call("swarm_repo_config", { repoFullName });
|
|
5712
|
+
} catch {
|
|
5713
|
+
return null;
|
|
5714
|
+
}
|
|
5715
|
+
if (res?.isError) return null;
|
|
5716
|
+
const sc = res.structuredContent ?? {};
|
|
5717
|
+
const v = sc.config?.autoReview;
|
|
5718
|
+
return typeof v === "boolean" ? v : null;
|
|
5719
|
+
}
|
|
5639
5720
|
/**
|
|
5640
5721
|
* TASWM-291: МАШИНЫ роя владельца (swarm_list_hosts) — чтобы `swarm spawn --host
|
|
5641
5722
|
* <имя>` резолвил человеческое имя в hostId, а `swarm hosts` показывал, какие
|
|
@@ -7119,6 +7200,41 @@ async function ghChecks(pr, run = realChecksRunner) {
|
|
|
7119
7200
|
|
|
7120
7201
|
// src/web.ts
|
|
7121
7202
|
init_config();
|
|
7203
|
+
|
|
7204
|
+
// src/mergeGate.ts
|
|
7205
|
+
init_src();
|
|
7206
|
+
function prNumberOf(ref) {
|
|
7207
|
+
const s = (ref || "").trim();
|
|
7208
|
+
if (!s) return null;
|
|
7209
|
+
const url = s.match(/\/pull\/(\d+)/);
|
|
7210
|
+
if (url) return Number(url[1]);
|
|
7211
|
+
const bare = s.match(/^#?(\d+)$/);
|
|
7212
|
+
return bare ? Number(bare[1]) : null;
|
|
7213
|
+
}
|
|
7214
|
+
function findRunByPr(runs, ref) {
|
|
7215
|
+
const num = prNumberOf(ref);
|
|
7216
|
+
if (num == null) return null;
|
|
7217
|
+
let best = null;
|
|
7218
|
+
for (const r of runs) {
|
|
7219
|
+
if (!r.pr_url || prNumberOf(r.pr_url) !== num) continue;
|
|
7220
|
+
if (!best || r.id > best.id) best = r;
|
|
7221
|
+
}
|
|
7222
|
+
return best;
|
|
7223
|
+
}
|
|
7224
|
+
function makeMergeGateResolver(deps) {
|
|
7225
|
+
return async (ref) => {
|
|
7226
|
+
const run = findRunByPr(deps.listRuns(), ref);
|
|
7227
|
+
if (!run) return { ...computeMergeGate({ hasRun: false, autoReview: false, reviewVerdict: null }), run: null };
|
|
7228
|
+
const autoReview = deps.autoReview ? await deps.autoReview(run.repo).catch(() => deps.fallbackAutoReview) : deps.fallbackAutoReview;
|
|
7229
|
+
const decision = computeMergeGate({ hasRun: true, autoReview, reviewVerdict: run.review_verdict });
|
|
7230
|
+
return {
|
|
7231
|
+
...decision,
|
|
7232
|
+
run: { id: run.id, task_slug: run.task_slug, name: run.name, pr_url: run.pr_url, review_verdict: run.review_verdict }
|
|
7233
|
+
};
|
|
7234
|
+
};
|
|
7235
|
+
}
|
|
7236
|
+
|
|
7237
|
+
// src/web.ts
|
|
7122
7238
|
init_droneChannel();
|
|
7123
7239
|
|
|
7124
7240
|
// src/oauth.ts
|
|
@@ -8670,6 +8786,12 @@ function makeHandler(deps) {
|
|
|
8670
8786
|
hostId: deps.selfHost?.hostId ?? null
|
|
8671
8787
|
});
|
|
8672
8788
|
}
|
|
8789
|
+
if (p === "/merge-gate") {
|
|
8790
|
+
const ref = url.searchParams.get("pr") ?? "";
|
|
8791
|
+
if (!ref.trim()) return Response.json({ error: "\u043D\u0443\u0436\u0435\u043D \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 pr=<url|\u043D\u043E\u043C\u0435\u0440>" }, { status: 400 });
|
|
8792
|
+
const resolve4 = deps.mergeGate ?? makeMergeGateResolver({ listRuns: () => deps.db.listRuns(), fallbackAutoReview: SWARM_AUTO_REVIEW });
|
|
8793
|
+
return Response.json(await resolve4(ref));
|
|
8794
|
+
}
|
|
8673
8795
|
if (p === "/resources") return Response.json(hostResourceSnapshot());
|
|
8674
8796
|
if (p === "/timeline") return Response.json(timelineState(deps.db, Date.now(), readNarratorLog(deps.narratorLog)));
|
|
8675
8797
|
const diffMatch = p.match(/^\/run\/(\d+)\/diff$/);
|
|
@@ -10182,6 +10304,9 @@ function usage() {
|
|
|
10182
10304
|
swarm wake <runId> \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D (id \u0438\u0437 swarm ls); \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E \u2192 \u043D\u0430\u0447\u043D\u0451\u0442 \u0441 \u043D\u0443\u043B\u044F
|
|
10183
10305
|
swarm cancel <slug> <\u043F\u0440\u0438\u0447\u0438\u043D\u0430\u2026>
|
|
10184
10306
|
\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430: \u0441\u0442\u0430\u0442\u0443\u0441 \xAB\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\xBB \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439, \u0432 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u043E\u0441\u0442\u0430\u043D\u0435\u0442\u0441\u044F
|
|
10307
|
+
swarm merge-gate <pr> \u043C\u043E\u0436\u043D\u043E \u043B\u0438 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u044D\u0442\u043E\u0442 PR (\u0441\u0441\u044B\u043B\u043A\u0430 \u0438\u043B\u0438 \u043D\u043E\u043C\u0435\u0440): \u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0441
|
|
10308
|
+
\u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430. \u0412\u044B\u0445\u043E\u0434 1 = \u043D\u0435\u043B\u044C\u0437\u044F.
|
|
10309
|
+
\u0422\u043E\u0442 \u0436\u0435 \u043E\u0442\u0432\u0435\u0442 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442 \u0445\u0443\u043A, \u043F\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044B\u0432\u0430\u044E\u0449\u0438\u0439 gh pr merge
|
|
10185
10310
|
swarm questions \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0434\u0440\u043E\u043D\u043E\u0432
|
|
10186
10311
|
swarm answer <qid> <\u0442\u0435\u043A\u0441\u0442\u2026>
|
|
10187
10312
|
\u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 \u0438 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430
|
|
@@ -10466,6 +10591,23 @@ if (cmd === "status") {
|
|
|
10466
10591
|
for (const line of repoMapLines(st.repos)) console.log(line);
|
|
10467
10592
|
process.exit(0);
|
|
10468
10593
|
}
|
|
10594
|
+
if (cmd === "merge-gate") {
|
|
10595
|
+
const ref = rawArgs[0];
|
|
10596
|
+
if (!ref) {
|
|
10597
|
+
console.error("\u043D\u0443\u0436\u043D\u043E: swarm merge-gate <\u0441\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 PR|\u043D\u043E\u043C\u0435\u0440>");
|
|
10598
|
+
process.exit(2);
|
|
10599
|
+
}
|
|
10600
|
+
const answer = await daemonMergeGate(ref);
|
|
10601
|
+
if (!answer) {
|
|
10602
|
+
console.error(
|
|
10603
|
+
`\u26D4 \u043C\u0435\u0440\u0436 \u0437\u0430\u043F\u0440\u0435\u0449\u0451\u043D: \u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B (\u043C\u043E\u0440\u0434\u0430 127.0.0.1:${WEB_PORT}) \u2014 \u0440\u043E\u0439 \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C, \u0447\u0442\u043E \u043F\u043E PR \u0435\u0441\u0442\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430. \u041F\u043E\u0434\u043D\u0438\u043C\u0438 \u0434\u0435\u043C\u043E\u043D\u0430 (swarm start) \u0438\u043B\u0438, \u0435\u0441\u043B\u0438 \u043C\u0435\u0440\u0436\u0438\u0448\u044C \u043E\u0441\u043E\u0437\u043D\u0430\u043D\u043D\u043E \u0440\u0443\u043A\u0430\u043C\u0438, \u0441\u043D\u0438\u043C\u0438 \u0433\u0435\u0439\u0442: SWARM_MERGE_GATE=off`
|
|
10604
|
+
);
|
|
10605
|
+
process.exit(1);
|
|
10606
|
+
}
|
|
10607
|
+
const who = answer.run ? ` (\u043F\u0440\u043E\u0433\u043E\u043D #${answer.run.id} ${answer.run.task_slug}, \u0434\u0440\u043E\u043D ${answer.run.name})` : "";
|
|
10608
|
+
console.log(`${mergeGateLine(answer)}${who}`);
|
|
10609
|
+
process.exit(answer.allowed ? 0 : 1);
|
|
10610
|
+
}
|
|
10469
10611
|
if (cmd === "pause" || cmd === "resume") {
|
|
10470
10612
|
const wantPause = cmd === "pause";
|
|
10471
10613
|
const reason = wantPause ? rawArgs.join(" ").trim() || null : null;
|
package/dist/overmind.js
CHANGED
|
@@ -75,7 +75,11 @@ var init_types = __esm({
|
|
|
75
75
|
// прогона перед самым спавном: «поехал молча» означало бы, что причина запуска исчезла из
|
|
76
76
|
// истории, и следующий разбор «почему он стартовал поверх незакрытого блокера» упёрся бы в
|
|
77
77
|
// пустоту. Успешное снятие (блокер смержен) события не порождает — это штатный ход.
|
|
78
|
-
"dep_cleared"
|
|
78
|
+
"dep_cleared",
|
|
79
|
+
// TASWM-412: дрон вернул сырую задачу автору (см. RunStatus "returned"). Веха, а не step:
|
|
80
|
+
// прогон на этом кончается, и в истории должно быть видно, что он кончился ВОЗВРАТОМ —
|
|
81
|
+
// иначе возврат читается как брошенная посреди работы задача.
|
|
82
|
+
"returned"
|
|
79
83
|
];
|
|
80
84
|
MILESTONE_KINDS = [
|
|
81
85
|
"spawned",
|
|
@@ -98,8 +102,10 @@ var init_types = __esm({
|
|
|
98
102
|
// надзор overmind (TASWM-194) — та же нотификация, что улетела в инбокс, вехой в Истории
|
|
99
103
|
"note",
|
|
100
104
|
// заметка дрона «замечено по пути» (TASWM-279) — веха, иначе находка тонет в ленте step'ов
|
|
101
|
-
"dep_cleared"
|
|
105
|
+
"dep_cleared",
|
|
102
106
|
// зависимость снята неуспешным финалом блокера (TASWM-361) — решение роя, веха как conflict
|
|
107
|
+
"returned"
|
|
108
|
+
// дрон вернул сырую задачу автору (TASWM-412) — финал прогона, веха как done/failed
|
|
103
109
|
];
|
|
104
110
|
MILESTONE_KIND_SET = new Set(MILESTONE_KINDS);
|
|
105
111
|
}
|
|
@@ -177,6 +183,14 @@ function buildSegments(created, events, laneEnd) {
|
|
|
177
183
|
push(e.at);
|
|
178
184
|
phase = "superseded";
|
|
179
185
|
break;
|
|
186
|
+
// TASWM-412: возврат автору — конец прогона, хвост дорожки не должен дорисовываться
|
|
187
|
+
// «работающим» зелёным (та же готча, что чинил TASWM-166 для stopped/superseded).
|
|
188
|
+
// Фаза — ЧУЖАЯ, "stopped": своя фаза значит свой цвет-токен в обеих темах web, то есть
|
|
189
|
+
// правку дизайн-системы; здесь важнее, что полоса перестаёт врать про живую работу.
|
|
190
|
+
case "returned":
|
|
191
|
+
push(e.at);
|
|
192
|
+
phase = "stopped";
|
|
193
|
+
break;
|
|
180
194
|
}
|
|
181
195
|
}
|
|
182
196
|
push(laneEnd);
|
|
@@ -214,6 +228,8 @@ function computeBlocker(status, stopReason, lastStep, pr, ci, deps, openQ) {
|
|
|
214
228
|
return { icon: "\u23F9", text: stopReason || "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", tone: "stopped" };
|
|
215
229
|
case "superseded":
|
|
216
230
|
return { icon: "\u21BA", text: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442", tone: "superseded" };
|
|
231
|
+
case "returned":
|
|
232
|
+
return { icon: "\u21A9", text: stopReason || "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435", tone: "stopped" };
|
|
217
233
|
case "done":
|
|
218
234
|
return { icon: "\u2705", text: "\u0441\u043C\u0435\u0440\u0436\u0435\u043D\u043E", tone: "done" };
|
|
219
235
|
default:
|
|
@@ -319,6 +335,32 @@ var init_reviewPhase = __esm({
|
|
|
319
335
|
}
|
|
320
336
|
});
|
|
321
337
|
|
|
338
|
+
// ../swarm-core/src/mergeGate.ts
|
|
339
|
+
function computeMergeGate(input) {
|
|
340
|
+
if (!input.hasRun) {
|
|
341
|
+
return { allowed: true, code: "unknown_pr", reason: "PR \u043D\u0435 \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0443 \u0440\u043E\u044F \u2014 \u0433\u0435\u0439\u0442 \u0440\u0435\u0432\u044C\u044E \u043A \u043D\u0435\u043C\u0443 \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F" };
|
|
342
|
+
}
|
|
343
|
+
if (!input.autoReview) {
|
|
344
|
+
return { allowed: true, code: "auto_review_off", reason: "\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F" };
|
|
345
|
+
}
|
|
346
|
+
if (input.reviewVerdict === "approve") {
|
|
347
|
+
return { allowed: true, code: "approved", reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u0430\u043B approve \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E" };
|
|
348
|
+
}
|
|
349
|
+
if (input.reviewVerdict === "changes") {
|
|
350
|
+
return { allowed: false, code: "changes_requested", reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u043B \u043F\u0440\u0430\u0432\u043A\u0438 (REQUEST CHANGES) \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F, \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u0438 \u043D\u043E\u0432\u043E\u0433\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430" };
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
allowed: false,
|
|
354
|
+
code: "no_verdict",
|
|
355
|
+
reason: "\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E, \u0430 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043F\u043E \u044D\u0442\u043E\u043C\u0443 PR \u0435\u0449\u0451 \u043D\u0435\u0442 \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F (TASWM-355)"
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
var init_mergeGate = __esm({
|
|
359
|
+
"../swarm-core/src/mergeGate.ts"() {
|
|
360
|
+
"use strict";
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
322
364
|
// ../swarm-core/src/status.ts
|
|
323
365
|
function isAwaitingCi(status, stopReason) {
|
|
324
366
|
return status === "paused" && stopReason === AWAITING_CI_REASON;
|
|
@@ -354,7 +396,11 @@ var init_status = __esm({
|
|
|
354
396
|
storm_dead: { pill: "storm", label: "\u0448\u0442\u043E\u0440\u043C \u2014 \u0437\u0430\u043C\u043E\u0440\u043E\u0436\u0435\u043D", color: "--warning", terminal: false },
|
|
355
397
|
// умер в шторм Anthropic, поднимется после отбоя (TASWM-23)
|
|
356
398
|
stopped: { pill: "stopped", label: "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", color: "--muted-foreground", terminal: true },
|
|
357
|
-
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true }
|
|
399
|
+
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true },
|
|
400
|
+
// TASWM-412: дрон вернул сырую задачу автору (вопросы комментом, задача назначена на автора
|
|
401
|
+
// и переведена в работу) и освободил слот. Терминальный — работа этого прогона закончена;
|
|
402
|
+
// цвет предупреждающий, а не destructive: смотреть должен АВТОР задачи, чинить нечего.
|
|
403
|
+
returned: { pill: "returned", label: "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443", color: "--warning", terminal: true }
|
|
358
404
|
};
|
|
359
405
|
AWAITING_CI_REASON = "\u0436\u0434\u0451\u0442 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 CI \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 PR \u0435\u0449\u0451 \u0438\u0434\u0443\u0442";
|
|
360
406
|
FALLBACK = STATUS_META.queued;
|
|
@@ -1336,6 +1382,7 @@ var init_src = __esm({
|
|
|
1336
1382
|
init_types();
|
|
1337
1383
|
init_timeline();
|
|
1338
1384
|
init_reviewPhase();
|
|
1385
|
+
init_mergeGate();
|
|
1339
1386
|
init_status();
|
|
1340
1387
|
init_deps();
|
|
1341
1388
|
init_time();
|
|
@@ -4204,13 +4251,17 @@ __export(taskless_exports, {
|
|
|
4204
4251
|
requireTaskBody: () => requireTaskBody,
|
|
4205
4252
|
resetLegacyTokenWarning: () => resetLegacyTokenWarning,
|
|
4206
4253
|
resolveApiToken: () => resolveApiToken,
|
|
4207
|
-
swarmToolArgs: () => swarmToolArgs
|
|
4254
|
+
swarmToolArgs: () => swarmToolArgs,
|
|
4255
|
+
taskAuthor: () => taskAuthor
|
|
4208
4256
|
});
|
|
4209
4257
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4210
4258
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4211
4259
|
function isClosingTaskCategory(category) {
|
|
4212
4260
|
return category === "completed" || category === "cancelled" || category === "archived";
|
|
4213
4261
|
}
|
|
4262
|
+
function taskAuthor(u) {
|
|
4263
|
+
return u?.id ? { id: u.id, name: u.displayName || "\u0430\u0432\u0442\u043E\u0440 \u0437\u0430\u0434\u0430\u0447\u0438" } : null;
|
|
4264
|
+
}
|
|
4214
4265
|
function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl = fetch) {
|
|
4215
4266
|
const ws = workspaceId ? { workspaceId } : {};
|
|
4216
4267
|
const idCache = /* @__PURE__ */ new Map();
|
|
@@ -4250,7 +4301,10 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4250
4301
|
documents: (task.documents ?? []).filter((d) => !!d.id).map((d) => ({ id: d.id, title: d.title ?? d.id, url: d.url ?? null })),
|
|
4251
4302
|
// TASWM-283: только ССЫЛКИ (порядок = порядок в задаче). Скачивание — на спавне,
|
|
4252
4303
|
// в worktree конкретного дрона; getTask зовут и там, где выкладывать некуда.
|
|
4253
|
-
attachments: collectTaskAttachments({ description: task.description, comments: task.comments ?? [] })
|
|
4304
|
+
attachments: collectTaskAttachments({ description: task.description, comments: task.comments ?? [] }),
|
|
4305
|
+
// TASWM-412: creator, иначе requester. Прод старее этой правки автора не отдаёт вовсе —
|
|
4306
|
+
// тогда null, и возврат просто обойдётся без переназначения (комментарий важнее).
|
|
4307
|
+
author: taskAuthor(task.creator) ?? taskAuthor(task.requester)
|
|
4254
4308
|
};
|
|
4255
4309
|
idCache.set(slug, body.id);
|
|
4256
4310
|
idCache.set(body.id, body.id);
|
|
@@ -4306,6 +4360,17 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4306
4360
|
startTask: (idOrSlug) => transition("start_task", idOrSlug),
|
|
4307
4361
|
// TASWM-294: intent — «человек, посмотри», а не «Готово» (см. докблок в TasklessClient).
|
|
4308
4362
|
finishTask: (idOrSlug) => transition("finish_task", idOrSlug, { intent: "needs_check" }),
|
|
4363
|
+
// TASWM-412: назначение — через общий update_task (тот же путь, что и у UI-роута),
|
|
4364
|
+
// отдельного «assign»-тула у Taskless нет. Успех считаем по факту ответа тула:
|
|
4365
|
+
// safeCall уже отсеял isError и падения, вернув null.
|
|
4366
|
+
assignTask: async (idOrSlug, userId) => {
|
|
4367
|
+
const id = await resolveId(idOrSlug);
|
|
4368
|
+
if (!id) {
|
|
4369
|
+
console.error(`[taskless] update_task(assigneeId) \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D: \u0437\u0430\u0434\u0430\u0447\u0430 ${idOrSlug} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430`);
|
|
4370
|
+
return false;
|
|
4371
|
+
}
|
|
4372
|
+
return await safeCall("update_task", { id, assigneeId: userId }) != null;
|
|
4373
|
+
},
|
|
4309
4374
|
uploadShot: (file) => uploadShot(safeCall, file, fetchImpl),
|
|
4310
4375
|
addComment: async (idOrSlug, body, label, attachments) => {
|
|
4311
4376
|
const id = await resolveId(idOrSlug);
|
|
@@ -7042,6 +7107,27 @@ var SwarmClient = class _SwarmClient {
|
|
|
7042
7107
|
const v = sc.config?.maxDrones;
|
|
7043
7108
|
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
|
|
7044
7109
|
}
|
|
7110
|
+
/**
|
|
7111
|
+
* Live АВТОРЕВЬЮ проекта из Taskless (TASWM-355): тот же swarm_repo_config по
|
|
7112
|
+
* repoFullName — Project.swarmConfig.autoReview. true → у проекта перед мержем обязан быть
|
|
7113
|
+
* вердикт дрона-ревьюера, и гейт мержа (mergeGate.ts) не пускает `gh pr merge`, пока
|
|
7114
|
+
* вердикта нет. До этой ручки демон знал только ГЛОБАЛЬНЫЙ env SWARM_AUTO_REVIEW
|
|
7115
|
+
* («поднимать ли ревьюеров на этой машине»), а настройка проекта до него не доезжала —
|
|
7116
|
+
* гейту же нужен ответ про КОНКРЕТНЫЙ проект. null (прод недоступен / repo не смэплен /
|
|
7117
|
+
* поля нет) → ридер (makeAutoReviewReader) возьмёт env, то есть прежнее поведение.
|
|
7118
|
+
*/
|
|
7119
|
+
async repoAutoReview(repoFullName) {
|
|
7120
|
+
let res;
|
|
7121
|
+
try {
|
|
7122
|
+
res = await this.call("swarm_repo_config", { repoFullName });
|
|
7123
|
+
} catch {
|
|
7124
|
+
return null;
|
|
7125
|
+
}
|
|
7126
|
+
if (res?.isError) return null;
|
|
7127
|
+
const sc = res.structuredContent ?? {};
|
|
7128
|
+
const v = sc.config?.autoReview;
|
|
7129
|
+
return typeof v === "boolean" ? v : null;
|
|
7130
|
+
}
|
|
7045
7131
|
/**
|
|
7046
7132
|
* TASWM-291: МАШИНЫ роя владельца (swarm_list_hosts) — чтобы `swarm spawn --host
|
|
7047
7133
|
* <имя>` резолвил человеческое имя в hostId, а `swarm hosts` показывал, какие
|
|
@@ -9703,6 +9789,7 @@ async function buildDroneServer(store, opts = {}) {
|
|
|
9703
9789
|
const server = new McpServer({ name: "overmind-drone", version: "0.1.0" });
|
|
9704
9790
|
const overmindDead = async () => !await store.alive();
|
|
9705
9791
|
const slugForInstruction = (fallback) => opts.expectedTaskSlug ?? fallback ?? "<slug>";
|
|
9792
|
+
const iAmReviewer = (run) => run?.reviews_run_id != null || run?.kind === "reviewer" || opts.reviewsRunId != null || opts.runKind === "reviewer";
|
|
9706
9793
|
const freeze = async (withdraw = false) => {
|
|
9707
9794
|
let slug;
|
|
9708
9795
|
let branch;
|
|
@@ -9855,6 +9942,120 @@ ${notFoundText}` : notFoundText }],
|
|
|
9855
9942
|
return { content: [{ type: "text", text: "\u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u0430 \u0438 \u0443\u0435\u0434\u0435\u0442 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443; \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0439 \u0440\u0430\u0431\u043E\u0442\u0443 \u2014 \u043E\u0442\u0432\u0435\u0442\u0430 \u043D\u0435 \u0436\u0434\u0438." }] };
|
|
9856
9943
|
})
|
|
9857
9944
|
);
|
|
9945
|
+
server.tool(
|
|
9946
|
+
"return_task",
|
|
9947
|
+
"\u0412\u0435\u0440\u043D\u0443\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0410\u0412\u0422\u041E\u0420\u0423 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D (\u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F, \u0440\u043E\u0439 \u0432\u043E\u0437\u044C\u043C\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443). \u0417\u043E\u0432\u0438, \u0422\u041E\u041B\u042C\u041A\u041E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0418\u041B\u0418 \u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0414\u0432\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B\u0445 \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u044F \u2014 \u041D\u0415 \u043F\u043E\u0432\u043E\u0434: \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u041F\u043E\u043D\u044F\u0442\u043D\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u2014 \u0442\u043E\u0436\u0435 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434: \u044D\u0442\u043E ask. \u0417\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u0430\u0432\u0442\u043E\u0440\u0443 (\u043D\u0430\u0437\u043D\u0430\u0447\u0438\u043C \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043C \u0432 \u0440\u0430\u0431\u043E\u0442\u0443), \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u043E\u0442 \u0442\u0432\u043E\u0435\u0433\u043E \u0438\u043C\u0435\u043D\u0438. \u041F\u043E\u0441\u043B\u0435 \u0432\u044B\u0437\u043E\u0432\u0430 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0437\u043E\u0432\u0438 \u2014 \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E.",
|
|
9948
|
+
{
|
|
9949
|
+
missing: z.string().min(30).describe("\u0447\u0435\u0433\u043E \u0432 \u0437\u0430\u0434\u0430\u0447\u0435 \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0447\u0442\u043E\u0431\u044B \u0435\u0451 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0438\u043B\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E, \u0430 \u043D\u0435 \xAB\u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E\xBB"),
|
|
9950
|
+
readings: z.array(z.string().min(10)).optional().describe("\u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u044F \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0442\u044B \u0440\u0430\u0441\u0441\u043C\u043E\u0442\u0440\u0435\u043B (\u0438 \u0447\u0435\u043C \u043A\u0430\u0436\u0434\u043E\u0435 \u043F\u043B\u043E\u0445\u043E/\u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0432\u043E)"),
|
|
9951
|
+
assumed: z.string().min(30).describe("\u0447\u0442\u043E \u0442\u044B \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u043E\u0436\u0438\u043B \u0431\u044B \u0441\u0434\u0435\u043B\u0430\u0442\u044C, \u0435\u0441\u043B\u0438 \u0431\u044B \u043F\u0440\u0438\u0448\u043B\u043E\u0441\u044C \u0433\u0430\u0434\u0430\u0442\u044C, \u0438 \u043F\u043E\u0447\u0435\u043C\u0443 \u043D\u0435 \u0441\u0442\u0430\u043B"),
|
|
9952
|
+
questions: z.array(z.string().min(10)).min(1).max(7).describe("\u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0430\u0432\u0442\u043E\u0440\u0443 \u2014 \u043E\u0442\u0432\u0435\u0442\u044B \u043D\u0430 \u043D\u0438\u0445 \u0434\u043E\u043B\u0436\u043D\u044B \u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043C\u043E\u0439")
|
|
9953
|
+
},
|
|
9954
|
+
guard(
|
|
9955
|
+
async ({
|
|
9956
|
+
missing,
|
|
9957
|
+
readings,
|
|
9958
|
+
assumed,
|
|
9959
|
+
questions
|
|
9960
|
+
}) => {
|
|
9961
|
+
if (await overmindDead()) return freeze();
|
|
9962
|
+
const run = await store.myRun();
|
|
9963
|
+
if (iAmReviewer(run) || run?.kind === "hero" || opts.runKind === "hero") {
|
|
9964
|
+
return {
|
|
9965
|
+
content: [
|
|
9966
|
+
{
|
|
9967
|
+
type: "text",
|
|
9968
|
+
text: "return_task \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u0434\u0440\u043E\u043D\u0430-\u0418\u0421\u041F\u041E\u041B\u041D\u0418\u0422\u0415\u041B\u042F: \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u044E\u0442 \u0421\u0412\u041E\u042E \u0437\u0430\u0434\u0430\u0447\u0443 \u0430\u0432\u0442\u043E\u0440\u0443. \u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u043C \u0432 done(), \u0413\u0435\u0440\u043E\u0439 \u2014 rest()."
|
|
9969
|
+
}
|
|
9970
|
+
],
|
|
9971
|
+
isError: true
|
|
9972
|
+
};
|
|
9973
|
+
}
|
|
9974
|
+
const slug = opts.expectedTaskSlug ?? run?.task_slug;
|
|
9975
|
+
if (!opts.taskless || !slug) {
|
|
9976
|
+
return {
|
|
9977
|
+
content: [
|
|
9978
|
+
{
|
|
9979
|
+
type: "text",
|
|
9980
|
+
text: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u0447\u0435\u043C: Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u0438\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430 \u2014 \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0430\u0432\u0442\u043E\u0440\u0443 \u0438 \u043F\u0435\u0440\u0435\u043D\u0430\u0437\u043D\u0430\u0447\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E. \u0421\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 ask(), \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C."
|
|
9981
|
+
}
|
|
9982
|
+
],
|
|
9983
|
+
isError: true
|
|
9984
|
+
};
|
|
9985
|
+
}
|
|
9986
|
+
const droneName = run?.name ?? "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)";
|
|
9987
|
+
const task = await opts.taskless.getTask(slug);
|
|
9988
|
+
const author = task?.author ?? null;
|
|
9989
|
+
const body = [
|
|
9990
|
+
`\u21A9\uFE0F **\u0412\u043E\u0437\u0432\u0440\u0430\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 \u0430\u0432\u0442\u043E\u0440\u0443** \u2014 \u0434\u0440\u043E\u043D ${droneName}${run ? `, \u043F\u0440\u043E\u0433\u043E\u043D #${run.id}` : ""}.`,
|
|
9991
|
+
"",
|
|
9992
|
+
`${author ? `${author.name}, ` : ""}\u044F \u043D\u0435 \u0431\u0435\u0440\u0443\u0441\u044C \u0437\u0430 \u044D\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443: \u0435\u0451 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C, \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u0430\u0432 \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0443 \u0437\u0430 \u0442\u0435\u0431\u044F. \u0427\u0442\u043E \u043C\u0435\u0448\u0430\u0435\u0442:`,
|
|
9993
|
+
"",
|
|
9994
|
+
`**\u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442.** ${missing.trim()}`,
|
|
9995
|
+
...readings?.length ? ["", "**\u041A\u0430\u043A \u044D\u0442\u043E \u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C.**", ...readings.map((r) => `- ${r.trim()}`)] : [],
|
|
9996
|
+
"",
|
|
9997
|
+
`**\u0427\u0442\u043E \u044F \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u043E\u0436\u0438\u043B \u0431\u044B \u0438 \u043F\u043E\u0447\u0435\u043C\u0443 \u043D\u0435 \u0441\u0442\u0430\u043B.** ${assumed.trim()}`,
|
|
9998
|
+
"",
|
|
9999
|
+
"**\u0412\u043E\u043F\u0440\u043E\u0441\u044B, \u043F\u043E\u0441\u043B\u0435 \u043E\u0442\u0432\u0435\u0442\u043E\u0432 \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0443 \u043C\u043E\u0436\u043D\u043E \u0431\u0440\u0430\u0442\u044C:**",
|
|
10000
|
+
...questions.map((q, i) => `${i + 1}. ${q.trim()}`)
|
|
10001
|
+
].join("\n");
|
|
10002
|
+
const failures = [];
|
|
10003
|
+
try {
|
|
10004
|
+
await opts.taskless.addComment(slug, body);
|
|
10005
|
+
} catch (e) {
|
|
10006
|
+
failures.push(`\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0441 \u0432\u043E\u043F\u0440\u043E\u0441\u0430\u043C\u0438 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D (${e instanceof Error ? e.message : String(e)})`);
|
|
10007
|
+
}
|
|
10008
|
+
let assignedOk = false;
|
|
10009
|
+
if (author) {
|
|
10010
|
+
assignedOk = await opts.taskless.assignTask(slug, author.id);
|
|
10011
|
+
if (!assignedOk) failures.push("\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0430 \u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0430");
|
|
10012
|
+
} else {
|
|
10013
|
+
failures.push("\u0430\u0432\u0442\u043E\u0440 \u0437\u0430\u0434\u0430\u0447\u0438 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D (get_task \u0435\u0433\u043E \u043D\u0435 \u043E\u0442\u0434\u0430\u043B) \u2014 \u0437\u0430\u0434\u0430\u0447\u0430 \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u043D\u0430 \u043F\u0440\u0435\u0436\u043D\u0435\u043C \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u0435");
|
|
10014
|
+
}
|
|
10015
|
+
await opts.taskless.startTask(slug).catch((e) => {
|
|
10016
|
+
failures.push(`\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043D\u0430 \u0432 \u0440\u0430\u0431\u043E\u0442\u0443 (${e instanceof Error ? e.message : String(e)})`);
|
|
10017
|
+
});
|
|
10018
|
+
if (!assignedOk) {
|
|
10019
|
+
const after = await opts.taskless.getTask(slug).catch(() => null);
|
|
10020
|
+
if (!after || after.statusCategory === "unstarted") {
|
|
10021
|
+
await store.addEvent(
|
|
10022
|
+
null,
|
|
10023
|
+
"step",
|
|
10024
|
+
`\u0432\u043E\u0437\u0432\u0440\u0430\u0442 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0442: \u0437\u0430\u0434\u0430\u0447\u0430 ${slug} \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C claimable (${failures.join("; ")}) \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D running, \u0447\u0442\u043E\u0431\u044B \u0440\u043E\u0439 \u043D\u0435 \u0432\u0437\u044F\u043B \u0435\u0451 \u043F\u043E \u043A\u0440\u0443\u0433\u0443`
|
|
10025
|
+
);
|
|
10026
|
+
return {
|
|
10027
|
+
content: [
|
|
10028
|
+
{
|
|
10029
|
+
type: "text",
|
|
10030
|
+
text: [
|
|
10031
|
+
`\u0412\u043E\u0437\u0432\u0440\u0430\u0442 \u041D\u0415 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D: ${failures.join("; ")}.`,
|
|
10032
|
+
`\u0417\u0430\u0434\u0430\u0447\u0430 ${slug} \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u0432 \u0442\u043E\u043C \u0436\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \u0438 \u043D\u0430 \u0442\u043E\u043C \u0436\u0435 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u0435 \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u044F \u043F\u0440\u043E\u0433\u043E\u043D \u0441\u0435\u0439\u0447\u0430\u0441, \u0440\u043E\u0439 \u0432\u0437\u044F\u043B \u0431\u044B \u0435\u0451 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0434\u0440\u043E\u043D\u043E\u043C \u043F\u043E \u043A\u0440\u0443\u0433\u0443.`,
|
|
10033
|
+
"\u0412\u043E\u043F\u0440\u043E\u0441\u044B \u0430\u0432\u0442\u043E\u0440\u0443 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0443\u0436\u0435 \u0443\u0435\u0445\u0430\u043B\u0438. \u041F\u043E\u0432\u0442\u043E\u0440\u0438 return_task \u0447\u0435\u0440\u0435\u0437 \u043C\u0438\u043D\u0443\u0442\u0443 (\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0437\u0430\u0434\u0432\u043E\u0438\u0442\u0441\u044F \u2014 \u044D\u0442\u043E \u043C\u0435\u043D\u044C\u0448\u0435\u0435 \u0437\u043B\u043E) \u043B\u0438\u0431\u043E \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 ask()."
|
|
10034
|
+
].join("\n")
|
|
10035
|
+
}
|
|
10036
|
+
],
|
|
10037
|
+
isError: true
|
|
10038
|
+
};
|
|
10039
|
+
}
|
|
10040
|
+
}
|
|
10041
|
+
const reason = `\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443${author ? ` (${author.name})` : ""}: ${missing.trim()}`;
|
|
10042
|
+
await store.updateRun(null, { status: "returned", stop_reason: reason });
|
|
10043
|
+
await store.addEvent(null, "returned", failures.length ? `${reason} \xB7 \u043D\u0435 \u0434\u043E\u0435\u0445\u0430\u043B\u043E: ${failures.join("; ")}` : reason);
|
|
10044
|
+
return {
|
|
10045
|
+
content: [
|
|
10046
|
+
{
|
|
10047
|
+
type: "text",
|
|
10048
|
+
text: [
|
|
10049
|
+
`\u0417\u0430\u0434\u0430\u0447\u0430 ${slug} \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443${author ? ` (${author.name})` : ""}, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442 \u043A\u0430\u043A \xAB\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443\xBB, \u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0451\u043D.`,
|
|
10050
|
+
...failures.length ? [`\u26A0\uFE0F \u041D\u0435 \u0434\u043E\u0435\u0445\u0430\u043B\u043E: ${failures.join("; ")} \u2014 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u044D\u0442\u043E \u0432 \u043B\u043E\u0433\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430.`] : [],
|
|
10051
|
+
"\u0411\u043E\u043B\u044C\u0448\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u041D\u0415 \u0437\u043E\u0432\u0438 \u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u0434\u0430\u043B\u044C\u0448\u0435 \u041D\u0415 \u0434\u0435\u043B\u0430\u0439 \u2014 \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E."
|
|
10052
|
+
].join("\n")
|
|
10053
|
+
}
|
|
10054
|
+
]
|
|
10055
|
+
};
|
|
10056
|
+
}
|
|
10057
|
+
)
|
|
10058
|
+
);
|
|
9858
10059
|
server.tool(
|
|
9859
10060
|
"rest",
|
|
9860
10061
|
"\u0422\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u0413\u0435\u0440\u043E\u044F-\u043B\u0435\u0439\u0442\u0435\u043D\u0430\u043D\u0442\u0430. \u0417\u0430\u043C\u0435\u0440\u0435\u0442\u044C \u0434\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u043E\u0442\u0440\u044F\u0434\u0430: \u0440\u044B\u0432\u043E\u043A \u0441\u0434\u0435\u043B\u0430\u043D (\u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438 \u0441\u043E\u0437\u0434\u0430\u043D\u044B / \u0434\u043E\u043A\u043B\u0430\u0434 \u0434\u0430\u043D / \u043E\u0442\u0432\u0435\u0442\u044B \u0440\u043E\u0437\u0434\u0430\u043D\u044B), \u0437\u0432\u0430\u0442\u044C \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0447\u0435\u0433\u043E. \u041F\u0440\u043E\u0433\u043E\u043D \u2192 paused, \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044F; \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442 \u043E\u0432\u0435\u0440\u043C\u0430\u0439\u043D\u0434 \u043F\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044E \u043E\u0442\u0440\u044F\u0434\u0430 \u0438\u043B\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0432\u0440\u0443\u0447\u043D\u0443\u044E. \u041E\u0431\u044B\u0447\u043D\u044B\u0439 \u0434\u0440\u043E\u043D \u0441\u0434\u0430\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0443 \u0447\u0435\u0440\u0435\u0437 done, \u043D\u0435 rest.",
|
|
@@ -9914,9 +10115,7 @@ ${notFoundText}` : notFoundText }],
|
|
|
9914
10115
|
isError: true
|
|
9915
10116
|
};
|
|
9916
10117
|
}
|
|
9917
|
-
|
|
9918
|
-
const iAmReviewer = run?.reviews_run_id != null || run?.kind === "reviewer" || reviewerBySpawn;
|
|
9919
|
-
if (iAmReviewer && run?.reviews_run_id == null) {
|
|
10118
|
+
if (iAmReviewer(run) && run?.reviews_run_id == null) {
|
|
9920
10119
|
return {
|
|
9921
10120
|
content: [
|
|
9922
10121
|
{
|
|
@@ -10911,6 +11110,15 @@ function makeExclusiveReader(opts) {
|
|
|
10911
11110
|
...opts.now !== void 0 ? { now: opts.now } : {}
|
|
10912
11111
|
});
|
|
10913
11112
|
}
|
|
11113
|
+
function makeAutoReviewReader(opts) {
|
|
11114
|
+
return makeRepoValueReader({
|
|
11115
|
+
fetch: opts.fetch,
|
|
11116
|
+
fallback: opts.fallback,
|
|
11117
|
+
valid: (v) => typeof v === "boolean",
|
|
11118
|
+
...opts.ttlMs !== void 0 ? { ttlMs: opts.ttlMs } : {},
|
|
11119
|
+
...opts.now !== void 0 ? { now: opts.now } : {}
|
|
11120
|
+
});
|
|
11121
|
+
}
|
|
10914
11122
|
function makeDroneCapReader(opts) {
|
|
10915
11123
|
const read = makeRepoValueReader({
|
|
10916
11124
|
fetch: opts.fetch,
|
|
@@ -10926,6 +11134,39 @@ function makeDroneCapReader(opts) {
|
|
|
10926
11134
|
};
|
|
10927
11135
|
}
|
|
10928
11136
|
|
|
11137
|
+
// src/mergeGate.ts
|
|
11138
|
+
init_src();
|
|
11139
|
+
function prNumberOf(ref) {
|
|
11140
|
+
const s = (ref || "").trim();
|
|
11141
|
+
if (!s) return null;
|
|
11142
|
+
const url = s.match(/\/pull\/(\d+)/);
|
|
11143
|
+
if (url) return Number(url[1]);
|
|
11144
|
+
const bare = s.match(/^#?(\d+)$/);
|
|
11145
|
+
return bare ? Number(bare[1]) : null;
|
|
11146
|
+
}
|
|
11147
|
+
function findRunByPr(runs, ref) {
|
|
11148
|
+
const num = prNumberOf(ref);
|
|
11149
|
+
if (num == null) return null;
|
|
11150
|
+
let best = null;
|
|
11151
|
+
for (const r of runs) {
|
|
11152
|
+
if (!r.pr_url || prNumberOf(r.pr_url) !== num) continue;
|
|
11153
|
+
if (!best || r.id > best.id) best = r;
|
|
11154
|
+
}
|
|
11155
|
+
return best;
|
|
11156
|
+
}
|
|
11157
|
+
function makeMergeGateResolver(deps) {
|
|
11158
|
+
return async (ref) => {
|
|
11159
|
+
const run = findRunByPr(deps.listRuns(), ref);
|
|
11160
|
+
if (!run) return { ...computeMergeGate({ hasRun: false, autoReview: false, reviewVerdict: null }), run: null };
|
|
11161
|
+
const autoReview = deps.autoReview ? await deps.autoReview(run.repo).catch(() => deps.fallbackAutoReview) : deps.fallbackAutoReview;
|
|
11162
|
+
const decision = computeMergeGate({ hasRun: true, autoReview, reviewVerdict: run.review_verdict });
|
|
11163
|
+
return {
|
|
11164
|
+
...decision,
|
|
11165
|
+
run: { id: run.id, task_slug: run.task_slug, name: run.name, pr_url: run.pr_url, review_verdict: run.review_verdict }
|
|
11166
|
+
};
|
|
11167
|
+
};
|
|
11168
|
+
}
|
|
11169
|
+
|
|
10929
11170
|
// src/envGate.ts
|
|
10930
11171
|
import { existsSync as existsSync13 } from "fs";
|
|
10931
11172
|
import { join as join24 } from "path";
|
|
@@ -11364,7 +11605,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
11364
11605
|
// package.json
|
|
11365
11606
|
var package_default = {
|
|
11366
11607
|
name: "@taskless-app/swarm",
|
|
11367
|
-
version: "0.6.
|
|
11608
|
+
version: "0.6.9",
|
|
11368
11609
|
type: "module",
|
|
11369
11610
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
11370
11611
|
keywords: [
|
|
@@ -11414,7 +11655,7 @@ var package_default = {
|
|
|
11414
11655
|
execa: "^10.0.0",
|
|
11415
11656
|
zod: "3.25.76"
|
|
11416
11657
|
},
|
|
11417
|
-
gitHead: "
|
|
11658
|
+
gitHead: "cefe25ffbc0e9791234055c8ba552ff3f8d07a3f"
|
|
11418
11659
|
};
|
|
11419
11660
|
|
|
11420
11661
|
// src/version.ts
|
|
@@ -12837,6 +13078,12 @@ function makeHandler(deps) {
|
|
|
12837
13078
|
hostId: deps.selfHost?.hostId ?? null
|
|
12838
13079
|
});
|
|
12839
13080
|
}
|
|
13081
|
+
if (p === "/merge-gate") {
|
|
13082
|
+
const ref = url.searchParams.get("pr") ?? "";
|
|
13083
|
+
if (!ref.trim()) return Response.json({ error: "\u043D\u0443\u0436\u0435\u043D \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 pr=<url|\u043D\u043E\u043C\u0435\u0440>" }, { status: 400 });
|
|
13084
|
+
const resolve4 = deps.mergeGate ?? makeMergeGateResolver({ listRuns: () => deps.db.listRuns(), fallbackAutoReview: SWARM_AUTO_REVIEW });
|
|
13085
|
+
return Response.json(await resolve4(ref));
|
|
13086
|
+
}
|
|
12840
13087
|
if (p === "/resources") return Response.json(hostResourceSnapshot());
|
|
12841
13088
|
if (p === "/timeline") return Response.json(timelineState(deps.db, Date.now(), readNarratorLog(deps.narratorLog)));
|
|
12842
13089
|
const diffMatch = p.match(/^\/run\/(\d+)\/diff$/);
|
|
@@ -14289,7 +14536,12 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
14289
14536
|
// вторую ветку мимо PR. Пишем ту, что реально записана на прогоне; пусто (первый спавн —
|
|
14290
14537
|
// ветку заводит spawnDrone из слага) — прежняя формулировка.
|
|
14291
14538
|
`\u0422\u044B \u2014 \u0434\u0440\u043E\u043D \u0440\u043E\u044F. \u0420\u0430\u0431\u043E\u0442\u0430\u0435\u0448\u044C \u0432 \u044D\u0442\u043E\u043C git-worktree, \u0432\u0435\u0442\u043A\u0430 \`${run.branch || run.task_slug}\`${run.branch && run.branch !== run.task_slug ? " (\u043E\u043D\u0430 \u041D\u0415 \u0440\u0430\u0432\u043D\u0430 \u0441\u043B\u0430\u0433\u0443 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u044D\u0442\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 \u0443\u0436\u0435 \u043E\u0442\u043A\u0440\u044B\u0442\u043E\u0433\u043E PR, \u043F\u0443\u0448 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u043D\u0435\u0451)" : " \u0441\u043E\u0437\u0434\u0430\u043D\u0430 \u043F\u043E slug \u0437\u0430\u0434\u0430\u0447\u0438"}.`,
|
|
14292
|
-
"\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B overmind (MCP): report_step(text) \u2014 \u043B\u043E\u0433\u0438\u0440\u0443\u0439 \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B; read_task() \u2014 \u0441\u0432\u0435\u0436\u0438\u0439 \u0441\u043D\u0430\u043F\u0448\u043E\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 (\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435+\u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438+\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u044B+\u0434\u043E\u043A\u0438+\u0438\u0441\u0445\u043E\u0434\u044B \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u043F\u043E \u0437\u043E\u043D\u0435 \u043A\u043E\u0434\u0430) \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432, \u0435\u0441\u043B\u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043C\u043E\u0433\u043B\u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u0442\u0430\u0440\u0442\u0430; ask(question, options?) \u2014 \u0435\u0441\u043B\u0438 \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u0420\u0415\u0428\u0415\u041D\u0418\u0415, \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 (\u0434\u0430\u0439 2\u20134 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430 \u0432 options, \u0435\u0441\u043B\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0430 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0430\u044F) \u0438 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0430 (\u041D\u0415 \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439); report_acceptance_check(id, state, evidence) \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u041A\u0410\u0416\u0414\u042B\u0419 \u043F\u0443\u043D\u043A\u0442 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0431\u043B\u043E\u043A\u0430 \xAB\u041A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438\xBB, state = passed|failed|pending \u043F\u043E \u0444\u0430\u043A\u0442\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, evidence \u2014 \u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B/\u0441\u0441\u044B\u043B\u043A\u0430/\u0447\u0442\u043E \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u043B); \u043F\u0440\u043E\u0439\u0434\u0438\u0441\u044C \u043F\u043E \u043D\u0438\u043C \u041F\u0415\u0420\u0415\u0414 done(), \u0438\u043D\u0430\u0447\u0435 \u0433\u0430\u043B\u043E\u0447\u043A\u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440; done(pr_url) \u2014 \u043A\u043E\u0433\u0434\u0430 \u043E\u0442\u043A\u0440\u043E\u0435\u0448\u044C PR; note(text) \u2014 \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \xAB\u0437\u0430\u043C\u0435\u0447\u0435\u043D\u043E \u043F\u043E \u043F\u0443\u0442\u0438\xBB \u0411\u0415\u0417 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438: \u043D\u0430\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u043D\u0430 \u043F\u0440\u043E\u0431\u0435\u043B \u0432 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435, \u043F\u0440\u043E\u0442\u0443\u0445\u0448\u0443\u044E \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044E, \u0431\u0430\u0433 \u0440\u044F\u0434\u043E\u043C \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0434\u0438\u0444\u0444\u043E\u043C \u2014 \u0441\u043A\u0430\u0436\u0438 \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u0434\u0430\u043B\u044C\u0448\u0435, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u0438 \u0441\u0430\u043C \u0440\u0435\u0448\u0438\u0442, \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 (\u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u0442\u0430\u043A\u043E\u0435 \u0432 \u0434\u043E\u0440\u043E\u0433\u043E\u0439 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0438\u0439 ask \u0438 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u0438 \u0432 \u043E\u0442\u0447\u0451\u0442\u0435 \u043E \u0441\u0434\u0430\u0447\u0435).",
|
|
14539
|
+
"\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B overmind (MCP): report_step(text) \u2014 \u043B\u043E\u0433\u0438\u0440\u0443\u0439 \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B; read_task() \u2014 \u0441\u0432\u0435\u0436\u0438\u0439 \u0441\u043D\u0430\u043F\u0448\u043E\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 (\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435+\u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438+\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u044B+\u0434\u043E\u043A\u0438+\u0438\u0441\u0445\u043E\u0434\u044B \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u043F\u043E \u0437\u043E\u043D\u0435 \u043A\u043E\u0434\u0430) \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432, \u0435\u0441\u043B\u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043C\u043E\u0433\u043B\u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u0442\u0430\u0440\u0442\u0430; ask(question, options?) \u2014 \u0435\u0441\u043B\u0438 \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u0420\u0415\u0428\u0415\u041D\u0418\u0415, \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 (\u0434\u0430\u0439 2\u20134 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430 \u0432 options, \u0435\u0441\u043B\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0430 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0430\u044F) \u0438 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0430 (\u041D\u0415 \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439); report_acceptance_check(id, state, evidence) \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u041A\u0410\u0416\u0414\u042B\u0419 \u043F\u0443\u043D\u043A\u0442 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0431\u043B\u043E\u043A\u0430 \xAB\u041A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438\xBB, state = passed|failed|pending \u043F\u043E \u0444\u0430\u043A\u0442\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, evidence \u2014 \u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B/\u0441\u0441\u044B\u043B\u043A\u0430/\u0447\u0442\u043E \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u043B); \u043F\u0440\u043E\u0439\u0434\u0438\u0441\u044C \u043F\u043E \u043D\u0438\u043C \u041F\u0415\u0420\u0415\u0414 done(), \u0438\u043D\u0430\u0447\u0435 \u0433\u0430\u043B\u043E\u0447\u043A\u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440; done(pr_url) \u2014 \u043A\u043E\u0433\u0434\u0430 \u043E\u0442\u043A\u0440\u043E\u0435\u0448\u044C PR; return_task(missing, readings, assumed, questions) \u2014 \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0421\u042B\u0420\u0423\u042E \u0437\u0430\u0434\u0430\u0447\u0443 \u0430\u0432\u0442\u043E\u0440\u0443 (\u0441\u043C. \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043D\u0438\u0436\u0435); note(text) \u2014 \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \xAB\u0437\u0430\u043C\u0435\u0447\u0435\u043D\u043E \u043F\u043E \u043F\u0443\u0442\u0438\xBB \u0411\u0415\u0417 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438: \u043D\u0430\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u043D\u0430 \u043F\u0440\u043E\u0431\u0435\u043B \u0432 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435, \u043F\u0440\u043E\u0442\u0443\u0445\u0448\u0443\u044E \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044E, \u0431\u0430\u0433 \u0440\u044F\u0434\u043E\u043C \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0434\u0438\u0444\u0444\u043E\u043C \u2014 \u0441\u043A\u0430\u0436\u0438 \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u0434\u0430\u043B\u044C\u0448\u0435, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u0438 \u0441\u0430\u043C \u0440\u0435\u0448\u0438\u0442, \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 (\u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u0442\u0430\u043A\u043E\u0435 \u0432 \u0434\u043E\u0440\u043E\u0433\u043E\u0439 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0438\u0439 ask \u0438 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u0438 \u0432 \u043E\u0442\u0447\u0451\u0442\u0435 \u043E \u0441\u0434\u0430\u0447\u0435).",
|
|
14540
|
+
// TASWM-412: третий выход из непонятной задачи. Раньше их было два, и оба плохие —
|
|
14541
|
+
// угадать постановку (сделает не то) или ask (слот занят, а ответить может только автор
|
|
14542
|
+
// задачи, не оператор роя). Граница здесь ОБЯЗАТЕЛЬНА: дрон, легко возвращающий задачи,
|
|
14543
|
+
// становится отписочником, а это хуже, чем дрон, который иногда угадывает неверно.
|
|
14544
|
+
"\u0421\u042B\u0420\u0423\u042E \u0417\u0410\u0414\u0410\u0427\u0423 \u041D\u0415 \u0412\u042B\u0414\u0423\u041C\u042B\u0412\u0410\u0419 \u2014 \u0412\u0415\u0420\u041D\u0418 \u0410\u0412\u0422\u041E\u0420\u0423. \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0432 \u0437\u0430\u0434\u0430\u0447\u0443, \u043E\u0442\u0432\u0435\u0442\u044C \u0441\u0435\u0431\u0435 \u043D\u0430 \u0434\u0432\u0430 \u0432\u043E\u043F\u0440\u043E\u0441\u0430: \u043F\u043E\u043D\u044F\u0442\u043D\u043E \u043B\u0438, \u0427\u0422\u041E \u043D\u0443\u0436\u043D\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C, \u0438 \u0435\u0441\u0442\u044C \u043B\u0438 \u0442\u043E, \u041F\u041E \u0427\u0415\u041C\u0423 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u041E\u0431\u0430 \xAB\u043D\u0435\u0442\xBB \u2192 return_task(missing, readings, assumed, questions): \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0430\u0432\u0442\u043E\u0440\u0443, \u0437\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u0432 \u0440\u0430\u0431\u043E\u0442\u0443, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u043E\u0435\u0442\u0441\u044F \u043A\u0430\u043A \xAB\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443\xBB (\u043D\u0435 \xAB\u043F\u0440\u043E\u0432\u0430\u043B\u0435\u043D\u0430\xBB), \u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F. \u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044C \u041C\u041E\u0416\u041D\u041E, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0438\u043B\u0438 \u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0422\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0439 \u0434\u0432\u0430 \u0438 \u043E\u0431\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B \u2014 \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u0417\u0430\u0434\u0430\u0447\u0430 \u043F\u043E\u043D\u044F\u0442\u043D\u0430, \u043D\u043E \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u044D\u0442\u043E ask, \u0430 \u043D\u0435 \u0432\u043E\u0437\u0432\u0440\u0430\u0442.",
|
|
14293
14545
|
"\u041F\u0440\u0430\u0432\u0438\u043B\u0430: \u0442\u0440\u043E\u0433\u0430\u0435\u0448\u044C UI \u2014 \u0421\u041D\u0410\u0427\u0410\u041B\u0410 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438, \u0435\u0441\u0442\u044C \u043B\u0438 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0441\u043A\u0438\u043B\u043B \u0441 UI-\u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u0438 \u0441\u043B\u0435\u0434\u0443\u0439 \u0435\u0433\u043E \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435 (\u043D\u0435\u0442 \u043D\u0443\u0436\u043D\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0430 \u2014 \u0437\u0430\u0432\u0435\u0434\u0438 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443, \u043D\u0435 \u0445\u0430\u0440\u0434\u043A\u043E\u0434\u044C \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u043E\u0435); \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u0441\u043E slug \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438; \u043F\u043E\u0441\u043B\u0435 UI-\u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043F\u0440\u0438\u043B\u043E\u0436\u0438 \u0441\u043A\u0440\u0438\u043D\u044B mobile+desktop \u041A \u0417\u0410\u0414\u0410\u0427\u0415 (\u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 screenshots \u0443 done); \u043E\u0442\u043A\u0440\u043E\u0439 PR \u0447\u0435\u0440\u0435\u0437 gh \u0438 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url).",
|
|
14294
14546
|
// TASWM-377: у сессии есть ПОТОЛОК ТРАТ, и он режет её без предупреждения, прямо посреди
|
|
14295
14547
|
// работы. Дрон об этом знать обязан: цена сессии растёт от ЧИСЛА ХОДОВ (каждый ход заново
|
|
@@ -16720,6 +16972,7 @@ async function runDaemon(opts) {
|
|
|
16720
16972
|
let envGate;
|
|
16721
16973
|
let exclusiveProject;
|
|
16722
16974
|
let projectDroneCap;
|
|
16975
|
+
let autoReviewProject;
|
|
16723
16976
|
if (SWARM_BACKEND === "client" && db instanceof SwarmClient) {
|
|
16724
16977
|
const client = db;
|
|
16725
16978
|
verifyModeReader = makeVerifyModeReader({
|
|
@@ -16766,6 +17019,14 @@ async function runDaemon(opts) {
|
|
|
16766
17019
|
},
|
|
16767
17020
|
ttlMs: 6e4
|
|
16768
17021
|
});
|
|
17022
|
+
autoReviewProject = makeAutoReviewReader({
|
|
17023
|
+
fetch: async (repo) => {
|
|
17024
|
+
const repoFullName = await remoteRepoFullName(repo).catch(() => null);
|
|
17025
|
+
return repoFullName ? client.repoAutoReview(repoFullName) : null;
|
|
17026
|
+
},
|
|
17027
|
+
fallback: SWARM_AUTO_REVIEW,
|
|
17028
|
+
ttlMs: 6e4
|
|
17029
|
+
});
|
|
16769
17030
|
}
|
|
16770
17031
|
let spawnReviewerFn;
|
|
16771
17032
|
let verdictCommentFn;
|
|
@@ -17334,6 +17595,15 @@ async function runDaemon(opts) {
|
|
|
17334
17595
|
// печатает `swarm status`. Без него человек снова гадал бы, какие репозитории машина
|
|
17335
17596
|
// считает своими и в каком дереве работает при двух копиях одного клона.
|
|
17336
17597
|
repoMap: () => repoMapRows(repoMapState),
|
|
17598
|
+
// Гейт мержа (TASWM-355): ручка /merge-gate отвечает по ЖИВОЙ настройке autoReview того
|
|
17599
|
+
// проекта, которому принадлежит PR (ридер выше), а не по глобальному env машины. Именно
|
|
17600
|
+
// этот ответ и превращается в запрет: PreToolUse-хук на `gh pr merge` спрашивает его
|
|
17601
|
+
// через `swarm merge-gate` и отказывает в команде, пока вердикта ревьюера нет.
|
|
17602
|
+
mergeGate: makeMergeGateResolver({
|
|
17603
|
+
listRuns: () => db.listRuns(),
|
|
17604
|
+
...autoReviewProject ? { autoReview: autoReviewProject } : {},
|
|
17605
|
+
fallbackAutoReview: SWARM_AUTO_REVIEW
|
|
17606
|
+
}),
|
|
17337
17607
|
// Оператор-пауза + «Оживить» (TASWM-214 Фаза 2): тот же shared-флаг, что читает тик;
|
|
17338
17608
|
// narrate — операторские действия видны в ленте роя; revive — сброс залипшего шторма.
|
|
17339
17609
|
control,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.9",
|
|
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": "cefe25ffbc0e9791234055c8ba552ff3f8d07a3f",
|
|
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",
|