@taskless-app/swarm 0.6.65 → 0.6.67
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 +24 -12
- package/dist/overmind.js +281 -28
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -254,7 +254,8 @@ var init_reviewPhase = __esm({
|
|
|
254
254
|
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
255
255
|
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
256
256
|
ready_awaiting_ci: "\u0413\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443 \u2014 \u0436\u0434\u0451\u0442 CI",
|
|
257
|
-
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
257
|
+
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
258
|
+
cycles_exhausted: "\u041B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
258
259
|
};
|
|
259
260
|
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
260
261
|
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
@@ -705,16 +706,17 @@ var init_modelPolicy = __esm({
|
|
|
705
706
|
roles: { executor: "glm-5.3", review: "glm-5.3" }
|
|
706
707
|
},
|
|
707
708
|
// Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
|
|
708
|
-
// имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
|
|
709
|
-
// TASWM-
|
|
710
|
-
//
|
|
709
|
+
// имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5.
|
|
710
|
+
// TASWM-607: исполнитель остаётся на свежей модели. Живой дефолт CLI демон применяет
|
|
711
|
+
// только к роли executor (liveModels.ts). Ревьюер и проверяющий — на облегчённой
|
|
712
|
+
// grok-4.7-build-fast, пока человек не задал свою модель ревьюеров.
|
|
711
713
|
// Тарифа нет: цену пишет CLI.
|
|
712
714
|
{
|
|
713
715
|
id: "xai",
|
|
714
716
|
label: "xAI (Grok)",
|
|
715
717
|
executorId: "grok",
|
|
716
718
|
selectable: true,
|
|
717
|
-
roles: { executor: "grok-4.7", review: "grok-4.7" },
|
|
719
|
+
roles: { executor: "grok-4.7", review: "grok-4.7-build-fast" },
|
|
718
720
|
models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
|
|
719
721
|
},
|
|
720
722
|
// TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
|
|
@@ -1788,17 +1790,22 @@ var init_effort = __esm({
|
|
|
1788
1790
|
});
|
|
1789
1791
|
|
|
1790
1792
|
// ../swarm-core/src/queuePlan.ts
|
|
1791
|
-
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS;
|
|
1793
|
+
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS, SERVICE_DONE_KEEP_MS, PR_HEAD_LAG_MS, SERVICE_DONE_MARKER_MS;
|
|
1792
1794
|
var init_queuePlan = __esm({
|
|
1793
1795
|
"../swarm-core/src/queuePlan.ts"() {
|
|
1794
1796
|
"use strict";
|
|
1795
1797
|
init_status();
|
|
1796
1798
|
init_hostPool();
|
|
1797
1799
|
init_hosts();
|
|
1800
|
+
init_modelPolicy();
|
|
1798
1801
|
init_time();
|
|
1802
|
+
init_verifyVerdict();
|
|
1799
1803
|
MIN_MS = 6e4;
|
|
1800
1804
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
1801
1805
|
QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
|
|
1806
|
+
SERVICE_DONE_KEEP_MS = 2 * 60 * MIN_MS;
|
|
1807
|
+
PR_HEAD_LAG_MS = 5 * MIN_MS;
|
|
1808
|
+
SERVICE_DONE_MARKER_MS = 8 * MIN_MS;
|
|
1802
1809
|
}
|
|
1803
1810
|
});
|
|
1804
1811
|
|
|
@@ -2852,7 +2859,8 @@ var init_liveModels = __esm({
|
|
|
2852
2859
|
|
|
2853
2860
|
// src/swarmModels.ts
|
|
2854
2861
|
function roleDefault(role, providerId) {
|
|
2855
|
-
return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
|
|
2862
|
+
if (role === "executor") return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
|
|
2863
|
+
return defaultModelFor(role, providerId);
|
|
2856
2864
|
}
|
|
2857
2865
|
function pick2(v) {
|
|
2858
2866
|
const s = typeof v === "string" ? v.trim() : "";
|
|
@@ -3872,7 +3880,7 @@ var init_package = __esm({
|
|
|
3872
3880
|
"package.json"() {
|
|
3873
3881
|
package_default = {
|
|
3874
3882
|
name: "@taskless-app/swarm",
|
|
3875
|
-
version: "0.6.
|
|
3883
|
+
version: "0.6.67",
|
|
3876
3884
|
type: "module",
|
|
3877
3885
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3878
3886
|
keywords: [
|
|
@@ -3923,7 +3931,7 @@ var init_package = __esm({
|
|
|
3923
3931
|
execa: "^10.0.0",
|
|
3924
3932
|
zod: "3.25.76"
|
|
3925
3933
|
},
|
|
3926
|
-
gitHead: "
|
|
3934
|
+
gitHead: "92a3176867bc1668677a4b6a25e02e0cb6661ce0"
|
|
3927
3935
|
};
|
|
3928
3936
|
}
|
|
3929
3937
|
});
|
|
@@ -9253,6 +9261,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
9253
9261
|
if (!(typeof n === "number" && Number.isFinite(n) && n > 0)) return null;
|
|
9254
9262
|
const str = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
|
|
9255
9263
|
const cost = obj?.swarmSettings?.maxCostUsd;
|
|
9264
|
+
const rawLimit = obj?.swarmSettings?.reviewCycleLimit;
|
|
9265
|
+
const reviewCycleLimit = typeof rawLimit === "number" && Number.isInteger(rawLimit) && rawLimit >= 1 && rawLimit <= 20 ? rawLimit : null;
|
|
9256
9266
|
return {
|
|
9257
9267
|
maxDrones: Math.trunc(n),
|
|
9258
9268
|
// Старый прод (до TASWM-364) поля swarmSettings не отдаёт вовсе — обе модели null,
|
|
@@ -9269,7 +9279,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
9269
9279
|
// значим («гарантия выключена осознанно»), поэтому пропускаем и его, а не только
|
|
9270
9280
|
// положительные: null остаётся ровно за «ручка не задана / старый прод», и только
|
|
9271
9281
|
// он уводит спавн на env-фолбэк и дефолт ядра.
|
|
9272
|
-
maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null
|
|
9282
|
+
maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null,
|
|
9283
|
+
reviewCycleLimit
|
|
9273
9284
|
};
|
|
9274
9285
|
}
|
|
9275
9286
|
/**
|
|
@@ -14847,10 +14858,11 @@ switch (cmd) {
|
|
|
14847
14858
|
// TASWM-592 (ревью): сессию ищет харнесс ПРОГОНА, а не глобальный исполнитель —
|
|
14848
14859
|
// иначе побудка codex-прогона при мёртвом демоне теряла продолжаемую сессию.
|
|
14849
14860
|
hasTranscript: (run2) => !!run2.worktree_path && executorForRun(run2, null).session.exists(run2.worktree_path),
|
|
14850
|
-
respawn: (r, mode) => respawnRun(
|
|
14861
|
+
respawn: (r, mode, nudge) => respawnRun(
|
|
14851
14862
|
{ db, launch, repoFallback: SWARM_REPO, worktreesDir: effectiveWorktreesDir(), getTask: () => Promise.resolve(null) },
|
|
14852
14863
|
r,
|
|
14853
|
-
mode
|
|
14864
|
+
mode,
|
|
14865
|
+
nudge
|
|
14854
14866
|
)
|
|
14855
14867
|
},
|
|
14856
14868
|
runId
|
package/dist/overmind.js
CHANGED
|
@@ -188,6 +188,9 @@ function reviewPhaseKindOf(label2) {
|
|
|
188
188
|
function computeReviewPhase(input) {
|
|
189
189
|
const { reviewVerdict, ciRed } = input;
|
|
190
190
|
const verifyVerdict = input.verifyVerdict ?? null;
|
|
191
|
+
if (input.cyclesExhausted) {
|
|
192
|
+
return { kind: "cycles_exhausted", label: REVIEW_PHASE_TEXT.cycles_exhausted, ready: true };
|
|
193
|
+
}
|
|
191
194
|
if (!ciRed && isNegativeVerifyVerdict(verifyVerdict)) {
|
|
192
195
|
const kind = verifyVerdict === "found_issues" ? "verify_found" : "verify_not_reproduced";
|
|
193
196
|
return { kind, label: REVIEW_PHASE_TEXT[kind], ready: false };
|
|
@@ -242,7 +245,8 @@ var init_reviewPhase = __esm({
|
|
|
242
245
|
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
243
246
|
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
244
247
|
ready_awaiting_ci: "\u0413\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443 \u2014 \u0436\u0434\u0451\u0442 CI",
|
|
245
|
-
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
248
|
+
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
249
|
+
cycles_exhausted: "\u041B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
246
250
|
};
|
|
247
251
|
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
248
252
|
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
@@ -702,16 +706,17 @@ var init_modelPolicy = __esm({
|
|
|
702
706
|
roles: { executor: "glm-5.3", review: "glm-5.3" }
|
|
703
707
|
},
|
|
704
708
|
// Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
|
|
705
|
-
// имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
|
|
706
|
-
// TASWM-
|
|
707
|
-
//
|
|
709
|
+
// имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5.
|
|
710
|
+
// TASWM-607: исполнитель остаётся на свежей модели. Живой дефолт CLI демон применяет
|
|
711
|
+
// только к роли executor (liveModels.ts). Ревьюер и проверяющий — на облегчённой
|
|
712
|
+
// grok-4.7-build-fast, пока человек не задал свою модель ревьюеров.
|
|
708
713
|
// Тарифа нет: цену пишет CLI.
|
|
709
714
|
{
|
|
710
715
|
id: "xai",
|
|
711
716
|
label: "xAI (Grok)",
|
|
712
717
|
executorId: "grok",
|
|
713
718
|
selectable: true,
|
|
714
|
-
roles: { executor: "grok-4.7", review: "grok-4.7" },
|
|
719
|
+
roles: { executor: "grok-4.7", review: "grok-4.7-build-fast" },
|
|
715
720
|
models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
|
|
716
721
|
},
|
|
717
722
|
// TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
|
|
@@ -2019,17 +2024,22 @@ var init_effort = __esm({
|
|
|
2019
2024
|
});
|
|
2020
2025
|
|
|
2021
2026
|
// ../swarm-core/src/queuePlan.ts
|
|
2022
|
-
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS;
|
|
2027
|
+
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS, SERVICE_DONE_KEEP_MS, PR_HEAD_LAG_MS, SERVICE_DONE_MARKER_MS;
|
|
2023
2028
|
var init_queuePlan = __esm({
|
|
2024
2029
|
"../swarm-core/src/queuePlan.ts"() {
|
|
2025
2030
|
"use strict";
|
|
2026
2031
|
init_status();
|
|
2027
2032
|
init_hostPool();
|
|
2028
2033
|
init_hosts();
|
|
2034
|
+
init_modelPolicy();
|
|
2029
2035
|
init_time();
|
|
2036
|
+
init_verifyVerdict();
|
|
2030
2037
|
MIN_MS = 6e4;
|
|
2031
2038
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
2032
2039
|
QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
|
|
2040
|
+
SERVICE_DONE_KEEP_MS = 2 * 60 * MIN_MS;
|
|
2041
|
+
PR_HEAD_LAG_MS = 5 * MIN_MS;
|
|
2042
|
+
SERVICE_DONE_MARKER_MS = 8 * MIN_MS;
|
|
2033
2043
|
}
|
|
2034
2044
|
});
|
|
2035
2045
|
|
|
@@ -3099,7 +3109,8 @@ var init_liveModels = __esm({
|
|
|
3099
3109
|
|
|
3100
3110
|
// src/swarmModels.ts
|
|
3101
3111
|
function roleDefault(role, providerId) {
|
|
3102
|
-
return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
|
|
3112
|
+
if (role === "executor") return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
|
|
3113
|
+
return defaultModelFor(role, providerId);
|
|
3103
3114
|
}
|
|
3104
3115
|
function pick2(v) {
|
|
3105
3116
|
const s = typeof v === "string" ? v.trim() : "";
|
|
@@ -3135,6 +3146,10 @@ function resolveRunModelRef(run2, swarm, env = process.env) {
|
|
|
3135
3146
|
const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
3136
3147
|
return { providerId, model: roleDefault(modelRoleOf(run2.kind), providerId) };
|
|
3137
3148
|
}
|
|
3149
|
+
function modelRefToRecord(run2, ref) {
|
|
3150
|
+
if (!run2 || pick2(run2.model)) return null;
|
|
3151
|
+
return `${ref.providerId}/${ref.model}`;
|
|
3152
|
+
}
|
|
3138
3153
|
function resolveRunEffort(run2, swarm, env = process.env) {
|
|
3139
3154
|
return toEffortLevel(run2.effort) ?? forKind(run2.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
3140
3155
|
}
|
|
@@ -9525,6 +9540,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
9525
9540
|
if (!(typeof n === "number" && Number.isFinite(n) && n > 0)) return null;
|
|
9526
9541
|
const str2 = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
|
|
9527
9542
|
const cost = obj?.swarmSettings?.maxCostUsd;
|
|
9543
|
+
const rawLimit = obj?.swarmSettings?.reviewCycleLimit;
|
|
9544
|
+
const reviewCycleLimit = typeof rawLimit === "number" && Number.isInteger(rawLimit) && rawLimit >= 1 && rawLimit <= 20 ? rawLimit : null;
|
|
9528
9545
|
return {
|
|
9529
9546
|
maxDrones: Math.trunc(n),
|
|
9530
9547
|
// Старый прод (до TASWM-364) поля swarmSettings не отдаёт вовсе — обе модели null,
|
|
@@ -9541,7 +9558,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
9541
9558
|
// значим («гарантия выключена осознанно»), поэтому пропускаем и его, а не только
|
|
9542
9559
|
// положительные: null остаётся ровно за «ручка не задана / старый прод», и только
|
|
9543
9560
|
// он уводит спавн на env-фолбэк и дефолт ядра.
|
|
9544
|
-
maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null
|
|
9561
|
+
maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null,
|
|
9562
|
+
reviewCycleLimit
|
|
9545
9563
|
};
|
|
9546
9564
|
}
|
|
9547
9565
|
/**
|
|
@@ -13056,7 +13074,7 @@ function parkReasonUnretriable(reason) {
|
|
|
13056
13074
|
// package.json
|
|
13057
13075
|
var package_default = {
|
|
13058
13076
|
name: "@taskless-app/swarm",
|
|
13059
|
-
version: "0.6.
|
|
13077
|
+
version: "0.6.67",
|
|
13060
13078
|
type: "module",
|
|
13061
13079
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
13062
13080
|
keywords: [
|
|
@@ -13107,7 +13125,7 @@ var package_default = {
|
|
|
13107
13125
|
execa: "^10.0.0",
|
|
13108
13126
|
zod: "3.25.76"
|
|
13109
13127
|
},
|
|
13110
|
-
gitHead: "
|
|
13128
|
+
gitHead: "92a3176867bc1668677a4b6a25e02e0cb6661ce0"
|
|
13111
13129
|
};
|
|
13112
13130
|
|
|
13113
13131
|
// src/version.ts
|
|
@@ -14116,12 +14134,36 @@ init_src();
|
|
|
14116
14134
|
|
|
14117
14135
|
// src/reviewCycle.ts
|
|
14118
14136
|
init_src();
|
|
14137
|
+
var REVIEW_CYCLE_LIMIT_DEFAULT = 3;
|
|
14138
|
+
var REVIEW_CYCLE_LIMIT_CAP = 20;
|
|
14139
|
+
function validReviewCycleLimit(n) {
|
|
14140
|
+
return n != null && Number.isInteger(n) && n >= 1 && n <= REVIEW_CYCLE_LIMIT_CAP;
|
|
14141
|
+
}
|
|
14142
|
+
function parseReviewCycleLimitEnv(raw) {
|
|
14143
|
+
if (raw == null || raw.trim() === "") return REVIEW_CYCLE_LIMIT_DEFAULT;
|
|
14144
|
+
const n = Number(raw);
|
|
14145
|
+
return validReviewCycleLimit(n) ? n : REVIEW_CYCLE_LIMIT_DEFAULT;
|
|
14146
|
+
}
|
|
14147
|
+
var REVIEW_CYCLE_LIMIT = parseReviewCycleLimitEnv(process.env.SWARM_REVIEW_CYCLE_LIMIT);
|
|
14148
|
+
var configuredReviewCycleLimit = null;
|
|
14149
|
+
function configureReviewCycleLimit(n) {
|
|
14150
|
+
configuredReviewCycleLimit = validReviewCycleLimit(n) ? n : null;
|
|
14151
|
+
}
|
|
14152
|
+
function effectiveReviewCycleLimit() {
|
|
14153
|
+
return configuredReviewCycleLimit ?? REVIEW_CYCLE_LIMIT;
|
|
14154
|
+
}
|
|
14155
|
+
function reviewCyclesExhausted(run2) {
|
|
14156
|
+
return (run2.review_cycle ?? 0) >= effectiveReviewCycleLimit();
|
|
14157
|
+
}
|
|
14119
14158
|
function needsFixIteration(run2) {
|
|
14120
14159
|
return run2.review_verdict === "changes" || isNegativeVerifyVerdict(toVerifyVerdict(run2.verify_verdict));
|
|
14121
14160
|
}
|
|
14122
14161
|
function reviewTransitionPatch(run2, prUrl) {
|
|
14123
14162
|
const isFixIteration = needsFixIteration(run2);
|
|
14124
14163
|
if (isFixIteration) {
|
|
14164
|
+
if (reviewCyclesExhausted(run2)) {
|
|
14165
|
+
return { isFixIteration: false, cycle: null, patch: { status: "review", pr_url: prUrl } };
|
|
14166
|
+
}
|
|
14125
14167
|
const cycle = (run2.review_cycle ?? 0) + 1;
|
|
14126
14168
|
return {
|
|
14127
14169
|
isFixIteration: true,
|
|
@@ -19122,7 +19164,87 @@ var BASE_MERGED_MARK = "\u0431\u0430\u0437\u0430 \u0432\u043B\u0438\u0442\u0430
|
|
|
19122
19164
|
var BASE_NOOP_MARK = "\u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E";
|
|
19123
19165
|
var RESOLVER_RESOLVED_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D \u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440\u043E\u043C";
|
|
19124
19166
|
var REVIEW_SPAWN_LIMIT = Number(process.env.SWARM_REVIEW_SPAWN_LIMIT) || 3;
|
|
19125
|
-
|
|
19167
|
+
function reviewCycleHumanBody(cycle, limit) {
|
|
19168
|
+
return `\u041B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D (${cycle} \u0438\u0437 ${limit}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A. \u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0440\u0435\u0432\u044C\u044E, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0438 \u043F\u043E\u0431\u0443\u0434\u043A\u0438 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u044B \u2014 PR \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443.`;
|
|
19169
|
+
}
|
|
19170
|
+
var CYCLE_TASK_COMMENT_SENT = "\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043B\u0438\u043C\u0438\u0442\u0430 \u043A\u0440\u0443\u0433\u043E\u0432 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D";
|
|
19171
|
+
var CYCLE_PR_COMMENT_SENT = "\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043B\u0438\u043C\u0438\u0442\u0430 \u043A\u0440\u0443\u0433\u043E\u0432 \u0432 PR \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D";
|
|
19172
|
+
var cycleCommentInflight = /* @__PURE__ */ new Map();
|
|
19173
|
+
function takeCycleCommentSlot(runId, side) {
|
|
19174
|
+
let slot = cycleCommentInflight.get(runId);
|
|
19175
|
+
if (!slot) {
|
|
19176
|
+
slot = { task: false, pr: false };
|
|
19177
|
+
cycleCommentInflight.set(runId, slot);
|
|
19178
|
+
}
|
|
19179
|
+
if (slot[side]) return false;
|
|
19180
|
+
slot[side] = true;
|
|
19181
|
+
return true;
|
|
19182
|
+
}
|
|
19183
|
+
function freeCycleCommentSlot(runId, side) {
|
|
19184
|
+
const slot = cycleCommentInflight.get(runId);
|
|
19185
|
+
if (slot) slot[side] = false;
|
|
19186
|
+
}
|
|
19187
|
+
async function noteReviewCyclesExhausted(deps, run2) {
|
|
19188
|
+
const texts = deps.db.events(run2.id).map((e) => e.text ?? "");
|
|
19189
|
+
const marked = texts.some((t) => t.includes(REVIEW_CYCLE_ESCALATION_MARK));
|
|
19190
|
+
const taskSent = texts.some((t) => t.includes(CYCLE_TASK_COMMENT_SENT));
|
|
19191
|
+
const prSent = texts.some((t) => t.includes(CYCLE_PR_COMMENT_SENT));
|
|
19192
|
+
const cycle = run2.review_cycle ?? 0;
|
|
19193
|
+
const body = reviewCycleHumanBody(cycle, effectiveReviewCycleLimit());
|
|
19194
|
+
if (!marked) {
|
|
19195
|
+
deps.db.addEvent(run2.id, "step", `${REVIEW_CYCLE_ESCALATION_MARK}: ${body}`);
|
|
19196
|
+
try {
|
|
19197
|
+
deps.narrate?.(body);
|
|
19198
|
+
} catch {
|
|
19199
|
+
}
|
|
19200
|
+
}
|
|
19201
|
+
if (deps.reportComment && !taskSent && takeCycleCommentSlot(run2.id, "task")) {
|
|
19202
|
+
const ok = await deps.reportComment(
|
|
19203
|
+
run2.task_slug,
|
|
19204
|
+
body,
|
|
19205
|
+
{ swarmKind: "report", swarmDroneName: run2.name || "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)", swarmReviewCycle: cycle },
|
|
19206
|
+
run2.workspace_id
|
|
19207
|
+
).then(() => true).catch((e) => {
|
|
19208
|
+
deps.db.addEvent(run2.id, "step", `\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \xAB\u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A\xBB \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0443\u0448\u0451\u043B: ${e instanceof Error ? e.message : String(e)}`);
|
|
19209
|
+
return false;
|
|
19210
|
+
});
|
|
19211
|
+
if (ok) deps.db.addEvent(run2.id, "step", CYCLE_TASK_COMMENT_SENT);
|
|
19212
|
+
freeCycleCommentSlot(run2.id, "task");
|
|
19213
|
+
}
|
|
19214
|
+
if (run2.pr_url && deps.prComment && !prSent && takeCycleCommentSlot(run2.id, "pr")) {
|
|
19215
|
+
const ok = await deps.prComment(run2.pr_url, body).then(() => true).catch((e) => {
|
|
19216
|
+
deps.db.addEvent(run2.id, "step", `\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \xAB\u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A\xBB \u0432 PR \u043D\u0435 \u0443\u0448\u0451\u043B: ${e instanceof Error ? e.message : String(e)}`);
|
|
19217
|
+
return false;
|
|
19218
|
+
});
|
|
19219
|
+
if (ok) deps.db.addEvent(run2.id, "step", CYCLE_PR_COMMENT_SENT);
|
|
19220
|
+
freeCycleCommentSlot(run2.id, "pr");
|
|
19221
|
+
}
|
|
19222
|
+
}
|
|
19223
|
+
function cycleHolder(db, run2) {
|
|
19224
|
+
if (run2.kind === "hero" || run2.resolves_run_id != null) return null;
|
|
19225
|
+
const parentId = run2.reviews_run_id ?? run2.verifies_run_id;
|
|
19226
|
+
if (parentId != null) return db.getRun(parentId) ?? null;
|
|
19227
|
+
return run2;
|
|
19228
|
+
}
|
|
19229
|
+
function cycleCapHolder(db, run2) {
|
|
19230
|
+
const holder = cycleHolder(db, run2);
|
|
19231
|
+
return holder && reviewCyclesExhausted(holder) ? holder : null;
|
|
19232
|
+
}
|
|
19233
|
+
var CYCLE_CAP_STOP_REASON = "\u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A";
|
|
19234
|
+
async function handOffAtCycleCap(deps, run2, holder, where) {
|
|
19235
|
+
await noteReviewCyclesExhausted(deps, holder);
|
|
19236
|
+
const text = `${CYCLE_CAP_STOP_REASON} (${holder.review_cycle ?? 0} \u0438\u0437 ${effectiveReviewCycleLimit()}): ${where} \u2014 \u0441\u0435\u0441\u0441\u0438\u044E \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E`;
|
|
19237
|
+
if (holder.id !== run2.id) {
|
|
19238
|
+
if (run2.worktree_path) await deps.removeWorktree(run2.repo, run2.worktree_path).catch(() => {
|
|
19239
|
+
});
|
|
19240
|
+
deps.db.updateRun(run2.id, { status: "stopped", stop_reason: CYCLE_CAP_STOP_REASON });
|
|
19241
|
+
deps.db.addEvent(run2.id, "stopped", text);
|
|
19242
|
+
return;
|
|
19243
|
+
}
|
|
19244
|
+
if (run2.pr_url) deps.db.updateRun(run2.id, { status: "review", stop_reason: null });
|
|
19245
|
+
else deps.db.updateRun(run2.id, { status: "paused", stop_reason: CYCLE_CAP_STOP_REASON });
|
|
19246
|
+
deps.db.addEvent(run2.id, "step", text);
|
|
19247
|
+
}
|
|
19126
19248
|
var CI_WAKE_LIMIT = Number(process.env.SWARM_CI_WAKE_LIMIT) || 3;
|
|
19127
19249
|
var CI_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CI_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
19128
19250
|
var CI_CHECK_COOLDOWN_MS = Number(process.env.SWARM_CI_CHECK_COOLDOWN_MS) || 60 * 1e3;
|
|
@@ -19149,7 +19271,7 @@ var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430
|
|
|
19149
19271
|
var CHANGES_FIX_COST_MARK = "\u0442\u0440\u0430\u0442\u044B \u0434\u043E \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438";
|
|
19150
19272
|
var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
19151
19273
|
var BUDGET_STOP_REASON = "\u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442: \u0441\u0435\u0441\u0441\u0438\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B \u0431\u044E\u0434\u0436\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430";
|
|
19152
|
-
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON, ABORTED_STOP_REASON, AWAITING_BUDGET_REASON];
|
|
19274
|
+
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON, ABORTED_STOP_REASON, AWAITING_BUDGET_REASON, CYCLE_CAP_STOP_REASON];
|
|
19153
19275
|
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)";
|
|
19154
19276
|
function verdictCommentMark(createdAt) {
|
|
19155
19277
|
return `\u043A\u043E\u043C\u043C\u0435\u043D\u0442-\u0432\u0435\u0440\u0434\u0438\u043A\u0442@${createdAt}`;
|
|
@@ -19312,7 +19434,9 @@ function reviewPhaseInputFor(deps, run2, projectWantsVerify = true) {
|
|
|
19312
19434
|
reviewerDead,
|
|
19313
19435
|
verifyVerdict: run2.verify_verdict,
|
|
19314
19436
|
verifyExpected,
|
|
19315
|
-
verifierDead
|
|
19437
|
+
verifierDead,
|
|
19438
|
+
// TASWM-606: потолок кругов важнее «чинит CI» и «ждёт ревьюера» — человека уже позвали.
|
|
19439
|
+
cyclesExhausted: reviewCyclesExhausted(run2)
|
|
19316
19440
|
};
|
|
19317
19441
|
}
|
|
19318
19442
|
async function refreshReviewPhases(deps) {
|
|
@@ -19605,11 +19729,13 @@ function relinkUnboundReviewers(deps) {
|
|
|
19605
19729
|
narrate(deps, `${droneLabel(rv)} \u0440\u0430\u0431\u043E\u0442\u0430\u043B \u0431\u0435\u0437 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043A \u043F\u0440\u043E\u0433\u043E\u043D\u0443 \u2014 \u0441\u0432\u044F\u0437\u0430\u043B \u043F\u043E PR-\u0441\u0441\u044B\u043B\u043A\u0435, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0442\u0435\u043F\u0435\u0440\u044C \u043B\u044F\u0436\u0435\u0442 \u0432 ${parent.task_slug}, \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u043A\u043E\u043C\u043C\u0435\u043D\u0442 GitHub.`);
|
|
19606
19730
|
}
|
|
19607
19731
|
}
|
|
19608
|
-
|
|
19732
|
+
function escalationLimits() {
|
|
19733
|
+
return { reviewSpawn: REVIEW_SPAWN_LIMIT, reviewCycle: effectiveReviewCycleLimit() };
|
|
19734
|
+
}
|
|
19609
19735
|
function deliberateReviewStop(db, run2) {
|
|
19610
19736
|
const texts = db.events(run2.id).map((e) => e.text ?? "");
|
|
19611
19737
|
const hit = findEscalation(texts, run2, "reviewer");
|
|
19612
|
-
return hit?.stopReason?.(run2,
|
|
19738
|
+
return hit?.stopReason?.(run2, escalationLimits()) ?? null;
|
|
19613
19739
|
}
|
|
19614
19740
|
function noteReviewBlocked(deps, run2, reason) {
|
|
19615
19741
|
if (!shouldNoteReason(deps.db, run2.id, reason)) return;
|
|
@@ -19694,6 +19820,10 @@ async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
|
19694
19820
|
if (run2.kind !== "drone") continue;
|
|
19695
19821
|
if (run2.reviews_run_id != null || run2.resolves_run_id != null || run2.verifies_run_id != null) continue;
|
|
19696
19822
|
if (!run2.pr_url || !isGithubPrUrl(run2.pr_url)) continue;
|
|
19823
|
+
if (reviewCyclesExhausted(run2)) {
|
|
19824
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
19825
|
+
continue;
|
|
19826
|
+
}
|
|
19697
19827
|
if (run2.verify_verdict != null) continue;
|
|
19698
19828
|
if (deps.db.verifierCountFor(run2.id) > 0) continue;
|
|
19699
19829
|
const attempts = deps.db.verifierAttemptCountFor(run2.id);
|
|
@@ -19719,6 +19849,18 @@ async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
|
19719
19849
|
}
|
|
19720
19850
|
}
|
|
19721
19851
|
var CONTINUE_NUDGE = "\u0422\u0432\u043E\u044F \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C (\u0441\u0435\u0442\u044C/\u043F\u0440\u043E\u0446\u0435\u0441\u0441) \u2014 \u0442\u044B headless-\u0434\u0440\u043E\u043D, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0435\u0448\u044C \u0422\u0423 \u0416\u0415 \u0437\u0430\u0434\u0430\u0447\u0443. \u041F\u043E\u0441\u043C\u043E\u0442\u0440\u0438 `git status`/`git log` \u0438 \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0441\u0432\u043E\u0438\u0445 \u0448\u0430\u0433\u043E\u0432: \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043D\u043E \u2014 \u041D\u0415 \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u044B\u0432\u0430\u0439, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438 \u0441 \u043C\u0435\u0441\u0442\u0430 \u043E\u0431\u0440\u044B\u0432\u0430. \u0414\u043E\u0432\u0435\u0434\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u0434\u043E \u043A\u043E\u043D\u0446\u0430, \u041A\u041E\u041C\u041C\u0418\u0422\u042C \u0418 \u041F\u0423\u0428\u042C \u043F\u043E\u0441\u043B\u0435 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0448\u0430\u0433\u0430, \u043E\u0442\u043A\u0440\u043E\u0439 PR \u0438 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url). \u0412\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0432 stdout \u043D\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0439 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0435\u0440\u0435\u0437 MCP-\u0442\u0443\u043B ask.";
|
|
19852
|
+
function buildSubmittedContinueNudge(opts) {
|
|
19853
|
+
const verify = opts.verifySummary ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E:
|
|
19854
|
+
${opts.verifySummary}` : "\u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E \u0432 \u043B\u0435\u043D\u0442\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u2014 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0435\u0433\u043E \u0441\u0430\u043C: `gh pr view --comments`.";
|
|
19855
|
+
const review = opts.reviewSummary ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430:
|
|
19856
|
+
${opts.reviewSummary}` : "";
|
|
19857
|
+
return [
|
|
19858
|
+
`\u0422\u044B \u0443\u0436\u0435 \u0441\u0434\u0430\u043B \u0440\u0430\u0431\u043E\u0442\u0443: PR ${opts.prUrl} \u043E\u0442\u043A\u0440\u044B\u0442. \u041D\u0415 \u0432\u044B\u0437\u044B\u0432\u0430\u0439 done() \u043D\u0430 \u0442\u043E\u043C \u0436\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u2014 \u044D\u0442\u043E \u0441\u043E\u0436\u0436\u0451\u0442 \u0435\u0449\u0451 \u043E\u0434\u0438\u043D \u043A\u0440\u0443\u0433 \u043F\u0440\u0430\u0432\u043E\u043A \u0432\u043F\u0443\u0441\u0442\u0443\u044E.`,
|
|
19859
|
+
verify,
|
|
19860
|
+
review,
|
|
19861
|
+
"\u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442, \u043F\u043E\u0447\u0438\u043D\u0438 \u043F\u0440\u0438\u0447\u0438\u043D\u0443, \u0437\u0430\u043A\u043E\u043C\u043C\u0438\u0442\u044C \u041D\u041E\u0412\u0423\u042E \u0440\u0430\u0431\u043E\u0442\u0443 \u0438 \u0437\u0430\u043F\u0443\u0448\u044C \u0432 \u0442\u0443 \u0436\u0435 \u0432\u0435\u0442\u043A\u0443. \u0422\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u043D\u043E\u0432\u043E\u0433\u043E \u043A\u043E\u043C\u043C\u0438\u0442\u0430 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url) \u0441 \u0442\u043E\u0439 \u0436\u0435 \u0441\u0441\u044B\u043B\u043A\u043E\u0439. \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."
|
|
19862
|
+
].filter((part) => part.length > 0).join("\n\n");
|
|
19863
|
+
}
|
|
19722
19864
|
function buildCiWakeNudge(summary) {
|
|
19723
19865
|
return [
|
|
19724
19866
|
"\u{1F534} CI \u0443\u043F\u0430\u043B \u043D\u0430 \u0442\u0432\u043E\u0451\u043C PR \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043B `gh pr checks` \u0438 \u043F\u0440\u0438\u0441\u043B\u0430\u043B \u0442\u0435\u0431\u0435 \u0441\u0436\u0430\u0442\u044B\u0439 \u0440\u0430\u0437\u0431\u043E\u0440 \u0444\u0435\u0439\u043B\u0430 (\u043D\u0435 \u0432\u0435\u0441\u044C \u043B\u043E\u0433, \u0442\u043E\u043A\u0435\u043D-\u0434\u0438\u0441\u0446\u0438\u043F\u043B\u0438\u043D\u0430):",
|
|
@@ -20040,6 +20182,15 @@ function lastVerdictEvent(db, reviewRunId) {
|
|
|
20040
20182
|
function lastChangesSummary(db, reviewRunId) {
|
|
20041
20183
|
return lastVerdictEvent(db, reviewRunId)?.text ?? null;
|
|
20042
20184
|
}
|
|
20185
|
+
var VERIFY_EVENT_PREFIXES = ["\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043E\u0442", "\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D \u0438\u0437 PR-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430"];
|
|
20186
|
+
function lastVerifySummary(db, runId) {
|
|
20187
|
+
const events = db.events(runId);
|
|
20188
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
20189
|
+
const text = events[i].text ?? "";
|
|
20190
|
+
if (VERIFY_EVENT_PREFIXES.some((p) => text.startsWith(p))) return text;
|
|
20191
|
+
}
|
|
20192
|
+
return null;
|
|
20193
|
+
}
|
|
20043
20194
|
function changesVerdictAt(db, reviewRunId) {
|
|
20044
20195
|
return lastVerdictEvent(db, reviewRunId)?.at ?? null;
|
|
20045
20196
|
}
|
|
@@ -20219,6 +20370,11 @@ async function resurrectStormDead(deps, worktreeExists, hasTranscript, opts) {
|
|
|
20219
20370
|
let resurrected = 0;
|
|
20220
20371
|
for (const run2 of deps.db.listRuns("storm_dead")) {
|
|
20221
20372
|
if (countActiveDrones(deps.db) >= cap) break;
|
|
20373
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
20374
|
+
if (capHolder) {
|
|
20375
|
+
await handOffAtCycleCap(deps, run2, capHolder, "\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u0441\u043B\u0435 \u0448\u0442\u043E\u0440\u043C\u0430");
|
|
20376
|
+
continue;
|
|
20377
|
+
}
|
|
20222
20378
|
if (!await projectQuotaGateAllows(deps, run2)) continue;
|
|
20223
20379
|
const wt = run2.worktree_path;
|
|
20224
20380
|
const mode = !!wt && worktreeExists(wt) && hasTranscript(run2) ? "continue" : "fresh";
|
|
@@ -20242,6 +20398,16 @@ async function wakeRun(deps, runId, opts = {}) {
|
|
|
20242
20398
|
}
|
|
20243
20399
|
if (run2.pid != null && deps.isAlive(run2.pid)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 (pid ${run2.pid}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
20244
20400
|
if (isTerminalStatus(run2.status)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435\u043D (${run2.status}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
20401
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
20402
|
+
if (capHolder) {
|
|
20403
|
+
await noteReviewCyclesExhausted(deps, capHolder);
|
|
20404
|
+
const limit = effectiveReviewCycleLimit();
|
|
20405
|
+
const whose = capHolder.id === run2.id ? `\u043F\u0440\u043E\u0433\u043E\u043D #${runId}` : `\u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D #${capHolder.id} (\u0434\u043B\u044F #${runId})`;
|
|
20406
|
+
return {
|
|
20407
|
+
ok: false,
|
|
20408
|
+
why: `${whose} \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043B \u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A (${capHolder.review_cycle ?? 0} \u0438\u0437 ${limit}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u043F\u043E\u0431\u0443\u0434\u043A\u0438 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u044B`
|
|
20409
|
+
};
|
|
20410
|
+
}
|
|
20245
20411
|
const stoppedForHuman = deps.db.events(run2.id).some((e) => isEscalationText(e.text ?? "", run2, "author"));
|
|
20246
20412
|
if (run2.status === "review" && run2.pr_url && !needsFixIteration(run2) && !stoppedForHuman) {
|
|
20247
20413
|
return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0441\u0434\u0430\u043D \u043D\u0430 \u0440\u0435\u0432\u044C\u044E (${run2.pr_url}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E; \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u043E\u0439 \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443` };
|
|
@@ -20255,8 +20421,11 @@ async function wakeRun(deps, runId, opts = {}) {
|
|
|
20255
20421
|
return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u2014 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 (\u0440\u0435\u0432\u044C\u044E\u0435\u0440/\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440), \u0430 \u0441\u0435\u0441\u0441\u0438\u044E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C: fresh \u043F\u043E\u0442\u0435\u0440\u044F\u043B \u0431\u044B \u0435\u0433\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442` };
|
|
20256
20422
|
}
|
|
20257
20423
|
const mode = canContinue ? "continue" : "fresh";
|
|
20424
|
+
const verifySummary = lastVerifySummary(deps.db, run2.id);
|
|
20425
|
+
const reviewSummary = lastChangesSummary(deps.db, run2.id);
|
|
20426
|
+
const nudge = mode === "continue" && !isSubrun(run2) && run2.kind !== "hero" && run2.pr_url && (needsFixIteration(run2) || verifySummary || reviewSummary) ? buildSubmittedContinueNudge({ prUrl: run2.pr_url, verifySummary, reviewSummary }) : void 0;
|
|
20258
20427
|
try {
|
|
20259
|
-
await deps.respawn(run2, mode);
|
|
20428
|
+
await deps.respawn(run2, mode, nudge);
|
|
20260
20429
|
} catch (e) {
|
|
20261
20430
|
return { ok: false, why: `\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${runId} \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C: ${String(e)}` };
|
|
20262
20431
|
}
|
|
@@ -20448,6 +20617,10 @@ function closeCiWatch(deps, run2, mark, why, narration) {
|
|
|
20448
20617
|
}
|
|
20449
20618
|
async function wakeAuthorOnRedCi(deps, run2, now, ci, worktreeExists, hasTranscript, droneCap) {
|
|
20450
20619
|
if (run2.pid != null && deps.isAlive(run2.pid)) return false;
|
|
20620
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20621
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20622
|
+
return false;
|
|
20623
|
+
}
|
|
20451
20624
|
if (deps.resourcesOk && !deps.resourcesOk()) return false;
|
|
20452
20625
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
20453
20626
|
if (!await projectQuotaGateAllows(deps, run2)) return false;
|
|
@@ -20517,6 +20690,15 @@ async function wakeReviewerOnCiReady(deps, run2, over) {
|
|
|
20517
20690
|
const reviewer = awaitingCiReviewer(deps.db, run2);
|
|
20518
20691
|
if (!reviewer) return false;
|
|
20519
20692
|
if (reviewer.pid != null && deps.isAlive(reviewer.pid)) return false;
|
|
20693
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20694
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20695
|
+
deps.db.addEvent(
|
|
20696
|
+
run2.id,
|
|
20697
|
+
"ci_checked",
|
|
20698
|
+
`${ciReadyLabel(over)} \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0431\u0443\u0436\u0443: \u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
|
|
20699
|
+
);
|
|
20700
|
+
return true;
|
|
20701
|
+
}
|
|
20520
20702
|
if (deps.resourcesOk && !deps.resourcesOk()) {
|
|
20521
20703
|
deps.db.addEvent(run2.id, "ci_checked", `${ciReadyLabel(over)} \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0436\u0434\u0451\u0442, \u043C\u0430\u0448\u0438\u043D\u0430 \u043F\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0435\u043D\u0430`);
|
|
20522
20704
|
return true;
|
|
@@ -20591,7 +20773,8 @@ async function spawnFreshReviewerAfterStall(deps, run2, why) {
|
|
|
20591
20773
|
deps.db.addEvent(run2.id, "step", `\u0441\u0432\u0435\u0436\u0438\u0439 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043D\u0435 \u043D\u0443\u0436\u0435\u043D: ${live2[0].name} \u0435\u0449\u0451 \u0436\u0438\u0432 (${live2[0].stop_reason ?? live2[0].status})`);
|
|
20592
20774
|
return;
|
|
20593
20775
|
}
|
|
20594
|
-
if (run2
|
|
20776
|
+
if (reviewCyclesExhausted(run2) || deps.db.reviewerAttemptCountFor(run2.id) >= REVIEW_SPAWN_LIMIT) {
|
|
20777
|
+
if (reviewCyclesExhausted(run2)) await noteReviewCyclesExhausted(deps, run2);
|
|
20595
20778
|
return;
|
|
20596
20779
|
}
|
|
20597
20780
|
const gate = await prGateForReviewer(deps, run2, run2.pr_url, "\u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B CI");
|
|
@@ -20615,7 +20798,10 @@ async function wakeAuthorOnChanges(deps, snapshot, now, droneCap) {
|
|
|
20615
20798
|
if (deps.resourcesOk && !deps.resourcesOk()) return false;
|
|
20616
20799
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
20617
20800
|
if (!await projectQuotaGateAllows(deps, run2)) return false;
|
|
20618
|
-
if (run2
|
|
20801
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20802
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20803
|
+
return false;
|
|
20804
|
+
}
|
|
20619
20805
|
const prUrl = run2.pr_url;
|
|
20620
20806
|
const events = deps.db.events(run2.id);
|
|
20621
20807
|
const mark = `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run2.review_cycle ?? 0})`;
|
|
@@ -21168,6 +21354,11 @@ async function resumeWatchdogParked(deps, droneCap) {
|
|
|
21168
21354
|
}
|
|
21169
21355
|
if (!candidate) return;
|
|
21170
21356
|
if (candidate.pid != null && deps.isAlive(candidate.pid)) return;
|
|
21357
|
+
const capHolder = cycleCapHolder(deps.db, candidate);
|
|
21358
|
+
if (capHolder) {
|
|
21359
|
+
await handOffAtCycleCap(deps, candidate, capHolder, "\u0432\u043E\u0437\u0432\u0440\u0430\u0442 \u0438\u0437 \u043F\u0430\u0440\u043A\u043E\u0432\u043A\u0438 \u0441\u0442\u043E\u0440\u043E\u0436\u0430");
|
|
21360
|
+
return;
|
|
21361
|
+
}
|
|
21171
21362
|
const worktreeExists = deps.worktreeExists ?? existsSync22;
|
|
21172
21363
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
21173
21364
|
const wt = candidate.worktree_path;
|
|
@@ -21227,6 +21418,11 @@ async function applyWatchdogAction(deps, action) {
|
|
|
21227
21418
|
}
|
|
21228
21419
|
case "fresh": {
|
|
21229
21420
|
if (!deps.respawn) return;
|
|
21421
|
+
const capHolder = cycleCapHolder(deps.db, action.run);
|
|
21422
|
+
if (capHolder) {
|
|
21423
|
+
await handOffAtCycleCap(deps, action.run, capHolder, "\u0441\u0432\u0435\u0436\u0438\u0439 \u043F\u043E\u0434\u044A\u0451\u043C \u0441\u0442\u043E\u0440\u043E\u0436\u0435\u043C \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0430");
|
|
21424
|
+
return;
|
|
21425
|
+
}
|
|
21230
21426
|
try {
|
|
21231
21427
|
await deps.respawn(action.run, "fresh", buildFreshRestartNudge(action.run));
|
|
21232
21428
|
deps.db.addEvent(action.run.id, "resumed", `\u0441\u0442\u043E\u0440\u043E\u0436 \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0430: ${action.reason}`);
|
|
@@ -21470,6 +21666,11 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
21470
21666
|
}
|
|
21471
21667
|
if (now - loopEntry.lastPauseAt < reliefBackoffMs(loopEntry.pauses, cfg.rampWindowMs, loopCfg)) return;
|
|
21472
21668
|
}
|
|
21669
|
+
const capHolder = cycleCapHolder(deps.db, resumeCandidate);
|
|
21670
|
+
if (capHolder) {
|
|
21671
|
+
await handOffAtCycleCap(deps, resumeCandidate, capHolder, "\u0432\u043E\u0437\u0432\u0440\u0430\u0442 \u0438\u0437 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043A\u0438");
|
|
21672
|
+
return;
|
|
21673
|
+
}
|
|
21473
21674
|
const wt = resumeCandidate.worktree_path;
|
|
21474
21675
|
const canContinue = !!wt && worktreeAndTranscriptAlive(deps, resumeCandidate);
|
|
21475
21676
|
const mode = canContinue ? "continue" : "fresh";
|
|
@@ -21890,6 +22091,8 @@ async function answerBudgetQuestions(deps, ctx) {
|
|
|
21890
22091
|
respawn: deps.respawn,
|
|
21891
22092
|
...deps.worktreeExists ? { worktreeExists: deps.worktreeExists } : {},
|
|
21892
22093
|
...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
|
|
22094
|
+
...deps.prComment ? { prComment: deps.prComment } : {},
|
|
22095
|
+
...deps.reportComment ? { reportComment: deps.reportComment } : {},
|
|
21893
22096
|
narrate: (m) => narrate(deps, m)
|
|
21894
22097
|
},
|
|
21895
22098
|
run2.id,
|
|
@@ -21899,6 +22102,11 @@ async function answerBudgetQuestions(deps, ctx) {
|
|
|
21899
22102
|
budgetSyncAt.delete(run2.id);
|
|
21900
22103
|
continue;
|
|
21901
22104
|
}
|
|
22105
|
+
if (reviewCyclesExhausted(run2) && run2.pr_url) {
|
|
22106
|
+
deps.db.updateRun(run2.id, { status: "review", stop_reason: null });
|
|
22107
|
+
budgetSyncAt.delete(run2.id);
|
|
22108
|
+
continue;
|
|
22109
|
+
}
|
|
21902
22110
|
const note = `\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u043F\u043E \u043E\u0442\u0432\u0435\u0442\u0443 \xAB\u0434\u0430\xBB \u043D\u0435 \u0432\u044B\u0448\u043B\u0430: ${woken.why} \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u044E`;
|
|
21903
22111
|
if (deps.db.events(run2.id).at(-1)?.text !== note) deps.db.addEvent(run2.id, "step", note);
|
|
21904
22112
|
}
|
|
@@ -22284,11 +22492,8 @@ async function tickOnce(deps) {
|
|
|
22284
22492
|
// рано, спавнить второго нельзя.
|
|
22285
22493
|
liveOrWaitingReviewers(deps.db, run2.id).length === 0;
|
|
22286
22494
|
if (reviewerGatesOk && deps.spawnReviewer && await projectWantsChecks(deps, run2)) {
|
|
22287
|
-
if (run2
|
|
22288
|
-
|
|
22289
|
-
deps.db.addEvent(run2.id, "step", `${REVIEW_CYCLE_ESCALATION_MARK}: ${run2.review_cycle} \u0446\u0438\u043A\u043B(\u043E\u0432) \u0440\u0435\u0432\u044C\u044E-\u0444\u0438\u043A\u0441\u043E\u0432 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`);
|
|
22290
|
-
narrate(deps, `\u0420\u0435-\u0440\u0435\u0432\u044C\u044E ${droneLabel(run2)} \u0437\u0430\u0446\u0438\u043A\u043B\u0438\u043B\u043E\u0441\u044C (${run2.review_cycle} \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A.`);
|
|
22291
|
-
}
|
|
22495
|
+
if (reviewCyclesExhausted(run2)) {
|
|
22496
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
22292
22497
|
continue;
|
|
22293
22498
|
}
|
|
22294
22499
|
const attempts = deps.db.reviewerAttemptCountFor(run2.id);
|
|
@@ -22444,6 +22649,14 @@ async function tickOnce(deps) {
|
|
|
22444
22649
|
continue;
|
|
22445
22650
|
}
|
|
22446
22651
|
}
|
|
22652
|
+
if (!alive && run2.status === "running" && !isSubrun(run2) && run2.kind !== "hero") {
|
|
22653
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
22654
|
+
if (capHolder) {
|
|
22655
|
+
await handOffAtCycleCap(deps, run2, capHolder, `\u0441\u0435\u0441\u0441\u0438\u044F \u0430\u0432\u0442\u043E\u0440\u0430 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0430\u0441\u044C (${outcome})`);
|
|
22656
|
+
narrate(deps, `${droneLabel(run2)} \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u2014 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E, PR \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443.`);
|
|
22657
|
+
continue;
|
|
22658
|
+
}
|
|
22659
|
+
}
|
|
22447
22660
|
if (!alive && (outcome === "graceful" || outcome === "aborted") && run2.status === "running" && !isSubrun(run2) && // суб-дроны (резолвер/ревьюер/верификатор) — своими ветками ниже, TASWM-528
|
|
22448
22661
|
run2.kind !== "hero") {
|
|
22449
22662
|
if (!wasAborted(outcome) && run2.pr_url) {
|
|
@@ -22549,6 +22762,11 @@ async function tickOnce(deps) {
|
|
|
22549
22762
|
}
|
|
22550
22763
|
}
|
|
22551
22764
|
if (!alive && outcome === "graceful" && run2.status === "running" && isSubrun(run2)) {
|
|
22765
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
22766
|
+
if (capHolder) {
|
|
22767
|
+
await handOffAtCycleCap(deps, run2, capHolder, "\u0441\u0443\u0431-\u0434\u0440\u043E\u043D \u0432\u044B\u0448\u0435\u043B \u0441\u0430\u043C, \u043D\u0435 \u0432\u044B\u043D\u0435\u0441\u044F \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430");
|
|
22768
|
+
continue;
|
|
22769
|
+
}
|
|
22552
22770
|
const wt = run2.worktree_path;
|
|
22553
22771
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run2);
|
|
22554
22772
|
if (deps.respawn && canContinue && run2.respawns < RESPAWN_LIMIT) {
|
|
@@ -22624,6 +22842,11 @@ async function tickOnce(deps) {
|
|
|
22624
22842
|
if (isReliefPause(run2.stop_reason)) continue;
|
|
22625
22843
|
if (NO_AUTO_WAKE_REASONS.some((r) => run2.stop_reason?.startsWith(r))) continue;
|
|
22626
22844
|
if (run2.pid != null && deps.isAlive(run2.pid)) continue;
|
|
22845
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
22846
|
+
if (capHolder) {
|
|
22847
|
+
await handOffAtCycleCap(deps, run2, capHolder, "\u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0435\u043D\u0438\u0435 \u0437\u0430\u043C\u0435\u0440\u0448\u0435\u0433\u043E");
|
|
22848
|
+
continue;
|
|
22849
|
+
}
|
|
22627
22850
|
if (!await projectQuotaGateAllows(deps, run2)) continue;
|
|
22628
22851
|
const wt = run2.worktree_path;
|
|
22629
22852
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run2);
|
|
@@ -23386,9 +23609,8 @@ async function runDaemon(opts) {
|
|
|
23386
23609
|
recordModelRef: (runId, ref, run2) => {
|
|
23387
23610
|
if (runModelRefPin.size > 4096) runModelRefPin.clear();
|
|
23388
23611
|
runModelRefPin.set(runId, ref);
|
|
23389
|
-
|
|
23390
|
-
|
|
23391
|
-
}
|
|
23612
|
+
const model = modelRefToRecord(run2, ref);
|
|
23613
|
+
if (model) db.updateRun(runId, { model });
|
|
23392
23614
|
},
|
|
23393
23615
|
maxCostUsd: () => lastMaxCostUsd,
|
|
23394
23616
|
// TASWM-535: поднятый потолок тяжёлого прогона живёт в его ленте — спавн читает её
|
|
@@ -23574,6 +23796,7 @@ async function runDaemon(opts) {
|
|
|
23574
23796
|
lastSwarmModels = settings.models;
|
|
23575
23797
|
lastSwarmEfforts = settings.efforts;
|
|
23576
23798
|
lastMaxCostUsd = settings.maxCostUsd;
|
|
23799
|
+
configureReviewCycleLimit(settings.reviewCycleLimit);
|
|
23577
23800
|
}
|
|
23578
23801
|
return settings?.maxDrones ?? null;
|
|
23579
23802
|
},
|
|
@@ -24307,7 +24530,18 @@ async function runDaemon(opts) {
|
|
|
24307
24530
|
wakeRun: async (slug) => {
|
|
24308
24531
|
const run2 = db.activeRunForSlug(slug);
|
|
24309
24532
|
if (!run2) return null;
|
|
24310
|
-
return wakeRun(
|
|
24533
|
+
return wakeRun(
|
|
24534
|
+
{
|
|
24535
|
+
db,
|
|
24536
|
+
isAlive: pidAlive,
|
|
24537
|
+
respawn: deps.respawn,
|
|
24538
|
+
narrate: narrator.narrate,
|
|
24539
|
+
...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
|
|
24540
|
+
...deps.prComment ? { prComment: deps.prComment } : {},
|
|
24541
|
+
...deps.reportComment ? { reportComment: deps.reportComment } : {}
|
|
24542
|
+
},
|
|
24543
|
+
run2.id
|
|
24544
|
+
);
|
|
24311
24545
|
},
|
|
24312
24546
|
update: async (onStep) => {
|
|
24313
24547
|
const pm = detectGlobalPm(process.argv[1]);
|
|
@@ -24457,7 +24691,18 @@ async function runDaemon(opts) {
|
|
|
24457
24691
|
// Побудка прогона по id (TASWM-240): вход для `swarm wake <id>` — тот же respawn с
|
|
24458
24692
|
// полными deps демона (getTask/verifyMode/реестр репо), что и авто-оживление, и по
|
|
24459
24693
|
// зеркалу демона, а не по короткоживущему стору CLI.
|
|
24460
|
-
wake: (runId) => wakeRun(
|
|
24694
|
+
wake: (runId) => wakeRun(
|
|
24695
|
+
{
|
|
24696
|
+
db,
|
|
24697
|
+
isAlive: pidAlive,
|
|
24698
|
+
respawn: deps.respawn,
|
|
24699
|
+
narrate: narrator.narrate,
|
|
24700
|
+
...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
|
|
24701
|
+
...deps.prComment ? { prComment: deps.prComment } : {},
|
|
24702
|
+
...deps.reportComment ? { reportComment: deps.reportComment } : {}
|
|
24703
|
+
},
|
|
24704
|
+
runId
|
|
24705
|
+
),
|
|
24461
24706
|
replay: (qid, chosen) => replayDecision(
|
|
24462
24707
|
{
|
|
24463
24708
|
db,
|
|
@@ -24592,6 +24837,7 @@ export {
|
|
|
24592
24837
|
CI_WAKE_LIMIT,
|
|
24593
24838
|
CONFLICT_RESOLVE_COOLDOWN_MS,
|
|
24594
24839
|
CONTINUE_NUDGE,
|
|
24840
|
+
CYCLE_CAP_STOP_REASON,
|
|
24595
24841
|
DEAD_HOLDER_SILENCE_MS,
|
|
24596
24842
|
EXITED_CONTINUE_NUDGE,
|
|
24597
24843
|
EXITED_NO_DONE_REASON,
|
|
@@ -24637,12 +24883,17 @@ export {
|
|
|
24637
24883
|
buildResolverPrompt,
|
|
24638
24884
|
buildReviewChecklist,
|
|
24639
24885
|
buildReviewerPrompt,
|
|
24886
|
+
buildSubmittedContinueNudge,
|
|
24640
24887
|
buildVerifierPrompt,
|
|
24641
24888
|
cancelRun,
|
|
24642
24889
|
capacityProvenance,
|
|
24890
|
+
configureReviewCycleLimit,
|
|
24891
|
+
cycleCapHolder,
|
|
24892
|
+
effectiveReviewCycleLimit,
|
|
24643
24893
|
hasLiveDescendant,
|
|
24644
24894
|
isSubrun,
|
|
24645
24895
|
lastChangesSummary,
|
|
24896
|
+
lastVerifySummary,
|
|
24646
24897
|
listDroneProcesses,
|
|
24647
24898
|
livePpids,
|
|
24648
24899
|
makeCheckerAttachmentsReader,
|
|
@@ -24651,6 +24902,8 @@ export {
|
|
|
24651
24902
|
resolveConflict,
|
|
24652
24903
|
respawnRun,
|
|
24653
24904
|
reviewBudgetMark,
|
|
24905
|
+
reviewCycleHumanBody,
|
|
24906
|
+
reviewCyclesExhausted,
|
|
24654
24907
|
reviewDefectClasses,
|
|
24655
24908
|
reviewUnrecoverableMark,
|
|
24656
24909
|
reviveStuck,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.67",
|
|
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": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"execa": "^10.0.0",
|
|
40
40
|
"zod": "3.25.76"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "92a3176867bc1668677a4b6a25e02e0cb6661ce0",
|
|
43
43
|
"scripts": {
|
|
44
44
|
"overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
|
|
45
45
|
"swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",
|