@taskless-app/swarm 0.6.66 → 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 +11 -6
- package/dist/overmind.js +253 -23
- 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
|
|
@@ -1789,7 +1790,7 @@ var init_effort = __esm({
|
|
|
1789
1790
|
});
|
|
1790
1791
|
|
|
1791
1792
|
// ../swarm-core/src/queuePlan.ts
|
|
1792
|
-
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;
|
|
1793
1794
|
var init_queuePlan = __esm({
|
|
1794
1795
|
"../swarm-core/src/queuePlan.ts"() {
|
|
1795
1796
|
"use strict";
|
|
@@ -1802,6 +1803,9 @@ var init_queuePlan = __esm({
|
|
|
1802
1803
|
MIN_MS = 6e4;
|
|
1803
1804
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
1804
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;
|
|
1805
1809
|
}
|
|
1806
1810
|
});
|
|
1807
1811
|
|
|
@@ -3876,7 +3880,7 @@ var init_package = __esm({
|
|
|
3876
3880
|
"package.json"() {
|
|
3877
3881
|
package_default = {
|
|
3878
3882
|
name: "@taskless-app/swarm",
|
|
3879
|
-
version: "0.6.
|
|
3883
|
+
version: "0.6.67",
|
|
3880
3884
|
type: "module",
|
|
3881
3885
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3882
3886
|
keywords: [
|
|
@@ -3927,7 +3931,7 @@ var init_package = __esm({
|
|
|
3927
3931
|
execa: "^10.0.0",
|
|
3928
3932
|
zod: "3.25.76"
|
|
3929
3933
|
},
|
|
3930
|
-
gitHead: "
|
|
3934
|
+
gitHead: "92a3176867bc1668677a4b6a25e02e0cb6661ce0"
|
|
3931
3935
|
};
|
|
3932
3936
|
}
|
|
3933
3937
|
});
|
|
@@ -14854,10 +14858,11 @@ switch (cmd) {
|
|
|
14854
14858
|
// TASWM-592 (ревью): сессию ищет харнесс ПРОГОНА, а не глобальный исполнитель —
|
|
14855
14859
|
// иначе побудка codex-прогона при мёртвом демоне теряла продолжаемую сессию.
|
|
14856
14860
|
hasTranscript: (run2) => !!run2.worktree_path && executorForRun(run2, null).session.exists(run2.worktree_path),
|
|
14857
|
-
respawn: (r, mode) => respawnRun(
|
|
14861
|
+
respawn: (r, mode, nudge) => respawnRun(
|
|
14858
14862
|
{ db, launch, repoFallback: SWARM_REPO, worktreesDir: effectiveWorktreesDir(), getTask: () => Promise.resolve(null) },
|
|
14859
14863
|
r,
|
|
14860
|
-
mode
|
|
14864
|
+
mode,
|
|
14865
|
+
nudge
|
|
14861
14866
|
)
|
|
14862
14867
|
},
|
|
14863
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
|
|
@@ -2020,7 +2024,7 @@ var init_effort = __esm({
|
|
|
2020
2024
|
});
|
|
2021
2025
|
|
|
2022
2026
|
// ../swarm-core/src/queuePlan.ts
|
|
2023
|
-
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;
|
|
2024
2028
|
var init_queuePlan = __esm({
|
|
2025
2029
|
"../swarm-core/src/queuePlan.ts"() {
|
|
2026
2030
|
"use strict";
|
|
@@ -2033,6 +2037,9 @@ var init_queuePlan = __esm({
|
|
|
2033
2037
|
MIN_MS = 6e4;
|
|
2034
2038
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
2035
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;
|
|
2036
2043
|
}
|
|
2037
2044
|
});
|
|
2038
2045
|
|
|
@@ -13067,7 +13074,7 @@ function parkReasonUnretriable(reason) {
|
|
|
13067
13074
|
// package.json
|
|
13068
13075
|
var package_default = {
|
|
13069
13076
|
name: "@taskless-app/swarm",
|
|
13070
|
-
version: "0.6.
|
|
13077
|
+
version: "0.6.67",
|
|
13071
13078
|
type: "module",
|
|
13072
13079
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
13073
13080
|
keywords: [
|
|
@@ -13118,7 +13125,7 @@ var package_default = {
|
|
|
13118
13125
|
execa: "^10.0.0",
|
|
13119
13126
|
zod: "3.25.76"
|
|
13120
13127
|
},
|
|
13121
|
-
gitHead: "
|
|
13128
|
+
gitHead: "92a3176867bc1668677a4b6a25e02e0cb6661ce0"
|
|
13122
13129
|
};
|
|
13123
13130
|
|
|
13124
13131
|
// src/version.ts
|
|
@@ -14127,12 +14134,36 @@ init_src();
|
|
|
14127
14134
|
|
|
14128
14135
|
// src/reviewCycle.ts
|
|
14129
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
|
+
}
|
|
14130
14158
|
function needsFixIteration(run2) {
|
|
14131
14159
|
return run2.review_verdict === "changes" || isNegativeVerifyVerdict(toVerifyVerdict(run2.verify_verdict));
|
|
14132
14160
|
}
|
|
14133
14161
|
function reviewTransitionPatch(run2, prUrl) {
|
|
14134
14162
|
const isFixIteration = needsFixIteration(run2);
|
|
14135
14163
|
if (isFixIteration) {
|
|
14164
|
+
if (reviewCyclesExhausted(run2)) {
|
|
14165
|
+
return { isFixIteration: false, cycle: null, patch: { status: "review", pr_url: prUrl } };
|
|
14166
|
+
}
|
|
14136
14167
|
const cycle = (run2.review_cycle ?? 0) + 1;
|
|
14137
14168
|
return {
|
|
14138
14169
|
isFixIteration: true,
|
|
@@ -19133,14 +19164,86 @@ var BASE_MERGED_MARK = "\u0431\u0430\u0437\u0430 \u0432\u043B\u0438\u0442\u0430
|
|
|
19133
19164
|
var BASE_NOOP_MARK = "\u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E";
|
|
19134
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";
|
|
19135
19166
|
var REVIEW_SPAWN_LIMIT = Number(process.env.SWARM_REVIEW_SPAWN_LIMIT) || 3;
|
|
19136
|
-
|
|
19137
|
-
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
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;
|
|
19141
19182
|
}
|
|
19142
|
-
function
|
|
19143
|
-
|
|
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);
|
|
19144
19247
|
}
|
|
19145
19248
|
var CI_WAKE_LIMIT = Number(process.env.SWARM_CI_WAKE_LIMIT) || 3;
|
|
19146
19249
|
var CI_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CI_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
@@ -19168,7 +19271,7 @@ var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430
|
|
|
19168
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";
|
|
19169
19272
|
var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
19170
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";
|
|
19171
|
-
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];
|
|
19172
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)";
|
|
19173
19276
|
function verdictCommentMark(createdAt) {
|
|
19174
19277
|
return `\u043A\u043E\u043C\u043C\u0435\u043D\u0442-\u0432\u0435\u0440\u0434\u0438\u043A\u0442@${createdAt}`;
|
|
@@ -19331,7 +19434,9 @@ function reviewPhaseInputFor(deps, run2, projectWantsVerify = true) {
|
|
|
19331
19434
|
reviewerDead,
|
|
19332
19435
|
verifyVerdict: run2.verify_verdict,
|
|
19333
19436
|
verifyExpected,
|
|
19334
|
-
verifierDead
|
|
19437
|
+
verifierDead,
|
|
19438
|
+
// TASWM-606: потолок кругов важнее «чинит CI» и «ждёт ревьюера» — человека уже позвали.
|
|
19439
|
+
cyclesExhausted: reviewCyclesExhausted(run2)
|
|
19335
19440
|
};
|
|
19336
19441
|
}
|
|
19337
19442
|
async function refreshReviewPhases(deps) {
|
|
@@ -19715,6 +19820,10 @@ async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
|
19715
19820
|
if (run2.kind !== "drone") continue;
|
|
19716
19821
|
if (run2.reviews_run_id != null || run2.resolves_run_id != null || run2.verifies_run_id != null) continue;
|
|
19717
19822
|
if (!run2.pr_url || !isGithubPrUrl(run2.pr_url)) continue;
|
|
19823
|
+
if (reviewCyclesExhausted(run2)) {
|
|
19824
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
19825
|
+
continue;
|
|
19826
|
+
}
|
|
19718
19827
|
if (run2.verify_verdict != null) continue;
|
|
19719
19828
|
if (deps.db.verifierCountFor(run2.id) > 0) continue;
|
|
19720
19829
|
const attempts = deps.db.verifierAttemptCountFor(run2.id);
|
|
@@ -19740,6 +19849,18 @@ async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
|
19740
19849
|
}
|
|
19741
19850
|
}
|
|
19742
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
|
+
}
|
|
19743
19864
|
function buildCiWakeNudge(summary) {
|
|
19744
19865
|
return [
|
|
19745
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):",
|
|
@@ -20061,6 +20182,15 @@ function lastVerdictEvent(db, reviewRunId) {
|
|
|
20061
20182
|
function lastChangesSummary(db, reviewRunId) {
|
|
20062
20183
|
return lastVerdictEvent(db, reviewRunId)?.text ?? null;
|
|
20063
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
|
+
}
|
|
20064
20194
|
function changesVerdictAt(db, reviewRunId) {
|
|
20065
20195
|
return lastVerdictEvent(db, reviewRunId)?.at ?? null;
|
|
20066
20196
|
}
|
|
@@ -20240,6 +20370,11 @@ async function resurrectStormDead(deps, worktreeExists, hasTranscript, opts) {
|
|
|
20240
20370
|
let resurrected = 0;
|
|
20241
20371
|
for (const run2 of deps.db.listRuns("storm_dead")) {
|
|
20242
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
|
+
}
|
|
20243
20378
|
if (!await projectQuotaGateAllows(deps, run2)) continue;
|
|
20244
20379
|
const wt = run2.worktree_path;
|
|
20245
20380
|
const mode = !!wt && worktreeExists(wt) && hasTranscript(run2) ? "continue" : "fresh";
|
|
@@ -20263,6 +20398,16 @@ async function wakeRun(deps, runId, opts = {}) {
|
|
|
20263
20398
|
}
|
|
20264
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` };
|
|
20265
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
|
+
}
|
|
20266
20411
|
const stoppedForHuman = deps.db.events(run2.id).some((e) => isEscalationText(e.text ?? "", run2, "author"));
|
|
20267
20412
|
if (run2.status === "review" && run2.pr_url && !needsFixIteration(run2) && !stoppedForHuman) {
|
|
20268
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` };
|
|
@@ -20276,8 +20421,11 @@ async function wakeRun(deps, runId, opts = {}) {
|
|
|
20276
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` };
|
|
20277
20422
|
}
|
|
20278
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;
|
|
20279
20427
|
try {
|
|
20280
|
-
await deps.respawn(run2, mode);
|
|
20428
|
+
await deps.respawn(run2, mode, nudge);
|
|
20281
20429
|
} catch (e) {
|
|
20282
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)}` };
|
|
20283
20431
|
}
|
|
@@ -20469,6 +20617,10 @@ function closeCiWatch(deps, run2, mark, why, narration) {
|
|
|
20469
20617
|
}
|
|
20470
20618
|
async function wakeAuthorOnRedCi(deps, run2, now, ci, worktreeExists, hasTranscript, droneCap) {
|
|
20471
20619
|
if (run2.pid != null && deps.isAlive(run2.pid)) return false;
|
|
20620
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20621
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20622
|
+
return false;
|
|
20623
|
+
}
|
|
20472
20624
|
if (deps.resourcesOk && !deps.resourcesOk()) return false;
|
|
20473
20625
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
20474
20626
|
if (!await projectQuotaGateAllows(deps, run2)) return false;
|
|
@@ -20538,6 +20690,15 @@ async function wakeReviewerOnCiReady(deps, run2, over) {
|
|
|
20538
20690
|
const reviewer = awaitingCiReviewer(deps.db, run2);
|
|
20539
20691
|
if (!reviewer) return false;
|
|
20540
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
|
+
}
|
|
20541
20702
|
if (deps.resourcesOk && !deps.resourcesOk()) {
|
|
20542
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`);
|
|
20543
20704
|
return true;
|
|
@@ -20612,7 +20773,8 @@ async function spawnFreshReviewerAfterStall(deps, run2, why) {
|
|
|
20612
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})`);
|
|
20613
20774
|
return;
|
|
20614
20775
|
}
|
|
20615
|
-
if (run2
|
|
20776
|
+
if (reviewCyclesExhausted(run2) || deps.db.reviewerAttemptCountFor(run2.id) >= REVIEW_SPAWN_LIMIT) {
|
|
20777
|
+
if (reviewCyclesExhausted(run2)) await noteReviewCyclesExhausted(deps, run2);
|
|
20616
20778
|
return;
|
|
20617
20779
|
}
|
|
20618
20780
|
const gate = await prGateForReviewer(deps, run2, run2.pr_url, "\u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B CI");
|
|
@@ -20636,7 +20798,10 @@ async function wakeAuthorOnChanges(deps, snapshot, now, droneCap) {
|
|
|
20636
20798
|
if (deps.resourcesOk && !deps.resourcesOk()) return false;
|
|
20637
20799
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
20638
20800
|
if (!await projectQuotaGateAllows(deps, run2)) return false;
|
|
20639
|
-
if (run2
|
|
20801
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20802
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20803
|
+
return false;
|
|
20804
|
+
}
|
|
20640
20805
|
const prUrl = run2.pr_url;
|
|
20641
20806
|
const events = deps.db.events(run2.id);
|
|
20642
20807
|
const mark = `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run2.review_cycle ?? 0})`;
|
|
@@ -21189,6 +21354,11 @@ async function resumeWatchdogParked(deps, droneCap) {
|
|
|
21189
21354
|
}
|
|
21190
21355
|
if (!candidate) return;
|
|
21191
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
|
+
}
|
|
21192
21362
|
const worktreeExists = deps.worktreeExists ?? existsSync22;
|
|
21193
21363
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
21194
21364
|
const wt = candidate.worktree_path;
|
|
@@ -21248,6 +21418,11 @@ async function applyWatchdogAction(deps, action) {
|
|
|
21248
21418
|
}
|
|
21249
21419
|
case "fresh": {
|
|
21250
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
|
+
}
|
|
21251
21426
|
try {
|
|
21252
21427
|
await deps.respawn(action.run, "fresh", buildFreshRestartNudge(action.run));
|
|
21253
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}`);
|
|
@@ -21491,6 +21666,11 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
21491
21666
|
}
|
|
21492
21667
|
if (now - loopEntry.lastPauseAt < reliefBackoffMs(loopEntry.pauses, cfg.rampWindowMs, loopCfg)) return;
|
|
21493
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
|
+
}
|
|
21494
21674
|
const wt = resumeCandidate.worktree_path;
|
|
21495
21675
|
const canContinue = !!wt && worktreeAndTranscriptAlive(deps, resumeCandidate);
|
|
21496
21676
|
const mode = canContinue ? "continue" : "fresh";
|
|
@@ -21911,6 +22091,8 @@ async function answerBudgetQuestions(deps, ctx) {
|
|
|
21911
22091
|
respawn: deps.respawn,
|
|
21912
22092
|
...deps.worktreeExists ? { worktreeExists: deps.worktreeExists } : {},
|
|
21913
22093
|
...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
|
|
22094
|
+
...deps.prComment ? { prComment: deps.prComment } : {},
|
|
22095
|
+
...deps.reportComment ? { reportComment: deps.reportComment } : {},
|
|
21914
22096
|
narrate: (m) => narrate(deps, m)
|
|
21915
22097
|
},
|
|
21916
22098
|
run2.id,
|
|
@@ -21920,6 +22102,11 @@ async function answerBudgetQuestions(deps, ctx) {
|
|
|
21920
22102
|
budgetSyncAt.delete(run2.id);
|
|
21921
22103
|
continue;
|
|
21922
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
|
+
}
|
|
21923
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`;
|
|
21924
22111
|
if (deps.db.events(run2.id).at(-1)?.text !== note) deps.db.addEvent(run2.id, "step", note);
|
|
21925
22112
|
}
|
|
@@ -22305,11 +22492,8 @@ async function tickOnce(deps) {
|
|
|
22305
22492
|
// рано, спавнить второго нельзя.
|
|
22306
22493
|
liveOrWaitingReviewers(deps.db, run2.id).length === 0;
|
|
22307
22494
|
if (reviewerGatesOk && deps.spawnReviewer && await projectWantsChecks(deps, run2)) {
|
|
22308
|
-
if (run2
|
|
22309
|
-
|
|
22310
|
-
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`);
|
|
22311
|
-
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.`);
|
|
22312
|
-
}
|
|
22495
|
+
if (reviewCyclesExhausted(run2)) {
|
|
22496
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
22313
22497
|
continue;
|
|
22314
22498
|
}
|
|
22315
22499
|
const attempts = deps.db.reviewerAttemptCountFor(run2.id);
|
|
@@ -22465,6 +22649,14 @@ async function tickOnce(deps) {
|
|
|
22465
22649
|
continue;
|
|
22466
22650
|
}
|
|
22467
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
|
+
}
|
|
22468
22660
|
if (!alive && (outcome === "graceful" || outcome === "aborted") && run2.status === "running" && !isSubrun(run2) && // суб-дроны (резолвер/ревьюер/верификатор) — своими ветками ниже, TASWM-528
|
|
22469
22661
|
run2.kind !== "hero") {
|
|
22470
22662
|
if (!wasAborted(outcome) && run2.pr_url) {
|
|
@@ -22570,6 +22762,11 @@ async function tickOnce(deps) {
|
|
|
22570
22762
|
}
|
|
22571
22763
|
}
|
|
22572
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
|
+
}
|
|
22573
22770
|
const wt = run2.worktree_path;
|
|
22574
22771
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run2);
|
|
22575
22772
|
if (deps.respawn && canContinue && run2.respawns < RESPAWN_LIMIT) {
|
|
@@ -22645,6 +22842,11 @@ async function tickOnce(deps) {
|
|
|
22645
22842
|
if (isReliefPause(run2.stop_reason)) continue;
|
|
22646
22843
|
if (NO_AUTO_WAKE_REASONS.some((r) => run2.stop_reason?.startsWith(r))) continue;
|
|
22647
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
|
+
}
|
|
22648
22850
|
if (!await projectQuotaGateAllows(deps, run2)) continue;
|
|
22649
22851
|
const wt = run2.worktree_path;
|
|
22650
22852
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run2);
|
|
@@ -24328,7 +24530,18 @@ async function runDaemon(opts) {
|
|
|
24328
24530
|
wakeRun: async (slug) => {
|
|
24329
24531
|
const run2 = db.activeRunForSlug(slug);
|
|
24330
24532
|
if (!run2) return null;
|
|
24331
|
-
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
|
+
);
|
|
24332
24545
|
},
|
|
24333
24546
|
update: async (onStep) => {
|
|
24334
24547
|
const pm = detectGlobalPm(process.argv[1]);
|
|
@@ -24478,7 +24691,18 @@ async function runDaemon(opts) {
|
|
|
24478
24691
|
// Побудка прогона по id (TASWM-240): вход для `swarm wake <id>` — тот же respawn с
|
|
24479
24692
|
// полными deps демона (getTask/verifyMode/реестр репо), что и авто-оживление, и по
|
|
24480
24693
|
// зеркалу демона, а не по короткоживущему стору CLI.
|
|
24481
|
-
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
|
+
),
|
|
24482
24706
|
replay: (qid, chosen) => replayDecision(
|
|
24483
24707
|
{
|
|
24484
24708
|
db,
|
|
@@ -24613,6 +24837,7 @@ export {
|
|
|
24613
24837
|
CI_WAKE_LIMIT,
|
|
24614
24838
|
CONFLICT_RESOLVE_COOLDOWN_MS,
|
|
24615
24839
|
CONTINUE_NUDGE,
|
|
24840
|
+
CYCLE_CAP_STOP_REASON,
|
|
24616
24841
|
DEAD_HOLDER_SILENCE_MS,
|
|
24617
24842
|
EXITED_CONTINUE_NUDGE,
|
|
24618
24843
|
EXITED_NO_DONE_REASON,
|
|
@@ -24658,14 +24883,17 @@ export {
|
|
|
24658
24883
|
buildResolverPrompt,
|
|
24659
24884
|
buildReviewChecklist,
|
|
24660
24885
|
buildReviewerPrompt,
|
|
24886
|
+
buildSubmittedContinueNudge,
|
|
24661
24887
|
buildVerifierPrompt,
|
|
24662
24888
|
cancelRun,
|
|
24663
24889
|
capacityProvenance,
|
|
24664
24890
|
configureReviewCycleLimit,
|
|
24891
|
+
cycleCapHolder,
|
|
24665
24892
|
effectiveReviewCycleLimit,
|
|
24666
24893
|
hasLiveDescendant,
|
|
24667
24894
|
isSubrun,
|
|
24668
24895
|
lastChangesSummary,
|
|
24896
|
+
lastVerifySummary,
|
|
24669
24897
|
listDroneProcesses,
|
|
24670
24898
|
livePpids,
|
|
24671
24899
|
makeCheckerAttachmentsReader,
|
|
@@ -24674,6 +24902,8 @@ export {
|
|
|
24674
24902
|
resolveConflict,
|
|
24675
24903
|
respawnRun,
|
|
24676
24904
|
reviewBudgetMark,
|
|
24905
|
+
reviewCycleHumanBody,
|
|
24906
|
+
reviewCyclesExhausted,
|
|
24677
24907
|
reviewDefectClasses,
|
|
24678
24908
|
reviewUnrecoverableMark,
|
|
24679
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",
|