@taskless-app/swarm 0.6.10 → 0.6.12
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 +1220 -356
- package/dist/overmind.js +1128 -301
- package/package.json +2 -2
package/dist/overmind.js
CHANGED
|
@@ -13,7 +13,7 @@ function isMilestoneKind(kind) {
|
|
|
13
13
|
return MILESTONE_KIND_SET.has(kind);
|
|
14
14
|
}
|
|
15
15
|
function toRunKind(role) {
|
|
16
|
-
return role === "hero" || role === "reviewer" ? role : "drone";
|
|
16
|
+
return role === "hero" || role === "reviewer" || role === "verifier" ? role : "drone";
|
|
17
17
|
}
|
|
18
18
|
var ACTIVE_DRONE_STATUSES, EVENT_KINDS, MILESTONE_KINDS, MILESTONE_KIND_SET;
|
|
19
19
|
var init_types = __esm({
|
|
@@ -335,6 +335,53 @@ var init_reviewPhase = __esm({
|
|
|
335
335
|
}
|
|
336
336
|
});
|
|
337
337
|
|
|
338
|
+
// ../swarm-core/src/verifyVerdict.ts
|
|
339
|
+
function isVerifyVerdict(v) {
|
|
340
|
+
return typeof v === "string" && VERIFY_VERDICT_SET.has(v);
|
|
341
|
+
}
|
|
342
|
+
function toVerifyVerdict(v) {
|
|
343
|
+
return isVerifyVerdict(v) ? v : null;
|
|
344
|
+
}
|
|
345
|
+
function isNegativeVerifyVerdict(v) {
|
|
346
|
+
return v === "not_reproduced" || v === "found_issues";
|
|
347
|
+
}
|
|
348
|
+
function verifyVerdictLabel(v) {
|
|
349
|
+
switch (v) {
|
|
350
|
+
case "confirmed":
|
|
351
|
+
return "\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E";
|
|
352
|
+
case "not_reproduced":
|
|
353
|
+
return "\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F";
|
|
354
|
+
case "found_issues":
|
|
355
|
+
return "\u041D\u0410\u0428\u0401\u041B";
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function parseVerifyVerdictComment(text) {
|
|
359
|
+
const line = (text ?? "").split("\n").find((l) => l.trim().length > 0);
|
|
360
|
+
if (!line) return null;
|
|
361
|
+
let best = null;
|
|
362
|
+
for (const [verdict, marks] of VERDICT_MARKS) {
|
|
363
|
+
for (const mark of marks) {
|
|
364
|
+
const at = line.indexOf(mark);
|
|
365
|
+
if (at < 0) continue;
|
|
366
|
+
if (!best || at < best.at) best = { verdict, at };
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return best?.verdict ?? null;
|
|
370
|
+
}
|
|
371
|
+
var VERIFY_VERDICTS, VERIFY_VERDICT_SET, VERDICT_MARKS;
|
|
372
|
+
var init_verifyVerdict = __esm({
|
|
373
|
+
"../swarm-core/src/verifyVerdict.ts"() {
|
|
374
|
+
"use strict";
|
|
375
|
+
VERIFY_VERDICTS = ["confirmed", "not_reproduced", "found_issues"];
|
|
376
|
+
VERIFY_VERDICT_SET = new Set(VERIFY_VERDICTS);
|
|
377
|
+
VERDICT_MARKS = [
|
|
378
|
+
["not_reproduced", ["\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F", "NOT REPRODUCED", "\u041D\u0415 \u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E", "NOT CONFIRMED"]],
|
|
379
|
+
["found_issues", ["\u041D\u0410\u0428\u0401\u041B", "\u041D\u0410\u0428\u0415\u041B", "FOUND"]],
|
|
380
|
+
["confirmed", ["\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E", "CONFIRMED"]]
|
|
381
|
+
];
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
338
385
|
// ../swarm-core/src/mergeGate.ts
|
|
339
386
|
function computeMergeGate(input) {
|
|
340
387
|
if (!input.hasRun) {
|
|
@@ -343,21 +390,38 @@ function computeMergeGate(input) {
|
|
|
343
390
|
if (!input.autoReview) {
|
|
344
391
|
return { allowed: true, code: "auto_review_off", reason: "\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F" };
|
|
345
392
|
}
|
|
346
|
-
if (input.reviewVerdict === "approve") {
|
|
347
|
-
return { allowed: true, code: "approved", reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u0430\u043B approve \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E" };
|
|
348
|
-
}
|
|
349
393
|
if (input.reviewVerdict === "changes") {
|
|
350
394
|
return { allowed: false, code: "changes_requested", reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u043B \u043F\u0440\u0430\u0432\u043A\u0438 (REQUEST CHANGES) \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F, \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u0438 \u043D\u043E\u0432\u043E\u0433\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430" };
|
|
351
395
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
396
|
+
if (input.reviewVerdict !== "approve") {
|
|
397
|
+
return {
|
|
398
|
+
allowed: false,
|
|
399
|
+
code: "no_verdict",
|
|
400
|
+
reason: "\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E, \u0430 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043F\u043E \u044D\u0442\u043E\u043C\u0443 PR \u0435\u0449\u0451 \u043D\u0435\u0442 \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F (TASWM-355)"
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (input.autoVerify) {
|
|
404
|
+
if (input.verifyVerdict == null) {
|
|
405
|
+
return {
|
|
406
|
+
allowed: false,
|
|
407
|
+
code: "no_verify_verdict",
|
|
408
|
+
reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u0430\u043B approve, \u043D\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0433\u043E \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u043F\u043E \u044D\u0442\u043E\u043C\u0443 PR \u0435\u0449\u0451 \u043D\u0435\u0442 \u2014 \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u0440\u0430\u0431\u043E\u0442\u0430 \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u043E\u0448\u0435\u0434\u0448\u0435\u0439 \u043F\u0440\u0438\u0451\u043C\u043A\u0443 (TASWM-344)"
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (isNegativeVerifyVerdict(input.verifyVerdict)) {
|
|
412
|
+
return {
|
|
413
|
+
allowed: false,
|
|
414
|
+
code: "verify_negative",
|
|
415
|
+
reason: `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0432\u044B\u043D\u0435\u0441 \xAB${verifyVerdictLabel(input.verifyVerdict)}\xBB \u2014 \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0435 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043B\u043E\u0441\u044C, \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F (TASWM-344)`
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return { allowed: true, code: "approved", reason: "\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u0430\u043B approve \u2014 \u043C\u0435\u0440\u0436\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E" };
|
|
357
420
|
}
|
|
358
421
|
var init_mergeGate = __esm({
|
|
359
422
|
"../swarm-core/src/mergeGate.ts"() {
|
|
360
423
|
"use strict";
|
|
424
|
+
init_verifyVerdict();
|
|
361
425
|
}
|
|
362
426
|
});
|
|
363
427
|
|
|
@@ -1383,6 +1447,7 @@ var init_src = __esm({
|
|
|
1383
1447
|
init_timeline();
|
|
1384
1448
|
init_reviewPhase();
|
|
1385
1449
|
init_mergeGate();
|
|
1450
|
+
init_verifyVerdict();
|
|
1386
1451
|
init_status();
|
|
1387
1452
|
init_deps();
|
|
1388
1453
|
init_time();
|
|
@@ -1532,11 +1597,18 @@ var init_swarmFiles = __esm({
|
|
|
1532
1597
|
});
|
|
1533
1598
|
|
|
1534
1599
|
// src/pricing.ts
|
|
1535
|
-
function
|
|
1536
|
-
const
|
|
1537
|
-
if (!
|
|
1600
|
+
function pricingAt(pricing, at) {
|
|
1601
|
+
const intro = pricing.intro;
|
|
1602
|
+
if (!intro) return pricing;
|
|
1603
|
+
const day = at.toISOString().slice(0, 10);
|
|
1604
|
+
return day <= intro.until ? { inputPerMTok: intro.inputPerMTok, outputPerMTok: intro.outputPerMTok } : pricing;
|
|
1605
|
+
}
|
|
1606
|
+
function costFromUsage(usage, model, table = MODEL_PRICING, at = /* @__PURE__ */ new Date()) {
|
|
1607
|
+
const entry = model ? table[model] : void 0;
|
|
1608
|
+
if (!entry) {
|
|
1538
1609
|
return { costUsd: 0, warning: `\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u0430\u0439\u0441\u0438\u043D\u0433\u0430: "${model ?? ""}" \u2014 cost \u043D\u0435 \u043F\u043E\u0441\u0447\u0438\u0442\u0430\u043D` };
|
|
1539
1610
|
}
|
|
1611
|
+
const pricing = pricingAt(entry, at);
|
|
1540
1612
|
const input = usage.input_tokens ?? 0;
|
|
1541
1613
|
const output = usage.output_tokens ?? 0;
|
|
1542
1614
|
const cacheWrite = usage.cache_creation_input_tokens ?? 0;
|
|
@@ -1549,10 +1621,12 @@ var init_pricing = __esm({
|
|
|
1549
1621
|
"src/pricing.ts"() {
|
|
1550
1622
|
"use strict";
|
|
1551
1623
|
MODEL_PRICING = {
|
|
1552
|
-
//
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1624
|
+
// Sonnet 5: вводные 2/10 по 31.08.2026 включительно, дальше базовые 3/15.
|
|
1625
|
+
"claude-sonnet-5": {
|
|
1626
|
+
inputPerMTok: 3,
|
|
1627
|
+
outputPerMTok: 15,
|
|
1628
|
+
intro: { inputPerMTok: 2, outputPerMTok: 10, until: "2026-08-31" }
|
|
1629
|
+
},
|
|
1556
1630
|
"claude-opus-5": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1557
1631
|
// Прогоны на 4.8 остаются в истории — строку держим ради их стоимости.
|
|
1558
1632
|
"claude-opus-4-8": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
@@ -1991,6 +2065,7 @@ __export(config_exports, {
|
|
|
1991
2065
|
SWARM_ANOMALY_RESOURCE_GATE_STALL_MS: () => SWARM_ANOMALY_RESOURCE_GATE_STALL_MS,
|
|
1992
2066
|
SWARM_ANOMALY_RUN_DOWN_WINDOW_MS: () => SWARM_ANOMALY_RUN_DOWN_WINDOW_MS,
|
|
1993
2067
|
SWARM_AUTO_REVIEW: () => SWARM_AUTO_REVIEW,
|
|
2068
|
+
SWARM_AUTO_VERIFY: () => SWARM_AUTO_VERIFY,
|
|
1994
2069
|
SWARM_BACKEND: () => SWARM_BACKEND,
|
|
1995
2070
|
SWARM_CLAIM_INTERVAL_MS: () => SWARM_CLAIM_INTERVAL_MS,
|
|
1996
2071
|
SWARM_CPU_SANITY_BUSY: () => SWARM_CPU_SANITY_BUSY,
|
|
@@ -2121,7 +2196,7 @@ function reviewCutoffAt() {
|
|
|
2121
2196
|
}
|
|
2122
2197
|
return at;
|
|
2123
2198
|
}
|
|
2124
|
-
var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_WATCHDOG_STALL_MS;
|
|
2199
|
+
var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, SWARM_AUTO_VERIFY, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_WATCHDOG_STALL_MS;
|
|
2125
2200
|
var init_config = __esm({
|
|
2126
2201
|
"src/config.ts"() {
|
|
2127
2202
|
"use strict";
|
|
@@ -2192,6 +2267,7 @@ var init_config = __esm({
|
|
|
2192
2267
|
TASKLESS_TASK_URL_BASE = process.env.TASKLESS_TASK_URL_BASE || (TASKLESS_WORKSPACE_SLUG ? `${TASKLESS_API.replace(/\/$/, "")}/app/${TASKLESS_WORKSPACE_SLUG}` : "");
|
|
2193
2268
|
SWARM_VERIFY = process.env.SWARM_VERIFY === "ci" ? "ci" : "local";
|
|
2194
2269
|
SWARM_AUTO_REVIEW = process.env.SWARM_AUTO_REVIEW !== "0";
|
|
2270
|
+
SWARM_AUTO_VERIFY = process.env.SWARM_AUTO_VERIFY !== "0";
|
|
2195
2271
|
REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join5(SWARM_HOME, "review-cutoff.json");
|
|
2196
2272
|
SWARM_REVIEW_CAP = Math.trunc(envNum("SWARM_REVIEW_CAP", 1));
|
|
2197
2273
|
SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
|
|
@@ -2631,6 +2707,105 @@ var init_heartbeat = __esm({
|
|
|
2631
2707
|
}
|
|
2632
2708
|
});
|
|
2633
2709
|
|
|
2710
|
+
// src/agentToken.ts
|
|
2711
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3 } from "fs";
|
|
2712
|
+
import { join as join8 } from "path";
|
|
2713
|
+
function loadAgentToken() {
|
|
2714
|
+
try {
|
|
2715
|
+
const s = JSON.parse(readFileSync7(STORE_PATH, "utf8"));
|
|
2716
|
+
return s.token ? s : null;
|
|
2717
|
+
} catch {
|
|
2718
|
+
return null;
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
async function probeAgentToken(args, fetchFn = fetch) {
|
|
2722
|
+
try {
|
|
2723
|
+
const res = await fetchFn(`${base(args.api)}/api/auth/me`, {
|
|
2724
|
+
headers: { Authorization: `Bearer ${args.token}` },
|
|
2725
|
+
// Оборвётся по таймауту — попадём в catch ниже, то есть в "unreachable": это ровно
|
|
2726
|
+
// то, чем висящая сеть и является. Соседи по CLI (daemonLink) живут так же.
|
|
2727
|
+
signal: AbortSignal.timeout(args.timeoutMs ?? TOKEN_PROBE_TIMEOUT_MS)
|
|
2728
|
+
});
|
|
2729
|
+
if (res.status === 401 || res.status === 403) return "revoked";
|
|
2730
|
+
return res.ok ? "ok" : "unreachable";
|
|
2731
|
+
} catch {
|
|
2732
|
+
return "unreachable";
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
function isRevokedTokenError(e) {
|
|
2736
|
+
if (e && typeof e === "object" && e.code === 401) return true;
|
|
2737
|
+
const text = e instanceof Error ? e.message : typeof e === "string" ? e : "";
|
|
2738
|
+
return /invalid_token|(?:http|status|code)[ =:]*401\b|\(401\)|401 unauthorized/i.test(text);
|
|
2739
|
+
}
|
|
2740
|
+
function revokedTokenLine() {
|
|
2741
|
+
return "[taskless] \u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043E\u0442\u043E\u0437\u0432\u0430\u043D (401 invalid_token) \u2014 \u0440\u043E\u0439 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0443. \u041F\u043E\u0447\u0438\u043D\u0438 \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: swarm login";
|
|
2742
|
+
}
|
|
2743
|
+
function revokedTokenComplaint() {
|
|
2744
|
+
return [
|
|
2745
|
+
"",
|
|
2746
|
+
"\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
|
|
2747
|
+
"\u2502 \u0422\u041E\u041A\u0415\u041D \u0420\u041E\u042F \u041D\u0415 \u041F\u0420\u0418\u041D\u042F\u0422 (401). \u0414\u043E\u0441\u0442\u0443\u043F \u043A Taskless \u0440\u0430\u0437\u043E\u0440\u0432\u0430\u043D. \u2502",
|
|
2748
|
+
"\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
|
|
2749
|
+
"\u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u2014 \u043E\u0434\u043D\u0430 \u0438\u0437 \u0442\u0440\u0451\u0445, \u0432\u0441\u0435 \u0442\u0440\u0438 \u0440\u0435\u0448\u0430\u044E\u0442\u0441\u044F \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0435\u043C:",
|
|
2750
|
+
" \u2022 \xAB\u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u044D\u0442\u043E\u0442 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\xBB \u0432 \u043F\u0430\u0440\u043A\u0435 \u043C\u0430\u0448\u0438\u043D \u2014 \u043E\u0442\u043E\u0437\u0432\u0430\u043D \u0442\u043E\u043A\u0435\u043D \u042D\u0422\u041E\u0419 \u043C\u0430\u0448\u0438\u043D\u044B;",
|
|
2751
|
+
" \u2022 \xAB\u0441\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u0440\u043E\u044F\xBB \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 (\u041F\u0440\u043E\u0444\u0438\u043B\u044C \u2192 \u0420\u043E\u0439) \u2014 \u043E\u0442\u043E\u0437\u0432\u0430\u043D\u044B \u0432\u0441\u0435;",
|
|
2752
|
+
" \u2022 \u0440\u043E\u0439 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D \u043C\u0430\u0441\u0442\u0435\u0440-\u0442\u0443\u043C\u0431\u043B\u0435\u0440\u043E\u043C \u2014 \u0442\u043E\u043A\u0435\u043D \u0430\u0433\u0435\u043D\u0442\u0430 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0451\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C.",
|
|
2753
|
+
"",
|
|
2754
|
+
"\u0412\u0445\u043E\u0434 \u043D\u0430 \u0414\u0420\u0423\u0413\u041E\u0419 \u043C\u0430\u0448\u0438\u043D\u0435 \u044D\u0442\u043E\u0442 \u0442\u043E\u043A\u0435\u043D \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0433\u0430\u0441\u0438\u0442 (TASWM-324): \u0443 \u043A\u0430\u0436\u0434\u043E\u0439",
|
|
2755
|
+
"\u043C\u0430\u0448\u0438\u043D\u044B \u0441\u0432\u043E\u0439 \u0442\u043E\u043A\u0435\u043D, \u043F\u0440\u0438\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 \u043A \u0435\u0451 hostId.",
|
|
2756
|
+
"",
|
|
2757
|
+
"\u0414\u0435\u043C\u043E\u043D \u041D\u0415 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E: \u043F\u043E\u043A\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435\u0442, \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u043D\u0435 \u0431\u0435\u0440\u0443\u0442\u0441\u044F \u0438 \u043E\u0442\u0447\u0451\u0442\u044B",
|
|
2758
|
+
"\u043D\u0435 \u043F\u0438\u0448\u0443\u0442\u0441\u044F. \u041F\u043E\u0447\u0438\u043D\u0438 \u0432\u0445\u043E\u0434\u043E\u043C \u0437\u0430\u043D\u043E\u0432\u043E: swarm login",
|
|
2759
|
+
"\u041F\u0440\u043E\u0431\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442, \u0430 \u0432\u043E\u0439\u0442\u0438 \u043C\u0435\u0448\u0430\u0435\u0442 \u0441\u0442\u0430\u0440\u044B\u0439 \u0444\u0430\u0439\u043B: swarm login --force",
|
|
2760
|
+
""
|
|
2761
|
+
].join("\n");
|
|
2762
|
+
}
|
|
2763
|
+
var STORE_PATH, base, TOKEN_PROBE_TIMEOUT_MS;
|
|
2764
|
+
var init_agentToken = __esm({
|
|
2765
|
+
"src/agentToken.ts"() {
|
|
2766
|
+
"use strict";
|
|
2767
|
+
init_config();
|
|
2768
|
+
STORE_PATH = join8(SWARM_HOME, "agent.json");
|
|
2769
|
+
base = (api) => api.replace(/\/$/, "");
|
|
2770
|
+
TOKEN_PROBE_TIMEOUT_MS = 8e3;
|
|
2771
|
+
}
|
|
2772
|
+
});
|
|
2773
|
+
|
|
2774
|
+
// src/linkFailure.ts
|
|
2775
|
+
function describeError(e, maxDepth = 4) {
|
|
2776
|
+
const parts = [];
|
|
2777
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2778
|
+
let cur = e;
|
|
2779
|
+
for (let depth = 0; depth < maxDepth && cur != null && !seen.has(cur); depth++) {
|
|
2780
|
+
seen.add(cur);
|
|
2781
|
+
const msg = (cur instanceof Error ? cur.message : typeof cur === "string" ? cur : String(cur)).trim();
|
|
2782
|
+
const code = cur.code;
|
|
2783
|
+
const codeText = code === void 0 || code === null ? "" : String(code);
|
|
2784
|
+
const label = codeText && !msg.includes(codeText) ? `${msg} [code=${codeText}]` : msg;
|
|
2785
|
+
if (label && !parts.includes(label)) parts.push(label);
|
|
2786
|
+
cur = cur.cause;
|
|
2787
|
+
}
|
|
2788
|
+
return parts.join(" \u2190 ") || String(e);
|
|
2789
|
+
}
|
|
2790
|
+
function explainLinkFailure(e) {
|
|
2791
|
+
const detail = describeError(e);
|
|
2792
|
+
if (detail.includes(UNKNOWN_LINK_REASON)) return { kind: "unknown", detail };
|
|
2793
|
+
if (isRevokedTokenError(e) || isRevokedTokenError(detail)) return { kind: "unauthorized", detail };
|
|
2794
|
+
if (MISSING_TOOL_RE.test(detail)) return { kind: "missing_tool", detail };
|
|
2795
|
+
if (OFFLINE_RE.test(detail)) return { kind: "offline", detail };
|
|
2796
|
+
return { kind: "rejected", detail };
|
|
2797
|
+
}
|
|
2798
|
+
var UNKNOWN_LINK_REASON, OFFLINE_RE, MISSING_TOOL_RE;
|
|
2799
|
+
var init_linkFailure = __esm({
|
|
2800
|
+
"src/linkFailure.ts"() {
|
|
2801
|
+
"use strict";
|
|
2802
|
+
init_agentToken();
|
|
2803
|
+
UNKNOWN_LINK_REASON = "\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F \u043D\u0435\u0442, \u0430 \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u043A\u043E\u043D\u043D\u0435\u043A\u0442\u043E\u0440 \u043D\u0435 \u043D\u0430\u0437\u0432\u0430\u043B";
|
|
2804
|
+
OFFLINE_RE = /fetch failed|econnreset|econnrefused|econnaborted|enotfound|eai_again|etimedout|ehostunreach|enetunreach|epipe|socket hang up|und_err|network error|connection (?:closed|refused|reset|error)|connect timeout/i;
|
|
2805
|
+
MISSING_TOOL_RE = /tool[^.]{0,40}not found|unknown tool|tool_not_found|method not found|-32601\b/i;
|
|
2806
|
+
}
|
|
2807
|
+
});
|
|
2808
|
+
|
|
2634
2809
|
// src/procList.ts
|
|
2635
2810
|
function parsePsRows(out) {
|
|
2636
2811
|
const rows = [];
|
|
@@ -2952,7 +3127,7 @@ var init_procCleanup = __esm({
|
|
|
2952
3127
|
// src/git.ts
|
|
2953
3128
|
import { existsSync as existsSync9, readdirSync as readdirSync10, statSync as statSync7 } from "fs";
|
|
2954
3129
|
import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
2955
|
-
import { isAbsolute as isAbsolute2, join as
|
|
3130
|
+
import { isAbsolute as isAbsolute2, join as join16, sep as sep2 } from "path";
|
|
2956
3131
|
import { $ as $2 } from "execa";
|
|
2957
3132
|
function isUnrecoverableRefError(e) {
|
|
2958
3133
|
if (e instanceof MissingRefError) return true;
|
|
@@ -2964,7 +3139,7 @@ async function reclaimStaleWorktree(repo, worktree) {
|
|
|
2964
3139
|
}
|
|
2965
3140
|
function isLinkedWorktree(dir) {
|
|
2966
3141
|
try {
|
|
2967
|
-
return statSync7(
|
|
3142
|
+
return statSync7(join16(dir, ".git")).isFile();
|
|
2968
3143
|
} catch {
|
|
2969
3144
|
return false;
|
|
2970
3145
|
}
|
|
@@ -3074,8 +3249,8 @@ async function ensureGitExcluded(worktree, pattern) {
|
|
|
3074
3249
|
try {
|
|
3075
3250
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
3076
3251
|
if (!commonDir) return false;
|
|
3077
|
-
const infoDir =
|
|
3078
|
-
const excludeFile =
|
|
3252
|
+
const infoDir = join16(commonDir, "info");
|
|
3253
|
+
const excludeFile = join16(infoDir, "exclude");
|
|
3079
3254
|
let current = "";
|
|
3080
3255
|
try {
|
|
3081
3256
|
current = await readFile(excludeFile, "utf8");
|
|
@@ -3095,7 +3270,7 @@ async function gitPath(worktree, flag) {
|
|
|
3095
3270
|
const out = await $2({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
|
|
3096
3271
|
const raw = out.stdout.trim();
|
|
3097
3272
|
if (out.exitCode !== 0 || !raw) return null;
|
|
3098
|
-
return isAbsolute2(raw) ? raw :
|
|
3273
|
+
return isAbsolute2(raw) ? raw : join16(worktree, raw);
|
|
3099
3274
|
}
|
|
3100
3275
|
async function ensureWorktreeHygiene(worktree) {
|
|
3101
3276
|
for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
|
|
@@ -3105,12 +3280,12 @@ async function installGuardHooks(worktree) {
|
|
|
3105
3280
|
try {
|
|
3106
3281
|
const gitDir = await gitPath(worktree, "--git-dir");
|
|
3107
3282
|
if (!gitDir) return false;
|
|
3108
|
-
const hooksDir =
|
|
3283
|
+
const hooksDir = join16(gitDir, "swarm-hooks");
|
|
3109
3284
|
const prev = await previousHooksPath(worktree);
|
|
3110
3285
|
await mkdir(hooksDir, { recursive: true });
|
|
3111
3286
|
for (const hook of GUARD_HOOKS) {
|
|
3112
|
-
await writeFile(
|
|
3113
|
-
await chmod(
|
|
3287
|
+
await writeFile(join16(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
|
|
3288
|
+
await chmod(join16(hooksDir, hook), 493);
|
|
3114
3289
|
}
|
|
3115
3290
|
await $2`git -C ${worktree} config extensions.worktreeConfig true`;
|
|
3116
3291
|
await $2`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
|
|
@@ -3129,7 +3304,7 @@ async function previousHooksPath(worktree) {
|
|
|
3129
3304
|
if (out.exitCode === 0 && value) return value;
|
|
3130
3305
|
}
|
|
3131
3306
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
3132
|
-
return commonDir ?
|
|
3307
|
+
return commonDir ? join16(commonDir, "hooks") : null;
|
|
3133
3308
|
}
|
|
3134
3309
|
async function removeWorktree(repo, path) {
|
|
3135
3310
|
const branch = (await $2({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
|
|
@@ -3148,7 +3323,7 @@ async function worktreeDirty(path) {
|
|
|
3148
3323
|
async function repairMovedWorktrees(targetDir) {
|
|
3149
3324
|
const subdirs = (dir) => {
|
|
3150
3325
|
try {
|
|
3151
|
-
return readdirSync10(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) =>
|
|
3326
|
+
return readdirSync10(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join16(dir, d.name));
|
|
3152
3327
|
} catch {
|
|
3153
3328
|
return [];
|
|
3154
3329
|
}
|
|
@@ -3275,18 +3450,18 @@ __export(droneChannel_exports, {
|
|
|
3275
3450
|
verifyLocalCliToken: () => verifyLocalCliToken
|
|
3276
3451
|
});
|
|
3277
3452
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
3278
|
-
import { chmodSync, mkdirSync as mkdirSync7, readFileSync as
|
|
3453
|
+
import { chmodSync, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
3279
3454
|
import { dirname as dirname3 } from "path";
|
|
3280
3455
|
import { serve } from "@hono/node-server";
|
|
3281
3456
|
function loadChannelSecret(path) {
|
|
3282
3457
|
try {
|
|
3283
|
-
const s =
|
|
3458
|
+
const s = readFileSync14(path, "utf8").trim();
|
|
3284
3459
|
if (s.length >= 32) return s;
|
|
3285
3460
|
} catch {
|
|
3286
3461
|
}
|
|
3287
3462
|
const secret = randomBytes(32).toString("hex");
|
|
3288
3463
|
mkdirSync7(dirname3(path), { recursive: true });
|
|
3289
|
-
|
|
3464
|
+
writeFileSync10(path, secret, { mode: 384 });
|
|
3290
3465
|
try {
|
|
3291
3466
|
chmodSync(path, 384);
|
|
3292
3467
|
} catch {
|
|
@@ -3616,7 +3791,7 @@ var init_droneChannel = __esm({
|
|
|
3616
3791
|
|
|
3617
3792
|
// src/attachments.ts
|
|
3618
3793
|
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
3619
|
-
import { join as
|
|
3794
|
+
import { join as join19 } from "path";
|
|
3620
3795
|
function attachmentsFromDoc(doc, origin) {
|
|
3621
3796
|
const out = [];
|
|
3622
3797
|
const walk2 = (node) => {
|
|
@@ -3707,7 +3882,7 @@ async function materializeAttachments(opts) {
|
|
|
3707
3882
|
};
|
|
3708
3883
|
const result = { files: [], skipped: [] };
|
|
3709
3884
|
if (opts.refs.length === 0) return result;
|
|
3710
|
-
const dir =
|
|
3885
|
+
const dir = join19(opts.worktree, ATTACHMENTS_DIR);
|
|
3711
3886
|
await io.removeDir(dir);
|
|
3712
3887
|
let dirReady = false;
|
|
3713
3888
|
let total = 0;
|
|
@@ -3753,7 +3928,7 @@ async function materializeAttachments(opts) {
|
|
|
3753
3928
|
await io.mkdir(dir);
|
|
3754
3929
|
dirReady = true;
|
|
3755
3930
|
}
|
|
3756
|
-
await io.writeFile(
|
|
3931
|
+
await io.writeFile(join19(dir, name), file.bytes);
|
|
3757
3932
|
} catch (e) {
|
|
3758
3933
|
result.skipped.push(`${label} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText(e)})`);
|
|
3759
3934
|
continue;
|
|
@@ -3906,7 +4081,7 @@ var init_attachments = __esm({
|
|
|
3906
4081
|
|
|
3907
4082
|
// src/shotUpload.ts
|
|
3908
4083
|
import { readFile as fsReadFile } from "fs/promises";
|
|
3909
|
-
import { basename as basename2, isAbsolute as isAbsolute3, join as
|
|
4084
|
+
import { basename as basename2, isAbsolute as isAbsolute3, join as join20 } from "path";
|
|
3910
4085
|
function shotMimetype(path) {
|
|
3911
4086
|
const dot = path.lastIndexOf(".");
|
|
3912
4087
|
if (dot < 0) return null;
|
|
@@ -3932,7 +4107,7 @@ async function readShots(paths, opts) {
|
|
|
3932
4107
|
}
|
|
3933
4108
|
let bytes;
|
|
3934
4109
|
try {
|
|
3935
|
-
bytes = await read(isAbsolute3(path) ? path :
|
|
4110
|
+
bytes = await read(isAbsolute3(path) ? path : join20(opts.worktree, path));
|
|
3936
4111
|
} catch (e) {
|
|
3937
4112
|
skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
|
|
3938
4113
|
continue;
|
|
@@ -4010,15 +4185,15 @@ var init_shotUpload = __esm({
|
|
|
4010
4185
|
|
|
4011
4186
|
// src/oauth.ts
|
|
4012
4187
|
import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
|
|
4013
|
-
import { readFileSync as
|
|
4014
|
-
import { join as
|
|
4188
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync4 } from "fs";
|
|
4189
|
+
import { join as join21 } from "path";
|
|
4015
4190
|
function makePkce() {
|
|
4016
4191
|
const verifier = randomBytes2(48).toString("base64url");
|
|
4017
4192
|
const challenge = createHash2("sha256").update(verifier).digest("base64url");
|
|
4018
4193
|
return { verifier, challenge };
|
|
4019
4194
|
}
|
|
4020
4195
|
async function registerClient(args, fetchFn = fetch) {
|
|
4021
|
-
const res = await fetchFn(`${
|
|
4196
|
+
const res = await fetchFn(`${base2(args.api)}${REGISTER}`, {
|
|
4022
4197
|
method: "POST",
|
|
4023
4198
|
headers: { "Content-Type": "application/json" },
|
|
4024
4199
|
body: JSON.stringify({
|
|
@@ -4033,7 +4208,7 @@ async function registerClient(args, fetchFn = fetch) {
|
|
|
4033
4208
|
return j.client_id;
|
|
4034
4209
|
}
|
|
4035
4210
|
function authorizeUrl(args) {
|
|
4036
|
-
const u = new URL(`${
|
|
4211
|
+
const u = new URL(`${base2(args.api)}${AUTHORIZE}`);
|
|
4037
4212
|
u.searchParams.set("response_type", "code");
|
|
4038
4213
|
u.searchParams.set("client_id", args.clientId);
|
|
4039
4214
|
u.searchParams.set("redirect_uri", args.redirectUri);
|
|
@@ -4044,7 +4219,7 @@ function authorizeUrl(args) {
|
|
|
4044
4219
|
return u.toString();
|
|
4045
4220
|
}
|
|
4046
4221
|
async function postToken(api, params, fetchFn) {
|
|
4047
|
-
const res = await fetchFn(`${
|
|
4222
|
+
const res = await fetchFn(`${base2(api)}${TOKEN}`, {
|
|
4048
4223
|
method: "POST",
|
|
4049
4224
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
4050
4225
|
body: new URLSearchParams(params).toString()
|
|
@@ -4095,23 +4270,23 @@ function toStore(clientId, t, now) {
|
|
|
4095
4270
|
}
|
|
4096
4271
|
function loadStore() {
|
|
4097
4272
|
try {
|
|
4098
|
-
return JSON.parse(
|
|
4273
|
+
return JSON.parse(readFileSync15(STORE_PATH2, "utf8"));
|
|
4099
4274
|
} catch {
|
|
4100
4275
|
return null;
|
|
4101
4276
|
}
|
|
4102
4277
|
}
|
|
4103
4278
|
function saveStore(s) {
|
|
4104
|
-
|
|
4279
|
+
writeFileSync11(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
|
|
4105
4280
|
}
|
|
4106
4281
|
function clearStore() {
|
|
4107
4282
|
try {
|
|
4108
|
-
|
|
4283
|
+
unlinkSync4(STORE_PATH2);
|
|
4109
4284
|
} catch {
|
|
4110
4285
|
}
|
|
4111
4286
|
}
|
|
4112
4287
|
function loadClientId() {
|
|
4113
4288
|
try {
|
|
4114
|
-
return JSON.parse(
|
|
4289
|
+
return JSON.parse(readFileSync15(CLIENT_PATH, "utf8")).clientId;
|
|
4115
4290
|
} catch {
|
|
4116
4291
|
return null;
|
|
4117
4292
|
}
|
|
@@ -4120,7 +4295,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
|
|
|
4120
4295
|
const existing = loadClientId();
|
|
4121
4296
|
if (existing) return existing;
|
|
4122
4297
|
const clientId = await registerClient({ api, redirectUri }, fetchFn);
|
|
4123
|
-
|
|
4298
|
+
writeFileSync11(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
|
|
4124
4299
|
return clientId;
|
|
4125
4300
|
}
|
|
4126
4301
|
async function ensureAccess(store, opts = {}) {
|
|
@@ -4137,7 +4312,7 @@ async function ensureAccess(store, opts = {}) {
|
|
|
4137
4312
|
return null;
|
|
4138
4313
|
}
|
|
4139
4314
|
}
|
|
4140
|
-
var REGISTER, AUTHORIZE, TOKEN, SCOPE,
|
|
4315
|
+
var REGISTER, AUTHORIZE, TOKEN, SCOPE, STORE_PATH2, CLIENT_PATH, base2;
|
|
4141
4316
|
var init_oauth = __esm({
|
|
4142
4317
|
"src/oauth.ts"() {
|
|
4143
4318
|
"use strict";
|
|
@@ -4146,64 +4321,45 @@ var init_oauth = __esm({
|
|
|
4146
4321
|
AUTHORIZE = "/api/oauth/authorize";
|
|
4147
4322
|
TOKEN = "/api/oauth/token";
|
|
4148
4323
|
SCOPE = "openid profile email taskless:full";
|
|
4149
|
-
|
|
4150
|
-
CLIENT_PATH =
|
|
4151
|
-
|
|
4324
|
+
STORE_PATH2 = join21(SWARM_HOME, "oauth.json");
|
|
4325
|
+
CLIENT_PATH = join21(SWARM_HOME, "oauth-client.json");
|
|
4326
|
+
base2 = (api) => api.replace(/\/$/, "");
|
|
4152
4327
|
}
|
|
4153
4328
|
});
|
|
4154
4329
|
|
|
4155
|
-
// src/
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
}
|
|
4165
|
-
}
|
|
4166
|
-
|
|
4167
|
-
try {
|
|
4168
|
-
const res = await fetchFn(`${base2(args.api)}/api/auth/me`, {
|
|
4169
|
-
headers: { Authorization: `Bearer ${args.token}` }
|
|
4170
|
-
});
|
|
4171
|
-
if (res.status === 401 || res.status === 403) return "revoked";
|
|
4172
|
-
return res.ok ? "ok" : "unreachable";
|
|
4173
|
-
} catch {
|
|
4174
|
-
return "unreachable";
|
|
4175
|
-
}
|
|
4330
|
+
// src/authState.ts
|
|
4331
|
+
function noteTokenRejected(nowMs = Date.now()) {
|
|
4332
|
+
if (!denial) {
|
|
4333
|
+
denial = { since: nowMs, lastShoutAt: nowMs, count: 1 };
|
|
4334
|
+
return revokedTokenComplaint();
|
|
4335
|
+
}
|
|
4336
|
+
denial.count += 1;
|
|
4337
|
+
if (nowMs - denial.lastShoutAt < SHOUT_EVERY_MS) return null;
|
|
4338
|
+
denial.lastShoutAt = nowMs;
|
|
4339
|
+
return `${revokedTokenComplaint()}
|
|
4340
|
+
${deniedSinceLine(nowMs)}
|
|
4341
|
+
`;
|
|
4176
4342
|
}
|
|
4177
|
-
function
|
|
4178
|
-
|
|
4179
|
-
const text = e instanceof Error ? e.message : typeof e === "string" ? e : "";
|
|
4180
|
-
return /invalid_token|(?:http|status|code)[ =:]*401\b|\(401\)|401 unauthorized/i.test(text);
|
|
4343
|
+
function noteTokenAccepted() {
|
|
4344
|
+
denial = null;
|
|
4181
4345
|
}
|
|
4182
|
-
function
|
|
4183
|
-
|
|
4346
|
+
function authStateLine(nowMs = Date.now()) {
|
|
4347
|
+
if (!denial) return null;
|
|
4348
|
+
return `\u0414\u041E\u0421\u0422\u0423\u041F \u041A TASKLESS \u041E\u0422\u041E\u0417\u0412\u0410\u041D \u2014 ${deniedSinceLine(nowMs)}. \u041F\u043E\u0447\u0438\u043D\u0438 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435: swarm login`;
|
|
4184
4349
|
}
|
|
4185
|
-
function
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
"\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
|
|
4191
|
-
"\u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u2014 \u043E\u0434\u043D\u0430 \u0438\u0437 \u0434\u0432\u0443\u0445, \u043E\u0431\u0435 \u0434\u0435\u043B\u0430\u044E\u0442\u0441\u044F \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0435\u043C \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435:",
|
|
4192
|
-
" \u2022 \xAB\u0441\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F\xBB \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 (\u041F\u0440\u043E\u0444\u0438\u043B\u044C \u2192 \u0420\u043E\u0439) \u2014 \u0442\u043E\u043A\u0435\u043D \u043E\u0442\u043E\u0437\u0432\u0430\u043D;",
|
|
4193
|
-
" \u2022 \u0440\u043E\u0439 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D \u043C\u0430\u0441\u0442\u0435\u0440-\u0442\u0443\u043C\u0431\u043B\u0435\u0440\u043E\u043C \u2014 \u0442\u043E\u043A\u0435\u043D \u0430\u0433\u0435\u043D\u0442\u0430 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0451\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C.",
|
|
4194
|
-
"",
|
|
4195
|
-
"\u0414\u0435\u043C\u043E\u043D \u041D\u0415 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E: \u043F\u043E\u043A\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435\u0442, \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u043D\u0435 \u0431\u0435\u0440\u0443\u0442\u0441\u044F \u0438 \u043E\u0442\u0447\u0451\u0442\u044B",
|
|
4196
|
-
"\u043D\u0435 \u043F\u0438\u0448\u0443\u0442\u0441\u044F. \u041F\u043E\u0447\u0438\u043D\u0438 \u0432\u0445\u043E\u0434\u043E\u043C \u0437\u0430\u043D\u043E\u0432\u043E: swarm login",
|
|
4197
|
-
""
|
|
4198
|
-
].join("\n");
|
|
4350
|
+
function deniedSinceLine(nowMs) {
|
|
4351
|
+
const d = denial;
|
|
4352
|
+
if (!d) return "";
|
|
4353
|
+
const min = Math.max(1, Math.round((nowMs - d.since) / 6e4));
|
|
4354
|
+
return `\u043F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0440\u0433\u0430\u0435\u0442 \u0442\u043E\u043A\u0435\u043D ${min} \u043C\u0438\u043D, \u043E\u0442\u043A\u0430\u0437\u043E\u0432 ${d.count}`;
|
|
4199
4355
|
}
|
|
4200
|
-
var
|
|
4201
|
-
var
|
|
4202
|
-
"src/
|
|
4356
|
+
var SHOUT_EVERY_MS, denial;
|
|
4357
|
+
var init_authState = __esm({
|
|
4358
|
+
"src/authState.ts"() {
|
|
4203
4359
|
"use strict";
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4360
|
+
init_agentToken();
|
|
4361
|
+
SHOUT_EVERY_MS = 10 * 6e4;
|
|
4362
|
+
denial = null;
|
|
4207
4363
|
}
|
|
4208
4364
|
});
|
|
4209
4365
|
|
|
@@ -4244,6 +4400,7 @@ __export(taskless_exports, {
|
|
|
4244
4400
|
extractText: () => extractText,
|
|
4245
4401
|
fetchTaskContext: () => fetchTaskContext,
|
|
4246
4402
|
isClosingTaskCategory: () => isClosingTaskCategory,
|
|
4403
|
+
lastSwarmConnectFailure: () => lastSwarmConnectFailure,
|
|
4247
4404
|
makeFileFetcher: () => makeFileFetcher,
|
|
4248
4405
|
makeTasklessClient: () => makeTasklessClient,
|
|
4249
4406
|
parseWorkspaces: () => parseWorkspaces,
|
|
@@ -4588,12 +4745,14 @@ async function preflightAuth(opts = {}) {
|
|
|
4588
4745
|
if (!agent?.token) return true;
|
|
4589
4746
|
const probe = await probeAgentToken({ api, token: agent.token }, opts.fetchFn ?? fetch);
|
|
4590
4747
|
if (probe === "revoked") {
|
|
4591
|
-
out(revokedTokenComplaint());
|
|
4748
|
+
out(noteTokenRejected() ?? revokedTokenComplaint());
|
|
4592
4749
|
return false;
|
|
4593
4750
|
}
|
|
4594
4751
|
if (probe === "unreachable") {
|
|
4595
4752
|
out("[taskless] \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (\u0441\u0435\u0442\u044C/\u043F\u0440\u043E\u0434 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D) \u2014 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u044E, \u043D\u043E \u0441\u0432\u044F\u0437\u0438 \u043C\u043E\u0436\u0435\u0442 \u043D\u0435 \u0431\u044B\u0442\u044C.");
|
|
4753
|
+
return true;
|
|
4596
4754
|
}
|
|
4755
|
+
noteTokenAccepted();
|
|
4597
4756
|
return true;
|
|
4598
4757
|
}
|
|
4599
4758
|
async function connectTaskless(cfg) {
|
|
@@ -4610,11 +4769,15 @@ async function connectTaskless(cfg) {
|
|
|
4610
4769
|
try {
|
|
4611
4770
|
await client.connect(transport, { timeout: TASKLESS_CONNECT_TIMEOUT_MS });
|
|
4612
4771
|
} catch (e) {
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4772
|
+
if (isRevokedTokenError(e)) {
|
|
4773
|
+
const shout = noteTokenRejected();
|
|
4774
|
+
if (shout) console.error(shout);
|
|
4775
|
+
} else {
|
|
4776
|
+
console.warn(`Taskless: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${describeError(e)}) \u2192 slug-only. \u0412\u043E\u0439\u0434\u0438 \u0437\u0430\u043D\u043E\u0432\u043E.`);
|
|
4777
|
+
}
|
|
4616
4778
|
return null;
|
|
4617
4779
|
}
|
|
4780
|
+
noteTokenAccepted();
|
|
4618
4781
|
const call = async (name, args) => await client.callTool({ name, arguments: args });
|
|
4619
4782
|
return makeTasklessClient(
|
|
4620
4783
|
call,
|
|
@@ -4627,27 +4790,41 @@ async function connectSwarmMcp(cfg) {
|
|
|
4627
4790
|
const api = cfg?.api ?? TASKLESS_API;
|
|
4628
4791
|
const workspace = cfg?.workspace ?? resolvedWorkspaceId();
|
|
4629
4792
|
const token = await resolveApiToken(api, cfg?.token);
|
|
4630
|
-
if (!token)
|
|
4793
|
+
if (!token) {
|
|
4794
|
+
swarmConnectFailure = "\u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 \u043D\u0435\u0442 \u0442\u043E\u043A\u0435\u043D\u0430 \u0440\u043E\u044F \u2014 \u043D\u0443\u0436\u0435\u043D \u0432\u0445\u043E\u0434: swarm login";
|
|
4795
|
+
return null;
|
|
4796
|
+
}
|
|
4631
4797
|
const url = new URL(mcpEndpoint(api));
|
|
4632
4798
|
const transport = new StreamableHTTPClientTransport(url, {
|
|
4633
4799
|
requestInit: { headers: { Authorization: `Bearer ${token}` } }
|
|
4634
4800
|
});
|
|
4635
4801
|
const client = new Client({ name: "taskless-swarm-client", version: "0.1.0" }, { capabilities: {} });
|
|
4636
4802
|
try {
|
|
4637
|
-
await client.connect(transport);
|
|
4803
|
+
await client.connect(transport, { timeout: TASKLESS_CONNECT_TIMEOUT_MS });
|
|
4638
4804
|
} catch (e) {
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4805
|
+
if (isRevokedTokenError(e)) {
|
|
4806
|
+
swarmConnectFailure = revokedTokenLine();
|
|
4807
|
+
const shout = noteTokenRejected();
|
|
4808
|
+
if (shout) console.error(`${shout}
|
|
4809
|
+
\u0421\u043E\u0431\u044B\u0442\u0438\u044F \u043F\u043E\u043A\u0430 \u043A\u043E\u043F\u044F\u0442\u0441\u044F \u0432 outbox \u0438 \u0443\u0435\u0434\u0443\u0442, \u043A\u043E\u0433\u0434\u0430 \u0434\u043E\u0441\u0442\u0443\u043F \u0432\u0435\u0440\u043D\u0451\u0442\u0441\u044F.`);
|
|
4810
|
+
} else {
|
|
4811
|
+
swarmConnectFailure = describeError(e);
|
|
4812
|
+
console.warn(`Swarm MCP: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${swarmConnectFailure}) \u2192 \u0431\u0443\u0444\u0435\u0440 \u0432 outbox.`);
|
|
4813
|
+
}
|
|
4642
4814
|
return null;
|
|
4643
4815
|
}
|
|
4816
|
+
swarmConnectFailure = null;
|
|
4817
|
+
noteTokenAccepted();
|
|
4644
4818
|
return async (name, args) => await client.callTool({ name, arguments: swarmToolArgs(name, args, workspace) });
|
|
4645
4819
|
}
|
|
4820
|
+
function lastSwarmConnectFailure() {
|
|
4821
|
+
return swarmConnectFailure;
|
|
4822
|
+
}
|
|
4646
4823
|
function swarmToolArgs(name, args, workspace) {
|
|
4647
4824
|
if (!WS_SCOPED_SWARM_TOOLS.has(name) || !workspace) return args;
|
|
4648
4825
|
return { workspaceId: workspace, ...args };
|
|
4649
4826
|
}
|
|
4650
|
-
var SLUG_RE, WS_FREE_TOOLS, TaskBodyUnavailableError, TASK_CONTEXT_MAX_DOCS, TASK_CONTEXT_MAX_DOC_CHARS, TASK_CONTEXT_MAX_COMMENTS, TASK_CONTEXT_MAX_COMMENT_CHARS, UNTRUSTED_TASK_INPUT_NOTICE, TASKLESS_CONNECT_TIMEOUT_MS, FILE_FETCH_TIMEOUT_MS, FILE_FETCH_MAX_REDIRECTS, legacyWarned, WS_SCOPED_SWARM_TOOLS;
|
|
4827
|
+
var SLUG_RE, WS_FREE_TOOLS, TaskBodyUnavailableError, TASK_CONTEXT_MAX_DOCS, TASK_CONTEXT_MAX_DOC_CHARS, TASK_CONTEXT_MAX_COMMENTS, TASK_CONTEXT_MAX_COMMENT_CHARS, UNTRUSTED_TASK_INPUT_NOTICE, TASKLESS_CONNECT_TIMEOUT_MS, FILE_FETCH_TIMEOUT_MS, FILE_FETCH_MAX_REDIRECTS, legacyWarned, swarmConnectFailure, WS_SCOPED_SWARM_TOOLS;
|
|
4651
4828
|
var init_taskless = __esm({
|
|
4652
4829
|
"src/taskless.ts"() {
|
|
4653
4830
|
"use strict";
|
|
@@ -4657,6 +4834,8 @@ var init_taskless = __esm({
|
|
|
4657
4834
|
init_config();
|
|
4658
4835
|
init_oauth();
|
|
4659
4836
|
init_agentToken();
|
|
4837
|
+
init_linkFailure();
|
|
4838
|
+
init_authState();
|
|
4660
4839
|
init_workspace();
|
|
4661
4840
|
SLUG_RE = /^[A-Za-z][A-Za-z0-9]*-\d+$/;
|
|
4662
4841
|
WS_FREE_TOOLS = /* @__PURE__ */ new Set(["list_workspaces", "who_am_i", "finalize_upload", "report_acceptance_check"]);
|
|
@@ -4687,6 +4866,7 @@ var init_taskless = __esm({
|
|
|
4687
4866
|
FILE_FETCH_TIMEOUT_MS = 15e3;
|
|
4688
4867
|
FILE_FETCH_MAX_REDIRECTS = 3;
|
|
4689
4868
|
legacyWarned = false;
|
|
4869
|
+
swarmConnectFailure = null;
|
|
4690
4870
|
WS_SCOPED_SWARM_TOOLS = /* @__PURE__ */ new Set(["swarm_run_upsert", "get_task"]);
|
|
4691
4871
|
}
|
|
4692
4872
|
});
|
|
@@ -4748,6 +4928,10 @@ function rowToRun(row) {
|
|
|
4748
4928
|
resolves_run_id: row.resolves_run_id ?? null,
|
|
4749
4929
|
reviews_run_id: row.reviews_run_id ?? null,
|
|
4750
4930
|
review_verdict: row.review_verdict ?? null,
|
|
4931
|
+
verifies_run_id: row.verifies_run_id ?? null,
|
|
4932
|
+
// TASWM-344: через узкий парсер, а не сырой строкой — в зеркале может лежать значение,
|
|
4933
|
+
// записанное демоном другой версии; непонятное здесь значит «вердикта нет».
|
|
4934
|
+
verify_verdict: toVerifyVerdict(row.verify_verdict),
|
|
4751
4935
|
review_phase: row.review_phase ?? null,
|
|
4752
4936
|
// вычисляемая фаза ревью (TASWM-230)
|
|
4753
4937
|
review_cycle: row.review_cycle ?? 0,
|
|
@@ -4816,6 +5000,8 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
4816
5000
|
resolves_run_id INTEGER,
|
|
4817
5001
|
reviews_run_id INTEGER,
|
|
4818
5002
|
review_verdict TEXT,
|
|
5003
|
+
verifies_run_id INTEGER,
|
|
5004
|
+
verify_verdict TEXT,
|
|
4819
5005
|
review_cycle INTEGER NOT NULL DEFAULT 0,
|
|
4820
5006
|
task_title TEXT,
|
|
4821
5007
|
task_desc TEXT,
|
|
@@ -4892,6 +5078,12 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4892
5078
|
// вердикт (approve|changes), который ревьюер пишет на проверяемый прогон.
|
|
4893
5079
|
"ALTER TABLE runs ADD COLUMN reviews_run_id INTEGER",
|
|
4894
5080
|
"ALTER TABLE runs ADD COLUMN review_verdict TEXT",
|
|
5081
|
+
// Верификатор (TASWM-344): прогон, который пытается ОПРОВЕРГНУТЬ работу
|
|
5082
|
+
// #verifies_run_id, и его вердикт (confirmed|not_reproduced|found_issues) на
|
|
5083
|
+
// проверяемом прогоне. Своя пара колонок, а не переиспользование reviews_*:
|
|
5084
|
+
// ревью и верификация идут на одном PR одновременно и считаются раздельно.
|
|
5085
|
+
"ALTER TABLE runs ADD COLUMN verifies_run_id INTEGER",
|
|
5086
|
+
"ALTER TABLE runs ADD COLUMN verify_verdict TEXT",
|
|
4895
5087
|
// Авто-ревью v2 (TASWM-162): номер цикла ревью — пере-ревью после фикс-итерации.
|
|
4896
5088
|
"ALTER TABLE runs ADD COLUMN review_cycle INTEGER NOT NULL DEFAULT 0",
|
|
4897
5089
|
// Вычисляемая фаза ревью (TASWM-230): человеко-фраза «чего ждёт PR», её считает демон.
|
|
@@ -4928,7 +5120,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4928
5120
|
createRun(input) {
|
|
4929
5121
|
const name = input.name ?? pickName(this.db.prepare("SELECT id, name, status FROM runs").all());
|
|
4930
5122
|
const res = this.db.prepare(
|
|
4931
|
-
"INSERT INTO runs (task_slug, name, kind, hero_run_id, repo, area, depends_on, model, effort, decision, resolves_run_id, reviews_run_id, review_cycle, priority, base_branch, workspace_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
5123
|
+
"INSERT INTO runs (task_slug, name, kind, hero_run_id, repo, area, depends_on, model, effort, decision, resolves_run_id, reviews_run_id, verifies_run_id, review_cycle, priority, base_branch, workspace_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4932
5124
|
).run(
|
|
4933
5125
|
input.task_slug,
|
|
4934
5126
|
name,
|
|
@@ -4946,6 +5138,8 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4946
5138
|
input.decision ? JSON.stringify(input.decision) : null,
|
|
4947
5139
|
input.resolves_run_id ?? null,
|
|
4948
5140
|
input.reviews_run_id ?? null,
|
|
5141
|
+
input.verifies_run_id ?? null,
|
|
5142
|
+
// прогон-верификатор (TASWM-344); обычный прогон — null
|
|
4949
5143
|
input.review_cycle ?? 0,
|
|
4950
5144
|
// цикл ревью для ревьюера (авто-ревью v2, TASWM-162); обычный прогон — 0
|
|
4951
5145
|
input.priority ?? 0,
|
|
@@ -4956,7 +5150,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4956
5150
|
// воркспейс задачи прогона (TASWM-288); null → глобальный пин
|
|
4957
5151
|
);
|
|
4958
5152
|
const id = Number(res.lastInsertRowid);
|
|
4959
|
-
const spawnNote = input.resolves_run_id ? `\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0434\u043B\u044F #${input.resolves_run_id}` : input.reviews_run_id ? `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u043B\u044F #${input.reviews_run_id}` : input.decision ? `\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u0441 \u0440\u0435\u0448\u0435\u043D\u0438\u0435\u043C \xAB${input.decision.chosen}\xBB` : `run for ${input.task_slug}`;
|
|
5153
|
+
const spawnNote = input.resolves_run_id ? `\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0434\u043B\u044F #${input.resolves_run_id}` : input.reviews_run_id ? `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u043B\u044F #${input.reviews_run_id}` : input.verifies_run_id ? `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0434\u043B\u044F #${input.verifies_run_id}` : input.decision ? `\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u0441 \u0440\u0435\u0448\u0435\u043D\u0438\u0435\u043C \xAB${input.decision.chosen}\xBB` : `run for ${input.task_slug}`;
|
|
4960
5154
|
this.addEvent(id, "spawned", spawnNote);
|
|
4961
5155
|
return this.getRun(id);
|
|
4962
5156
|
}
|
|
@@ -4982,13 +5176,14 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4982
5176
|
*/
|
|
4983
5177
|
ensureRun(input) {
|
|
4984
5178
|
this.db.prepare(
|
|
4985
|
-
"INSERT OR IGNORE INTO runs (id, task_slug, kind, status, name, reviews_run_id, hero_run_id) VALUES (?, ?, ?, 'running', ?, ?, ?)"
|
|
5179
|
+
"INSERT OR IGNORE INTO runs (id, task_slug, kind, status, name, reviews_run_id, verifies_run_id, hero_run_id) VALUES (?, ?, ?, 'running', ?, ?, ?, ?)"
|
|
4986
5180
|
).run(
|
|
4987
5181
|
input.id,
|
|
4988
5182
|
input.task_slug,
|
|
4989
5183
|
input.kind ?? "drone",
|
|
4990
5184
|
input.name || "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)",
|
|
4991
5185
|
input.reviews_run_id ?? null,
|
|
5186
|
+
input.verifies_run_id ?? null,
|
|
4992
5187
|
input.hero_run_id ?? null
|
|
4993
5188
|
);
|
|
4994
5189
|
}
|
|
@@ -5133,6 +5328,31 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5133
5328
|
const rows = this.db.prepare(`SELECT * FROM runs WHERE reviews_run_id = ? AND status NOT IN (${TERMINAL_SQL}) ORDER BY id DESC`).all(reviewRunId);
|
|
5134
5329
|
return rows.map(rowToRun);
|
|
5135
5330
|
}
|
|
5331
|
+
/**
|
|
5332
|
+
* ВЕРИФИКАЦИЯ (TASWM-344) — те же три счётчика, что у ревью выше, но по своей связи
|
|
5333
|
+
* `verifies_run_id` и со своим капом. Скоуп «текущий цикл ревью» тот же и по той же
|
|
5334
|
+
* причине: фикс-итерация бампит review_cycle и обнуляет вердикты (reviewTransitionPatch),
|
|
5335
|
+
* значит исправленную работу обязан проверить СВЕЖИЙ верификатор, а не считаться уже
|
|
5336
|
+
* полученным подтверждением по коду, которого больше нет.
|
|
5337
|
+
*/
|
|
5338
|
+
verifierCountFor(verifyRunId) {
|
|
5339
|
+
const row = this.db.prepare(
|
|
5340
|
+
"SELECT COUNT(*) n FROM runs WHERE verifies_run_id = ? AND status NOT IN ('failed','stalled','stopped','superseded') AND review_cycle = COALESCE((SELECT review_cycle FROM runs WHERE id = ?), 0)"
|
|
5341
|
+
).get(verifyRunId, verifyRunId);
|
|
5342
|
+
return row.n;
|
|
5343
|
+
}
|
|
5344
|
+
/** Плоский кап попыток верификации на цикл — любой статус, кроме сброшенных оператором. */
|
|
5345
|
+
verifierAttemptCountFor(verifyRunId) {
|
|
5346
|
+
const row = this.db.prepare(
|
|
5347
|
+
"SELECT COUNT(*) n FROM runs WHERE verifies_run_id = ? AND status != 'superseded' AND review_cycle = COALESCE((SELECT review_cycle FROM runs WHERE id = ?), 0)"
|
|
5348
|
+
).get(verifyRunId, verifyRunId);
|
|
5349
|
+
return row.n;
|
|
5350
|
+
}
|
|
5351
|
+
/** Нетерминальные верификаторы прогона — закрыть зависших, когда PR уже смержен/закрыт. */
|
|
5352
|
+
activeVerifiersFor(verifyRunId) {
|
|
5353
|
+
const rows = this.db.prepare(`SELECT * FROM runs WHERE verifies_run_id = ? AND status NOT IN (${TERMINAL_SQL}) ORDER BY id DESC`).all(verifyRunId);
|
|
5354
|
+
return rows.map(rowToRun);
|
|
5355
|
+
}
|
|
5136
5356
|
/**
|
|
5137
5357
|
* Последний живой (не терминальный) прогон по slug — для swarm cancel. Служебные
|
|
5138
5358
|
* сабраны (reviews_run_id/resolves_run_id — ревьюер/резолвер, TASWM-273) исключены:
|
|
@@ -5147,7 +5367,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5147
5367
|
*/
|
|
5148
5368
|
activeRunForSlug(slug) {
|
|
5149
5369
|
const row = this.db.prepare(
|
|
5150
|
-
`SELECT * FROM runs WHERE task_slug = ? AND status NOT IN (${TERMINAL_SQL}) AND reviews_run_id IS NULL AND resolves_run_id IS NULL ORDER BY id DESC LIMIT 1`
|
|
5370
|
+
`SELECT * FROM runs WHERE task_slug = ? AND status NOT IN (${TERMINAL_SQL}) AND reviews_run_id IS NULL AND resolves_run_id IS NULL AND verifies_run_id IS NULL ORDER BY id DESC LIMIT 1`
|
|
5151
5371
|
).get(slug);
|
|
5152
5372
|
return row ? rowToRun(row) : null;
|
|
5153
5373
|
}
|
|
@@ -5187,7 +5407,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5187
5407
|
if (!depends_on || depends_on.length === 0) return [];
|
|
5188
5408
|
const slugs = [...new Set(depends_on)];
|
|
5189
5409
|
const rows = this.db.prepare(
|
|
5190
|
-
`SELECT task_slug, status, name, stop_reason FROM runs WHERE task_slug IN (${slugs.map(() => "?").join(",")}) AND resolves_run_id IS NULL AND reviews_run_id IS NULL ORDER BY id`
|
|
5410
|
+
`SELECT task_slug, status, name, stop_reason FROM runs WHERE task_slug IN (${slugs.map(() => "?").join(",")}) AND resolves_run_id IS NULL AND reviews_run_id IS NULL AND verifies_run_id IS NULL ORDER BY id`
|
|
5191
5411
|
).all(...slugs);
|
|
5192
5412
|
const bySlug = /* @__PURE__ */ new Map();
|
|
5193
5413
|
for (const r of rows) {
|
|
@@ -5935,7 +6155,16 @@ var SwarmClient = class _SwarmClient {
|
|
|
5935
6155
|
static REJECT_LIMIT = 5;
|
|
5936
6156
|
static BACKOFF_BASE = 500;
|
|
5937
6157
|
static BACKOFF_MAX = 3e4;
|
|
6158
|
+
// TASWM-325: момент УДАЧНОГО пульса — «когда прод последний раз слышал эту машину»
|
|
6159
|
+
// (прод пишет SwarmHost.lastSeenAt ровно на swarm_heartbeat). Единственное, что про
|
|
6160
|
+
// связь знает КЛИЕНТ: жива ли она вообще — знает транспорт (mcpResilient.link), а
|
|
6161
|
+
// отвергнут ли токен — authState. Здесь их не угадываем: первая редакция считала
|
|
6162
|
+
// успехом любой возврат call и на боевом каллере (который не бросает, а возвращает
|
|
6163
|
+
// isError) всегда показывала «связь есть».
|
|
6164
|
+
heartbeatOkAt = null;
|
|
6165
|
+
now;
|
|
5938
6166
|
constructor(opts) {
|
|
6167
|
+
this.now = opts.now ?? Date.now;
|
|
5939
6168
|
this.call = opts.call;
|
|
5940
6169
|
this.defaultRepo = normalizeRepo(opts.defaultRepo ?? "");
|
|
5941
6170
|
this.autoFlush = opts.autoFlush ?? true;
|
|
@@ -6078,6 +6307,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6078
6307
|
if ("decision" in patch) out.decision = patch.decision ? JSON.stringify(patch.decision) : null;
|
|
6079
6308
|
if ("kind" in patch && patch.kind !== void 0) out.role = patch.kind;
|
|
6080
6309
|
if ("review_verdict" in patch) out.reviewVerdict = patch.review_verdict ?? null;
|
|
6310
|
+
if ("verify_verdict" in patch) out.verifyVerdict = patch.verify_verdict ?? null;
|
|
6081
6311
|
if ("host_id" in patch && patch.host_id) {
|
|
6082
6312
|
out.hostId = patch.host_id;
|
|
6083
6313
|
out.hostPinned = patch.host_pinned === true;
|
|
@@ -6088,6 +6318,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6088
6318
|
this.setRunRefField(out, "resolvesRunId", patch, "resolves_run_id");
|
|
6089
6319
|
this.setRunRefField(out, "supersededById", patch, "superseded_by");
|
|
6090
6320
|
this.setRunRefField(out, "reviewsRunId", patch, "reviews_run_id");
|
|
6321
|
+
this.setRunRefField(out, "verifiesRunId", patch, "verifies_run_id");
|
|
6091
6322
|
return out;
|
|
6092
6323
|
}
|
|
6093
6324
|
// Положить self-ref прогона в прод-патч: локальный number → отложенная ссылка
|
|
@@ -6150,6 +6381,9 @@ var SwarmClient = class _SwarmClient {
|
|
|
6150
6381
|
resolves_run_id: input.resolves_run_id ?? null,
|
|
6151
6382
|
reviews_run_id: input.reviews_run_id ?? null,
|
|
6152
6383
|
review_verdict: null,
|
|
6384
|
+
verifies_run_id: input.verifies_run_id ?? null,
|
|
6385
|
+
// прогон-верификатор (TASWM-344)
|
|
6386
|
+
verify_verdict: null,
|
|
6153
6387
|
review_phase: null,
|
|
6154
6388
|
// вычисляемая фаза ревью (TASWM-230) — демон посчитает на review-тике
|
|
6155
6389
|
review_cycle: input.review_cycle ?? 0,
|
|
@@ -6201,6 +6435,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
6201
6435
|
// резолвер конфликта → связь на review-прогон
|
|
6202
6436
|
reviews_run_id: d.reviews_run_id,
|
|
6203
6437
|
// ревьюер (авто-ревью, TASWM-148) → связь на review-прогон
|
|
6438
|
+
verifies_run_id: d.verifies_run_id,
|
|
6439
|
+
// верификатор (TASWM-344) → связь на проверяемый прогон
|
|
6204
6440
|
host_id: d.host_id,
|
|
6205
6441
|
// машина прогона (TASWM-281) — своя с рождения либо заданная --host
|
|
6206
6442
|
host_pinned: d.host_pinned,
|
|
@@ -6208,7 +6444,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6208
6444
|
base_branch: d.base_branch
|
|
6209
6445
|
// базовая ветка PR (TASWM-285), если задана на спавне
|
|
6210
6446
|
}));
|
|
6211
|
-
const spawnNote = input.resolves_run_id ? `\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0434\u043B\u044F #${input.resolves_run_id}` : input.reviews_run_id ? `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u043B\u044F #${input.reviews_run_id}` : input.decision ? `\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u0441 \u0440\u0435\u0448\u0435\u043D\u0438\u0435\u043C \xAB${input.decision.chosen}\xBB` : `run for ${input.task_slug}`;
|
|
6447
|
+
const spawnNote = input.resolves_run_id ? `\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0434\u043B\u044F #${input.resolves_run_id}` : input.reviews_run_id ? `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u043B\u044F #${input.reviews_run_id}` : input.verifies_run_id ? `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0434\u043B\u044F #${input.verifies_run_id}` : input.decision ? `\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u0441 \u0440\u0435\u0448\u0435\u043D\u0438\u0435\u043C \xAB${input.decision.chosen}\xBB` : `run for ${input.task_slug}`;
|
|
6212
6448
|
this.addEvent(id, "spawned", spawnNote);
|
|
6213
6449
|
return this.mat(d);
|
|
6214
6450
|
}
|
|
@@ -6312,6 +6548,24 @@ var SwarmClient = class _SwarmClient {
|
|
|
6312
6548
|
activeReviewersFor(reviewRunId) {
|
|
6313
6549
|
return [...this.runs.values()].filter((r) => r.reviews_run_id === reviewRunId && !TERMINAL_STATUSES.includes(r.status)).sort((a, b) => b.id - a.id).map((d) => this.mat(d));
|
|
6314
6550
|
}
|
|
6551
|
+
// Верификация (TASWM-344) — зеркало трёх счётчиков ревью выше по своей связи
|
|
6552
|
+
// verifies_run_id; правила «кто не в счёт» те же (см. db.ts:verifierCountFor).
|
|
6553
|
+
verifierCountFor(verifyRunId) {
|
|
6554
|
+
const cycle = this.runs.get(verifyRunId)?.review_cycle ?? 0;
|
|
6555
|
+
const dead = /* @__PURE__ */ new Set(["failed", "stalled", "stopped", "superseded"]);
|
|
6556
|
+
return [...this.runs.values()].filter(
|
|
6557
|
+
(r) => r.verifies_run_id === verifyRunId && r.review_cycle === cycle && !dead.has(r.status)
|
|
6558
|
+
).length;
|
|
6559
|
+
}
|
|
6560
|
+
verifierAttemptCountFor(verifyRunId) {
|
|
6561
|
+
const cycle = this.runs.get(verifyRunId)?.review_cycle ?? 0;
|
|
6562
|
+
return [...this.runs.values()].filter(
|
|
6563
|
+
(r) => r.verifies_run_id === verifyRunId && r.review_cycle === cycle && r.status !== "superseded"
|
|
6564
|
+
).length;
|
|
6565
|
+
}
|
|
6566
|
+
activeVerifiersFor(verifyRunId) {
|
|
6567
|
+
return [...this.runs.values()].filter((r) => r.verifies_run_id === verifyRunId && !TERMINAL_STATUSES.includes(r.status)).sort((a, b) => b.id - a.id).map((d) => this.mat(d));
|
|
6568
|
+
}
|
|
6315
6569
|
activeRunForSlug(slug) {
|
|
6316
6570
|
const cands = [...this.runs.values()].filter((r) => r.task_slug === slug && !SLUG_TERMINAL.has(r.status)).sort((a, b) => b.id - a.id);
|
|
6317
6571
|
return cands.length ? this.mat(cands[0]) : null;
|
|
@@ -6347,7 +6601,9 @@ var SwarmClient = class _SwarmClient {
|
|
|
6347
6601
|
if (!depends_on || depends_on.length === 0) return [];
|
|
6348
6602
|
const doneInMirror = /* @__PURE__ */ new Set();
|
|
6349
6603
|
for (const r of this.runs.values()) {
|
|
6350
|
-
if (r.status === "done" && r.resolves_run_id == null && r.reviews_run_id == null
|
|
6604
|
+
if (r.status === "done" && r.resolves_run_id == null && r.reviews_run_id == null && r.verifies_run_id == null) {
|
|
6605
|
+
doneInMirror.add(r.task_slug);
|
|
6606
|
+
}
|
|
6351
6607
|
}
|
|
6352
6608
|
return depends_on.map((slug) => {
|
|
6353
6609
|
if (doneInMirror.has(slug)) {
|
|
@@ -6393,7 +6649,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6393
6649
|
// Служебный сабран под тем же слагом (резолвер конфликта / ревьюер): его финал —
|
|
6394
6650
|
// не финал задачи. Резолвер вычёркивался и до TASWM-361, ревьюер добавлен вместе
|
|
6395
6651
|
// с расширением критерия — иначе остановленный ревьюер закрывал бы зависимость.
|
|
6396
|
-
service: Boolean(r.resolvesRunId) || Boolean(r.reviewsRunId),
|
|
6652
|
+
service: Boolean(r.resolvesRunId) || Boolean(r.reviewsRunId) || Boolean(r.verifiesRunId),
|
|
6397
6653
|
// В проде имя дрона — колонка droneName (см. hydrateRun); `name` терпим как
|
|
6398
6654
|
// запасной вариант ради фикстур и старых ответов.
|
|
6399
6655
|
name: r.droneName ?? r.name ?? null,
|
|
@@ -6609,7 +6865,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6609
6865
|
return this.eventsLog.filter((e) => {
|
|
6610
6866
|
if (e.kind !== "failed" || e.run_id == null || e.at < cutoff) return false;
|
|
6611
6867
|
const r = this.runs.get(e.run_id);
|
|
6612
|
-
return !!r && r.resolves_run_id == null && r.reviews_run_id == null;
|
|
6868
|
+
return !!r && r.resolves_run_id == null && r.reviews_run_id == null && r.verifies_run_id == null;
|
|
6613
6869
|
}).length;
|
|
6614
6870
|
}
|
|
6615
6871
|
recordStormProbe(probe, ext, reason, resetHint) {
|
|
@@ -6734,7 +6990,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6734
6990
|
const workerWt = droneWorktreePath(opts.worktreesDir, d.repo || this.defaultRepo, d.task_slug);
|
|
6735
6991
|
let matchWt = null;
|
|
6736
6992
|
let matchPid = null;
|
|
6737
|
-
if (d.resolves_run_id == null && d.reviews_run_id == null) {
|
|
6993
|
+
if (d.resolves_run_id == null && d.reviews_run_id == null && d.verifies_run_id == null) {
|
|
6738
6994
|
const p = pidByWorktree.get(workerWt);
|
|
6739
6995
|
if (p != null) {
|
|
6740
6996
|
matchWt = workerWt;
|
|
@@ -6750,9 +7006,10 @@ var SwarmClient = class _SwarmClient {
|
|
|
6750
7006
|
}
|
|
6751
7007
|
}
|
|
6752
7008
|
} else {
|
|
6753
|
-
const
|
|
7009
|
+
const checkedId = d.reviews_run_id ?? d.verifies_run_id;
|
|
7010
|
+
const reviewed = this.runs.get(checkedId);
|
|
6754
7011
|
const reviewedWt = reviewed ? droneWorktreePath(opts.worktreesDir, reviewed.repo || this.defaultRepo, reviewed.task_slug) : workerWt;
|
|
6755
|
-
const prefix = `${reviewedWt}-review-`;
|
|
7012
|
+
const prefix = d.reviews_run_id != null ? `${reviewedWt}-review-` : `${reviewedWt}-verify-`;
|
|
6756
7013
|
for (const [wt, p] of pidByWorktree) {
|
|
6757
7014
|
if (wt.startsWith(prefix)) {
|
|
6758
7015
|
matchWt = wt;
|
|
@@ -6765,7 +7022,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6765
7022
|
this.runtime.set(d.id, { pid: matchPid, worktree_path: matchWt });
|
|
6766
7023
|
alive++;
|
|
6767
7024
|
} else {
|
|
6768
|
-
const wt = d.resolves_run_id == null && d.reviews_run_id == null && wtExists(workerWt) ? workerWt : null;
|
|
7025
|
+
const wt = d.resolves_run_id == null && d.reviews_run_id == null && d.verifies_run_id == null && wtExists(workerWt) ? workerWt : null;
|
|
6769
7026
|
this.runtime.set(d.id, { pid: null, worktree_path: wt });
|
|
6770
7027
|
dead++;
|
|
6771
7028
|
}
|
|
@@ -6786,6 +7043,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
6786
7043
|
if (sup) local.superseded_by = this.localIdForProdId(sup) ?? local.superseded_by;
|
|
6787
7044
|
const rev = pr.reviewsRunId ?? null;
|
|
6788
7045
|
if (rev) local.reviews_run_id = this.localIdForProdId(rev) ?? local.reviews_run_id;
|
|
7046
|
+
const ver = pr.verifiesRunId ?? null;
|
|
7047
|
+
if (ver) local.verifies_run_id = this.localIdForProdId(ver) ?? local.verifies_run_id;
|
|
6789
7048
|
}
|
|
6790
7049
|
/**
|
|
6791
7050
|
* Авто-интейк (TASWM-100): взять следующую claimable-задачу владельца токена через
|
|
@@ -7282,9 +7541,16 @@ var SwarmClient = class _SwarmClient {
|
|
|
7282
7541
|
* простоя, видимого только в логе на самой машине. Не передали (нечем измерить) → ключ
|
|
7283
7542
|
* не шлём, прод прошлое не трогает — та же логика, что у capacity/repoFullNames.
|
|
7284
7543
|
*
|
|
7544
|
+
* TASWM-333: тем же пульсом уходит ШТОРМ этой машины (`storm` — штормим ли сейчас, и с
|
|
7545
|
+
* какой формулировкой мы в него вошли). Состояние живёт в наших событиях и переживает
|
|
7546
|
+
* рестарт, а в проде его не было вовсе: рой стоял, а парк машин показывал живую свободную
|
|
7547
|
+
* машину — 10.08 это стоило десяти часов простоя, замеченных человеком вручную. Шлём
|
|
7548
|
+
* ВСЕГДА (в т.ч. `storm:false`), как паузу: это состояние, а не команда, и молчание тут
|
|
7549
|
+
* означало бы «прод помнит вчерашний шторм», когда рой давно работает.
|
|
7550
|
+
*
|
|
7285
7551
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
7286
7552
|
*/
|
|
7287
|
-
async heartbeat(daemonVersion, pause, capacity, resources) {
|
|
7553
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm) {
|
|
7288
7554
|
try {
|
|
7289
7555
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
7290
7556
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -7294,12 +7560,17 @@ var SwarmClient = class _SwarmClient {
|
|
|
7294
7560
|
args.paused = !!pause?.paused;
|
|
7295
7561
|
if (pause?.reason) args.pauseReason = pause.reason;
|
|
7296
7562
|
}
|
|
7563
|
+
if (storm !== void 0 && storm !== null) {
|
|
7564
|
+
args.storm = storm.active;
|
|
7565
|
+
if (storm.reason) args.stormReason = storm.reason;
|
|
7566
|
+
}
|
|
7297
7567
|
if (this.host) {
|
|
7298
7568
|
args.hostId = this.host.hostId;
|
|
7299
7569
|
args.hostName = this.host.hostName;
|
|
7300
7570
|
}
|
|
7301
7571
|
const res = await this.call("swarm_heartbeat", args);
|
|
7302
7572
|
if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
|
|
7573
|
+
this.heartbeatOkAt = this.now();
|
|
7303
7574
|
const data = res?.structuredContent ?? {};
|
|
7304
7575
|
return {
|
|
7305
7576
|
commands: Array.isArray(data.commands) ? data.commands.filter(isHostCommandShape) : [],
|
|
@@ -7511,6 +7782,9 @@ var SwarmClient = class _SwarmClient {
|
|
|
7511
7782
|
resolves_run_id: null,
|
|
7512
7783
|
reviews_run_id: null,
|
|
7513
7784
|
review_verdict: null,
|
|
7785
|
+
verifies_run_id: null,
|
|
7786
|
+
// прогон-верификатор (TASWM-344) — прод-поле, заполняется ниже
|
|
7787
|
+
verify_verdict: null,
|
|
7514
7788
|
review_phase: null,
|
|
7515
7789
|
// вычисляемая фаза ревью (TASWM-230) — прод хранит и раздаёт
|
|
7516
7790
|
// Локальная бухгалтерия (в прод-схему не уходит) — на гидрации из прода дефолт 0;
|
|
@@ -7554,6 +7828,9 @@ var SwarmClient = class _SwarmClient {
|
|
|
7554
7828
|
const revProd = pr.reviewsRunId ?? null;
|
|
7555
7829
|
if (revProd) local.reviews_run_id = this.localIdForProdId(revProd) ?? local.reviews_run_id;
|
|
7556
7830
|
local.review_verdict = pr.reviewVerdict ?? local.review_verdict;
|
|
7831
|
+
const verProd = pr.verifiesRunId ?? null;
|
|
7832
|
+
if (verProd) local.verifies_run_id = this.localIdForProdId(verProd) ?? local.verifies_run_id;
|
|
7833
|
+
local.verify_verdict = toVerifyVerdict(pr.verifyVerdict) ?? local.verify_verdict;
|
|
7557
7834
|
local.review_phase = pr.reviewPhase ?? local.review_phase;
|
|
7558
7835
|
local.repo = this.resolveRepo(pr.repo ?? null);
|
|
7559
7836
|
local.host_id = pr.hostId ?? null;
|
|
@@ -7842,6 +8119,18 @@ var SwarmClient = class _SwarmClient {
|
|
|
7842
8119
|
pendingCount() {
|
|
7843
8120
|
return this.pending.length;
|
|
7844
8121
|
}
|
|
8122
|
+
/**
|
|
8123
|
+
* Когда прод в последний раз ПРИНЯЛ пульс этой машины (ISO) — вход `swarm status`.
|
|
8124
|
+
* null — ни разу за жизнь процесса.
|
|
8125
|
+
*
|
|
8126
|
+
* Почему именно пульс, а не «последний удачный вызов»: в парке машин lastSeenAt прод
|
|
8127
|
+
* пишет ровно на swarm_heartbeat. Спрашивать за тем же самым swarm_list_hosts значило бы
|
|
8128
|
+
* завести второй источник правды и платить прод-вызовом в команде, которую зовут именно
|
|
8129
|
+
* тогда, когда прод не отвечает.
|
|
8130
|
+
*/
|
|
8131
|
+
lastHeardAt() {
|
|
8132
|
+
return this.heartbeatOkAt == null ? null : new Date(this.heartbeatOkAt).toISOString();
|
|
8133
|
+
}
|
|
7845
8134
|
close() {
|
|
7846
8135
|
if (this.flushTimer) {
|
|
7847
8136
|
clearTimeout(this.flushTimer);
|
|
@@ -7995,13 +8284,14 @@ function pickRepoPaths(args) {
|
|
|
7995
8284
|
// src/repoDiscovery.ts
|
|
7996
8285
|
init_src();
|
|
7997
8286
|
init_config();
|
|
8287
|
+
init_linkFailure();
|
|
7998
8288
|
import { homedir as homedir3 } from "os";
|
|
7999
|
-
import { readFileSync as
|
|
8000
|
-
import { isAbsolute, join as
|
|
8289
|
+
import { readFileSync as readFileSync9, renameSync as renameSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
8290
|
+
import { isAbsolute, join as join10, resolve as resolve2 } from "path";
|
|
8001
8291
|
|
|
8002
8292
|
// src/repoScan.ts
|
|
8003
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
8004
|
-
import { join as
|
|
8293
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
8294
|
+
import { join as join9 } from "path";
|
|
8005
8295
|
var SKIP = /* @__PURE__ */ new Set([
|
|
8006
8296
|
"node_modules",
|
|
8007
8297
|
"vendor",
|
|
@@ -8031,7 +8321,7 @@ function parseOriginFromGitConfig(text) {
|
|
|
8031
8321
|
}
|
|
8032
8322
|
function originOf(dir) {
|
|
8033
8323
|
try {
|
|
8034
|
-
return parseOriginFromGitConfig(
|
|
8324
|
+
return parseOriginFromGitConfig(readFileSync8(join9(dir, ".git", "config"), "utf8"));
|
|
8035
8325
|
} catch {
|
|
8036
8326
|
return null;
|
|
8037
8327
|
}
|
|
@@ -8066,7 +8356,7 @@ function walk(dir, depth, maxDepth, found) {
|
|
|
8066
8356
|
}
|
|
8067
8357
|
for (const name of entries) {
|
|
8068
8358
|
if (name.startsWith(".") || SKIP.has(name)) continue;
|
|
8069
|
-
const child =
|
|
8359
|
+
const child = join9(dir, name);
|
|
8070
8360
|
try {
|
|
8071
8361
|
if (!statSync5(child).isDirectory()) continue;
|
|
8072
8362
|
} catch {
|
|
@@ -8088,7 +8378,7 @@ function defaultScanRoots(home, knownPaths = []) {
|
|
|
8088
8378
|
if (parent && parent !== p) add(parent);
|
|
8089
8379
|
}
|
|
8090
8380
|
for (const name of ["projects", "code", "src", "dev", "work", "repos", "git", "Projects", "Code", "Developer", "Documents/GitHub", "go/src"]) {
|
|
8091
|
-
add(
|
|
8381
|
+
add(join9(home, name));
|
|
8092
8382
|
}
|
|
8093
8383
|
return out;
|
|
8094
8384
|
}
|
|
@@ -8102,7 +8392,7 @@ function resolveScanRoots(raw, home = homedir3(), knownPaths = []) {
|
|
|
8102
8392
|
for (const part of v.split(",")) {
|
|
8103
8393
|
const p = part.trim();
|
|
8104
8394
|
if (!p) continue;
|
|
8105
|
-
const abs = p === "~" ? home : p.startsWith("~/") ?
|
|
8395
|
+
const abs = p === "~" ? home : p.startsWith("~/") ? join10(home, p.slice(2)) : isAbsolute(p) ? p : resolve2(home, p);
|
|
8106
8396
|
if (!out.includes(abs)) out.push(abs);
|
|
8107
8397
|
}
|
|
8108
8398
|
return out;
|
|
@@ -8115,16 +8405,24 @@ function normalizeFullNames(list) {
|
|
|
8115
8405
|
}
|
|
8116
8406
|
return out;
|
|
8117
8407
|
}
|
|
8408
|
+
function errorText(res) {
|
|
8409
|
+
return (res.content ?? []).map((c) => c?.text ?? "").filter(Boolean).join(" ").trim();
|
|
8410
|
+
}
|
|
8118
8411
|
async function fetchSwarmRepos(call) {
|
|
8119
8412
|
let res;
|
|
8120
8413
|
try {
|
|
8121
8414
|
res = await call("swarm_my_repos", {});
|
|
8122
|
-
} catch {
|
|
8123
|
-
return
|
|
8415
|
+
} catch (e) {
|
|
8416
|
+
return { ok: false, ...explainLinkFailure(e) };
|
|
8417
|
+
}
|
|
8418
|
+
if (!res) return { ok: false, kind: "offline", detail: "\u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u0434\u0430\u043B \u043E\u0442\u0432\u0435\u0442\u0430" };
|
|
8419
|
+
if (res.isError) {
|
|
8420
|
+
return { ok: false, ...explainLinkFailure(errorText(res) || "\u0431\u0435\u0437 \u0442\u0435\u043A\u0441\u0442\u0430 \u043E\u0448\u0438\u0431\u043A\u0438") };
|
|
8124
8421
|
}
|
|
8125
|
-
if (!res || res.isError) return null;
|
|
8126
8422
|
const raw = res.structuredContent?.repos;
|
|
8127
|
-
if (!Array.isArray(raw))
|
|
8423
|
+
if (!Array.isArray(raw)) {
|
|
8424
|
+
return { ok: false, kind: "malformed", detail: `\u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043D\u0435\u0442 \u0441\u043F\u0438\u0441\u043A\u0430 repos (${typeof raw})` };
|
|
8425
|
+
}
|
|
8128
8426
|
const out = [];
|
|
8129
8427
|
for (const item of raw) {
|
|
8130
8428
|
if (!item || typeof item !== "object") continue;
|
|
@@ -8137,7 +8435,24 @@ async function fetchSwarmRepos(call) {
|
|
|
8137
8435
|
workspaceSlug: typeof r.workspaceSlug === "string" ? r.workspaceSlug : ""
|
|
8138
8436
|
});
|
|
8139
8437
|
}
|
|
8140
|
-
return out;
|
|
8438
|
+
return { ok: true, repos: out };
|
|
8439
|
+
}
|
|
8440
|
+
function describeReposFailure(kind, detail) {
|
|
8441
|
+
const head = "[overmind] \u043D\u0435 \u0443\u0437\u043D\u0430\u043B \u0443 \u043F\u0440\u043E\u0434\u0430, \u043A\u0430\u043A\u0438\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0443 \u0440\u043E\u044F (swarm_my_repos) \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u044E \u043F\u043E \u043D\u0430\u0439\u0434\u0435\u043D\u043D\u043E\u043C\u0443 \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435. ";
|
|
8442
|
+
switch (kind) {
|
|
8443
|
+
case "missing_tool":
|
|
8444
|
+
return `${head}\u041F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u0442\u0430\u043A\u043E\u0439 \u0440\u0443\u0447\u043A\u0438 \u0443 \u043D\u0435\u0433\u043E \u043D\u0435\u0442 (${detail}) \u2014 \u0432\u043E\u0442 \u044D\u0442\u043E \u0438 \u0435\u0441\u0442\u044C \xAB\u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u0442\u0430\u0440\u0435\u0435 \u0434\u0435\u043C\u043E\u043D\u0430\xBB, \u043B\u0435\u0447\u0438\u0442\u0441\u044F \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435\u043C \u043F\u0440\u043E\u0434\u0430.`;
|
|
8445
|
+
case "unauthorized":
|
|
8446
|
+
return `${head}\u041F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0438 \u041D\u0415 \u041F\u0423\u0421\u0422\u0418\u041B: \u0442\u043E\u043A\u0435\u043D \u043D\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u0435\u043D (${detail}). \u041B\u0435\u0447\u0438\u0442\u0441\u044F \u043F\u0435\u0440\u0435\u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439: swarm login.`;
|
|
8447
|
+
case "offline":
|
|
8448
|
+
return `${head}\u0412\u042B\u0417\u041E\u0412 \u041D\u0415 \u0414\u041E\u0428\u0401\u041B \u0434\u043E \u043F\u0440\u043E\u0434\u0430 \u2014 \u0441\u0432\u044F\u0437\u044C \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C (${detail}). \u042D\u0442\u043E \u041D\u0415 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043D\u0435 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0430\u044F \u0440\u0443\u0447\u043A\u0430: \u043F\u043E\u0432\u0442\u043E\u0440\u044E \u043D\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0442\u0438\u043A\u0435, \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043F\u0440\u043E\u0434 \u043D\u0435 \u043D\u0443\u0436\u043D\u043E.`;
|
|
8449
|
+
case "malformed":
|
|
8450
|
+
return `${head}\u041F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0442\u0438\u043B, \u043D\u043E \u0432 \u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0435 (${detail}).`;
|
|
8451
|
+
case "unknown":
|
|
8452
|
+
return `${head}\u041F\u041E\u0427\u0415\u041C\u0423 \u2014 \u041D\u0415\u0418\u0417\u0412\u0415\u0421\u0422\u041D\u041E (${detail}). \u041D\u0438 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043D\u0438 \u043E\u0431\u0440\u044B\u0432 \u0441\u0432\u044F\u0437\u0438 \u043E\u0442\u0441\u044E\u0434\u0430 \u043D\u0435 \u0441\u043B\u0435\u0434\u0443\u044E\u0442: \u0441\u043C\u043E\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0443 \xABSwarm MCP: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0432\u044B\u0448\u043B\u043E \u2026\xBB \u0432\u044B\u0448\u0435 \u2014 \u0442\u0430\u043C \u043F\u0440\u0438\u0447\u0438\u043D\u0430, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u0432\u0438\u0434\u0435\u043B \u043A\u043E\u043D\u043D\u0435\u043A\u0442\u043E\u0440.`;
|
|
8453
|
+
default:
|
|
8454
|
+
return `${head}\u041F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043E\u0442\u043A\u0430\u0437\u043E\u043C: ${detail}.`;
|
|
8455
|
+
}
|
|
8141
8456
|
}
|
|
8142
8457
|
function emptyRepoMapState(roots) {
|
|
8143
8458
|
return { clones: [], missing: [], roots: [...roots], at: null, unknown: true };
|
|
@@ -8147,7 +8462,8 @@ async function refreshRepoMap(opts) {
|
|
|
8147
8462
|
});
|
|
8148
8463
|
const scan = opts.scan ?? scanRootsAll;
|
|
8149
8464
|
const repoAt = opts.repoAt ?? repoFullNameAt;
|
|
8150
|
-
const
|
|
8465
|
+
const answer = await fetchSwarmRepos(opts.call);
|
|
8466
|
+
const wanted = answer.ok ? answer.repos : null;
|
|
8151
8467
|
const clones = /* @__PURE__ */ new Map();
|
|
8152
8468
|
const take = (repoFullName, path, source, alternatives = []) => {
|
|
8153
8469
|
if (clones.has(repoFullName)) return;
|
|
@@ -8182,10 +8498,8 @@ async function refreshRepoMap(opts) {
|
|
|
8182
8498
|
opts.cachePath,
|
|
8183
8499
|
[...clones.values()].map((c) => ({ repoFullName: c.repoFullName, path: c.path }))
|
|
8184
8500
|
);
|
|
8185
|
-
if (!
|
|
8186
|
-
log(
|
|
8187
|
-
"[overmind] \u043F\u0440\u043E\u0434 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B, \u043A\u0430\u043A\u0438\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0443 \u0440\u043E\u044F (swarm_my_repos) \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u044E \u043F\u043E \u043D\u0430\u0439\u0434\u0435\u043D\u043D\u043E\u043C\u0443 \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435. \u0421\u0435\u0440\u0432\u0435\u0440 \u0441\u0442\u0430\u0440\u0435\u0435 \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u044D\u0442\u043E \u043E\u0436\u0438\u0434\u0430\u0435\u043C\u043E \u0438 \u043B\u0435\u0447\u0438\u0442\u0441\u044F \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435\u043C \u043F\u0440\u043E\u0434\u0430."
|
|
8188
|
-
);
|
|
8501
|
+
if (!answer.ok) {
|
|
8502
|
+
log(describeReposFailure(answer.kind, answer.detail));
|
|
8189
8503
|
return registry;
|
|
8190
8504
|
}
|
|
8191
8505
|
opts.state.unknown = false;
|
|
@@ -8199,7 +8513,7 @@ async function refreshRepoMap(opts) {
|
|
|
8199
8513
|
}
|
|
8200
8514
|
log(`[overmind] \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0440\u043E\u044F (${wantedNames.length}), \u043A\u0430\u0440\u0442\u0430 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435:`);
|
|
8201
8515
|
for (const c of opts.state.clones) log(`[overmind] ${describeClone(c)}`);
|
|
8202
|
-
const prefixOf = new Map(
|
|
8516
|
+
const prefixOf = new Map(answer.repos.map((r) => [r.repoFullName, r.taskPrefix]));
|
|
8203
8517
|
for (const name of opts.state.missing) {
|
|
8204
8518
|
const prefix = prefixOf.get(name);
|
|
8205
8519
|
log(`[overmind] ${describeMissing(prefix ? `${name} (\u043F\u0440\u043E\u0435\u043A\u0442 ${prefix})` : name, opts.roots)}`);
|
|
@@ -8228,7 +8542,7 @@ function repoMapRows(state) {
|
|
|
8228
8542
|
}
|
|
8229
8543
|
function readRepoMapCache(path) {
|
|
8230
8544
|
try {
|
|
8231
|
-
const raw = JSON.parse(
|
|
8545
|
+
const raw = JSON.parse(readFileSync9(path, "utf8"));
|
|
8232
8546
|
if (!Array.isArray(raw?.entries)) return [];
|
|
8233
8547
|
return raw.entries.filter(
|
|
8234
8548
|
(e) => !!e && typeof e.repoFullName === "string" && typeof e.path === "string" && !!e.repoFullName && !!e.path
|
|
@@ -8240,7 +8554,7 @@ function readRepoMapCache(path) {
|
|
|
8240
8554
|
function writeRepoMapCache(path, entries) {
|
|
8241
8555
|
try {
|
|
8242
8556
|
const tmp = `${path}.tmp`;
|
|
8243
|
-
|
|
8557
|
+
writeFileSync8(tmp, JSON.stringify({ at: stamp(), entries }, null, 2));
|
|
8244
8558
|
renameSync5(tmp, path);
|
|
8245
8559
|
} catch {
|
|
8246
8560
|
}
|
|
@@ -8248,17 +8562,17 @@ function writeRepoMapCache(path, entries) {
|
|
|
8248
8562
|
|
|
8249
8563
|
// src/hostIdentity.ts
|
|
8250
8564
|
import { hostname } from "os";
|
|
8251
|
-
import { existsSync as existsSync7, readFileSync as
|
|
8252
|
-
import { join as
|
|
8565
|
+
import { existsSync as existsSync7, readFileSync as readFileSync10, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "fs";
|
|
8566
|
+
import { join as join11 } from "path";
|
|
8253
8567
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
8254
8568
|
function hostIdentityPath(home) {
|
|
8255
|
-
return
|
|
8569
|
+
return join11(home, "host.json");
|
|
8256
8570
|
}
|
|
8257
8571
|
function loadHostIdentity(deps) {
|
|
8258
8572
|
const exists = deps.exists ?? existsSync7;
|
|
8259
|
-
const readFile2 = deps.readFile ?? ((p) =>
|
|
8573
|
+
const readFile2 = deps.readFile ?? ((p) => readFileSync10(p, "utf8"));
|
|
8260
8574
|
const writeFile3 = deps.writeFile ?? ((p, data) => {
|
|
8261
|
-
|
|
8575
|
+
writeFileSync9(`${p}.tmp`, data);
|
|
8262
8576
|
renameSync6(`${p}.tmp`, p);
|
|
8263
8577
|
});
|
|
8264
8578
|
const host = deps.hostname ?? hostname;
|
|
@@ -8444,12 +8758,12 @@ import { spawn } from "child_process";
|
|
|
8444
8758
|
import { closeSync as closeSync2, existsSync as existsSync8, mkdirSync as mkdirSync6, openSync as openSync2, readdirSync as readdirSync7 } from "fs";
|
|
8445
8759
|
import { createRequire } from "module";
|
|
8446
8760
|
import { homedir as homedir4 } from "os";
|
|
8447
|
-
import { dirname as dirname2, join as
|
|
8761
|
+
import { dirname as dirname2, join as join13 } from "path";
|
|
8448
8762
|
import { fileURLToPath } from "url";
|
|
8449
8763
|
|
|
8450
8764
|
// src/droneHome.ts
|
|
8451
8765
|
import { lstatSync, mkdirSync as mkdirSync5, readdirSync as readdirSync6, rmSync, statSync as statSync6, symlinkSync as symlinkSync2 } from "fs";
|
|
8452
|
-
import { join as
|
|
8766
|
+
import { join as join12, relative, sep } from "path";
|
|
8453
8767
|
var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
|
|
8454
8768
|
function droneHomeDeny(env = process.env) {
|
|
8455
8769
|
const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -8473,20 +8787,20 @@ function buildDroneHome(opts) {
|
|
|
8473
8787
|
if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
|
|
8474
8788
|
for (const existing of readdirSync6(droneHome)) {
|
|
8475
8789
|
if (wanted.has(existing)) continue;
|
|
8476
|
-
const p =
|
|
8790
|
+
const p = join12(droneHome, existing);
|
|
8477
8791
|
try {
|
|
8478
8792
|
if (lstatSync(p).isSymbolicLink()) rmSync(p);
|
|
8479
8793
|
} catch {
|
|
8480
8794
|
}
|
|
8481
8795
|
}
|
|
8482
8796
|
for (const name of wanted) {
|
|
8483
|
-
const link =
|
|
8797
|
+
const link = join12(droneHome, name);
|
|
8484
8798
|
try {
|
|
8485
8799
|
lstatSync(link);
|
|
8486
8800
|
continue;
|
|
8487
8801
|
} catch {
|
|
8488
8802
|
}
|
|
8489
|
-
const target =
|
|
8803
|
+
const target = join12(realHome, name);
|
|
8490
8804
|
let isDir;
|
|
8491
8805
|
try {
|
|
8492
8806
|
isDir = statSync6(target).isDirectory();
|
|
@@ -8507,7 +8821,7 @@ var REAL_PATH_ENV_PINS = [
|
|
|
8507
8821
|
{ env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
|
|
8508
8822
|
];
|
|
8509
8823
|
function realPathEnvPins(realHome) {
|
|
8510
|
-
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env,
|
|
8824
|
+
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join12(realHome, ...segments)]));
|
|
8511
8825
|
}
|
|
8512
8826
|
function dronePrivatePaths(realHome, swarmHome) {
|
|
8513
8827
|
return [
|
|
@@ -8605,7 +8919,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
|
|
|
8605
8919
|
const env = { ...base3 };
|
|
8606
8920
|
const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
|
|
8607
8921
|
if (isolate && !base3.TURBO_CACHE_DIR) {
|
|
8608
|
-
env.TURBO_CACHE_DIR =
|
|
8922
|
+
env.TURBO_CACHE_DIR = join13(worktree, ".turbo", "cache");
|
|
8609
8923
|
}
|
|
8610
8924
|
Object.assign(env, droneTestEnv(droneCap, base3));
|
|
8611
8925
|
for (const key of DAEMON_STATE_ENV) delete env[key];
|
|
@@ -8633,16 +8947,16 @@ var DAEMON_STATE_ENV = [
|
|
|
8633
8947
|
"TASKLESS_TOKEN"
|
|
8634
8948
|
];
|
|
8635
8949
|
function bundledRoleDir(kind) {
|
|
8636
|
-
return
|
|
8950
|
+
return join13(dirname2(fileURLToPath(import.meta.url)), "..", "roles", kind);
|
|
8637
8951
|
}
|
|
8638
8952
|
function roleDir(worktree, kind) {
|
|
8639
|
-
return
|
|
8953
|
+
return join13(worktree, ".claude", "roles", kind);
|
|
8640
8954
|
}
|
|
8641
8955
|
function roleDirs(worktree, kind) {
|
|
8642
8956
|
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync8(dir));
|
|
8643
8957
|
}
|
|
8644
8958
|
function nextRunLogPath(runLogsDir, runId) {
|
|
8645
|
-
const dir =
|
|
8959
|
+
const dir = join13(runLogsDir, String(runId));
|
|
8646
8960
|
mkdirSync6(dir, { recursive: true });
|
|
8647
8961
|
let files = [];
|
|
8648
8962
|
try {
|
|
@@ -8654,7 +8968,7 @@ function nextRunLogPath(runLogsDir, runId) {
|
|
|
8654
8968
|
const m = /^(\d+)\.json/.exec(f);
|
|
8655
8969
|
if (m) next = Math.max(next, Number(m[1]) + 1);
|
|
8656
8970
|
}
|
|
8657
|
-
return
|
|
8971
|
+
return join13(dir, `${next}.json`);
|
|
8658
8972
|
}
|
|
8659
8973
|
function openNextRunLog(runLogsDir, runId) {
|
|
8660
8974
|
for (let attempt = 0; attempt < 50; attempt++) {
|
|
@@ -8722,6 +9036,9 @@ function makeLauncher(cfg) {
|
|
|
8722
9036
|
SWARM_RUN_NAME: run.name,
|
|
8723
9037
|
...run.workspace_id ? { SWARM_WORKSPACE_ID: run.workspace_id } : {},
|
|
8724
9038
|
...run.reviews_run_id != null ? { SWARM_REVIEWS_RUN_ID: String(run.reviews_run_id) } : {},
|
|
9039
|
+
// TASWM-344: та же роль, что у SWARM_REVIEWS_RUN_ID выше, для верификатора — без неё
|
|
9040
|
+
// его done() в client-режиме пошёл бы веткой обычной сдачи PR, и вердикт пропал бы.
|
|
9041
|
+
...run.verifies_run_id != null ? { SWARM_VERIFIES_RUN_ID: String(run.verifies_run_id) } : {},
|
|
8725
9042
|
...run.hero_run_id != null ? { SWARM_HERO_RUN_ID: String(run.hero_run_id) } : {}
|
|
8726
9043
|
}
|
|
8727
9044
|
};
|
|
@@ -8734,9 +9051,9 @@ function makeLauncher(cfg) {
|
|
|
8734
9051
|
}
|
|
8735
9052
|
const realHome = cfg.droneIsolation?.realHome ?? homedir4();
|
|
8736
9053
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
8737
|
-
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync8(
|
|
9054
|
+
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync8(join13(cfg.droneIsolation.swarmHome, "token"))) {
|
|
8738
9055
|
console.error(
|
|
8739
|
-
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${
|
|
9056
|
+
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join13(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
|
|
8740
9057
|
);
|
|
8741
9058
|
}
|
|
8742
9059
|
const executor = cfg.executor ?? resolveExecutor();
|
|
@@ -8792,8 +9109,8 @@ init_procCleanup();
|
|
|
8792
9109
|
|
|
8793
9110
|
// src/cost.ts
|
|
8794
9111
|
init_executors();
|
|
8795
|
-
import { readFileSync as
|
|
8796
|
-
import { join as
|
|
9112
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync8, renameSync as renameSync7 } from "fs";
|
|
9113
|
+
import { join as join14 } from "path";
|
|
8797
9114
|
var UNPROCESSED_LOG_RE = /^\d+\.json$/;
|
|
8798
9115
|
function unprocessedLogFiles(dir) {
|
|
8799
9116
|
try {
|
|
@@ -8803,7 +9120,7 @@ function unprocessedLogFiles(dir) {
|
|
|
8803
9120
|
}
|
|
8804
9121
|
}
|
|
8805
9122
|
function accountRunCost(deps, run) {
|
|
8806
|
-
const dir =
|
|
9123
|
+
const dir = join14(deps.runLogsDir, String(run.id));
|
|
8807
9124
|
const files = unprocessedLogFiles(dir);
|
|
8808
9125
|
if (files.length === 0) return;
|
|
8809
9126
|
const model = resolveRunModel(run, deps.swarmModels?.() ?? null);
|
|
@@ -8814,10 +9131,10 @@ function accountRunCost(deps, run) {
|
|
|
8814
9131
|
let durationMs = 0;
|
|
8815
9132
|
const processed = [];
|
|
8816
9133
|
for (const f of files) {
|
|
8817
|
-
const full =
|
|
9134
|
+
const full = join14(dir, f);
|
|
8818
9135
|
let raw = null;
|
|
8819
9136
|
try {
|
|
8820
|
-
raw =
|
|
9137
|
+
raw = readFileSync11(full, "utf8");
|
|
8821
9138
|
} catch {
|
|
8822
9139
|
continue;
|
|
8823
9140
|
}
|
|
@@ -8854,14 +9171,14 @@ function makeCostAccountant(deps) {
|
|
|
8854
9171
|
|
|
8855
9172
|
// src/runOutcome.ts
|
|
8856
9173
|
init_executors();
|
|
8857
|
-
import { readdirSync as readdirSync9, readFileSync as
|
|
8858
|
-
import { join as
|
|
9174
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync12 } from "fs";
|
|
9175
|
+
import { join as join15 } from "path";
|
|
8859
9176
|
function endedItself(outcome) {
|
|
8860
9177
|
return outcome === "graceful" || outcome === "budget";
|
|
8861
9178
|
}
|
|
8862
9179
|
var RESULT_FILE_RE = /^(\d+)\.json(\.done)?$/;
|
|
8863
9180
|
function classifyExit(runLogsDir, runId, executor = resolveExecutor()) {
|
|
8864
|
-
const dir =
|
|
9181
|
+
const dir = join15(runLogsDir, String(runId));
|
|
8865
9182
|
let files;
|
|
8866
9183
|
try {
|
|
8867
9184
|
files = readdirSync9(dir);
|
|
@@ -8878,7 +9195,7 @@ function classifyExit(runLogsDir, runId, executor = resolveExecutor()) {
|
|
|
8878
9195
|
if (!latest) return "unknown";
|
|
8879
9196
|
let raw;
|
|
8880
9197
|
try {
|
|
8881
|
-
raw =
|
|
9198
|
+
raw = readFileSync12(join15(dir, latest.name), "utf8");
|
|
8882
9199
|
} catch {
|
|
8883
9200
|
return "unknown";
|
|
8884
9201
|
}
|
|
@@ -8896,20 +9213,20 @@ init_src();
|
|
|
8896
9213
|
init_git();
|
|
8897
9214
|
|
|
8898
9215
|
// src/deps.ts
|
|
8899
|
-
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as
|
|
8900
|
-
import { join as
|
|
9216
|
+
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync13 } from "fs";
|
|
9217
|
+
import { join as join18 } from "path";
|
|
8901
9218
|
import { execa } from "execa";
|
|
8902
9219
|
|
|
8903
9220
|
// src/prepareLock.ts
|
|
8904
9221
|
import { createHash } from "crypto";
|
|
8905
9222
|
import { closeSync as closeSync3, openSync as openSync3, rmSync as rmSync2, statSync as statSync8, writeSync } from "fs";
|
|
8906
|
-
import { join as
|
|
9223
|
+
import { join as join17 } from "path";
|
|
8907
9224
|
import { tmpdir } from "os";
|
|
8908
9225
|
var WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
8909
9226
|
var POLL_MS = 300;
|
|
8910
9227
|
var STALE_MS = 20 * 6e4;
|
|
8911
9228
|
function machineLockPath(key, dir = tmpdir()) {
|
|
8912
|
-
return
|
|
9229
|
+
return join17(dir, `swarm-prepare-${createHash("sha1").update(key).digest("hex").slice(0, 16)}.lock`);
|
|
8913
9230
|
}
|
|
8914
9231
|
function defaultTryCreate(path, content) {
|
|
8915
9232
|
try {
|
|
@@ -8983,7 +9300,7 @@ async function runCommand(cmd, worktree) {
|
|
|
8983
9300
|
}
|
|
8984
9301
|
function safeReadText(p) {
|
|
8985
9302
|
try {
|
|
8986
|
-
return
|
|
9303
|
+
return readFileSync13(p, "utf8");
|
|
8987
9304
|
} catch {
|
|
8988
9305
|
return null;
|
|
8989
9306
|
}
|
|
@@ -9025,19 +9342,19 @@ function overridePlan(override) {
|
|
|
9025
9342
|
function detectPackageManager(worktree, io = {}) {
|
|
9026
9343
|
const exists = io.exists ?? existsSync10;
|
|
9027
9344
|
const readText = io.readText ?? safeReadText;
|
|
9028
|
-
const hasPkgJson = exists(
|
|
9345
|
+
const hasPkgJson = exists(join18(worktree, "package.json"));
|
|
9029
9346
|
if (!hasPkgJson) {
|
|
9030
|
-
if (exists(
|
|
9347
|
+
if (exists(join18(worktree, "deno.json")) || exists(join18(worktree, "deno.jsonc")) || exists(join18(worktree, "deno.lock"))) return "deno";
|
|
9031
9348
|
return null;
|
|
9032
9349
|
}
|
|
9033
|
-
const pkg = readJson(readText,
|
|
9350
|
+
const pkg = readJson(readText, join18(worktree, "package.json"));
|
|
9034
9351
|
const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
|
|
9035
9352
|
if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
|
|
9036
|
-
if (exists(
|
|
9037
|
-
if (exists(
|
|
9038
|
-
if (exists(
|
|
9039
|
-
if (exists(
|
|
9040
|
-
if (exists(
|
|
9353
|
+
if (exists(join18(worktree, "bun.lock")) || exists(join18(worktree, "bun.lockb"))) return "bun";
|
|
9354
|
+
if (exists(join18(worktree, "pnpm-lock.yaml"))) return "pnpm";
|
|
9355
|
+
if (exists(join18(worktree, "yarn.lock"))) return "yarn";
|
|
9356
|
+
if (exists(join18(worktree, "package-lock.json"))) return "npm";
|
|
9357
|
+
if (exists(join18(worktree, "deno.lock"))) return "deno";
|
|
9041
9358
|
return "npm";
|
|
9042
9359
|
}
|
|
9043
9360
|
function jsInstallCommand(pm) {
|
|
@@ -9098,7 +9415,7 @@ function detectPlan(worktree, io) {
|
|
|
9098
9415
|
// node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
|
|
9099
9416
|
// У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
|
|
9100
9417
|
// заново, но он дешёвый: тянет из того же общего кэша.
|
|
9101
|
-
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists:
|
|
9418
|
+
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join18(worktree, "node_modules"), source: "detect" }
|
|
9102
9419
|
];
|
|
9103
9420
|
for (const target of workspaceBuildTargets(worktree, io)) {
|
|
9104
9421
|
commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
|
|
@@ -9107,12 +9424,12 @@ function detectPlan(worktree, io) {
|
|
|
9107
9424
|
return { source: "detect", ecosystem: `node/${pm}`, commands };
|
|
9108
9425
|
}
|
|
9109
9426
|
for (const marker of ECOSYSTEM_MARKERS) {
|
|
9110
|
-
const hit = marker.files.some((f) => exists(
|
|
9427
|
+
const hit = marker.files.some((f) => exists(join18(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
|
|
9111
9428
|
if (!hit) continue;
|
|
9112
9429
|
let ecosystem = marker.ecosystem;
|
|
9113
9430
|
let command = marker.command;
|
|
9114
9431
|
for (const refine of marker.refine ?? []) {
|
|
9115
|
-
if (!exists(
|
|
9432
|
+
if (!exists(join18(worktree, refine.file))) continue;
|
|
9116
9433
|
ecosystem = refine.ecosystem;
|
|
9117
9434
|
command = refine.command;
|
|
9118
9435
|
break;
|
|
@@ -9169,12 +9486,12 @@ var CI_INSTALL_PATTERNS = [
|
|
|
9169
9486
|
function ciWorkflowPlan(worktree, io) {
|
|
9170
9487
|
const readText = io.readText ?? safeReadText;
|
|
9171
9488
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
9172
|
-
const dir =
|
|
9489
|
+
const dir = join18(worktree, ".github", "workflows");
|
|
9173
9490
|
const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
|
|
9174
9491
|
const seen = /* @__PURE__ */ new Set();
|
|
9175
9492
|
const commands = [];
|
|
9176
9493
|
for (const name of files.sort()) {
|
|
9177
|
-
const yaml = readText(
|
|
9494
|
+
const yaml = readText(join18(dir, name));
|
|
9178
9495
|
if (yaml === null) continue;
|
|
9179
9496
|
for (const raw of workflowRunLines(yaml)) {
|
|
9180
9497
|
const line = raw.replace(/\s+/g, " ").trim();
|
|
@@ -9189,7 +9506,7 @@ function ciWorkflowPlan(worktree, io) {
|
|
|
9189
9506
|
return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
|
|
9190
9507
|
}
|
|
9191
9508
|
function workspaceGlobs(worktree, readText) {
|
|
9192
|
-
const yaml = readText(
|
|
9509
|
+
const yaml = readText(join18(worktree, "pnpm-workspace.yaml"));
|
|
9193
9510
|
if (yaml !== null) {
|
|
9194
9511
|
const globs = [];
|
|
9195
9512
|
let inPackages = false;
|
|
@@ -9209,7 +9526,7 @@ function workspaceGlobs(worktree, readText) {
|
|
|
9209
9526
|
}
|
|
9210
9527
|
if (globs.length) return globs;
|
|
9211
9528
|
}
|
|
9212
|
-
const pkg = readJson(readText,
|
|
9529
|
+
const pkg = readJson(readText, join18(worktree, "package.json"));
|
|
9213
9530
|
const ws = pkg?.workspaces;
|
|
9214
9531
|
const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
|
|
9215
9532
|
return list.filter((g) => typeof g === "string");
|
|
@@ -9220,14 +9537,14 @@ function workspaceDirs(worktree, globs, io) {
|
|
|
9220
9537
|
if (glob.startsWith("!")) continue;
|
|
9221
9538
|
const star = glob.indexOf("*");
|
|
9222
9539
|
if (star === -1) {
|
|
9223
|
-
const dir =
|
|
9224
|
-
if (io.exists(
|
|
9540
|
+
const dir = join18(worktree, glob);
|
|
9541
|
+
if (io.exists(join18(dir, "package.json"))) dirs.add(dir);
|
|
9225
9542
|
continue;
|
|
9226
9543
|
}
|
|
9227
|
-
const base3 =
|
|
9544
|
+
const base3 = join18(worktree, glob.slice(0, star).replace(/\/$/, ""));
|
|
9228
9545
|
for (const name of io.listDirs(base3)) {
|
|
9229
|
-
const dir =
|
|
9230
|
-
if (io.exists(
|
|
9546
|
+
const dir = join18(base3, name);
|
|
9547
|
+
if (io.exists(join18(dir, "package.json"))) dirs.add(dir);
|
|
9231
9548
|
}
|
|
9232
9549
|
}
|
|
9233
9550
|
return [...dirs];
|
|
@@ -9255,11 +9572,11 @@ function workspaceBuildTargets(worktree, io = {}) {
|
|
|
9255
9572
|
if (!globs.length) return [];
|
|
9256
9573
|
const pkgs = [];
|
|
9257
9574
|
for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
|
|
9258
|
-
const pkg = readJson(readText,
|
|
9575
|
+
const pkg = readJson(readText, join18(dir, "package.json"));
|
|
9259
9576
|
if (!pkg || typeof pkg.name !== "string") continue;
|
|
9260
9577
|
const scripts = pkg.scripts ?? {};
|
|
9261
9578
|
const entries = entryPaths(pkg);
|
|
9262
|
-
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(
|
|
9579
|
+
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join18(dir, e)));
|
|
9263
9580
|
const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
|
|
9264
9581
|
pkgs.push({ name: pkg.name, dir, deps, needsBuild });
|
|
9265
9582
|
}
|
|
@@ -9287,7 +9604,7 @@ function devDatabaseUrl(worktree, io = {}) {
|
|
|
9287
9604
|
const fromEnv = (io.env ?? process.env).DATABASE_URL;
|
|
9288
9605
|
if (fromEnv && fromEnv.trim()) return fromEnv.trim();
|
|
9289
9606
|
for (const file of [".env", ".env.example"]) {
|
|
9290
|
-
const raw = readText(
|
|
9607
|
+
const raw = readText(join18(worktree, file));
|
|
9291
9608
|
if (raw === null) continue;
|
|
9292
9609
|
const m = /^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/m.exec(raw);
|
|
9293
9610
|
const value = (m?.[1] ?? "").trim().replace(/^['"]|['"]$/g, "");
|
|
@@ -9307,7 +9624,7 @@ function prismaPackages(worktree, io) {
|
|
|
9307
9624
|
const readText = io.readText ?? safeReadText;
|
|
9308
9625
|
const listDirs = io.listDirs ?? safeListDirs;
|
|
9309
9626
|
const dirs = [worktree, ...workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs })];
|
|
9310
|
-
return dirs.filter((dir) => exists(
|
|
9627
|
+
return dirs.filter((dir) => exists(join18(dir, "prisma", "schema.prisma")) && listDirs(join18(dir, "prisma", "migrations")).length > 0);
|
|
9311
9628
|
}
|
|
9312
9629
|
function jsExecBin(pm, command) {
|
|
9313
9630
|
switch (pm) {
|
|
@@ -9508,6 +9825,23 @@ ${notice}` : built;
|
|
|
9508
9825
|
});
|
|
9509
9826
|
}
|
|
9510
9827
|
|
|
9828
|
+
// src/spawnDefer.ts
|
|
9829
|
+
function noteDefer(episodes, key, reason, heartbeatTicks) {
|
|
9830
|
+
const prev = episodes.get(key);
|
|
9831
|
+
const ticks = (prev?.ticks ?? 0) + 1;
|
|
9832
|
+
const first = prev === void 0;
|
|
9833
|
+
const changed = first || reason !== prev.reason;
|
|
9834
|
+
const beat = !changed && heartbeatTicks > 0 && ticks % heartbeatTicks === 0;
|
|
9835
|
+
episodes.set(key, { ticks, reason });
|
|
9836
|
+
return { log: changed || beat, changed, ticks };
|
|
9837
|
+
}
|
|
9838
|
+
function clearDefer(episodes, key) {
|
|
9839
|
+
return episodes.delete(key);
|
|
9840
|
+
}
|
|
9841
|
+
function pruneDefers(episodes, live) {
|
|
9842
|
+
for (const key of episodes.keys()) if (!live.has(key)) episodes.delete(key);
|
|
9843
|
+
}
|
|
9844
|
+
|
|
9511
9845
|
// src/overmind.ts
|
|
9512
9846
|
init_git();
|
|
9513
9847
|
|
|
@@ -9596,6 +9930,13 @@ function verdictLabel(verdict) {
|
|
|
9596
9930
|
return verdict === "approve" ? "\u2705 APPROVE" : "\u274C REQUEST CHANGES";
|
|
9597
9931
|
}
|
|
9598
9932
|
async function latestVerdictComment(prUrl, sinceIso, run = realCommentsRunner) {
|
|
9933
|
+
return scanPrComments(prUrl, sinceIso, run, (body) => {
|
|
9934
|
+
const m = body.match(VERDICT_RE);
|
|
9935
|
+
if (!m) return null;
|
|
9936
|
+
return APPROVE_RE.test(m[1]) ? "approve" : "changes";
|
|
9937
|
+
});
|
|
9938
|
+
}
|
|
9939
|
+
async function scanPrComments(prUrl, sinceIso, run, pick3) {
|
|
9599
9940
|
if (!isGithubPrUrl(prUrl)) return null;
|
|
9600
9941
|
let out;
|
|
9601
9942
|
try {
|
|
@@ -9615,13 +9956,15 @@ async function latestVerdictComment(prUrl, sinceIso, run = realCommentsRunner) {
|
|
|
9615
9956
|
const c = comments[i];
|
|
9616
9957
|
if (!c?.body || !c.createdAt) continue;
|
|
9617
9958
|
if (Number.isFinite(sinceMs) && Date.parse(c.createdAt) < sinceMs) continue;
|
|
9618
|
-
const
|
|
9619
|
-
if (
|
|
9620
|
-
const verdict = APPROVE_RE.test(m[1]) ? "approve" : "changes";
|
|
9959
|
+
const verdict = pick3(c.body);
|
|
9960
|
+
if (verdict == null) continue;
|
|
9621
9961
|
return { verdict, createdAt: c.createdAt };
|
|
9622
9962
|
}
|
|
9623
9963
|
return null;
|
|
9624
9964
|
}
|
|
9965
|
+
async function latestVerifyVerdictComment(prUrl, sinceIso, run = realCommentsRunner) {
|
|
9966
|
+
return scanPrComments(prUrl, sinceIso, run, (body) => parseVerifyVerdictComment(body));
|
|
9967
|
+
}
|
|
9625
9968
|
var realPrCommitsRunner = async (prUrl) => (await $3`gh pr view ${prUrl} --json commits`).stdout;
|
|
9626
9969
|
async function prHasCommitsSince(prUrl, sinceIso, run = realPrCommitsRunner) {
|
|
9627
9970
|
if (!isGithubPrUrl(prUrl)) return false;
|
|
@@ -9762,6 +10105,7 @@ init_attachments();
|
|
|
9762
10105
|
init_shotUpload();
|
|
9763
10106
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9764
10107
|
import { z } from "zod";
|
|
10108
|
+
init_src();
|
|
9765
10109
|
|
|
9766
10110
|
// src/reviewCycle.ts
|
|
9767
10111
|
function reviewTransitionPatch(run, prUrl) {
|
|
@@ -9835,6 +10179,7 @@ async function buildDroneServer(store, opts = {}) {
|
|
|
9835
10179
|
const overmindDead = async () => !await store.alive();
|
|
9836
10180
|
const slugForInstruction = (fallback) => opts.expectedTaskSlug ?? fallback ?? "<slug>";
|
|
9837
10181
|
const iAmReviewer = (run) => run?.reviews_run_id != null || run?.kind === "reviewer" || opts.reviewsRunId != null || opts.runKind === "reviewer";
|
|
10182
|
+
const iAmVerifier = (run) => run?.verifies_run_id != null || run?.kind === "verifier" || opts.verifiesRunId != null || opts.runKind === "verifier";
|
|
9838
10183
|
const freeze = async (withdraw = false) => {
|
|
9839
10184
|
let slug;
|
|
9840
10185
|
let branch;
|
|
@@ -10123,7 +10468,9 @@ ${notFoundText}` : notFoundText }],
|
|
|
10123
10468
|
"\u0420\u0430\u0431\u043E\u0442\u0430 \u0441\u0434\u0430\u043D\u0430. \u0421 \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u043C GitHub-PR (github.com/OWNER/REPO/pull/N) \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u0443\u0445\u043E\u0434\u0438\u0442 \u043D\u0430 \u0440\u0435\u0432\u044C\u044E, \u0440\u043E\u0439 \u0434\u043E\u043C\u0435\u0440\u0436\u0438\u0442. \u0411\u0435\u0437 \u0441\u0432\u043E\u0435\u0433\u043E PR (\u0440\u0435\u0432\u044C\u044E/meta-\u0437\u0430\u0434\u0430\u0447\u0430) \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044F \u0441\u0440\u0430\u0437\u0443; \u043C\u043E\u0436\u0435\u0448\u044C \u043F\u0435\u0440\u0435\u0434\u0430\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u0437\u0430\u0434\u0430\u0447\u0443. summary \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D \u0438 \u0443 \u0418\u0421\u041F\u041E\u041B\u041D\u0418\u0422\u0415\u041B\u042F, \u0438 \u0443 \u0420\u0415\u0412\u042C\u042E\u0415\u0420\u0410 \u2014 \u043E\u043D \u0443\u0445\u043E\u0434\u0438\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 (TASWM-259). \u0414\u0440\u043E\u043D-\u0420\u0415\u0412\u042C\u042E\u0415\u0420 (\u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E, TASWM-148) \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u043E\u0431\u044F\u0437\u0430\u043D \u043F\u0435\u0440\u0435\u0434\u0430\u0442\u044C verdict \u2014 pr_url \u0442\u043E\u0433\u0434\u0430 \u0442\u0430 \u0436\u0435 \u0441\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 PR.",
|
|
10124
10469
|
{
|
|
10125
10470
|
pr_url: z.string().describe("\u0441\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0439 GitHub-PR; \u0434\u043B\u044F \u0437\u0430\u0434\u0430\u0447 \u0431\u0435\u0437 PR \u2014 \u0441\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u0437\u0430\u0434\u0430\u0447\u0443 (\u043F\u0440\u043E\u0433\u043E\u043D \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044F)"),
|
|
10126
|
-
verdict: z.enum(["approve", "changes"]).optional().describe(
|
|
10471
|
+
verdict: z.enum(["approve", "changes", ...VERIFY_VERDICTS]).optional().describe(
|
|
10472
|
+
"\u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430. \u0420\u0435\u0432\u044C\u044E\u0435\u0440 (\u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E): approve | changes. \u0412\u0415\u0420\u0418\u0424\u0418\u041A\u0410\u0422\u041E\u0420 (TASWM-344): confirmed (\u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044E) | not_reproduced (\u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0441\u044F) | found_issues (\u043D\u0430\u0448\u0451\u043B \u0432\u043E\u0442 \u044D\u0442\u043E). \u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D \u0434\u043B\u044F \u043E\u0431\u043E\u0438\u0445."
|
|
10473
|
+
),
|
|
10127
10474
|
summary: z.string().optional().describe(
|
|
10128
10475
|
"\u041E\u0442\u0447\u0451\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443, \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D \u043F\u0440\u0438 \u0441\u0434\u0430\u0447\u0435 \u0441 PR. \u0418\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C: \u0447\u0442\u043E \u0441\u0434\u0435\u043B\u0430\u043D\u043E, \u043A\u0430\u043A\u0438\u0435 \u0444\u0430\u0439\u043B\u044B \u0437\u0430\u0442\u0440\u043E\u043D\u0443\u0442\u044B, \u043A\u0430\u043A\u0438\u0435 \u0440\u0435\u0448\u0435\u043D\u0438\u044F \u043F\u0440\u0438\u043D\u044F\u043B \u0438 \u0447\u0442\u043E \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C \u0437\u0430 \u0441\u043A\u043E\u0431\u043A\u0430\u043C\u0438. \u0420\u0435\u0432\u044C\u044E\u0435\u0440: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B."
|
|
10129
10476
|
),
|
|
@@ -10226,6 +10573,66 @@ ${notFoundText}` : notFoundText }],
|
|
|
10226
10573
|
\u26A0\uFE0F \u0412\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u041D\u0415 \u0443\u0435\u0445\u0430\u043B (${commentWarning}) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0435. \u041F\u0440\u043E\u0434\u0443\u0431\u043B\u0438\u0440\u0443\u0439 \u0435\u0433\u043E \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 PR: \`gh pr comment ${trustedPrUrl} --body "${verdictText} \u2026"\`.` : "";
|
|
10227
10574
|
return { content: [{ type: "text", text: `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0437\u0430\u043F\u0438\u0441\u0430\u043D, \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E${shotsNote(shots)}${commentNote}` }] };
|
|
10228
10575
|
}
|
|
10576
|
+
if (iAmVerifier(run) && run?.verifies_run_id == null) {
|
|
10577
|
+
return {
|
|
10578
|
+
content: [
|
|
10579
|
+
{
|
|
10580
|
+
type: "text",
|
|
10581
|
+
text: '\u0422\u044B \u0434\u0440\u043E\u043D-\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440, \u043D\u043E \u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0441\u0432\u044F\u0437\u044C \u0441 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u043E\u043C \u2014 \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u043A\u0443\u0434\u0430, \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u043B\u044C\u0437\u044F (TASWM-344). \u041E\u0441\u0442\u0430\u0432\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 PR (`gh pr comment <pr> --body "**\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E**|**\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F**|**\u041D\u0410\u0428\u0401\u041B** \u2026"`) \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438 done() \u0447\u0435\u0440\u0435\u0437 \u043C\u0438\u043D\u0443\u0442\u0443.'
|
|
10582
|
+
}
|
|
10583
|
+
],
|
|
10584
|
+
isError: true
|
|
10585
|
+
};
|
|
10586
|
+
}
|
|
10587
|
+
if (run?.verifies_run_id != null) {
|
|
10588
|
+
if (isTerminalStatus(run.status)) {
|
|
10589
|
+
return { content: [{ type: "text", text: "\u043F\u0440\u043E\u0433\u043E\u043D \u0443\u0436\u0435 \u0437\u0430\u043A\u0440\u044B\u0442 (PR \u0441\u043C\u0435\u0440\u0436\u0435\u043D \u0440\u0430\u043D\u044C\u0448\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D" }] };
|
|
10590
|
+
}
|
|
10591
|
+
if (!isVerifyVerdict(verdict)) {
|
|
10592
|
+
return {
|
|
10593
|
+
content: [
|
|
10594
|
+
{
|
|
10595
|
+
type: "text",
|
|
10596
|
+
text: '\u0422\u044B \u0434\u0440\u043E\u043D-\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 (TASWM-344) \u2014 done() \u0442\u0440\u0435\u0431\u0443\u0435\u0442 verdict: "confirmed" (\u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044E) | "not_reproduced" (\u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0441\u044F) | "found_issues" (\u043D\u0430\u0448\u0451\u043B \u0432\u043E\u0442 \u044D\u0442\u043E). \u0427\u0435\u0442\u0432\u0451\u0440\u0442\u043E\u0433\u043E \u0438\u0441\u0445\u043E\u0434\u0430 \u043D\u0435\u0442, \u0438 \u0443\u0441\u043F\u0435\u0445\u043E\u043C \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442\u0441\u044F: \u043F\u043E\u043A\u0430 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u0440\u0430\u0431\u043E\u0442\u0430 \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u043E\u0448\u0435\u0434\u0448\u0435\u0439 \u043F\u0440\u0438\u0451\u043C\u043A\u0443. \u041F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u043E\u0432\u0441\u0435 \u2014 \u044D\u0442\u043E "not_reproduced" \u0441 \u0447\u0435\u0441\u0442\u043D\u043E\u0439 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 \u0432 summary, \u0430 \u043D\u0435 \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u0435.'
|
|
10597
|
+
}
|
|
10598
|
+
],
|
|
10599
|
+
isError: true
|
|
10600
|
+
};
|
|
10601
|
+
}
|
|
10602
|
+
const verified = await store.getRun(run.verifies_run_id);
|
|
10603
|
+
const verdictText = verifyVerdictLabel(verdict);
|
|
10604
|
+
await store.updateRun(run.verifies_run_id, { verify_verdict: verdict });
|
|
10605
|
+
await store.addEvent(run.verifies_run_id, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043E\u0442 ${run.name}: ${verdictText}${summary ? ` \u2014 ${summary}` : ""}`);
|
|
10606
|
+
const verifiedSlug = reportSlug(verified?.task_slug);
|
|
10607
|
+
let shots = { attachments: [], warning: "" };
|
|
10608
|
+
let commentWarning = "";
|
|
10609
|
+
if (opts.taskless && verifiedSlug) {
|
|
10610
|
+
shots = await uploadShots();
|
|
10611
|
+
try {
|
|
10612
|
+
await opts.taskless.addComment(
|
|
10613
|
+
verifiedSlug,
|
|
10614
|
+
`\u{1F9EA} \u0412\u0415\u0420\u0414\u0418\u041A\u0422 \u0412\u0415\u0420\u0418\u0424\u0418\u041A\u0410\u0422\u041E\u0420\u0410 ${run.name}: **${verdictText}**
|
|
10615
|
+
|
|
10616
|
+
${(summary ?? "").trim()}${shots.warning}`,
|
|
10617
|
+
void 0,
|
|
10618
|
+
shots.attachments
|
|
10619
|
+
);
|
|
10620
|
+
} catch (e) {
|
|
10621
|
+
commentWarning = e instanceof Error ? e.message : String(e);
|
|
10622
|
+
await store.addEvent(run.verifies_run_id, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 ${verifiedSlug} \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${commentWarning}`);
|
|
10623
|
+
}
|
|
10624
|
+
} else {
|
|
10625
|
+
commentWarning = opts.taskless ? "\u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430" : "\u043A\u043B\u0438\u0435\u043D\u0442 Taskless \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D";
|
|
10626
|
+
await store.addEvent(run.verifies_run_id, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${commentWarning}`);
|
|
10627
|
+
}
|
|
10628
|
+
const trustedPrUrl = verified?.pr_url ?? pr_url;
|
|
10629
|
+
await store.updateRun(null, { status: "review", pr_url: trustedPrUrl });
|
|
10630
|
+
await store.addEvent(null, "pr_opened", trustedPrUrl);
|
|
10631
|
+
const commentNote = commentWarning ? `
|
|
10632
|
+
|
|
10633
|
+
\u26A0\uFE0F \u0412\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u041D\u0415 \u0443\u0435\u0445\u0430\u043B (${commentWarning}) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0435. \u041F\u0440\u043E\u0434\u0443\u0431\u043B\u0438\u0440\u0443\u0439 \u0435\u0433\u043E \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 PR: \`gh pr comment ${trustedPrUrl} --body "${verdictText} \u2026"\`.` : "";
|
|
10634
|
+
return { content: [{ type: "text", text: `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0437\u0430\u043F\u0438\u0441\u0430\u043D (${verdictText})${shotsNote(shots)}${commentNote}` }] };
|
|
10635
|
+
}
|
|
10229
10636
|
if (isGithubPrUrl(pr_url)) {
|
|
10230
10637
|
const report = (summary ?? "").trim();
|
|
10231
10638
|
if (!report) {
|
|
@@ -10434,6 +10841,8 @@ function ensureRunHydrated(db, runId, info) {
|
|
|
10434
10841
|
kind: info.kind,
|
|
10435
10842
|
name: info.name,
|
|
10436
10843
|
reviews_run_id: info.reviews_run_id,
|
|
10844
|
+
verifies_run_id: info.verifies_run_id,
|
|
10845
|
+
// TASWM-344
|
|
10437
10846
|
hero_run_id: info.hero_run_id
|
|
10438
10847
|
});
|
|
10439
10848
|
}
|
|
@@ -10447,12 +10856,14 @@ async function runDroneServer(runId) {
|
|
|
10447
10856
|
const expectedTaskSlug = process.env.SWARM_TASK_SLUG || void 0;
|
|
10448
10857
|
const expectedKind = toRunKind(process.env.SWARM_RUN_KIND);
|
|
10449
10858
|
const spawnReviewsRunId = Number(process.env.SWARM_REVIEWS_RUN_ID);
|
|
10859
|
+
const spawnVerifiesRunId = Number(process.env.SWARM_VERIFIES_RUN_ID);
|
|
10450
10860
|
const common = {
|
|
10451
10861
|
taskless,
|
|
10452
10862
|
runKind: expectedKind,
|
|
10453
10863
|
prHeadBranch,
|
|
10454
10864
|
...expectedTaskSlug ? { expectedTaskSlug } : {},
|
|
10455
|
-
...Number.isInteger(spawnReviewsRunId) && spawnReviewsRunId > 0 ? { reviewsRunId: spawnReviewsRunId } : {}
|
|
10865
|
+
...Number.isInteger(spawnReviewsRunId) && spawnReviewsRunId > 0 ? { reviewsRunId: spawnReviewsRunId } : {},
|
|
10866
|
+
...Number.isInteger(spawnVerifiesRunId) && spawnVerifiesRunId > 0 ? { verifiesRunId: spawnVerifiesRunId } : {}
|
|
10456
10867
|
};
|
|
10457
10868
|
const channelUrl = process.env.SWARM_DRONE_CHANNEL_URL;
|
|
10458
10869
|
const channelToken = process.env.SWARM_DRONE_CHANNEL_TOKEN;
|
|
@@ -10473,6 +10884,7 @@ async function runDroneServer(runId) {
|
|
|
10473
10884
|
kind: expectedKind,
|
|
10474
10885
|
name: process.env.SWARM_RUN_NAME || void 0,
|
|
10475
10886
|
reviews_run_id: process.env.SWARM_REVIEWS_RUN_ID ? Number(process.env.SWARM_REVIEWS_RUN_ID) : null,
|
|
10887
|
+
verifies_run_id: process.env.SWARM_VERIFIES_RUN_ID ? Number(process.env.SWARM_VERIFIES_RUN_ID) : null,
|
|
10476
10888
|
hero_run_id: process.env.SWARM_HERO_RUN_ID ? Number(process.env.SWARM_HERO_RUN_ID) : null
|
|
10477
10889
|
});
|
|
10478
10890
|
const store = localDroneStore2(db, runId, { heartbeatPath: HEARTBEAT_PATH2, heartbeatStaleMs: HEARTBEAT_STALE_MS2 });
|
|
@@ -10668,6 +11080,9 @@ function joinCapped(labels) {
|
|
|
10668
11080
|
joined = labels.slice(0, Math.max(cut, 1)).join(", ");
|
|
10669
11081
|
return `${joined} \u0438 \u0435\u0449\u0451 ${labels.length - Math.max(cut, 1)}`;
|
|
10670
11082
|
}
|
|
11083
|
+
function autoReviewOnFor(flag, repo) {
|
|
11084
|
+
return typeof flag === "function" ? flag(repo) : flag;
|
|
11085
|
+
}
|
|
10671
11086
|
function buildAnomalyCandidates(db, now, thresholds, opts) {
|
|
10672
11087
|
const candidates = [];
|
|
10673
11088
|
const allRuns = db.listRuns().filter((r) => r.superseded_by == null);
|
|
@@ -10802,7 +11217,7 @@ function buildAnomalyCandidates(db, now, thresholds, opts) {
|
|
|
10802
11217
|
...hrefOf(run.task_url)
|
|
10803
11218
|
})
|
|
10804
11219
|
});
|
|
10805
|
-
} else if (opts.autoReviewEnabled && run.review_verdict == null && db.reviewerCountFor(run.id) === 0) {
|
|
11220
|
+
} else if (autoReviewOnFor(opts.autoReviewEnabled, run.repo) && run.review_verdict == null && db.reviewerCountFor(run.id) === 0) {
|
|
10806
11221
|
candidates.push({
|
|
10807
11222
|
key: `pr_no_reviewer:${run.id}`,
|
|
10808
11223
|
minDurationMs: thresholds.prNoReviewerMs,
|
|
@@ -10989,9 +11404,11 @@ init_droneChannel();
|
|
|
10989
11404
|
init_procList();
|
|
10990
11405
|
|
|
10991
11406
|
// src/mcpResilient.ts
|
|
11407
|
+
init_linkFailure();
|
|
10992
11408
|
init_taskless();
|
|
10993
11409
|
var DEFAULT_BACKOFF_MS = [5e3, 15e3, 3e4, 6e4];
|
|
10994
11410
|
var DEFAULT_DOWN_AFTER_MS = 5 * 6e4;
|
|
11411
|
+
var DEFAULT_RETRY_DELAYS_MS = [200, 800];
|
|
10995
11412
|
function errorResult(reason) {
|
|
10996
11413
|
return { isError: true, content: [{ type: "text", text: `Swarm MCP \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${reason}` }] };
|
|
10997
11414
|
}
|
|
@@ -10999,11 +11416,16 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
10999
11416
|
const connect = opts.connect ?? (() => connectSwarmMcp());
|
|
11000
11417
|
const backoff = opts.backoffMs?.length ? opts.backoffMs : DEFAULT_BACKOFF_MS;
|
|
11001
11418
|
const downAfterMs = opts.downAfterMs ?? DEFAULT_DOWN_AFTER_MS;
|
|
11419
|
+
const retryDelays = opts.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
|
|
11420
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
11002
11421
|
let caller = null;
|
|
11003
11422
|
let lastTryAt = 0;
|
|
11004
11423
|
let failures = 0;
|
|
11005
11424
|
let downSince = null;
|
|
11006
11425
|
let downReported = false;
|
|
11426
|
+
let lastConnectReason = null;
|
|
11427
|
+
let lastOkAt = null;
|
|
11428
|
+
let lastCallReason = null;
|
|
11007
11429
|
function maybeReportDown() {
|
|
11008
11430
|
if (downReported || downSince === null) return;
|
|
11009
11431
|
const downMs = Date.now() - downSince;
|
|
@@ -11011,45 +11433,65 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
11011
11433
|
downReported = true;
|
|
11012
11434
|
opts.onDown?.(downMs);
|
|
11013
11435
|
}
|
|
11014
|
-
async function ensureCaller() {
|
|
11436
|
+
async function ensureCaller(force = false) {
|
|
11015
11437
|
if (caller) return caller;
|
|
11016
11438
|
const now = Date.now();
|
|
11017
11439
|
const wait = backoff[Math.max(0, Math.min(failures - 1, backoff.length - 1))];
|
|
11018
|
-
if (lastTryAt !== 0 && now - lastTryAt < wait) return null;
|
|
11019
|
-
lastTryAt = now;
|
|
11440
|
+
if (!force && lastTryAt !== 0 && now - lastTryAt < wait) return null;
|
|
11441
|
+
if (!force) lastTryAt = now;
|
|
11020
11442
|
let fresh = null;
|
|
11021
11443
|
try {
|
|
11022
11444
|
fresh = await connect();
|
|
11023
|
-
|
|
11445
|
+
if (!fresh) lastConnectReason = lastSwarmConnectFailure() ?? UNKNOWN_LINK_REASON;
|
|
11446
|
+
} catch (e) {
|
|
11024
11447
|
fresh = null;
|
|
11448
|
+
lastConnectReason = describeError(e);
|
|
11025
11449
|
}
|
|
11026
11450
|
if (fresh) {
|
|
11027
11451
|
caller = fresh;
|
|
11028
11452
|
failures = 0;
|
|
11453
|
+
lastConnectReason = null;
|
|
11029
11454
|
if (downReported) opts.onUp?.();
|
|
11030
11455
|
downSince = null;
|
|
11031
11456
|
downReported = false;
|
|
11032
11457
|
return caller;
|
|
11033
11458
|
}
|
|
11034
|
-
failures += 1;
|
|
11459
|
+
if (!force) failures += 1;
|
|
11035
11460
|
if (downSince === null) downSince = now;
|
|
11036
11461
|
return null;
|
|
11037
11462
|
}
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11463
|
+
const call = async (name, args) => {
|
|
11464
|
+
for (let attempt = 0; ; attempt++) {
|
|
11465
|
+
const active = await ensureCaller(attempt > 0);
|
|
11466
|
+
if (!active) {
|
|
11467
|
+
maybeReportDown();
|
|
11468
|
+
return errorResult(lastConnectReason ?? UNKNOWN_LINK_REASON);
|
|
11469
|
+
}
|
|
11470
|
+
try {
|
|
11471
|
+
const res = await active(name, args);
|
|
11472
|
+
lastOkAt = Date.now();
|
|
11473
|
+
lastCallReason = null;
|
|
11474
|
+
return res;
|
|
11475
|
+
} catch (e) {
|
|
11476
|
+
caller = null;
|
|
11477
|
+
if (downSince === null) downSince = Date.now();
|
|
11478
|
+
const { kind, detail: reason } = explainLinkFailure(e);
|
|
11479
|
+
lastCallReason = reason;
|
|
11480
|
+
const delay = retryDelays[attempt];
|
|
11481
|
+
if (delay === void 0 || kind !== "offline") {
|
|
11482
|
+
maybeReportDown();
|
|
11483
|
+
return errorResult(reason);
|
|
11484
|
+
}
|
|
11485
|
+
opts.onRetry?.({ tool: name, attempt: attempt + 1, reason });
|
|
11486
|
+
await sleep2(delay);
|
|
11487
|
+
}
|
|
11051
11488
|
}
|
|
11052
11489
|
};
|
|
11490
|
+
return Object.assign(call, {
|
|
11491
|
+
// Причина — ТА ЖЕ, что уехала в лог: сначала свежий отказ вызова, иначе отказ
|
|
11492
|
+
// подключения. Своего разбора здесь нет намеренно (см. lastCallReason выше).
|
|
11493
|
+
link: () => ({ connected: caller !== null, downSince, lastOkAt, lastError: caller !== null ? null : lastCallReason ?? lastConnectReason })
|
|
11494
|
+
});
|
|
11053
11495
|
}
|
|
11054
11496
|
|
|
11055
11497
|
// src/overmind.ts
|
|
@@ -11085,6 +11527,73 @@ function workspaceAccessLines(input) {
|
|
|
11085
11527
|
return lines;
|
|
11086
11528
|
}
|
|
11087
11529
|
|
|
11530
|
+
// src/overmind.ts
|
|
11531
|
+
init_authState();
|
|
11532
|
+
|
|
11533
|
+
// src/swarmStatus.ts
|
|
11534
|
+
init_linkFailure();
|
|
11535
|
+
|
|
11536
|
+
// src/release.ts
|
|
11537
|
+
init_src();
|
|
11538
|
+
init_claudeBin();
|
|
11539
|
+
var SWARM_PACKAGE = SWARM_PACKAGE_NAME;
|
|
11540
|
+
var LATEST_TIMEOUT_MS = 4e3;
|
|
11541
|
+
async function fetchLatestVersion(opts = {}) {
|
|
11542
|
+
const fetchFn = opts.fetchFn ?? fetch;
|
|
11543
|
+
const timeoutMs = opts.timeoutMs ?? LATEST_TIMEOUT_MS;
|
|
11544
|
+
const pkg = opts.pkg ?? SWARM_PACKAGE;
|
|
11545
|
+
const ctrl = new AbortController();
|
|
11546
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
11547
|
+
try {
|
|
11548
|
+
const res = await fetchFn(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
11549
|
+
signal: ctrl.signal,
|
|
11550
|
+
headers: { accept: "application/vnd.npm.install-v1+json, application/json" }
|
|
11551
|
+
});
|
|
11552
|
+
if (!res.ok) return null;
|
|
11553
|
+
const body = await res.json();
|
|
11554
|
+
return typeof body.version === "string" && body.version ? body.version : null;
|
|
11555
|
+
} catch {
|
|
11556
|
+
return null;
|
|
11557
|
+
} finally {
|
|
11558
|
+
clearTimeout(timer);
|
|
11559
|
+
}
|
|
11560
|
+
}
|
|
11561
|
+
function isOutdated(current, latest) {
|
|
11562
|
+
if (!latest || !current) return false;
|
|
11563
|
+
return compareVersionDirs(latest, current) > 0;
|
|
11564
|
+
}
|
|
11565
|
+
function outdatedLine(current, latest, updateCommand = "swarm update") {
|
|
11566
|
+
if (!isOutdated(current, latest)) return null;
|
|
11567
|
+
return `\u26A0 \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u043E\u044F \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430: \u0443 \u0442\u0435\u0431\u044F v${current}, \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430 v${latest} \u2014 \u043E\u0431\u043D\u043E\u0432\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: ${updateCommand}`;
|
|
11568
|
+
}
|
|
11569
|
+
|
|
11570
|
+
// src/swarmStatus.ts
|
|
11571
|
+
init_src();
|
|
11572
|
+
function classifyLinkError(e) {
|
|
11573
|
+
const { kind, detail } = explainLinkFailure(e);
|
|
11574
|
+
const short2 = detail.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
11575
|
+
const map = {
|
|
11576
|
+
unauthorized: "revoked",
|
|
11577
|
+
missing_tool: "server",
|
|
11578
|
+
offline: "network",
|
|
11579
|
+
rejected: "server",
|
|
11580
|
+
unknown: "down"
|
|
11581
|
+
};
|
|
11582
|
+
return { kind: map[kind], detail: short2 };
|
|
11583
|
+
}
|
|
11584
|
+
function composeLink(input) {
|
|
11585
|
+
const base3 = { lastOkAt: input.transport?.lastOkAt != null ? new Date(input.transport.lastOkAt).toISOString() : null, pending: input.pending, api: input.api };
|
|
11586
|
+
if (input.authDenied) return { kind: "revoked", detail: input.authDenied, ...base3 };
|
|
11587
|
+
if (!input.transport) return { kind: "no-prod", detail: "sqlite-\u0440\u0435\u0436\u0438\u043C \u2014 \u043F\u0440\u043E\u0434\u0430 \u0443 \u044D\u0442\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442 \u0432\u043E\u0432\u0441\u0435", ...base3 };
|
|
11588
|
+
if (input.transport.connected) return { kind: "ok", detail: "", ...base3 };
|
|
11589
|
+
if (input.transport.lastOkAt == null && input.transport.downSince == null && !input.transport.lastError) {
|
|
11590
|
+
return { kind: "starting", detail: "\u0434\u0435\u043C\u043E\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0434\u043D\u044F\u043B\u0441\u044F \u2014 \u0432 \u043F\u0440\u043E\u0434 \u0435\u0449\u0451 \u043D\u0435 \u0445\u043E\u0434\u0438\u043B", ...base3 };
|
|
11591
|
+
}
|
|
11592
|
+
const why = input.transport.lastError;
|
|
11593
|
+
if (!why) return { kind: "down", detail: "", ...base3 };
|
|
11594
|
+
return { ...classifyLinkError(new Error(why)), ...base3 };
|
|
11595
|
+
}
|
|
11596
|
+
|
|
11088
11597
|
// src/overmind.ts
|
|
11089
11598
|
init_workspace();
|
|
11090
11599
|
init_attachments();
|
|
@@ -11219,10 +11728,23 @@ function makeMergeGateResolver(deps) {
|
|
|
11219
11728
|
const run = findRunByPr(deps.listRuns(), ref);
|
|
11220
11729
|
if (!run) return { ...computeMergeGate({ hasRun: false, autoReview: false, reviewVerdict: null }), run: null };
|
|
11221
11730
|
const autoReview = deps.autoReview ? await deps.autoReview(run.repo).catch(() => deps.fallbackAutoReview) : deps.fallbackAutoReview;
|
|
11222
|
-
const decision = computeMergeGate({
|
|
11731
|
+
const decision = computeMergeGate({
|
|
11732
|
+
hasRun: true,
|
|
11733
|
+
autoReview,
|
|
11734
|
+
reviewVerdict: run.review_verdict,
|
|
11735
|
+
autoVerify: deps.autoVerify === true,
|
|
11736
|
+
verifyVerdict: run.verify_verdict ?? null
|
|
11737
|
+
});
|
|
11223
11738
|
return {
|
|
11224
11739
|
...decision,
|
|
11225
|
-
run: {
|
|
11740
|
+
run: {
|
|
11741
|
+
id: run.id,
|
|
11742
|
+
task_slug: run.task_slug,
|
|
11743
|
+
name: run.name,
|
|
11744
|
+
pr_url: run.pr_url,
|
|
11745
|
+
review_verdict: run.review_verdict,
|
|
11746
|
+
verify_verdict: run.verify_verdict ?? null
|
|
11747
|
+
}
|
|
11226
11748
|
};
|
|
11227
11749
|
};
|
|
11228
11750
|
}
|
|
@@ -11665,7 +12187,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
11665
12187
|
// package.json
|
|
11666
12188
|
var package_default = {
|
|
11667
12189
|
name: "@taskless-app/swarm",
|
|
11668
|
-
version: "0.6.
|
|
12190
|
+
version: "0.6.12",
|
|
11669
12191
|
type: "module",
|
|
11670
12192
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
11671
12193
|
keywords: [
|
|
@@ -11715,7 +12237,7 @@ var package_default = {
|
|
|
11715
12237
|
execa: "^10.0.0",
|
|
11716
12238
|
zod: "3.25.76"
|
|
11717
12239
|
},
|
|
11718
|
-
gitHead: "
|
|
12240
|
+
gitHead: "43c844efdbea61a27c54edc17a111d658ce6e2b4"
|
|
11719
12241
|
};
|
|
11720
12242
|
|
|
11721
12243
|
// src/version.ts
|
|
@@ -11727,6 +12249,7 @@ init_storm();
|
|
|
11727
12249
|
init_resources();
|
|
11728
12250
|
init_config();
|
|
11729
12251
|
init_droneChannel();
|
|
12252
|
+
init_authState();
|
|
11730
12253
|
init_oauth();
|
|
11731
12254
|
var esc = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
11732
12255
|
var jsonForScript = (v) => JSON.stringify(v).replace(/</g, "\\u003c");
|
|
@@ -11735,6 +12258,15 @@ var sqliteIso = (ts) => {
|
|
|
11735
12258
|
const ms = sqliteMs(ts);
|
|
11736
12259
|
return Number.isNaN(ms) ? ts : new Date(ms).toISOString();
|
|
11737
12260
|
};
|
|
12261
|
+
function stormFacts(db) {
|
|
12262
|
+
if (!db.stormActive()) return null;
|
|
12263
|
+
const since = db.currentStormStartedAt();
|
|
12264
|
+
const recs = since ? db.stormProbeRecordsSince(since) : [];
|
|
12265
|
+
return {
|
|
12266
|
+
reason: lastProbeReason(recs) === "limit" ? "limit" : "provider",
|
|
12267
|
+
hint: recs.length ? recs[recs.length - 1].resetHint ?? null : null
|
|
12268
|
+
};
|
|
12269
|
+
}
|
|
11738
12270
|
function roySnapshot(db, now = Date.now(), overmindLog = [], paused = false, daemonVersion = null, restart = null, selfHost = null) {
|
|
11739
12271
|
const all = db.listRuns();
|
|
11740
12272
|
const c = (s) => all.filter((r) => r.status === s).length;
|
|
@@ -11744,11 +12276,11 @@ function roySnapshot(db, now = Date.now(), overmindLog = [], paused = false, dae
|
|
|
11744
12276
|
if (r.status === "done" || r.status === "stopped" || r.status === "failed") return sqliteMs(r.updated_at) >= now - DAY_MS;
|
|
11745
12277
|
return true;
|
|
11746
12278
|
});
|
|
11747
|
-
const
|
|
12279
|
+
const storm = stormFacts(db);
|
|
12280
|
+
const stormActive = storm !== null;
|
|
11748
12281
|
const stormSince = db.currentStormStartedAt();
|
|
11749
|
-
const
|
|
11750
|
-
const
|
|
11751
|
-
const stormResetHint = stormRecs.length ? stormRecs[stormRecs.length - 1].resetHint ?? null : null;
|
|
12282
|
+
const stormReason = storm?.reason ?? null;
|
|
12283
|
+
const stormResetHint = storm?.hint ?? null;
|
|
11752
12284
|
return {
|
|
11753
12285
|
metrics: {
|
|
11754
12286
|
running: c("running"),
|
|
@@ -13031,7 +13563,17 @@ function daemonStatus(deps) {
|
|
|
13031
13563
|
const counts = {};
|
|
13032
13564
|
for (const r of deps.db.listRuns()) counts[r.status] = (counts[r.status] ?? 0) + 1;
|
|
13033
13565
|
const live = deps.liveSessionIds?.() ?? [];
|
|
13566
|
+
const ver = deps.daemonVersion?.() ?? null;
|
|
13567
|
+
const queuedReady = deps.db.listRuns("queued").filter((r) => deps.db.unmetDeps(r.depends_on).length === 0).length;
|
|
13034
13568
|
return {
|
|
13569
|
+
api: deps.api ?? TASKLESS_API,
|
|
13570
|
+
queuedReady,
|
|
13571
|
+
startedAt: ver?.startedAt ?? null,
|
|
13572
|
+
latestVersion: ver?.latestVersion ?? null,
|
|
13573
|
+
link: deps.link?.() ?? null,
|
|
13574
|
+
questions: deps.db.openQuestions().length,
|
|
13575
|
+
maxDrones: deps.maxDrones?.() ?? null,
|
|
13576
|
+
storm: stormFacts(deps.db),
|
|
13035
13577
|
hostId: deps.selfHost?.hostId ?? null,
|
|
13036
13578
|
hostName: deps.selfHost?.hostName ?? null,
|
|
13037
13579
|
backend: SWARM_BACKEND,
|
|
@@ -13041,7 +13583,9 @@ function daemonStatus(deps) {
|
|
|
13041
13583
|
counts,
|
|
13042
13584
|
live,
|
|
13043
13585
|
version: PACKAGE_VERSION,
|
|
13044
|
-
repos: deps.repoMap?.() ?? []
|
|
13586
|
+
repos: deps.repoMap?.() ?? [],
|
|
13587
|
+
// TASWM-324: «нас не пускают» — то же состояние процесса, что кричит рамкой в лог.
|
|
13588
|
+
authDenied: authStateLine()
|
|
13045
13589
|
};
|
|
13046
13590
|
}
|
|
13047
13591
|
function eventsStream(db, narratorLog, control, daemonVersion, restart, liveSessionIds2, selfHost) {
|
|
@@ -13141,7 +13685,12 @@ function makeHandler(deps) {
|
|
|
13141
13685
|
if (p === "/merge-gate") {
|
|
13142
13686
|
const ref = url.searchParams.get("pr") ?? "";
|
|
13143
13687
|
if (!ref.trim()) return Response.json({ error: "\u043D\u0443\u0436\u0435\u043D \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 pr=<url|\u043D\u043E\u043C\u0435\u0440>" }, { status: 400 });
|
|
13144
|
-
const resolve4 = deps.mergeGate ?? makeMergeGateResolver({
|
|
13688
|
+
const resolve4 = deps.mergeGate ?? makeMergeGateResolver({
|
|
13689
|
+
listRuns: () => deps.db.listRuns(),
|
|
13690
|
+
fallbackAutoReview: SWARM_AUTO_REVIEW,
|
|
13691
|
+
autoVerify: SWARM_AUTO_VERIFY
|
|
13692
|
+
// TASWM-344
|
|
13693
|
+
});
|
|
13145
13694
|
return Response.json(await resolve4(ref));
|
|
13146
13695
|
}
|
|
13147
13696
|
if (p === "/resources") return Response.json(hostResourceSnapshot());
|
|
@@ -13301,40 +13850,6 @@ function closeWebServer(server) {
|
|
|
13301
13850
|
});
|
|
13302
13851
|
}
|
|
13303
13852
|
|
|
13304
|
-
// src/release.ts
|
|
13305
|
-
init_src();
|
|
13306
|
-
init_claudeBin();
|
|
13307
|
-
var SWARM_PACKAGE = SWARM_PACKAGE_NAME;
|
|
13308
|
-
var LATEST_TIMEOUT_MS = 4e3;
|
|
13309
|
-
async function fetchLatestVersion(opts = {}) {
|
|
13310
|
-
const fetchFn = opts.fetchFn ?? fetch;
|
|
13311
|
-
const timeoutMs = opts.timeoutMs ?? LATEST_TIMEOUT_MS;
|
|
13312
|
-
const pkg = opts.pkg ?? SWARM_PACKAGE;
|
|
13313
|
-
const ctrl = new AbortController();
|
|
13314
|
-
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
13315
|
-
try {
|
|
13316
|
-
const res = await fetchFn(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
13317
|
-
signal: ctrl.signal,
|
|
13318
|
-
headers: { accept: "application/vnd.npm.install-v1+json, application/json" }
|
|
13319
|
-
});
|
|
13320
|
-
if (!res.ok) return null;
|
|
13321
|
-
const body = await res.json();
|
|
13322
|
-
return typeof body.version === "string" && body.version ? body.version : null;
|
|
13323
|
-
} catch {
|
|
13324
|
-
return null;
|
|
13325
|
-
} finally {
|
|
13326
|
-
clearTimeout(timer);
|
|
13327
|
-
}
|
|
13328
|
-
}
|
|
13329
|
-
function isOutdated(current, latest) {
|
|
13330
|
-
if (!latest || !current) return false;
|
|
13331
|
-
return compareVersionDirs(latest, current) > 0;
|
|
13332
|
-
}
|
|
13333
|
-
function outdatedLine(current, latest, updateCommand = "swarm update") {
|
|
13334
|
-
if (!isOutdated(current, latest)) return null;
|
|
13335
|
-
return `\u26A0 \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u043E\u044F \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430: \u0443 \u0442\u0435\u0431\u044F v${current}, \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430 v${latest} \u2014 \u043E\u0431\u043D\u043E\u0432\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: ${updateCommand}`;
|
|
13336
|
-
}
|
|
13337
|
-
|
|
13338
13853
|
// src/daemonVersion.ts
|
|
13339
13854
|
function captureDaemonStart(now = () => /* @__PURE__ */ new Date()) {
|
|
13340
13855
|
return { startedAt: now().toISOString(), version: PACKAGE_VERSION };
|
|
@@ -14521,20 +15036,57 @@ async function closeMergedTaskRuns(deps) {
|
|
|
14521
15036
|
}
|
|
14522
15037
|
async function closeOrphanedReviewers(deps) {
|
|
14523
15038
|
for (const rv of deps.db.listRuns()) {
|
|
14524
|
-
|
|
15039
|
+
const checkedId = rv.kind === "reviewer" ? rv.reviews_run_id : rv.kind === "verifier" ? rv.verifies_run_id : null;
|
|
15040
|
+
if (checkedId == null) continue;
|
|
14525
15041
|
if (!REVIEWER_ALIVE_STATUSES.has(rv.status)) continue;
|
|
14526
|
-
const parent = deps.db.getRun(
|
|
15042
|
+
const parent = deps.db.getRun(checkedId);
|
|
14527
15043
|
if (parent && !isTerminalStatus(parent.status)) continue;
|
|
14528
15044
|
try {
|
|
14529
15045
|
const why = parent ? `\u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442 (${STATUS_META[parent.status]?.label ?? parent.status})` : "\u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u0438\u0441\u0447\u0435\u0437";
|
|
14530
|
-
await closeReviewerRun(deps, rv, `${why} \u2014 PR \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043D\u0430 \u0440\u0435\u0432\u044C\u044E, \u0440\
|
|
14531
|
-
if (parent) deps.db.addEvent(parent.id, "step",
|
|
15046
|
+
await closeReviewerRun(deps, rv, `${why} \u2014 PR \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043D\u0430 \u0440\u0435\u0432\u044C\u044E, \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E`);
|
|
15047
|
+
if (parent) deps.db.addEvent(parent.id, "step", `${rv.kind === "verifier" ? "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440" : "\u0440\u0435\u0432\u044C\u044E\u0435\u0440"} ${rv.name} (#${rv.id}) \u0437\u0430\u043A\u0440\u044B\u0442: ${why}`);
|
|
14532
15048
|
narrate(deps, `${droneLabel(rv)} \u043E\u0441\u0442\u0430\u043B\u0441\u044F \u043D\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u043E\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u0435 \u2014 \u0433\u0430\u0448\u0443 \u0441\u0430\u043C, \u0441\u043B\u043E\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0451\u043D.`);
|
|
14533
15049
|
} catch (e) {
|
|
14534
15050
|
console.error(`[overmind] \u0437\u0430\u043A\u0440\u044B\u0442\u0438\u0435 \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 #${rv.id} \u0441\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C:`, e);
|
|
14535
15051
|
}
|
|
14536
15052
|
}
|
|
14537
15053
|
}
|
|
15054
|
+
var VERIFY_SPAWN_LIMIT = Number(process.env.SWARM_VERIFY_SPAWN_LIMIT) || 2;
|
|
15055
|
+
var VERIFY_ESCALATION_MARK = "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F: \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B";
|
|
15056
|
+
function countActiveVerifiers(db) {
|
|
15057
|
+
return db.listRuns().filter((r) => r.kind === "verifier" && ACTIVE_DRONE_STATUSES.has(r.status)).length;
|
|
15058
|
+
}
|
|
15059
|
+
async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
15060
|
+
if (!deps.spawnVerifier) return;
|
|
15061
|
+
if (stormActive) return;
|
|
15062
|
+
for (const run of deps.db.listRuns("review")) {
|
|
15063
|
+
try {
|
|
15064
|
+
if (run.kind !== "drone") continue;
|
|
15065
|
+
if (run.reviews_run_id != null || run.resolves_run_id != null || run.verifies_run_id != null) continue;
|
|
15066
|
+
if (!run.pr_url || !isGithubPrUrl(run.pr_url)) continue;
|
|
15067
|
+
if (run.verify_verdict != null) continue;
|
|
15068
|
+
if (deps.db.verifierCountFor(run.id) > 0) continue;
|
|
15069
|
+
const attempts = deps.db.verifierAttemptCountFor(run.id);
|
|
15070
|
+
if (attempts >= VERIFY_SPAWN_LIMIT) {
|
|
15071
|
+
if (!deps.db.events(run.id).some((e) => (e.text ?? "").includes(VERIFY_ESCALATION_MARK))) {
|
|
15072
|
+
deps.db.addEvent(run.id, "step", `${VERIFY_ESCALATION_MARK}: ${attempts} \u043F\u043E\u043F\u044B\u0442(\u043E\u043A) \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`);
|
|
15073
|
+
narrate(deps, `\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F ${droneLabel(run)} \u043D\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u0437\u0430 ${attempts} \u043F\u043E\u043F\u044B\u0442(\u043E\u043A) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A.`);
|
|
15074
|
+
}
|
|
15075
|
+
continue;
|
|
15076
|
+
}
|
|
15077
|
+
if (deps.resourcesOk && !deps.resourcesOk()) continue;
|
|
15078
|
+
if (deps.reviewCap !== void 0 && countActiveVerifiers(deps.db) >= deps.reviewCap) continue;
|
|
15079
|
+
const st = await deps.prStatus(run.pr_url).catch(() => null);
|
|
15080
|
+
if (prLiveness(st) !== "open") continue;
|
|
15081
|
+
if (st?.isDraft || st?.conflicting) continue;
|
|
15082
|
+
await deps.spawnVerifier(run, st?.files ?? []);
|
|
15083
|
+
narrate(deps, `PR ${droneLabel(run)} \u043E\u0442\u043A\u0440\u044B\u0442 \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430: \u043F\u0443\u0441\u0442\u044C \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0435\u0442 \u043E\u043F\u0440\u043E\u0432\u0435\u0440\u0433\u043D\u0443\u0442\u044C \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0435.`);
|
|
15084
|
+
} catch (e) {
|
|
15085
|
+
deps.db.addEvent(run.id, "step", `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C: ${String(e)}`);
|
|
15086
|
+
console.error(`[overmind] \u043F\u043E\u0434\u044A\u0451\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u0434\u043B\u044F #${run.id} (${run.task_slug}) \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:`, e);
|
|
15087
|
+
}
|
|
15088
|
+
}
|
|
15089
|
+
}
|
|
14538
15090
|
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.";
|
|
14539
15091
|
function buildCiWakeNudge(summary) {
|
|
14540
15092
|
return [
|
|
@@ -14711,6 +15263,16 @@ function ciTrustLine(prUrl, verifyMode) {
|
|
|
14711
15263
|
}
|
|
14712
15264
|
return `\u041D\u0435 \u043F\u0435\u0440\u0435\u0433\u043E\u043D\u044F\u0439 \u0442\u0435\u0441\u0442-\u0441\u044C\u044E\u0442/typecheck \u0437\u0430\u043D\u043E\u0432\u043E \u2014 \u044D\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043B CI \u043D\u0430 \u044D\u0442\u043E\u043C \u0436\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 (\u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043E\u0442\u043C\u0435\u043D\u0451\u043D, TASWM-249). \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0435\u0433\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u0441\u0430\u043C: ${readChecks}. \u0414\u0430\u043B\u044C\u0448\u0435 \u0442\u0440\u0438 \u0440\u0430\u0437\u043D\u044B\u0445 \u0441\u043B\u0443\u0447\u0430\u044F: \u2460 \u0445\u043E\u0442\u044C \u043E\u0434\u0438\u043D \u0447\u0435\u043A \u0435\u0449\u0451 PENDING \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u043E\u043E\u0431\u0449\u0435 \u043D\u0435 \u0432\u044B\u043D\u043E\u0441\u0438: \u043D\u0435 \u043F\u043E\u0441\u0442\u0438 REQUEST CHANGES, \u043D\u0435 \u0437\u043E\u0432\u0438 done(), \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E \u043A\u0430\u043A \u0435\u0441\u0442\u044C \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0437\u0430\u043C\u0435\u0442\u0438\u0442, \u0447\u0442\u043E \u0442\u044B \u0436\u0434\u0451\u0448\u044C CI, \u0438 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442 \u0434\u043E\u0434\u0435\u043B\u0430\u0442\u044C \u0440\u0435\u0432\u044C\u044E, \u043A\u043E\u0433\u0434\u0430 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u0441\u044F (\u044D\u0442\u043E \u0448\u0442\u0430\u0442\u043D\u044B\u0439 \u043F\u0443\u0442\u044C, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0437\u0432\u0430\u0442\u044C \u043D\u0435 \u043D\u0443\u0436\u043D\u043E). \u2461 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C \u0438 \u0435\u0441\u0442\u044C FAIL \u2014 REQUEST CHANGES, \u043D\u0430\u0437\u043E\u0432\u0438 \u043F\u043E\u0438\u043C\u0451\u043D\u043D\u043E, \u0447\u0442\u043E \u0443\u043F\u0430\u043B\u043E. \u2462 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0447\u0435\u043A\u043E\u0432 \u043D\u0435\u0442 \u0432\u043E\u0432\u0441\u0435 \u2014 \u0442\u043E\u0436\u0435 REQUEST CHANGES: \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C (\u0443 \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0430 verifyMode=ci, \u0442\u043E \u0435\u0441\u0442\u044C CI \u043E\u0431\u044F\u0437\u0430\u043D \u0431\u044B\u0442\u044C). ${dontTrustAuthor}`;
|
|
14713
15265
|
}
|
|
15266
|
+
var REVIEW_DEFECT_CLASSES = [
|
|
15267
|
+
"\u041A\u043B\u0430\u0441\u0441\u044B \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u0432 \u2014 \u043F\u0440\u043E\u0439\u0434\u0438 \u043F\u043E \u043A\u0430\u0436\u0434\u043E\u043C\u0443 \u0438 \u0434\u0430\u0439 \u043E\u0442\u0432\u0435\u0442 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435; \xAB\u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\xBB \u2014 \u0437\u0430\u043A\u043E\u043D\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442, \u043D\u043E \u043D\u0430\u0437\u0432\u0430\u043D\u043D\u044B\u0439 \u0438 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 \u0432 \u043F\u043E\u043B\u0441\u0442\u0440\u043E\u043A\u0438 (\xAB\u0440\u0443\u0447\u0435\u043A \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E\xBB, \xAB\u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u0438\u0439 \u043D\u0435 \u0442\u0440\u043E\u043D\u0443\u0442\u043E\xBB). \u0428\u0435\u0441\u0442\u044C \u0440\u0430\u0437 \xAB\u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\xBB \u0431\u0435\u0437 \u043F\u0440\u0438\u0447\u0438\u043D \u2014 \u043D\u0435 \u0440\u0435\u0432\u044C\u044E, \u0430 \u043E\u0442\u043F\u0438\u0441\u043A\u0430:",
|
|
15268
|
+
"- [ ] \u041F\u0440\u0430\u0432\u0430 \u0430\u043A\u0442\u043E\u0440\u0430 \u0438 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441: \u043A\u0442\u043E \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043A\u0438 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u0437\u0432\u0430\u0442\u044C \u043D\u043E\u0432\u0443\u044E \u0440\u0443\u0447\u043A\u0443 \u0438\u043B\u0438 \u043D\u0430\u0436\u0430\u0442\u044C \u043A\u043D\u043E\u043F\u043A\u0443 \u2014 \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u0434\u0440\u043E\u043D \u0440\u043E\u044F, \u0433\u043E\u0441\u0442\u044C, \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A \u0427\u0423\u0416\u041E\u0413\u041E \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430? \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u0432\u043E\u0434\u0438\u043B\u0430 \u0440\u0430\u0434\u0438 \u0420\u0415\u0428\u0415\u041D\u0418\u042F \u0427\u0415\u041B\u041E\u0412\u0415\u041A\u0410 (\u043F\u0443\u0431\u043B\u0438\u043A\u0430\u0446\u0438\u044F, \u0430\u043F\u0440\u0443\u0432, \u043E\u043F\u043B\u0430\u0442\u0430, \u0440\u0430\u0441\u0441\u044B\u043B\u043A\u0430), \u043E\u0431\u044F\u0437\u0430\u043D\u043E \u043E\u0442\u0431\u0438\u0432\u0430\u0442\u044C \u0430\u0433\u0435\u043D\u0442\u0430 \u044F\u0432\u043D\u043E (denyAgentActor \u0438 \u0430\u043D\u0430\u043B\u043E\u0433\u0438), \u0430 \u043D\u0435 \u043F\u043E\u043B\u0430\u0433\u0430\u0442\u044C\u0441\u044F \u043D\u0430 \u0442\u043E, \u0447\u0442\u043E \u0443 \u043D\u0435\u0433\u043E \u0440\u043E\u043B\u044C \u043F\u043E\u0436\u0438\u0436\u0435 \u2014 \u0443 \u0434\u0440\u043E\u043D\u0430 \u043E\u043D\u0430 \u043E\u0431\u044B\u0447\u043D\u043E \u043A\u0430\u043A \u0443 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430. \u041B\u044E\u0431\u043E\u0439 id, \u043F\u0440\u0438\u0435\u0445\u0430\u0432\u0448\u0438\u0439 \u0432 \u0442\u0435\u043B\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u0430, \u043E\u0431\u044F\u0437\u0430\u043D \u0441\u0432\u0435\u0440\u044F\u0442\u044C\u0441\u044F \u0441 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u043E\u043C \u0437\u0432\u043E\u043D\u044F\u0449\u0435\u0433\u043E: \u0440\u043E\u0443\u0442 \u0431\u0435\u0437 \u0442\u0430\u043A\u043E\u0439 \u0441\u0432\u0435\u0440\u043A\u0438 \u2014 \u043A\u0440\u043E\u0441\u0441-\u0442\u0435\u043D\u0430\u043D\u0442\u043D\u0430\u044F \u0434\u044B\u0440\u0430, \u0438 \u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u044D\u0442\u043E \u0441\u0430\u043C\u044B\u0439 \u0447\u0430\u0441\u0442\u044B\u0439 \u043A\u043B\u0430\u0441\u0441.",
|
|
15269
|
+
"- [ ] \u041F\u043E\u043B\u043D\u043E\u0442\u0430 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432: PR \u0434\u043E\u0431\u0430\u0432\u0438\u043B \u0432\u0430\u0440\u0438\u0430\u043D\u0442 \u0432 enum/union/\u043D\u0430\u0431\u043E\u0440 \u0441\u0442\u0440\u043E\u043A \u2014 \u043D\u0430\u0439\u0434\u0438 \u0412\u0421\u0415 \u043C\u0435\u0441\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u044D\u0442\u0438 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u044B \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u044F\u044E\u0442 (\u0431\u0435\u043B\u044B\u0435 \u0441\u043F\u0438\u0441\u043A\u0438, switch, \u043C\u0430\u043F\u043F\u0438\u043D\u0433\u0438, \u0443\u0441\u043B\u043E\u0432\u0438\u044F \u0432 UI), \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044C, \u0447\u0442\u043E \u043D\u043E\u0432\u044B\u0439 \u0442\u0430\u043C \u0435\u0441\u0442\u044C. typecheck \u044D\u0442\u043E \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442, \u043A\u043E\u0433\u0434\u0430 \u0441\u043F\u0438\u0441\u043E\u043A \u2014 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435, \u0430 \u043D\u0435 exhaustive-switch. \u042D\u0442\u043E \u041E\u0414\u0418\u041D grep \u043F\u043E \u0438\u043C\u0435\u043D\u0438 \u0442\u0438\u043F\u0430 \u0438\u043B\u0438 \u043F\u043E \u0441\u0430\u043C\u043E\u043C\u0443 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044E, \u0430 \u043D\u0435 \u0447\u0442\u0435\u043D\u0438\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \xAB\u0434\u043B\u044F \u043F\u043E\u043D\u0438\u043C\u0430\u043D\u0438\u044F\xBB \u2014 \u043A\u043E\u043D\u0442\u0443\u0440 \u044D\u043A\u043E\u043D\u043E\u043C\u0438\u0438 \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0440\u0435\u0449\u0430\u0435\u0442.",
|
|
15270
|
+
"- [ ] \u0412\u0441\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0430: \u0443 \u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0438 \u043F\u043E\u0447\u0442\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u043E\u043B\u044C\u0448\u0435 \u0434\u0432\u0443\u0445 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0439 (\u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u043F\u0430\u0443\u0437\u0430, drain, \u043C\u043E\u043B\u0447\u0438\u0442, \u0434\u0430\u043D\u043D\u044B\u0445 \u0435\u0449\u0451 \u043D\u0435\u0442, \u0434\u043E\u0441\u0442\u0443\u043F \u0437\u0430\u043A\u0440\u044B\u0442). \u0420\u0430\u0437\u043E\u0431\u0440\u0430\u043D\u044B \u0412\u0421\u0415 \u0438\u043B\u0438 \u0442\u043E\u043B\u044C\u043A\u043E happy path \u0438 \u043E\u0434\u043D\u0430 \u043E\u0448\u0438\u0431\u043A\u0430? \u0427\u0430\u0441\u0442\u043E \u043F\u043E\u043B\u043D\u044B\u0439 \u043D\u0430\u0431\u043E\u0440 \u0443\u0436\u0435 \u043B\u0435\u0436\u0438\u0442 \u0440\u044F\u0434\u043E\u043C \u0432 \u044F\u0434\u0440\u0435 \u2014 \u043F\u043E\u0438\u0449\u0438 \u0435\u0433\u043E, \u043F\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043C \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u043D\u044B\u043C.",
|
|
15271
|
+
"- [ ] \u041E\u0448\u0438\u0431\u043A\u0430 \u0434\u043E\u0445\u043E\u0434\u0438\u0442 \u0434\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430: \u043E\u0442\u043A\u0430\u0437 \u0437\u0430\u043F\u0440\u043E\u0441\u0430, \u043F\u0440\u043E\u0432\u0430\u043B \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438, \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u2014 \u0432\u0438\u0434\u043D\u044B \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435, \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 console.warn/\u043B\u043E\u0433. \u041A\u043D\u043E\u043F\u043A\u0430, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u043C\u043E\u043B\u0447\u0430 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0434\u0435\u043B\u0430\u043B\u0430, \u2014 \u0434\u0435\u0444\u0435\u043A\u0442, \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E.",
|
|
15272
|
+
"- [ ] \u0414\u0435\u0444\u043E\u043B\u0442 \u043F\u0440\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445: \u043F\u043E\u043B\u044F \u043D\u0435\u0442, \u043E\u0442\u0432\u0435\u0442 \u043F\u0443\u0441\u0442\u043E\u0439, \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \u043C\u043E\u043B\u0447\u0438\u0442 \u2014 \u043A\u0443\u0434\u0430 \u043F\u0430\u0434\u0430\u0435\u0442 \u0440\u0435\u0448\u0435\u043D\u0438\u0435? \u0414\u043B\u044F \u0443\u0434\u0430\u043B\u044F\u044E\u0449\u0438\u0445, \u043F\u0443\u0431\u043B\u0438\u043A\u0443\u044E\u0449\u0438\u0445 \u0438 \u0441\u043F\u0438\u0441\u044B\u0432\u0430\u044E\u0449\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u0435\u0444\u043E\u043B\u0442 \u043E\u0431\u044F\u0437\u0430\u043D \u0431\u044B\u0442\u044C \u0432 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u0443\u044E \u0441\u0442\u043E\u0440\u043E\u043D\u0443 (\u043D\u0435 \u0434\u0435\u043B\u0430\u0442\u044C), \u0430 \xAB\u043D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445\xBB \u043D\u0435 \u0441\u043C\u0435\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044C\u0441\u044F \u043A\u0430\u043A \xAB\u0447\u0438\u0441\u0442\u043E, \u043C\u043E\u0436\u043D\u043E\xBB.",
|
|
15273
|
+
"- [ ] \u0422\u0435\u0441\u0442\u044B PR \u0434\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u044E\u0442 \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0435. \u0415\u0441\u043B\u0438 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0423\u0416\u0415 \u043F\u043E\u0434\u043D\u044F\u0442\u043E (\u0432 worktree \u0435\u0441\u0442\u044C node_modules \u0438 \u043F\u0430\u043A\u0435\u0442\u044B \u0441\u043E\u0431\u0440\u0430\u043D\u044B): \u0441\u043D\u0438\u043C\u0438 \u0432 \u041A\u041E\u0414\u0415 \u0442\u0443 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043D\u043E\u0432\u044B\u0439 \u0442\u0435\u0441\u0442 \u044F\u043A\u043E\u0431\u044B \u0441\u0442\u043E\u0440\u043E\u0436\u0438\u0442 (\u043E\u0434\u043D\u0443 \u0441\u0442\u0440\u043E\u043A\u0443 \u2014 \u0433\u0435\u0439\u0442, \u0443\u0441\u043B\u043E\u0432\u0438\u0435, \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u044E), \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u041E\u0414\u0418\u041D \u044D\u0442\u043E\u0442 \u0442\u0435\u0441\u0442 (\u043D\u0435 \u0432\u0435\u0441\u044C \u0441\u044C\u044E\u0442), \u0432\u0435\u0440\u043D\u0438 \u0444\u0430\u0439\u043B \u043A\u0430\u043A \u0431\u044B\u043B\u043E. \u0422\u0435\u0441\u0442 \u043E\u0431\u044F\u0437\u0430\u043D \u043F\u043E\u043A\u0440\u0430\u0441\u043D\u0435\u0442\u044C; \u043E\u0441\u0442\u0430\u043B\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C \u2014 \u043D\u0430\u0445\u043E\u0434\u043A\u0430: \u043E\u043D \u043D\u0435 \u0441\u0442\u043E\u0440\u043E\u0436\u0438\u0442 \u043D\u0438\u0447\u0435\u0433\u043E.",
|
|
15274
|
+
" \u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u041D\u0415 \u043F\u043E\u0434\u043D\u044F\u0442\u043E \u2014 \u0442\u0430\u043A \u0438 \u0431\u0443\u0434\u0435\u0442: \u0442\u0435\u0431\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0438 \u0447\u0438\u0441\u0442\u044B\u0439 \u0447\u0435\u043A\u0430\u0443\u0442, \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u043B. \u041F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C \u0438\u0445 \u041D\u0415 \u041D\u0410\u0414\u041E, \u044D\u0442\u043E \u0434\u043E\u0440\u043E\u0436\u0435 \u0432\u0441\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E: \u0441\u043A\u0430\u0436\u0438 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435 \xAB\u043F\u0440\u043E\u0433\u043D\u0430\u0442\u044C \u043D\u0435 \u043C\u043E\u0433, \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0435\u0442\xBB \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0442\u0435\u0441\u0442 \u0427\u0422\u0415\u041D\u0418\u0415\u041C. \u0422\u0440\u0438 \u0441\u043F\u043E\u0441\u043E\u0431\u0430 \u0441\u043E\u0432\u0440\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C, \u0432\u0438\u0434\u043D\u044B\u0435 \u0433\u043B\u0430\u0437\u0430\u043C\u0438: (\u0430) \u0438\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0433\u043B\u043E\u0447\u0435\u043D\u043E (`try/except`, \u0437\u043E\u043D\u0442 \u0441\u0442\u043E\u0440\u043E\u0436\u0430, `.catch(() => {})`) \u2014 \u0442\u0435\u0441\u0442 \u043D\u0435 \u043F\u0430\u0434\u0430\u0435\u0442 \u0442\u0430\u043C, \u0433\u0434\u0435 \u043E\u0431\u044F\u0437\u0430\u043D; (\u0431) \u0430\u0441\u0441\u0435\u0440\u0442 \u043D\u0430 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435, \u0432\u0435\u0440\u043D\u043E\u0435 \u0432\u0441\u0435\u0433\u0434\u0430, \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E \u043E\u0442 \u043F\u0440\u0430\u0432\u043A\u0438; (\u0432) \u0442\u0435\u0441\u0442 \u043D\u0435 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u0438\u0437\u043C\u0435\u043D\u0451\u043D\u043D\u044B\u0439 \u0433\u0435\u0439\u0442 \u0432\u043E\u0432\u0441\u0435 \u2014 \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0439 \u043F\u0443\u0442\u044C."
|
|
15275
|
+
].join("\n");
|
|
14714
15276
|
function buildReviewChecklist(reviewRun, files, priorReview, verifyMode = SWARM_VERIFY) {
|
|
14715
15277
|
const header = files.length ? `\u0424\u0430\u0439\u043B\u044B \u0434\u0438\u0444\u0444\u0430 (${files.length}):
|
|
14716
15278
|
${files.map((f) => `- ${f}`).join("\n")}` : "\u0424\u0430\u0439\u043B\u044B \u0434\u0438\u0444\u0444\u0430: gh \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0441\u043F\u0438\u0441\u043E\u043A \u2014 \u0441\u043C. PR.";
|
|
@@ -14730,7 +15292,11 @@ ${priorReview}` : "",
|
|
|
14730
15292
|
"\u041A\u043E\u0440\u043E\u0442\u043A\u0438\u0439 \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0440\u0435-\u0447\u0435\u043A\u0430:",
|
|
14731
15293
|
"- [ ] \u0417\u0430\u043C\u0435\u0447\u0430\u043D\u0438\u044F \u043F\u0440\u043E\u0448\u043B\u043E\u0433\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u044B \u2014 \u043F\u043E \u043A\u0430\u0436\u0434\u043E\u043C\u0443 \u043F\u0443\u043D\u043A\u0442\u0443.",
|
|
14732
15294
|
`- [ ] \u0420\u0435\u0433\u0440\u0435\u0441\u0441\u0430 \u043D\u0435\u0442: ${ciTrustLine(prUrl, verifyMode)}`,
|
|
14733
|
-
"- [ ] \u041D\u043E\u0432\u044B\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u043D\u0435 \u043F\u0440\u0438\u0432\u043D\u0435\u0441\u043B\u0438 \u043D\u043E\u0432\u044B\u0445 \u043F\u0440\u043E\u0431\u043B\u0435\u043C
|
|
15295
|
+
"- [ ] \u041D\u043E\u0432\u044B\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u043D\u0435 \u043F\u0440\u0438\u0432\u043D\u0435\u0441\u043B\u0438 \u043D\u043E\u0432\u044B\u0445 \u043F\u0440\u043E\u0431\u043B\u0435\u043C: \u0433\u043E\u043D\u043A\u0438/\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438/UI \u043F\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\u0441\u0442\u0438 \u2014 \u0438 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u043F\u0440\u0430\u0432\u0430 \u0430\u043A\u0442\u043E\u0440\u0430, \u0434\u0435\u0444\u043E\u043B\u0442 \u043F\u0440\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445, \u043F\u043E\u043B\u043D\u043E\u0442\u0430 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432. \u041F\u0440\u0430\u0432\u043A\u0430 \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443 \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u0442\u0435 \u0436\u0435 \u043C\u0435\u0441\u0442\u0430, \u0433\u0434\u0435 \u0434\u0435\u0444\u0435\u043A\u0442 \u0438 \u0431\u044B\u043B, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0438\u043C\u0435\u043D\u043D\u043E \u043E\u043D\u0430 \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043E \u043F\u0440\u0438\u043D\u043E\u0441\u0438\u0442 \u043D\u043E\u0432\u044B\u0439.",
|
|
15296
|
+
// Единственный класс из REVIEW_DEFECT_CLASSES, который на втором круге обязателен:
|
|
15297
|
+
// правки по вердикту почти всегда приносят НОВЫЕ тесты, и именно тут прошёл #819.
|
|
15298
|
+
// Остальные пять классов заново не гоняем — ре-чек намеренно короткий (TASWM-437).
|
|
15299
|
+
"- [ ] \u041D\u043E\u0432\u044B\u0435 \u0442\u0435\u0441\u0442\u044B \u043F\u0440\u0430\u0432\u043A\u0438: \u0441\u043D\u0438\u043C\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u0442\u0435\u0441\u0442 \u0441\u0442\u043E\u0440\u043E\u0436\u0438\u0442, \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u041E\u0414\u0418\u041D \u044D\u0442\u043E\u0442 \u0442\u0435\u0441\u0442, \u0432\u0435\u0440\u043D\u0438 \u043A\u0430\u043A \u0431\u044B\u043B\u043E \u2014 \u043E\u043D \u043E\u0431\u044F\u0437\u0430\u043D \u043F\u043E\u043A\u0440\u0430\u0441\u043D\u0435\u0442\u044C."
|
|
14734
15300
|
].filter(Boolean).join("\n");
|
|
14735
15301
|
}
|
|
14736
15302
|
return [
|
|
@@ -14740,8 +15306,11 @@ ${priorReview}` : "",
|
|
|
14740
15306
|
"",
|
|
14741
15307
|
verifyMode === "local" ? "\u0427\u0435\u043A-\u043B\u0438\u0441\u0442 (\u0433\u0435\u0439\u0442 \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0433\u043E\u043D\u044F\u0435\u0448\u044C \u0442\u044B \u0441\u0430\u043C \u2014 \u0441\u043C. \u043F\u0435\u0440\u0432\u044B\u0439 \u043F\u0443\u043D\u043A\u0442; \u0434\u0430\u043B\u044C\u0448\u0435 \u0440\u0443\u043A\u0430\u043C\u0438 \u0442\u043E, \u0447\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0435 \u043F\u043E\u043A\u0440\u044B\u0432\u0430\u0435\u0442):" : "\u0427\u0435\u043A-\u043B\u0438\u0441\u0442 (\u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0439 \u0440\u0443\u043A\u0430\u043C\u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u043E, \u0447\u0435\u0433\u043E CI \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043A\u0438 \u043D\u0435 \u043F\u043E\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u2014 \u043D\u0435 \u0432\u0435\u0440\u044C \u0446\u0438\u0444\u0440\u0430\u043C \u0430\u0432\u0442\u043E\u0440\u0430 PR):",
|
|
14742
15308
|
`- [ ] ${ciTrustLine(prUrl, verifyMode)}`,
|
|
15309
|
+
"",
|
|
15310
|
+
REVIEW_DEFECT_CLASSES,
|
|
15311
|
+
"",
|
|
14743
15312
|
"- [ ] \u041C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 (\u0435\u0441\u043B\u0438 \u0435\u0441\u0442\u044C) \u2014 \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u043E\u0441\u0442\u044C \u0441\u043E \u0441\u0442\u0430\u0440\u044B\u043C\u0438 \u0434\u0430\u043D\u043D\u044B\u043C\u0438, \u043E\u0442\u043A\u0430\u0442.",
|
|
14744
|
-
"- [ ] \u0413\u043E\u043D\u043A\u0438
|
|
15313
|
+
"- [ ] \u0413\u043E\u043D\u043A\u0438 \u2014 \u043F\u043E\u0440\u044F\u0434\u043E\u043A \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0439, \u0438\u0434\u0435\u043C\u043F\u043E\u0442\u0435\u043D\u0442\u043D\u043E\u0441\u0442\u044C \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E\u0433\u043E \u0432\u044B\u0437\u043E\u0432\u0430.",
|
|
14745
15314
|
"- [ ] UI-\u0434\u0438\u0441\u0446\u0438\u043F\u043B\u0438\u043D\u0430 (\u0434\u043B\u044F web-PR) \u2014 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435 (uikit), \u0441\u043A\u0440\u0438\u043D\u044B mobile+desktop \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u044B \u041A \u0417\u0410\u0414\u0410\u0427\u0415 (done(screenshots), TASWM-294), \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u043A PR.",
|
|
14746
15315
|
"- [ ] \u0415\u0441\u043B\u0438 \u043F\u0440\u0430\u0432\u0438\u0442\u0441\u044F router.tsx \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0430 per-page meta \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0430 (\u0438\u043D\u0430\u0447\u0435 NL-\u043D\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044F \u043C\u043E\u043B\u0447\u0430 \u043B\u043E\u043C\u0430\u0435\u0442\u0441\u044F)."
|
|
14747
15316
|
].join("\n");
|
|
@@ -14767,9 +15336,16 @@ var REVIEW_SCOPE_BLOCK = [
|
|
|
14767
15336
|
"\u2022 \u0417\u0430\u0445\u043E\u0434\u043E\u0432 \u043D\u0430 \u043E\u0434\u0438\u043D \u0432\u043E\u043F\u0440\u043E\u0441 \u2014 \u043E\u0434\u0438\u043D. \u041D\u0435 \u043D\u0430\u0448\u0451\u043B \u043E\u0442\u0432\u0435\u0442\u0430 \u0441 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0440\u0430\u0437\u0430 (\u0438\u043B\u0438 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 \u043C\u043E\u043B\u0447\u0438\u0442) \u2014 \u044D\u0442\u043E \u0443\u0436\u0435 \u043D\u0430\u0445\u043E\u0434\u043A\u0430: \u0441\u043A\u0430\u0436\u0438 \u043E\u0431 \u044D\u0442\u043E\u043C \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435, \u0430 \u043D\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0439 \u0442\u0440\u0435\u0442\u0438\u0439 \u043A\u0440\u0443\u0433 \u043F\u043E\u0438\u0441\u043A\u0430.",
|
|
14768
15337
|
"\u2022 \u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u0443\u0441\u043F\u0435\u043B \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C, \u0412\u0421\u0415\u0413\u0414\u0410 \u043B\u0443\u0447\u0448\u0435 \u0438\u0434\u0435\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043D\u0435 \u0431\u0443\u0434\u0435\u0442: \u043E\u0434\u043D\u043E\u0433\u043E \u043E\u0431\u043E\u0441\u043D\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u0437\u0430\u043C\u0435\u0447\u0430\u043D\u0438\u044F \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0447\u0442\u043E\u0431\u044B PR \u043F\u043E\u0435\u0445\u0430\u043B \u043D\u0430 \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u043A\u0443. \u041A\u043E\u043F\u0430\u0435\u0448\u044C \u0434\u043E\u043B\u0433\u043E \u2014 \u0441\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0439\u0441\u044F \u0438 \u0432\u044B\u043D\u043E\u0441\u0438 \u0442\u043E, \u0447\u0442\u043E \u0435\u0441\u0442\u044C, \u0447\u0435\u0441\u0442\u043D\u043E \u043D\u0430\u0437\u0432\u0430\u0432, \u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u043C\u043E\u0442\u0440\u0435\u043B."
|
|
14769
15338
|
].join("\n");
|
|
15339
|
+
var REVIEW_MISSION_BLOCK = [
|
|
15340
|
+
"\u{1F3AF} \u0422\u0412\u041E\u042F \u0420\u0410\u0411\u041E\u0422\u0410 \u2014 \u041D\u0410\u0419\u0422\u0418 \u0414\u0415\u0424\u0415\u041A\u0422\u042B. \u041F\u0435\u0440\u0435\u0441\u043A\u0430\u0437 \u0442\u043E\u0433\u043E, \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0442 PR, \u0440\u0430\u0431\u043E\u0442\u043E\u0439 \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F: \u044D\u0442\u043E \u0430\u0432\u0442\u043E\u0440 \u0443\u0436\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043B \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438. \u0422\u044B \u0438\u0449\u0435\u0448\u044C, \u0447\u0442\u043E \u0441\u043B\u043E\u043C\u0430\u0435\u0442\u0441\u044F.",
|
|
15341
|
+
"\u2022 \u0417\u0435\u043B\u0451\u043D\u044B\u0439 CI \u043D\u0435 \u0434\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u0447\u0442\u043E \u0434\u0435\u0444\u0435\u043A\u0442\u0430 \u043D\u0435\u0442: \u043E\u043D \u0433\u043E\u043D\u044F\u0435\u0442 \u0442\u0435\u0441\u0442\u044B \u0430\u0432\u0442\u043E\u0440\u0430 \u0438 \u0442\u0438\u043F\u044B. \u0421\u043C\u043E\u0442\u0440\u0438 \u0440\u043E\u0432\u043D\u043E \u0442\u043E, \u0447\u0435\u0433\u043E \u043E\u043D \u043D\u0435 \u0432\u0438\u0434\u0438\u0442 \u2014 \u043F\u0440\u0430\u0432\u0430 \u0430\u043A\u0442\u043E\u0440\u0430, \u043C\u0443\u043B\u044C\u0442\u0438\u0442\u0435\u043D\u0430\u043D\u0442\u043D\u043E\u0441\u0442\u044C, \u0434\u0435\u0444\u043E\u043B\u0442\u044B \u043F\u0440\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445, \u043D\u0435\u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u043D\u044B\u0435 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u044B \u0438 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F, \u0442\u0435\u043A\u0441\u0442 \u043E\u0448\u0438\u0431\u043A\u0438 \u0434\u043B\u044F \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430, \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u043F\u0440\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438.",
|
|
15342
|
+
"\u2022 \u041D\u0430\u0445\u043E\u0434\u043A\u0430 \u0434\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u0421\u0426\u0415\u041D\u0410\u0420\u0418\u0415\u041C: \xAB\u0442\u0430\u043A\u043E\u0439 \u0432\u0445\u043E\u0434 \u0438\u043B\u0438 \u0442\u0430\u043A\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u2192 \u0432\u043E\u0442 \u0442\u0430\u043A\u043E\u0439 \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\xBB. \xAB\u0412\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043D\u0435\u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\xBB, \xAB\u0441\u0442\u043E\u0438\u0442 \u043F\u0435\u0440\u0435\u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C\xBB, \xAB\u0436\u0435\u043B\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043F\u043E\u043A\u0440\u044B\u0442\u044C \u0442\u0435\u0441\u0442\u0430\u043C\u0438\xBB \u2014 \u043D\u0435 \u043D\u0430\u0445\u043E\u0434\u043A\u0438; \u0442\u0430\u043A\u043E\u0435 \u043D\u0435 \u043F\u0438\u0448\u0438.",
|
|
15343
|
+
"\u2022 \u041D\u043E\u043B\u044C \u043D\u0430\u0445\u043E\u0434\u043E\u043A \u2014 \u0437\u0430\u043A\u043E\u043D\u043D\u044B\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442, \u043D\u043E \u0442\u043E\u0433\u0434\u0430 \u043D\u0430\u0437\u043E\u0432\u0438, \u0427\u0422\u041E \u0438\u043C\u0435\u043D\u043D\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u043A\u0430\u043A\u0438\u043C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0435\u043C. APPROVE \u0431\u0435\u0437 \u044D\u0442\u043E\u0433\u043E \u0440\u043E\u0439 \u0447\u0438\u0442\u0430\u0435\u0442 \u043A\u0430\u043A \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437, \u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 PR \u0437\u0430\u043D\u043E\u0432\u043E \u0432\u0440\u0443\u0447\u043D\u0443\u044E."
|
|
15344
|
+
].join("\n");
|
|
14770
15345
|
function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachmentsBlock, verifyMode = SWARM_VERIFY) {
|
|
14771
15346
|
const name = reviewer.name || `#${reviewer.id}`;
|
|
14772
15347
|
const prUrl = reviewRun.pr_url ?? "(\u0441\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 PR \u2014 \u0441\u043C. \u043B\u043E\u0433 \u043F\u0440\u043E\u0433\u043E\u043D\u0430)";
|
|
15348
|
+
const classesStep = reviewRun.review_cycle > 0 ? "" : "\u043F\u0440\u043E\u0439\u0434\u0438 \u043A\u043B\u0430\u0441\u0441\u044B \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u0432 (\u043F\u043E \u043A\u0430\u0436\u0434\u043E\u043C\u0443 \u2014 \u043E\u0442\u0432\u0435\u0442 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439, \xAB\u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\xBB \u0442\u043E\u0436\u0435), ";
|
|
14773
15349
|
return [
|
|
14774
15350
|
`\u0422\u044B \u2014 \u0434\u0440\u043E\u043D-\u0420\u0415\u0412\u042C\u042E\u0415\u0420 ${name} \u0440\u043E\u044F, headless-\u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C (\u041D\u0415 \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0447\u0430\u0442). \u0410\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E (TASWM-148): \u0440\u043E\u0439 \u0441\u0430\u043C \u043F\u043E\u0434\u043D\u044F\u043B \u0442\u0435\u0431\u044F \u043D\u0430 PR \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F \u2014 \u0442\u044B \u041D\u0415 \u0432\u044B\u0431\u0440\u0430\u043D \u0438\u043C \u0438 \u043E\u0442 \u043D\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0448\u044C.`,
|
|
14775
15351
|
"",
|
|
@@ -14783,7 +15359,12 @@ function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments
|
|
|
14783
15359
|
`\u26A0\uFE0F \u042D\u0442\u043E\u0442 PR \u043E\u0442\u043A\u0440\u044B\u0442 \u0432 \u0432\u0435\u0442\u043A\u0443 \`${reviewRun.base_branch}\` (\u0438\u043D\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043E\u043D\u043D\u0430\u044F \u043F\u0430\u0447\u043A\u0430), \u0430 \u041D\u0415 \u0432 \u0432\u0435\u0442\u043A\u0443 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E. \u0414\u0438\u0444 \u0441\u043C\u043E\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u0433\u043E \u043E\u0442 \u043D\u0435\u0451: \`git diff origin/${reviewRun.base_branch}...HEAD\` (\u0441\u043F\u0438\u0441\u043E\u043A \u0444\u0430\u0439\u043B\u043E\u0432 \u043D\u0438\u0436\u0435 \u0443\u0436\u0435 \u043F\u043E\u0441\u0447\u0438\u0442\u0430\u043D gh \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u044D\u0442\u043E\u0439 \u0431\u0430\u0437\u044B). \u0421\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0435 \u0441 main \u043F\u043E\u043A\u0430\u0437\u0430\u043B\u043E \u0431\u044B \u0442\u0435\u0431\u0435 \u0447\u0443\u0436\u0438\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u043F\u0430\u0447\u043A\u0438 \u043A\u0430\u043A \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u044D\u0442\u043E\u0433\u043E \u0434\u0440\u043E\u043D\u0430 \u2014 \u043D\u0435 \u0434\u0435\u043B\u0430\u0439 \u0442\u0430\u043A.`
|
|
14784
15360
|
] : [],
|
|
14785
15361
|
"",
|
|
14786
|
-
|
|
15362
|
+
// TASWM-437: мутационная проверка теста — это правка файла, и без явного исключения
|
|
15363
|
+
// ревьюер честно откажется её делать. Разрешаем ровно её: локально, с возвратом файла,
|
|
15364
|
+
// без коммита и пуша (worktree ревьюера всё равно удаляется после прогона).
|
|
15365
|
+
"\u26A0\uFE0F \u0422\u042B \u041D\u0415 \u041F\u0420\u0410\u0412\u0418\u0428\u042C \u041A\u041E\u0414 PR \u0418 \u041D\u0415 \u041F\u0423\u0428\u0418\u0428\u042C \u041A\u041E\u041C\u041C\u0418\u0422\u042B. \u0422\u0432\u043E\u044F \u0440\u0430\u0431\u043E\u0442\u0430 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0438 \u0432\u044B\u043D\u0435\u0441\u0442\u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442. \u0415\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u0430\u044F \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D\u043D\u0430\u044F \u043F\u0440\u0430\u0432\u043A\u0430 \u2014 \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u043C\u0443\u0442\u0430\u0446\u0438\u044F \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0442\u0435\u0441\u0442\u0430 (\u043F\u0443\u043D\u043A\u0442 \u0447\u0435\u043A-\u043B\u0438\u0441\u0442\u0430 \u043F\u0440\u043E \u0442\u0435\u0441\u0442\u044B): \u0441\u043D\u044F\u043B \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443, \u043F\u0440\u043E\u0433\u043D\u0430\u043B \u043E\u0434\u0438\u043D \u0442\u0435\u0441\u0442, \u0432\u0435\u0440\u043D\u0443\u043B \u0444\u0430\u0439\u043B (`git checkout -- <\u0444\u0430\u0439\u043B>`). \u0412\u043E\u0437\u0432\u0440\u0430\u0442 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D \u0414\u041E \u043A\u043E\u043D\u0446\u0430 \u0441\u0435\u0441\u0441\u0438\u0438, \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u0441\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0448\u044C\u0441\u044F \u0434\u043E\u0441\u0440\u043E\u0447\u043D\u043E (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 \u0443\u0445\u043E\u0434\u0438\u0448\u044C \u0436\u0434\u0430\u0442\u044C CI): `git status` \u0432 worktree \u043E\u0431\u044F\u0437\u0430\u043D \u0431\u044B\u0442\u044C \u0447\u0438\u0441\u0442. \u041A\u043E\u043C\u043C\u0438\u0442\u0438\u0442\u044C, \u043F\u0443\u0448\u0438\u0442\u044C \u0438 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u0441\u0432\u043E\u0438 PR \u043D\u0435\u043B\u044C\u0437\u044F \u043D\u0438\u0447\u0435\u0433\u043E \u0438 \u043D\u0438\u043A\u043E\u0433\u0434\u0430.",
|
|
15366
|
+
"",
|
|
15367
|
+
REVIEW_MISSION_BLOCK,
|
|
14787
15368
|
"",
|
|
14788
15369
|
REVIEW_SCOPE_BLOCK,
|
|
14789
15370
|
// TASWM-283: ревьюер читает ТУ ЖЕ задачу и так же слеп к её картинкам — без макета
|
|
@@ -14795,11 +15376,11 @@ function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments
|
|
|
14795
15376
|
"",
|
|
14796
15377
|
"\u041F\u043E\u0440\u044F\u0434\u043E\u043A:",
|
|
14797
15378
|
...verifyMode === "local" ? [
|
|
14798
|
-
|
|
15379
|
+
`1. \u041F\u0440\u043E\u0439\u0434\u0438 \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0432\u044B\u0448\u0435: \u0433\u0435\u0439\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u0421\u0410\u041C \u0432 \u0441\u0432\u043E\u0451\u043C worktree (\u044D\u0442\u043E \u0438 \u0435\u0441\u0442\u044C \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u0437\u0435\u043B\u0451\u043D\u043E\u0433\u043E \u2014 \u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 verifyMode=local), ${classesStep}\u043F\u0440\u043E\u0432\u0435\u0440\u044C \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438/\u0433\u043E\u043D\u043A\u0438/UI/per-page meta \u043F\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\u0441\u0442\u0438.`,
|
|
14799
15380
|
" \u0427\u0435\u043A\u043E\u0432 \u043D\u0430 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u043D\u0435\u0442 (\u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435\u0442 CI) \u2014 \u044D\u0442\u043E \u041D\u0415 \u0434\u0435\u0444\u0435\u043A\u0442 PR \u0438 \u041D\u0415 \u043F\u043E\u0432\u043E\u0434 \u0434\u043B\u044F REQUEST CHANGES: \u043D\u0435 \u0436\u0434\u0438 \u0438\u0445, \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0439 \u0437\u0430\u0432\u0435\u0441\u0442\u0438 workflow, \u0441\u0443\u0434\u0438 \u043F\u043E \u0441\u0432\u043E\u0435\u043C\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0443. \u041A\u0440\u0430\u0441\u043D\u044B\u0439 \u0417\u0410\u0412\u0415\u0420\u0428\u0418\u0412\u0428\u0418\u0419\u0421\u042F \u0447\u0435\u043A, \u0435\u0441\u043B\u0438 CI \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0432\u0441\u0451-\u0442\u0430\u043A\u0438 \u0435\u0441\u0442\u044C, \u2014 \u043F\u043E-\u043F\u0440\u0435\u0436\u043D\u0435\u043C\u0443 \u0431\u043B\u043E\u043A.",
|
|
14800
15381
|
`2. \u041F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u043E\u043D\u0447\u0435\u043D \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043D\u0430 \u0441\u0430\u043C PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u0435\u0433\u043E \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041D\u0430\u0447\u043D\u0438 \u0441 **APPROVE** \u0438\u043B\u0438 **REQUEST CHANGES** \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u0438\u043A\u0430: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B (\u043F\u0440\u0438\u0432\u0435\u0434\u0438 \u0432\u044B\u0432\u043E\u0434 \u0441\u0432\u043E\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430), \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B; \u0434\u043B\u044F REQUEST CHANGES \u2014 \u0442\u043E\u0447\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B/\u0441\u0442\u0440\u043E\u043A\u0438 \u0438 \u0447\u0442\u043E \u0447\u0438\u043D\u0438\u0442\u044C. \u041F\u0440\u0435\u0442\u0435\u043D\u0437\u0438\u044F \u043A \u0438\u043D\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F (\u043D\u0435\u0442 CI, \u043A\u043E\u043D\u0444\u0438\u0433\u0438, \u0434\u0435\u043F\u043B\u043E\u0439) \u2014 \u043D\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E PR: \u0441\u0447\u0438\u0442\u0430\u0435\u0448\u044C, \u0447\u0442\u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E \u043D\u0443\u0436\u0435\u043D CI, \u2014 \u0441\u043A\u0430\u0436\u0438 \u044D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \u0432 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0435, \u043D\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043E\u0442 \u043D\u0435\u0451 \u043D\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442.`
|
|
14801
15382
|
] : [
|
|
14802
|
-
|
|
15383
|
+
`1. \u041F\u0440\u043E\u0439\u0434\u0438 \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0432\u044B\u0448\u0435: \u0441\u0442\u0430\u0442\u0443\u0441 CI \u043F\u043E \u044D\u0442\u043E\u043C\u0443 \u043A\u043E\u043C\u043C\u0438\u0442\u0443 \u0447\u0438\u0442\u0430\u0439 \u0441\u0430\u043C (\u043D\u0435 \u0432\u0435\u0440\u044C \u043E\u0442\u0447\u0451\u0442\u0443 \u0430\u0432\u0442\u043E\u0440\u0430 PR, \u043D\u043E \u0438 \u043D\u0435 \u043F\u0435\u0440\u0435\u0433\u043E\u043D\u044F\u0439 CI \u0432\u0440\u0443\u0447\u043D\u0443\u044E), ${classesStep}\u043F\u0440\u043E\u0432\u0435\u0440\u044C \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438/\u0433\u043E\u043D\u043A\u0438/UI/per-page meta \u043F\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\u0441\u0442\u0438.`,
|
|
14803
15384
|
" \u0415\u0441\u043B\u0438 \u0445\u043E\u0442\u044C \u043E\u0434\u0438\u043D \u0447\u0435\u043A \u0435\u0449\u0451 PENDING \u2014 \u043D\u0430 \u044D\u0442\u043E\u043C \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C: \u0448\u0430\u0433\u0438 2 \u0438 3 \u043D\u0438\u0436\u0435 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438 \u0446\u0435\u043B\u0438\u043A\u043E\u043C, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043D\u0435 \u0432\u044B\u043D\u043E\u0441\u0438, \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E (\u0441\u043C. \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0432\u044B\u0448\u0435, \u043F\u043E\u0447\u0435\u043C\u0443 \u044D\u0442\u043E \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E \u0438 \u0447\u0442\u043E \u043F\u0440\u043E\u0438\u0437\u043E\u0439\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435).",
|
|
14804
15385
|
`2. \u0427\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C (\u0437\u0435\u043B\u0451\u043D\u044B\u0435 \u0438\u043B\u0438 \u0441 \u043A\u0440\u0430\u0441\u043D\u044B\u043C) \u0438\u043B\u0438 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0438\u0445 \u0432\u043E\u0432\u0441\u0435 \u043D\u0435\u0442 \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043D\u0430 \u0441\u0430\u043C PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u0435\u0433\u043E \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041D\u0430\u0447\u043D\u0438 \u0441 **APPROVE** \u0438\u043B\u0438 **REQUEST CHANGES** \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u0438\u043A\u0430: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B, \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B; \u0434\u043B\u044F REQUEST CHANGES \u2014 \u0442\u043E\u0447\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B/\u0441\u0442\u0440\u043E\u043A\u0438 \u0438 \u0447\u0442\u043E \u0447\u0438\u043D\u0438\u0442\u044C.`
|
|
14805
15386
|
],
|
|
@@ -14836,6 +15417,7 @@ async function manageStorm(deps, now, worktreeExists, hasTranscript) {
|
|
|
14836
15417
|
const consec = trailingProbeSuccesses(recs);
|
|
14837
15418
|
if (stormShouldEnd({ consecutiveProbeOk: consec, externalDown: lastExternalDown(recs) === true }, cfg)) {
|
|
14838
15419
|
db.endStorm(`${STORM_END_TEXT} (${consec} \u0443\u0441\u043F\u0435\u0448\u043D\u044B\u0435 health-probe \u043F\u043E\u0434\u0440\u044F\u0434)`);
|
|
15420
|
+
if (deps.spawnDefer) clearDefer(deps.spawnDefer, "storm");
|
|
14839
15421
|
narrate(deps, "\u0428\u0442\u043E\u0440\u043C \u043A\u043E\u043D\u0447\u0438\u043B\u0441\u044F \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0437\u0430\u043C\u043E\u0440\u043E\u0436\u0435\u043D\u043D\u044B\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u044B.");
|
|
14840
15422
|
await resurrectStormDead(deps, worktreeExists, hasTranscript);
|
|
14841
15423
|
return false;
|
|
@@ -15117,8 +15699,10 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
15117
15699
|
if (stats.count >= CI_WAKE_LIMIT || !canContinue) {
|
|
15118
15700
|
const why = !canContinue ? "worktree/\u0441\u0435\u0441\u0441\u0438\u044F \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u2014 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E" : `${CI_WAKE_LIMIT} \u043F\u043E\u0431\u0443\u0434(\u043E\u043A) \u043F\u043E \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 CI \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E`;
|
|
15119
15701
|
deps.db.updateRun(reviewer.id, { status: "stalled" });
|
|
15120
|
-
|
|
15121
|
-
|
|
15702
|
+
const freshWanted = await projectWantsReviewer(deps, run);
|
|
15703
|
+
const freshPlan = freshWanted ? "\u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0430 \u0442\u043E\u043C \u0436\u0435 PR" : "\u0441\u0432\u0435\u0436\u0435\u0433\u043E \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E: \u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E";
|
|
15704
|
+
deps.db.addEvent(reviewer.id, "stalled", `CI \u0433\u043E\u0442\u043E\u0432, \u043D\u043E \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044C \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0432\u044B\u0448\u043B\u043E: ${why} \u2014 ${freshPlan}`);
|
|
15705
|
+
narrate(deps, `${droneLabel(reviewer)} \u0436\u0434\u0430\u043B CI, \u043D\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${why}) \u2014 ${freshPlan}.`);
|
|
15122
15706
|
if (deps.watchdog?.notify) {
|
|
15123
15707
|
const signal = {
|
|
15124
15708
|
key: `reviewer_ci_wake_failed:${reviewer.id}`,
|
|
@@ -15127,7 +15711,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
15127
15711
|
// поднимает свежего ревьюера на том же PR. Решать человеку тут нечего; если и
|
|
15128
15712
|
// свежий не доедет, конвейер встанет и об этом скажет уже pr_stuck_* пушем.
|
|
15129
15713
|
level: "feed",
|
|
15130
|
-
body: `\u0420\u0435\u0432\u044C\u044E\u0435\u0440 ${droneLabel(reviewer)} \u0434\u043E\u0436\u0434\u0430\u043B\u0441\u044F \u0433\u043E\u0442\u043E\u0432\u043E\u0433\u043E CI, \u043D\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${why}). \u041F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0430 \u0442\u043E\u043C \u0436\u0435 PR \u2014 ${run.pr_url ?? "?"}.`,
|
|
15714
|
+
body: `\u0420\u0435\u0432\u044C\u044E\u0435\u0440 ${droneLabel(reviewer)} \u0434\u043E\u0436\u0434\u0430\u043B\u0441\u044F \u0433\u043E\u0442\u043E\u0432\u043E\u0433\u043E CI, \u043D\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u043D\u0435 \u0432\u044B\u0448\u043B\u043E (${why}). ${freshWanted ? "\u041F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0430 \u0442\u043E\u043C \u0436\u0435 PR" : "\u0421\u0432\u0435\u0436\u0435\u0433\u043E \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E (\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0430\u0432\u0442\u043E\u0440\u0435\u0432\u044C\u044E \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E)"} \u2014 ${run.pr_url ?? "?"}.`,
|
|
15131
15715
|
runId: reviewer.id,
|
|
15132
15716
|
...hrefOf(run.task_url)
|
|
15133
15717
|
};
|
|
@@ -15137,7 +15721,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
15137
15721
|
console.error(`[overmind] \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0435 \u043E \u043F\u043E\u0442\u0435\u0440\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 #${reviewer.id} \u043D\u0435 \u0434\u043E\u043B\u0435\u0442\u0435\u043B\u043E:`, e);
|
|
15138
15722
|
}
|
|
15139
15723
|
}
|
|
15140
|
-
await spawnFreshReviewerAfterStall(deps, run, why);
|
|
15724
|
+
if (freshWanted) await spawnFreshReviewerAfterStall(deps, run, why);
|
|
15141
15725
|
return true;
|
|
15142
15726
|
}
|
|
15143
15727
|
try {
|
|
@@ -15159,6 +15743,10 @@ function worktreeAndTranscriptAlive(deps, worktree) {
|
|
|
15159
15743
|
const transcript = deps.hasTranscript ?? executorHasSession;
|
|
15160
15744
|
return exists(worktree) && transcript(worktree);
|
|
15161
15745
|
}
|
|
15746
|
+
async function projectWantsReviewer(deps, run) {
|
|
15747
|
+
if (!deps.autoReviewForRepo) return true;
|
|
15748
|
+
return await deps.autoReviewForRepo(effectiveRepo(deps, run)).catch(() => true);
|
|
15749
|
+
}
|
|
15162
15750
|
async function spawnFreshReviewerAfterStall(deps, run, why) {
|
|
15163
15751
|
if (!deps.spawnReviewer) return;
|
|
15164
15752
|
const live = liveOrWaitingReviewers(deps.db, run.id);
|
|
@@ -15251,23 +15839,23 @@ async function freshChangesWakeAllowed(deps, run, prUrl, now) {
|
|
|
15251
15839
|
const moved = await deps.commitsSince(prUrl, verdictAt ?? run.created_at).catch(() => true);
|
|
15252
15840
|
return !moved;
|
|
15253
15841
|
}
|
|
15254
|
-
async function
|
|
15255
|
-
if (!deps.db.refreshRun) return
|
|
15842
|
+
async function prodOverrideReason(deps, run, allowed) {
|
|
15843
|
+
if (!deps.db.refreshRun) return null;
|
|
15256
15844
|
let fresh;
|
|
15257
15845
|
try {
|
|
15258
15846
|
fresh = await deps.db.refreshRun(run.id);
|
|
15259
15847
|
} catch {
|
|
15260
|
-
return
|
|
15848
|
+
return null;
|
|
15261
15849
|
}
|
|
15262
|
-
if (!fresh) return
|
|
15263
|
-
if (allowed.includes(fresh.status)) return
|
|
15850
|
+
if (!fresh) return "\u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442 \u0432 \u043C\u043E\u0451\u043C \u0437\u0435\u0440\u043A\u0430\u043B\u0435 \u2014 \u043F\u0440\u043E\u0434 \u043E\u0442\u0434\u0430\u043B \u0435\u0433\u043E \u0434\u0440\u0443\u0433\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 (TASWM-300)";
|
|
15851
|
+
if (allowed.includes(fresh.status)) return null;
|
|
15264
15852
|
deps.db.addEvent(
|
|
15265
15853
|
run.id,
|
|
15266
15854
|
"step",
|
|
15267
15855
|
`\u043F\u0440\u043E\u0434 \u0433\u043E\u0432\u043E\u0440\u0438\u0442 \xAB${fresh.status}\xBB (\u0443 \u043C\u0435\u043D\u044F \u0431\u044B\u043B\u043E \xAB${run.status}\xBB) \u2014 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E, \u0441\u0442\u0430\u0442\u0443\u0441 \u043F\u0440\u043E\u0434\u0430 \u0441\u0438\u043B\u044C\u043D\u0435\u0435 (TASWM-300)`
|
|
15268
15856
|
);
|
|
15269
15857
|
narrate(deps, `${droneLabel(run)} \u2014 \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0443\u0436\u0435 \xAB${fresh.status}\xBB, \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u044E: \u043F\u0440\u043E\u0434 \u0441\u0438\u043B\u044C\u043D\u0435\u0435 \u043C\u043E\u0435\u0433\u043E \u0437\u0435\u0440\u043A\u0430\u043B\u0430.`);
|
|
15270
|
-
return
|
|
15858
|
+
return `\u043F\u0440\u043E\u0434 \u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043B \u043F\u0440\u043E\u0433\u043E\u043D, \u043F\u043E\u043A\u0430 \u0442\u043E\u0442 \u0436\u0434\u0430\u043B: \u0442\u0430\u043C \u0443\u0436\u0435 \xAB${fresh.status}\xBB, \u0443 \u043C\u0435\u043D\u044F \u0431\u044B\u043B\u043E \xAB${run.status}\xBB (TASWM-300)`;
|
|
15271
15859
|
}
|
|
15272
15860
|
async function envGateAllows(deps, run) {
|
|
15273
15861
|
if (!deps.envGate) return true;
|
|
@@ -15373,13 +15961,18 @@ async function projectQuotaGateAllows(deps, run) {
|
|
|
15373
15961
|
return true;
|
|
15374
15962
|
}
|
|
15375
15963
|
function pruneGateLogs(deps) {
|
|
15376
|
-
if (!deps.envGateLogged?.size && !deps.exclusiveLogged?.size && !deps.overlapLogged?.size) return;
|
|
15964
|
+
if (!deps.envGateLogged?.size && !deps.exclusiveLogged?.size && !deps.overlapLogged?.size && !deps.spawnDefer?.size) return;
|
|
15377
15965
|
const open = /* @__PURE__ */ new Set();
|
|
15378
15966
|
for (const run of deps.db.listRuns()) if (!isTerminalStatus(run.status)) open.add(run.id);
|
|
15379
15967
|
for (const map of [deps.envGateLogged, deps.exclusiveLogged, deps.overlapLogged]) {
|
|
15380
15968
|
if (!map) continue;
|
|
15381
15969
|
for (const id of [...map.keys()]) if (!open.has(id)) map.delete(id);
|
|
15382
15970
|
}
|
|
15971
|
+
if (deps.spawnDefer) {
|
|
15972
|
+
const live = /* @__PURE__ */ new Set(["storm", "control-pause"]);
|
|
15973
|
+
for (const id of open) live.add(`run:${id}`);
|
|
15974
|
+
pruneDefers(deps.spawnDefer, live);
|
|
15975
|
+
}
|
|
15383
15976
|
}
|
|
15384
15977
|
async function projectQuotaBlockedRunIds(deps) {
|
|
15385
15978
|
const blocked = /* @__PURE__ */ new Set();
|
|
@@ -15417,8 +16010,38 @@ function noteExhaustedDeps(deps, run, verdicts) {
|
|
|
15417
16010
|
narrate(deps, `${droneLabel(run)} \u2014 ${text}`);
|
|
15418
16011
|
}
|
|
15419
16012
|
}
|
|
16013
|
+
function deferSpawn(deps, run, reason, opts = {}) {
|
|
16014
|
+
const eps = deps.spawnDefer ??= /* @__PURE__ */ new Map();
|
|
16015
|
+
const d = noteDefer(eps, `run:${run.id}`, reason, GATE_LOG_HEARTBEAT_TICKS);
|
|
16016
|
+
if (d.changed && opts.event !== false) deps.db.addEvent(run.id, "step", `\u0441\u043F\u0430\u0432\u043D \u043E\u0442\u043B\u043E\u0436\u0435\u043D: ${reason} \u2014 \u0436\u0434\u0443 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 (TASWM-333)`);
|
|
16017
|
+
const shout = opts.logKey ? noteDefer(eps, opts.logKey, reason, GATE_LOG_HEARTBEAT_TICKS) : d;
|
|
16018
|
+
if (shout.log && !opts.quiet) {
|
|
16019
|
+
const suffix = shout.changed ? "" : ` \u2014 \u0435\u0449\u0451 \u0432\u0438\u0441\u0438\u0442, \u0442\u0438\u043A ${shout.ticks}`;
|
|
16020
|
+
const who = opts.logKey ? "" : `${droneLabel(run)} (${run.task_slug}) \u2014 `;
|
|
16021
|
+
console.error(`[overmind] \u0441\u043F\u0430\u0432\u043D \u043E\u0442\u043B\u043E\u0436\u0435\u043D: ${who}${reason}${suffix}`);
|
|
16022
|
+
}
|
|
16023
|
+
return "skipped";
|
|
16024
|
+
}
|
|
16025
|
+
function noteStormStall(deps) {
|
|
16026
|
+
const waiting = deps.db.listRuns("queued").filter((r) => deps.db.unmetDeps(r.depends_on).length === 0);
|
|
16027
|
+
if (waiting.length === 0) return;
|
|
16028
|
+
const startedAt = deps.db.currentStormStartedAt();
|
|
16029
|
+
const reason = `\u0440\u043E\u0439 \u0432 \u0448\u0442\u043E\u0440\u043C\u043E\u0432\u043E\u043C \u0440\u0435\u0436\u0438\u043C\u0435${startedAt ? ` \u0441 ${startedAt} UTC` : ""} \u2014 \u043D\u043E\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C \u0434\u043E \u043E\u0442\u0431\u043E\u044F (TASWM-23)`;
|
|
16030
|
+
const d = noteDefer(deps.spawnDefer ??= /* @__PURE__ */ new Map(), "storm", reason, GATE_LOG_HEARTBEAT_TICKS);
|
|
16031
|
+
if (d.log) {
|
|
16032
|
+
const suffix = d.changed ? "" : ` \u2014 \u0435\u0449\u0451 \u0432\u0438\u0441\u0438\u0442, \u0442\u0438\u043A ${d.ticks}`;
|
|
16033
|
+
console.error(`[overmind] \u0441\u043F\u0430\u0432\u043D \u043E\u0442\u043B\u043E\u0436\u0435\u043D: ${reason}; \u0436\u0434\u0443\u0442 ${waiting.length} \u043F\u0440\u043E\u0433\u043E\u043D(\u043E\u0432)${suffix}`);
|
|
16034
|
+
}
|
|
16035
|
+
for (const run of waiting) deferSpawn(deps, run, reason, { quiet: true });
|
|
16036
|
+
}
|
|
15420
16037
|
async function trySpawnQueuedRun(deps, run) {
|
|
15421
|
-
if (deps.control?.paused)
|
|
16038
|
+
if (deps.control?.paused) {
|
|
16039
|
+
const why = deps.control.state?.reason;
|
|
16040
|
+
return deferSpawn(deps, run, `\u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B \u043F\u0440\u0438\u0451\u043C \u043D\u043E\u0432\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435${why ? ` \u2014 ${why}` : ""} (\u043F\u0430\u0443\u0437\u0430)`, {
|
|
16041
|
+
logKey: "control-pause"
|
|
16042
|
+
// причина одна на машину — в лог уходит одной строкой, а не по строке на прогон
|
|
16043
|
+
});
|
|
16044
|
+
}
|
|
15422
16045
|
const mergedUrl = doneMergedPrForSlug(deps.db, run.task_slug) ?? (deps.mergedPrForBranch ? await deps.mergedPrForBranch(run.task_slug, run.repo || "").catch(() => null) : null);
|
|
15423
16046
|
if (mergedUrl) {
|
|
15424
16047
|
await closeDuplicateTaskRun(deps, run, mergedUrl);
|
|
@@ -15428,15 +16051,20 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
15428
16051
|
return "closed";
|
|
15429
16052
|
}
|
|
15430
16053
|
const depVerdicts = deps.db.depVerdicts(run.depends_on);
|
|
15431
|
-
|
|
16054
|
+
const unmet = depVerdicts.filter((d) => !d.met);
|
|
16055
|
+
if (unmet.length) return deferSpawn(deps, run, `\u0436\u0434\u0451\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438: ${unmet.map((d) => d.slug).join(", ")}`);
|
|
15432
16056
|
if (!await envGateAllows(deps, run)) return "skipped";
|
|
15433
16057
|
if (!await projectQuotaGateAllows(deps, run)) return "skipped";
|
|
15434
|
-
|
|
15435
|
-
if (
|
|
16058
|
+
const prodOverride = await prodOverrideReason(deps, run, ["queued"]);
|
|
16059
|
+
if (prodOverride) return deferSpawn(deps, run, prodOverride, { event: false });
|
|
16060
|
+
if (deps.db.takeRun && !await deps.db.takeRun(run.id)) {
|
|
16061
|
+
return deferSpawn(deps, run, "\u043D\u0435 \u0432\u0437\u044F\u043B \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0430 \u0441\u0432\u043E\u044E \u043C\u0430\u0448\u0438\u043D\u0443 \u2014 \u0441\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u0434\u0430\u043B \u0435\u0433\u043E \u0434\u0440\u0443\u0433\u043E\u043C\u0443 \u0434\u0435\u043C\u043E\u043D\u0443 (TASWM-281)");
|
|
16062
|
+
}
|
|
15436
16063
|
noteExhaustedDeps(deps, run, depVerdicts);
|
|
15437
16064
|
await fillSuggestedArea(deps, run);
|
|
15438
16065
|
try {
|
|
15439
16066
|
await deps.spawn(run);
|
|
16067
|
+
if (deps.spawnDefer) clearDefer(deps.spawnDefer, `run:${run.id}`);
|
|
15440
16068
|
narrate(deps, `\u0411\u0435\u0440\u0443 ${run.task_slug} \u0438\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u2014 \u0441\u043F\u0430\u0432\u043D\u044E ${droneLabel(run)}.`);
|
|
15441
16069
|
return "spawned";
|
|
15442
16070
|
} catch (e) {
|
|
@@ -15449,7 +16077,7 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
15449
16077
|
}
|
|
15450
16078
|
return "skipped";
|
|
15451
16079
|
}
|
|
15452
|
-
if (e instanceof AreaBusyError) return
|
|
16080
|
+
if (e instanceof AreaBusyError) return deferSpawn(deps, run, e.message);
|
|
15453
16081
|
deps.db.updateRun(run.id, { status: "failed" });
|
|
15454
16082
|
deps.db.addEvent(run.id, "failed", String(e));
|
|
15455
16083
|
return "failed";
|
|
@@ -15820,6 +16448,17 @@ async function salvageVerdictFromPrComment(deps, rv, reviewsRunId, since, ctx) {
|
|
|
15820
16448
|
}
|
|
15821
16449
|
return true;
|
|
15822
16450
|
}
|
|
16451
|
+
async function salvageVerifyVerdictFromPrComment(deps, vr, verifiesRunId, since, how) {
|
|
16452
|
+
if (!deps.verifyVerdictComment || !vr.pr_url) return false;
|
|
16453
|
+
const found = await deps.verifyVerdictComment(vr.pr_url, since).catch(() => null);
|
|
16454
|
+
if (!found) return false;
|
|
16455
|
+
const verdictText = verifyVerdictLabel(found.verdict);
|
|
16456
|
+
deps.db.updateRun(verifiesRunId, { verify_verdict: found.verdict });
|
|
16457
|
+
deps.db.addEvent(verifiesRunId, "step", `\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 (${how}): ${verdictText}`);
|
|
16458
|
+
await closeReviewerRun(deps, vr, `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u044B\u043D\u0435\u0441\u0435\u043D \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C (${verdictText}), ${how} \u2014 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u0437\u0430\u043A\u0440\u044B\u0442\u0430 \u0431\u0435\u0437 \u0441\u043F\u0430\u0432\u043D\u0430 \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430`);
|
|
16459
|
+
narrate(deps, `${droneLabel(vr)} \u043E\u0441\u0442\u0430\u0432\u0438\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 (${verdictText}) \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u2014 \u0437\u0430\u0441\u0447\u0438\u0442\u0430\u043B, \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E.`);
|
|
16460
|
+
return true;
|
|
16461
|
+
}
|
|
15823
16462
|
async function failReviewerRun(deps, reviewer, reviewsRunId, opts) {
|
|
15824
16463
|
if (!opts.keepWorktree && reviewer.worktree_path) {
|
|
15825
16464
|
await deps.removeWorktree(reviewer.repo, reviewer.worktree_path).catch(() => {
|
|
@@ -16022,6 +16661,7 @@ async function tickOnce(deps) {
|
|
|
16022
16661
|
stormActive = await manageStorm(deps, now, worktreeExists, hasTranscript);
|
|
16023
16662
|
}
|
|
16024
16663
|
const droneCap = deps.maxDrones ? await deps.maxDrones() : Infinity;
|
|
16664
|
+
if (stormActive) noteStormStall(deps);
|
|
16025
16665
|
if (deps.resourcesOk) {
|
|
16026
16666
|
await manageRelief(deps, now, droneCap, stormActive);
|
|
16027
16667
|
} else if (!stormActive) {
|
|
@@ -16065,6 +16705,26 @@ async function tickOnce(deps) {
|
|
|
16065
16705
|
narrate(deps, `${droneLabel(run)} \u0432\u044B\u043D\u0435\u0441 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0440\u0435\u0432\u044C\u044E \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442.`);
|
|
16066
16706
|
continue;
|
|
16067
16707
|
}
|
|
16708
|
+
if (run.verifies_run_id != null) {
|
|
16709
|
+
const verified = deps.db.getRun(run.verifies_run_id);
|
|
16710
|
+
const sameCycle = verified != null && (verified.review_cycle ?? 0) === (run.review_cycle ?? 0);
|
|
16711
|
+
if (verified && sameCycle && verified.verify_verdict == null) {
|
|
16712
|
+
if (await salvageVerifyVerdictFromPrComment(deps, run, run.verifies_run_id, run.created_at, "\u043F\u043E\u0437\u0432\u0430\u043B done(), \u043D\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B")) continue;
|
|
16713
|
+
if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
|
|
16714
|
+
});
|
|
16715
|
+
deps.db.updateRun(run.id, { status: "failed" });
|
|
16716
|
+
deps.db.addEvent(run.id, "failed", "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0437\u0430\u043A\u0440\u044B\u043B\u0441\u044F \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043D\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043B\u0430\u0441\u044C");
|
|
16717
|
+
deps.db.addEvent(run.verifies_run_id, "step", `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 ${run.name} (#${run.id}) \u0437\u0430\u043A\u0440\u044B\u043B\u0441\u044F \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 \u0437\u0430\u0445\u043E\u0434`);
|
|
16718
|
+
narrate(deps, `${droneLabel(run)} \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B, \u043D\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438, \u2014 \u0437\u0430\u0441\u0447\u0438\u0442\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E.`);
|
|
16719
|
+
continue;
|
|
16720
|
+
}
|
|
16721
|
+
if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
|
|
16722
|
+
});
|
|
16723
|
+
deps.db.updateRun(run.id, { status: "done" });
|
|
16724
|
+
deps.db.addEvent(run.id, "done", "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u044B\u043D\u0435\u0441\u0435\u043D");
|
|
16725
|
+
narrate(deps, `${droneLabel(run)} \u0432\u044B\u043D\u0435\u0441 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442.`);
|
|
16726
|
+
continue;
|
|
16727
|
+
}
|
|
16068
16728
|
if (!run.pr_url) continue;
|
|
16069
16729
|
const prUrl = run.pr_url;
|
|
16070
16730
|
const st = await deps.prStatus(prUrl);
|
|
@@ -16125,7 +16785,7 @@ async function tickOnce(deps) {
|
|
|
16125
16785
|
if (run.review_verdict === "changes" && !stormActive && !st.conflicting && !deps.db.activeResolverFor(run.id) && await wakeAuthorOnChanges(deps, run, now, droneCap)) {
|
|
16126
16786
|
continue;
|
|
16127
16787
|
}
|
|
16128
|
-
|
|
16788
|
+
const reviewerGatesOk = !st.conflicting && !st.isDraft && !!deps.spawnReviewer && !stormActive && (!deps.reviewCutoffAt || run.updated_at >= deps.reviewCutoffAt) && deps.db.reviewerCountFor(run.id) === 0 && // ГЕЙТ ПЕРВЫМ: живой/успешный ревьюер уже есть — ни капа, ни спавна не трогаем
|
|
16129
16789
|
// …и НИ ОДНОГО живого/ждущего ревьюера ЛЮБОГО цикла (TASWM-299). reviewerCountFor
|
|
16130
16790
|
// выше скоупится по review_cycle, поэтому сама по себе она не инвариант «один
|
|
16131
16791
|
// ревьюер на PR», а лишь идемпотентность внутри круга: фикс-итерация бампит цикл, и
|
|
@@ -16133,7 +16793,8 @@ async function tickOnce(deps) {
|
|
|
16133
16793
|
// такого уже закрыла (тот же тик, выше) — этот гейт держит инвариант СТРУКТУРНО, а
|
|
16134
16794
|
// не порядком веток в 900-строчной функции: увидели живого — значит закрывать его
|
|
16135
16795
|
// рано, спавнить второго нельзя.
|
|
16136
|
-
liveOrWaitingReviewers(deps.db, run.id).length === 0
|
|
16796
|
+
liveOrWaitingReviewers(deps.db, run.id).length === 0;
|
|
16797
|
+
if (reviewerGatesOk && deps.spawnReviewer && await projectWantsReviewer(deps, run)) {
|
|
16137
16798
|
if (run.review_cycle >= REVIEW_CYCLE_LIMIT) {
|
|
16138
16799
|
if (!deps.db.events(run.id).some((e) => e.kind === "step" && (e.text ?? "").includes(REVIEW_CYCLE_ESCALATION_MARK))) {
|
|
16139
16800
|
deps.db.addEvent(run.id, "step", `${REVIEW_CYCLE_ESCALATION_MARK}: ${run.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`);
|
|
@@ -16202,6 +16863,7 @@ async function tickOnce(deps) {
|
|
|
16202
16863
|
}
|
|
16203
16864
|
await closeMergedTaskRuns(deps);
|
|
16204
16865
|
await closeOrphanedReviewers(deps);
|
|
16866
|
+
await spawnVerifiersForOpenPrs(deps, stormActive);
|
|
16205
16867
|
refreshReviewPhases(deps);
|
|
16206
16868
|
let liveChildPpids;
|
|
16207
16869
|
try {
|
|
@@ -16213,7 +16875,7 @@ async function tickOnce(deps) {
|
|
|
16213
16875
|
for (const run of [...deps.db.listRuns("running"), ...deps.db.listRuns("blocked")]) {
|
|
16214
16876
|
const alive = run.pid != null && deps.isAlive(run.pid);
|
|
16215
16877
|
if (!alive && deps.db.takeRun && !await deps.db.takeRun(run.id)) continue;
|
|
16216
|
-
if (!alive &&
|
|
16878
|
+
if (!alive && await prodOverrideReason(deps, run, ["running", "blocked"])) continue;
|
|
16217
16879
|
const outcome = alive ? "unknown" : deps.exitOutcome?.(run) ?? "unknown";
|
|
16218
16880
|
if (!alive && deps.accountCost) {
|
|
16219
16881
|
try {
|
|
@@ -16284,7 +16946,7 @@ async function tickOnce(deps) {
|
|
|
16284
16946
|
continue;
|
|
16285
16947
|
}
|
|
16286
16948
|
}
|
|
16287
|
-
if (!alive && outcome === "graceful" && run.status === "running" && run.resolves_run_id == null && run.reviews_run_id == null && run.kind !== "hero") {
|
|
16949
|
+
if (!alive && outcome === "graceful" && run.status === "running" && run.resolves_run_id == null && run.reviews_run_id == null && run.verifies_run_id == null && run.kind !== "hero") {
|
|
16288
16950
|
const wt = run.worktree_path;
|
|
16289
16951
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
16290
16952
|
if (deps.respawn && canContinue && run.respawns < RESPAWN_LIMIT) {
|
|
@@ -16309,7 +16971,7 @@ async function tickOnce(deps) {
|
|
|
16309
16971
|
narrate(deps, `${droneLabel(run)} \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B \u0441\u0435\u0441\u0441\u0438\u044E \u0441\u0430\u043C, \u043D\u0435 \u043F\u043E\u0437\u0432\u0430\u0432 done() \u2014 \u0441\u0442\u0430\u0442\u0443\u0441 \xAB\u0437\u0430\u043C\u0435\u0440\xBB, \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u0432 \u043B\u043E\u0433\u0435 (TASWM-229).`);
|
|
16310
16972
|
continue;
|
|
16311
16973
|
}
|
|
16312
|
-
if (!alive && run.status === "running" && run.resolves_run_id == null && run.reviews_run_id == null && deps.respawn && run.respawns < RESPAWN_LIMIT) {
|
|
16974
|
+
if (!alive && run.status === "running" && run.resolves_run_id == null && run.reviews_run_id == null && run.verifies_run_id == null && deps.respawn && run.respawns < RESPAWN_LIMIT) {
|
|
16313
16975
|
const wt = run.worktree_path;
|
|
16314
16976
|
const wtAlive = !!wt && worktreeExists(wt);
|
|
16315
16977
|
const canContinue = wtAlive && hasTranscript(wt);
|
|
@@ -16345,6 +17007,10 @@ async function tickOnce(deps) {
|
|
|
16345
17007
|
});
|
|
16346
17008
|
if (salvaged) continue;
|
|
16347
17009
|
}
|
|
17010
|
+
if (!alive && run.status === "running" && run.verifies_run_id != null && run.pr_url && deps.verifyVerdictComment) {
|
|
17011
|
+
const salvaged = await salvageVerifyVerdictFromPrComment(deps, run, run.verifies_run_id, run.created_at, "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0432\u044B\u0448\u0435\u043B, \u043D\u0435 \u043F\u043E\u0437\u0432\u0430\u0432 done()");
|
|
17012
|
+
if (salvaged) continue;
|
|
17013
|
+
}
|
|
16348
17014
|
if (!alive && run.status === "running" && run.reviews_run_id != null && run.pr_url && deps.ci) {
|
|
16349
17015
|
const parent = deps.db.getRun(run.reviews_run_id);
|
|
16350
17016
|
const ciWatchOff = parent != null && ciWatchClosed(deps.db, parent);
|
|
@@ -16435,9 +17101,17 @@ async function tickOnce(deps) {
|
|
|
16435
17101
|
const az = deps.anomalies;
|
|
16436
17102
|
let ready = [];
|
|
16437
17103
|
try {
|
|
17104
|
+
const autoReviewSnapshot = /* @__PURE__ */ new Map();
|
|
17105
|
+
if (deps.autoReviewForRepo && az.autoReviewEnabled) {
|
|
17106
|
+
for (const r of deps.db.listRuns("review")) {
|
|
17107
|
+
if (!r.pr_url || autoReviewSnapshot.has(r.repo)) continue;
|
|
17108
|
+
autoReviewSnapshot.set(r.repo, await deps.autoReviewForRepo(effectiveRepo(deps, r)).catch(() => true));
|
|
17109
|
+
}
|
|
17110
|
+
}
|
|
16438
17111
|
const candidates = [
|
|
16439
17112
|
...buildAnomalyCandidates(deps.db, now, az.thresholds, {
|
|
16440
|
-
|
|
17113
|
+
// Машинный ответ, уточнённый проектом (TASWM-396): нет снимка — как раньше.
|
|
17114
|
+
autoReviewEnabled: (repo) => az.autoReviewEnabled && (autoReviewSnapshot.get(repo) ?? true),
|
|
16441
17115
|
...az.outboxPendingCount ? { outboxPendingCount: az.outboxPendingCount() } : {},
|
|
16442
17116
|
// resource-gate-stall (TASWM-212): та же причина, что видит лог manageRelief — один
|
|
16443
17117
|
// deps.gateReason() на тик (кэш hostReading() внутри resources.ts, лишнего спавна нет).
|
|
@@ -16633,6 +17307,76 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
16633
17307
|
throw e;
|
|
16634
17308
|
}
|
|
16635
17309
|
}
|
|
17310
|
+
var VERIFY_SCOPE_BLOCK = [
|
|
17311
|
+
"\u{1F4B8} \u041A\u041E\u041D\u0422\u0423\u0420 \u0412\u0415\u0420\u0418\u0424\u0418\u041A\u0410\u0426\u0418\u0418 (\u0436\u0451\u0441\u0442\u043A\u043E). \u0421\u0435\u0441\u0441\u0438\u044F \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0430 \u0431\u044E\u0434\u0436\u0435\u0442\u043E\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0438 \u0441\u0440\u0435\u0436\u0435\u0442 \u0442\u0435\u0431\u044F \u0431\u0435\u0437 \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u044F \u2014 \u0443\u043F\u0440\u0451\u0448\u044C\u0441\u044F, \u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u043E\u0432\u0441\u0435. \u041F\u043E\u044D\u0442\u043E\u043C\u0443:",
|
|
17312
|
+
"\u2022 \u0422\u044B \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0448\u044C \u0417\u0410\u042F\u0412\u041B\u0415\u041D\u041D\u041E\u0415, \u0430 \u043D\u0435 \u0432\u0435\u0441\u044C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439. \u0421\u043F\u0438\u0441\u043E\u043A \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u2014 \u0438\u0437 \u043E\u0442\u0447\u0451\u0442\u0430 \u0434\u0440\u043E\u043D\u0430 \u0438 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438; \u0432\u0441\u0451 \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0435 \u0442\u0440\u043E\u0433\u0430\u0439, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u043E \u043B\u043E\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0432\u043E\u0435\u0439 \u0436\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u043E\u0439.",
|
|
17313
|
+
"\u2022 \u041F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u2014 \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 \u0434\u0432\u0443\u0445 \u0437\u0430\u0445\u043E\u0434\u043E\u0432. \u041D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u043E\u0441\u044C \u2014 \u044D\u0442\u043E \u0438 \u0435\u0441\u0442\u044C \u0447\u0430\u0441\u0442\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 (\xAB\u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C, \u043F\u043E\u0442\u043E\u043C\u0443 \u0447\u0442\u043E \u2026\xBB), \u0430 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434 \u0447\u0438\u043D\u0438\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442.",
|
|
17314
|
+
"\u2022 \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u043E\u0435 \u043D\u0435 \u043F\u0435\u0440\u0435\u0447\u0438\u0442\u044B\u0432\u0430\u0439, \u043E\u0434\u0438\u043D \u0432\u043E\u043F\u0440\u043E\u0441 \u2014 \u043E\u0434\u0438\u043D \u0437\u0430\u0445\u043E\u0434. \u041D\u0435 \u0441\u043E\u0448\u043B\u043E\u0441\u044C \u0441\u043E \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u0440\u0430\u0437\u0430 \u2014 \u0437\u0430\u043F\u0438\u0441\u044B\u0432\u0430\u0439 \u043A\u0430\u043A \u043D\u0430\u0445\u043E\u0434\u043A\u0443, \u0430 \u043D\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u0439 \u0442\u0440\u0435\u0442\u0438\u0439 \u043A\u0440\u0443\u0433.",
|
|
17315
|
+
"\u2022 \u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u0443\u0441\u043F\u0435\u043B \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C, \u0412\u0421\u0415\u0413\u0414\u0410 \u043B\u0443\u0447\u0448\u0435 \u0438\u0434\u0435\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043D\u0435 \u0431\u0443\u0434\u0435\u0442. \u0427\u0435\u0441\u0442\u043D\u043E \u043D\u0430\u0437\u043E\u0432\u0438, \u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u043C\u043E\u0442\u0440\u0435\u043B."
|
|
17316
|
+
].join("\n");
|
|
17317
|
+
function buildVerifierPrompt(verifiedRun, verifier, branch, files, attachmentsBlock, verifyMode = SWARM_VERIFY) {
|
|
17318
|
+
const name = verifier.name || `#${verifier.id}`;
|
|
17319
|
+
const prUrl = verifiedRun.pr_url ?? "(\u0441\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 PR \u2014 \u0441\u043C. \u043B\u043E\u0433 \u043F\u0440\u043E\u0433\u043E\u043D\u0430)";
|
|
17320
|
+
const taskUrl = verifiedRun.task_url ? ` \u0417\u0430\u0434\u0430\u0447\u0430: ${verifiedRun.task_url}.` : "";
|
|
17321
|
+
const filesBlock = files.length ? `\u0424\u0430\u0439\u043B\u044B \u0434\u0438\u0444\u0444\u0430 (${files.length}): ${files.slice(0, 40).join(", ")}${files.length > 40 ? " \u2026" : ""}.` : "\u0421\u043F\u0438\u0441\u043E\u043A \u0444\u0430\u0439\u043B\u043E\u0432 \u0434\u0438\u0444\u0444\u0430 gh \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u2014 \u0432\u043E\u0437\u044C\u043C\u0438 \u0435\u0433\u043E \u0441\u0430\u043C: `gh pr diff --name-only`.";
|
|
17322
|
+
return [
|
|
17323
|
+
`\u0422\u044B \u2014 \u0434\u0440\u043E\u043D-\u0412\u0415\u0420\u0418\u0424\u0418\u041A\u0410\u0422\u041E\u0420 ${name} \u0440\u043E\u044F, headless-\u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C (\u041D\u0415 \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0447\u0430\u0442). \u0420\u043E\u0439 \u043F\u043E\u0434\u043D\u044F\u043B \u0442\u0435\u0431\u044F \u0441\u0430\u043C \u043D\u0430 PR \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F: \u0442\u044B \u043E\u0442 \u043D\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0448\u044C \u0438 \u0435\u0433\u043E \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u044B\u0432\u0430\u0435\u0448\u044C.`,
|
|
17324
|
+
"",
|
|
17325
|
+
HEADLESS_BLOCK,
|
|
17326
|
+
"",
|
|
17327
|
+
`\u0417\u0410\u0414\u0410\u0427\u0410: \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C, \u041F\u0420\u0410\u0412\u0414\u0410 \u041B\u0418 \u0441\u0434\u0435\u043B\u0430\u043D\u043E \u0442\u043E, \u0447\u0442\u043E \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043E \u0432 PR ${prUrl} \u043F\u0440\u043E\u0433\u043E\u043D\u0430 ${verifiedRun.task_slug} (\u0434\u0440\u043E\u043D ${verifiedRun.name}).${taskUrl} \u0422\u044B \u0432 git-worktree \u0441 \u0447\u0435\u043A\u0430\u0443\u0442\u043E\u043C \u0432\u0435\u0442\u043A\u0438 \`${branch}\` (detached, \u043A\u043E\u043D\u0447\u0438\u043A origin) \u2014 \u043A\u043E\u0434 \u0443\u0436\u0435 \u043D\u0430 \u0434\u0438\u0441\u043A\u0435.`,
|
|
17328
|
+
...verifiedRun.base_branch ? [`\u26A0\uFE0F PR \u043E\u0442\u043A\u0440\u044B\u0442 \u0432 \u0432\u0435\u0442\u043A\u0443 \`${verifiedRun.base_branch}\`, \u0430 \u043D\u0435 \u0432 \u0432\u0435\u0442\u043A\u0443 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E: \u0441\u0440\u0430\u0432\u043D\u0438\u0432\u0430\u0439 \u0441 \u043D\u0435\u0439 (\`git diff origin/${verifiedRun.base_branch}...HEAD\`), \u0438\u043D\u0430\u0447\u0435 \u043F\u0440\u0438\u043C\u0435\u0448\u044C \u0447\u0443\u0436\u0438\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u043F\u0430\u0447\u043A\u0438 \u0437\u0430 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u044D\u0442\u043E\u0433\u043E \u0434\u0440\u043E\u043D\u0430.`] : [],
|
|
17329
|
+
filesBlock,
|
|
17330
|
+
"",
|
|
17331
|
+
"\u26A0\uFE0F \u0422\u0412\u041E\u042F \u0417\u0410\u0414\u0410\u0427\u0410 \u2014 \u041E\u041F\u0420\u041E\u0412\u0415\u0420\u0413\u041D\u0423\u0422\u042C, \u0410 \u041D\u0415 \u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042C. \u0410\u0432\u0442\u043E\u0440 \u0440\u0430\u0431\u043E\u0442\u044B \u2014 \u043F\u043B\u043E\u0445\u043E\u0439 \u0435\u0451 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0438\u0439: \u043E\u043D \u0438\u0449\u0435\u0442 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043B. \u0422\u044B \u043D\u0430\u043D\u044F\u0442 \u043D\u0430 \u043E\u0431\u0440\u0430\u0442\u043D\u043E\u0435. \u0418\u0441\u0445\u043E\u0434\u0438 \u0438\u0437 \u0442\u043E\u0433\u043E, \u0447\u0442\u043E \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0435 \u041D\u0415 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442, \u0438 \u0434\u043E\u0431\u0440\u043E\u0441\u043E\u0432\u0435\u0441\u0442\u043D\u043E \u043F\u043E\u043F\u044B\u0442\u0430\u0439\u0441\u044F \u044D\u0442\u043E \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C. \u041D\u0435 \u043D\u0430\u0448\u0451\u043B \u2014 \u0442\u043E\u0433\u0434\u0430 \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u043E\u0433\u0434\u0430 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0439.",
|
|
17332
|
+
"\u26A0\uFE0F \u0424\u0410\u0419\u041B\u042B \u041D\u0415 \u041F\u0420\u0410\u0412\u0418\u0428\u042C, \u041A\u041E\u041C\u041C\u0418\u0422\u042B \u041D\u0415 \u041F\u0423\u0428\u0418\u0428\u042C. \u0427\u0438\u043D\u0438\u0442\u044C \u043D\u0430\u0439\u0434\u0435\u043D\u043D\u043E\u0435 \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043E\u0440. \u0422\u0432\u043E\u0439 \u043F\u0440\u043E\u0434\u0443\u043A\u0442 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442.",
|
|
17333
|
+
"\u26A0\uFE0F \u041F\u0415\u0420\u0415\u0421\u041A\u0410\u0417 \u0414\u0418\u0424\u0424\u0410 \u2014 \u041D\u0415 \u0412\u0415\u0420\u0414\u0418\u041A\u0422. \xAB\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u044B\u0433\u043B\u044F\u0434\u044F\u0442 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\xBB, \xAB\u043A\u043E\u0434 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0435\xBB \u2014 \u044D\u0442\u043E \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430, \u0430 \u043C\u043D\u0435\u043D\u0438\u0435 \u043E \u0442\u0435\u043A\u0441\u0442\u0435. \u0414\u0438\u0444\u0444 \u0443\u0436\u0435 \u0447\u0438\u0442\u0430\u0435\u0442 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u0434\u0440\u043E\u043D-\u0440\u0435\u0432\u044C\u044E\u0435\u0440; \u0442\u044B \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0448\u044C \u0437\u0430 \u041F\u041E\u0412\u0415\u0414\u0415\u041D\u0418\u0415. \u041A\u0430\u0436\u0434\u044B\u0439 \u043F\u0443\u043D\u043A\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043E\u0431\u044F\u0437\u0430\u043D \u043E\u043F\u0438\u0440\u0430\u0442\u044C\u0441\u044F \u043D\u0430 \u0442\u043E, \u0447\u0442\u043E \u0442\u044B \u0417\u0410\u041F\u0423\u0421\u0422\u0418\u041B \u0438 \u0423\u0412\u0418\u0414\u0415\u041B (\u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B, \u043B\u043E\u0433, \u043E\u0442\u0432\u0435\u0442 \u0440\u0443\u0447\u043A\u0438, \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u044D\u043A\u0440\u0430\u043D\u0430).",
|
|
17334
|
+
"",
|
|
17335
|
+
VERIFY_SCOPE_BLOCK,
|
|
17336
|
+
...attachmentsBlock ? ["", attachmentsBlock] : [],
|
|
17337
|
+
"",
|
|
17338
|
+
"\u041F\u043E\u0440\u044F\u0434\u043E\u043A:",
|
|
17339
|
+
"1. \u0412\u044B\u044F\u0441\u043D\u0438, \u0427\u0422\u041E \u0418\u041C\u0415\u041D\u041D\u041E \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043E: `gh pr view " + prUrl + " --body` (\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 PR) + read_task() (\u0442\u0430\u043C \u0436\u0435 \u043E\u0442\u0447\u0451\u0442 \u0434\u0440\u043E\u043D\u0430 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0438 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438). \u0412\u044B\u043F\u0438\u0448\u0438 \u0441\u0435\u0431\u0435 \u0441\u043F\u0438\u0441\u043E\u043A \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0445 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0445 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439 \u2014 \u043F\u043E \u043D\u0438\u043C \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0448\u044C.",
|
|
17340
|
+
"2. \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u0415\u0414\u0418 \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0435. \u0422\u0435\u0441\u0442\u044B \u2014 \u043D\u0435 \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u0441\u0430\u043C\u0438 \u043F\u043E \u0441\u0435\u0431\u0435: \u0438\u0445 \u043F\u0438\u0441\u0430\u043B \u0442\u043E\u0442 \u0436\u0435 \u0430\u0432\u0442\u043E\u0440. \u0413\u0434\u0435 \u043C\u043E\u0436\u043D\u043E \u2014 \u0433\u043E\u043D\u044F\u0439 \u0444\u0438\u0447\u0443 \u0442\u0430\u043A, \u043A\u0430\u043A \u0435\u0451 \u0443\u0432\u0438\u0434\u0438\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A (\u043F\u043E\u0434\u043D\u0438\u043C\u0438 \u0441\u0442\u0435\u043A/\u0437\u0430\u043F\u0443\u0441\u0442\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u0443/\u0434\u0451\u0440\u043D\u0438 \u0440\u0443\u0447\u043A\u0443; \u0440\u0435\u0446\u0435\u043F\u0442 \u2014 README \u0438 \u0441\u043A\u0438\u043B\u043B\u044B \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F). \u041D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0441\u044F \u2014 \u044D\u0442\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \xAB\u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0441\u044F\xBB, \u0438 \u043E\u043D \u0446\u0435\u043D\u043D\u0435\u0435 \u0432\u0435\u0436\u043B\u0438\u0432\u043E\u0433\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F.",
|
|
17341
|
+
"3. \u041F\u041E\u0418\u0429\u0418, \u0427\u0422\u041E \u0421\u041B\u041E\u041C\u0410\u041B\u041E\u0421\u042C \u0420\u042F\u0414\u041E\u041C. \u0421\u043E\u0441\u0435\u0434\u043D\u0438\u0435 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0438 \u0442\u043E\u0439 \u0436\u0435 \u0437\u043E\u043D\u044B, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0434\u0438\u0444\u0444 \u0437\u0430\u0434\u0435\u043B \u043A\u043E\u0441\u0432\u0435\u043D\u043D\u043E; \u0441\u0442\u0430\u0440\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 (\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u2014 \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u043E\u0441\u0442\u044C \u0438 \u043E\u0442\u043A\u0430\u0442); \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 (\u0438\u0434\u0435\u043C\u043F\u043E\u0442\u0435\u043D\u0442\u043D\u043E\u0441\u0442\u044C); \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044C\u043D\u044B\u0439 \u0432\u044B\u0437\u043E\u0432. " + (verifyMode === "local" ? "\u0413\u0435\u0439\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u0441\u0430\u043C (\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 verifyMode=local, CI \u043C\u043E\u0436\u0435\u0442 \u043D\u0435 \u0431\u044B\u0442\u044C \u0432\u043E\u0432\u0441\u0435 \u2014 \u0435\u0433\u043E \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u041D\u0415 \u0434\u0435\u0444\u0435\u043A\u0442 PR \u0438 \u0447\u0438\u043D\u0438\u0442\u044C \u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0434\u043E)." : "\u0422\u0435\u0441\u0442-\u0441\u044C\u044E\u0442 \u0446\u0435\u043B\u0438\u043A\u043E\u043C \u043D\u0435 \u043F\u0435\u0440\u0435\u0433\u043E\u043D\u044F\u0439 \u2014 \u0435\u0433\u043E \u0443\u0436\u0435 \u043F\u0440\u043E\u0433\u043D\u0430\u043B CI \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 (`gh pr checks " + prUrl + "`); \u0442\u0432\u043E\u0451 \u0434\u0435\u043B\u043E \u0442\u043E, \u0447\u0435\u0433\u043E \u0432 CI \u043D\u0435\u0442."),
|
|
17342
|
+
"4. \u041F\u0420\u041E\u0414\u0410\u0412\u0418 \u041A\u0420\u0410\u0419\u041D\u0418\u0415 \u0421\u041B\u0423\u0427\u0410\u0418: \u043F\u0443\u0441\u0442\u043E/\u043D\u043E\u043B\u044C/\u043E\u0447\u0435\u043D\u044C \u0434\u043B\u0438\u043D\u043D\u043E\u0435, \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u043F\u0440\u0430\u0432\u0430, \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430\u044F \u0441\u0435\u0442\u044C, \u0433\u043E\u043D\u043A\u0430, \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u0430\u044F \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0430. \u0425\u043E\u0442\u044F \u0431\u044B \u0434\u0432\u0430 \u0442\u0430\u043A\u0438\u0445 \u0437\u0430\u0445\u043E\u0434\u0430 \u2014 \u044D\u0442\u043E \u043C\u0438\u043D\u0438\u043C\u0443\u043C \u0434\u043E\u0431\u0440\u043E\u0441\u043E\u0432\u0435\u0441\u0442\u043D\u043E\u0439 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u043E\u043F\u0440\u043E\u0432\u0435\u0440\u0433\u043D\u0443\u0442\u044C.",
|
|
17343
|
+
`5. \u041E\u0441\u0442\u0430\u0432\u044C \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u043D\u0430 PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u0435\u0433\u043E \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041F\u0415\u0420\u0412\u041E\u0419 \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \u2014 \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u043D\u043E \u0438\u0437 \u0417\u0410\u0413\u041B\u0410\u0412\u041D\u042B\u041C\u0418: **\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E** / **\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F** / **\u041D\u0410\u0428\u0401\u041B**. \u0414\u0430\u043B\u044C\u0448\u0435: \u0447\u0442\u043E \u0438\u043C\u0435\u043D\u043D\u043E \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B (\u043A\u043E\u043C\u0430\u043D\u0434\u0430\u043C\u0438), \u0447\u0442\u043E \u0443\u0432\u0438\u0434\u0435\u043B, \u0438 \u2014 \u0434\u043B\u044F \u0434\u0432\u0443\u0445 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0445 \u2014 \u043A\u0430\u043A \u044D\u0442\u043E \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C.`,
|
|
17344
|
+
`6. \u0412\u044B\u0437\u043E\u0432\u0438 done(${prUrl}, verdict: "confirmed" | "not_reproduced" | "found_issues", summary: "\u0447\u0442\u043E \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B, \u0447\u0442\u043E \u0443\u0432\u0438\u0434\u0435\u043B, \u0447\u0442\u043E \u0440\u0435\u0448\u0438\u043B"). \u0420\u043E\u0439 \u0437\u0430\u043F\u0438\u0448\u0435\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043D\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u0438 \u0440\u0430\u0437\u043D\u0435\u0441\u0451\u0442 summary \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 \u0437\u0430\u0434\u0430\u0447\u0443. \u041F\u043E\u043A\u0430 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u0440\u0430\u0431\u043E\u0442\u0430 \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u043E\u0448\u0435\u0434\u0448\u0435\u0439 \u043F\u0440\u0438\u0451\u043C\u043A\u0443 \u2014 \u0442\u0440\u0435\u0442\u044C\u0435\u0433\u043E \u0438\u0441\u0445\u043E\u0434\u0430 \u043D\u0435\u0442: \u043D\u0435 \u0441\u043C\u043E\u0433 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0432\u043E\u0432\u0441\u0435 \u2014 \u044D\u0442\u043E "not_reproduced" \u0441 \u0447\u0435\u0441\u0442\u043D\u043E\u0439 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 \u0432 summary.`,
|
|
17345
|
+
"",
|
|
17346
|
+
"\u0427\u0442\u043E \u041D\u0415 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043E\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u043C \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u043C: \u0432\u043A\u0443\u0441\u043E\u0432\u044B\u0435 \u043F\u0440\u0430\u0432\u043A\u0438 \u043A\u043E\u0434\u0430, \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 CI \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438, \u0447\u0443\u0436\u0438\u0435 \u0441\u043B\u043E\u043C\u0430\u043D\u043D\u044B\u0435 \u0442\u0435\u0441\u0442\u044B, \u043D\u0435 \u043E\u0442\u043D\u043E\u0441\u044F\u0449\u0438\u0435\u0441\u044F \u043A \u044D\u0442\u043E\u043C\u0443 \u0434\u0438\u0444\u0444\u0443. \u0417\u0430\u043C\u0435\u0442\u0438\u043B \u0442\u0430\u043A\u043E\u0435 \u2014 \u0441\u043A\u0430\u0436\u0438 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439 (\u0438\u043B\u0438 \u0447\u0435\u0440\u0435\u0437 note), \u043D\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043E\u0442 \u044D\u0442\u043E\u0433\u043E \u043D\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442."
|
|
17347
|
+
].join("\n");
|
|
17348
|
+
}
|
|
17349
|
+
async function spawnVerifier(deps, verifiedRun, files) {
|
|
17350
|
+
const repo = normalizeRepo(verifiedRun.repo || deps.repoFallback);
|
|
17351
|
+
const branch = await worktreeBranchFor(verifiedRun, deps.prHeadBranch);
|
|
17352
|
+
const verifyMode = deps.verifyMode ? await deps.verifyMode(repo).catch(() => void 0) : void 0;
|
|
17353
|
+
const verifier = deps.db.createRun({
|
|
17354
|
+
task_slug: verifiedRun.task_slug,
|
|
17355
|
+
repo,
|
|
17356
|
+
kind: "verifier",
|
|
17357
|
+
verifies_run_id: verifiedRun.id,
|
|
17358
|
+
review_cycle: verifiedRun.review_cycle,
|
|
17359
|
+
// штамп цикла: фикс-итерацию проверяет свежий верификатор
|
|
17360
|
+
workspace_id: verifiedRun.workspace_id
|
|
17361
|
+
});
|
|
17362
|
+
const wt = join29(deps.worktreesDir, basename4(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
|
|
17363
|
+
try {
|
|
17364
|
+
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
17365
|
+
await deps.git.setWorktreeIdentity(wt, verifier.name);
|
|
17366
|
+
const attachments = await deps.taskAttachments?.(verifiedRun.task_slug, wt, verifiedRun.workspace_id);
|
|
17367
|
+
const prompt = buildVerifierPrompt(verifiedRun, verifier, branch, files, attachments, verifyMode);
|
|
17368
|
+
const pid = deps.launch({ worktree: wt, run: verifier, prompt });
|
|
17369
|
+
deps.db.updateRun(verifier.id, { status: "running", worktree_path: wt, branch, pid, pr_url: verifiedRun.pr_url });
|
|
17370
|
+
deps.db.addEvent(verifiedRun.id, "step", `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F: \u043F\u043E\u0434\u043D\u044F\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 ${verifier.name} (#${verifier.id}) \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0435 \u043D\u0430 \u043E\u043F\u0440\u043E\u0432\u0435\u0440\u0436\u0435\u043D\u0438\u0435`);
|
|
17371
|
+
return deps.db.getRun(verifier.id);
|
|
17372
|
+
} catch (e) {
|
|
17373
|
+
await deps.git.removeWorktree(repo, wt).catch(() => {
|
|
17374
|
+
});
|
|
17375
|
+
deps.db.updateRun(verifier.id, { status: "failed" });
|
|
17376
|
+
deps.db.addEvent(verifier.id, "failed", `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C: ${String(e)}`);
|
|
17377
|
+
throw e;
|
|
17378
|
+
}
|
|
17379
|
+
}
|
|
16636
17380
|
async function respawnRun(deps, run, mode, nudge) {
|
|
16637
17381
|
const repo = normalizeRepo(run.repo || deps.repoFallback);
|
|
16638
17382
|
const wt = run.worktree_path || (mode === "fresh" ? droneWorktreePath(deps.worktreesDir, repo, run.task_slug) : null);
|
|
@@ -16751,7 +17495,11 @@ async function makeStore(repo, worktreesDir, repoFlag) {
|
|
|
16751
17495
|
}
|
|
16752
17496
|
const call = resilientSwarmMcp({
|
|
16753
17497
|
onDown: (ms) => console.warn(`[overmind] \u043F\u0440\u043E\u0434-MCP \u043C\u043E\u043B\u0447\u0438\u0442 ${Math.round(ms / 1e3)}\u0441 \u2014 \u0440\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E, \u0441\u0434\u0430\u0447\u0438 \u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u044B \u043D\u0435 \u0434\u043E\u0435\u0437\u0436\u0430\u044E\u0442.`),
|
|
16754
|
-
onUp: () => console.warn("[overmind] \u043F\u0440\u043E\u0434-MCP \u0441\u043D\u043E\u0432\u0430 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438.")
|
|
17498
|
+
onUp: () => console.warn("[overmind] \u043F\u0440\u043E\u0434-MCP \u0441\u043D\u043E\u0432\u0430 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438."),
|
|
17499
|
+
// Ретрай не молчит: обрыв, переигранный в том же вызове, всё равно виден.
|
|
17500
|
+
// Без этой строки самолечение связи — единственный путь, о котором в логе
|
|
17501
|
+
// ни слова, и разбор «почему тик подтормозил» упирается в тишину.
|
|
17502
|
+
onRetry: ({ tool, attempt, reason }) => console.warn(`[overmind] \u043F\u0440\u043E\u0434-MCP: ${tool} \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0441\u044F (${reason}) \u2014 \u043F\u043E\u0432\u0442\u043E\u0440 ${attempt}.`)
|
|
16755
17503
|
});
|
|
16756
17504
|
const explicitRegistry = await buildRepoRegistry(repoPaths);
|
|
16757
17505
|
if (repoPaths.length > 0 && explicitRegistry.length < repoPaths.length) {
|
|
@@ -16793,6 +17541,8 @@ async function makeStore(repo, worktreesDir, repoFlag) {
|
|
|
16793
17541
|
reconciled: rec.reconciled,
|
|
16794
17542
|
host,
|
|
16795
17543
|
repoMap,
|
|
17544
|
+
prodMcp: call,
|
|
17545
|
+
// TASWM-325: состояние связи для `swarm status`
|
|
16796
17546
|
// Периодическая пересборка (шаг 0a4 тика): применяем новый реестр к ЖИВОМУ клиенту —
|
|
16797
17547
|
// и список для claim/heartbeat, и обратную карту owner/repo → путь.
|
|
16798
17548
|
refreshRepoMap: async () => {
|
|
@@ -16861,7 +17611,8 @@ async function runDaemon(opts) {
|
|
|
16861
17611
|
reconciled,
|
|
16862
17612
|
host,
|
|
16863
17613
|
repoMap: repoMapState,
|
|
16864
|
-
refreshRepoMap: rebuildRepoMap
|
|
17614
|
+
refreshRepoMap: rebuildRepoMap,
|
|
17615
|
+
prodMcp
|
|
16865
17616
|
} = await makeStore(opts.repo, worktreesDir, opts.repoFlag);
|
|
16866
17617
|
await preflightAuth();
|
|
16867
17618
|
const outdated = outdatedLine(PACKAGE_VERSION, await latestRelease);
|
|
@@ -16966,7 +17717,15 @@ async function runDaemon(opts) {
|
|
|
16966
17717
|
const client = db;
|
|
16967
17718
|
heartbeat = {
|
|
16968
17719
|
push: async () => {
|
|
16969
|
-
const
|
|
17720
|
+
const stormActive = client.stormActive();
|
|
17721
|
+
const stormStartedAt = stormActive ? client.currentStormStartedAt() : null;
|
|
17722
|
+
const reply = await client.heartbeat(
|
|
17723
|
+
daemonVersion?.get() ?? null,
|
|
17724
|
+
control.state ?? null,
|
|
17725
|
+
lastOwnCapacity,
|
|
17726
|
+
hostResourcesForProd(),
|
|
17727
|
+
{ active: stormActive, reason: stormStartedAt ? `\u043D\u043E\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441 ${stormStartedAt} UTC` : null }
|
|
17728
|
+
);
|
|
16970
17729
|
if (reply.maxDrones !== void 0) operatorMaxDrones = reply.maxDrones;
|
|
16971
17730
|
if (reply.commands.length) onHostCommands(reply.commands);
|
|
16972
17731
|
if (reply.logRequests.length) {
|
|
@@ -17180,6 +17939,36 @@ async function runDaemon(opts) {
|
|
|
17180
17939
|
verdictCommentFn = (prUrl, sinceIso) => latestVerdictComment(prUrl, sinceIso);
|
|
17181
17940
|
reviewCutoffAtValue = reviewCutoffAt();
|
|
17182
17941
|
}
|
|
17942
|
+
let spawnVerifierFn;
|
|
17943
|
+
let verifyVerdictCommentFn;
|
|
17944
|
+
if (SWARM_AUTO_VERIFY) {
|
|
17945
|
+
verifyVerdictCommentFn = (prUrl, sinceIso) => latestVerifyVerdictComment(prUrl, sinceIso);
|
|
17946
|
+
spawnVerifierFn = (verifiedRun, files) => spawnVerifier(
|
|
17947
|
+
{
|
|
17948
|
+
db,
|
|
17949
|
+
repoFallback: opts.repo,
|
|
17950
|
+
worktreesDir,
|
|
17951
|
+
launch,
|
|
17952
|
+
git: { prepareDetachedWorktree, setWorktreeIdentity, removeWorktree },
|
|
17953
|
+
prHeadBranch,
|
|
17954
|
+
...verifyModeReader ? { verifyMode: verifyModeReader } : {},
|
|
17955
|
+
...tl?.fetchFile ? {
|
|
17956
|
+
taskAttachments: async (slug, worktree, workspaceId) => {
|
|
17957
|
+
const client = tl.forWorkspace(workspaceId);
|
|
17958
|
+
const task = await client.getTask(slug);
|
|
17959
|
+
return prepareTaskAttachments({
|
|
17960
|
+
worktree,
|
|
17961
|
+
refs: task?.attachments,
|
|
17962
|
+
fetchFile: client.fetchFile.bind(client)
|
|
17963
|
+
});
|
|
17964
|
+
}
|
|
17965
|
+
} : {}
|
|
17966
|
+
},
|
|
17967
|
+
verifiedRun,
|
|
17968
|
+
files
|
|
17969
|
+
).then(() => {
|
|
17970
|
+
});
|
|
17971
|
+
}
|
|
17183
17972
|
const narrator = makeOvermindNarrator();
|
|
17184
17973
|
const bootPause = loadPauseState(SWARM_HOME);
|
|
17185
17974
|
const control = { paused: bootPause.paused, state: bootPause.paused ? bootPause : null };
|
|
@@ -17474,7 +18263,10 @@ async function runDaemon(opts) {
|
|
|
17474
18263
|
// Авто-ревью (TASWM-148): ключи есть только когда SWARM_AUTO_REVIEW включён (дефолт да) —
|
|
17475
18264
|
// spawnReviewerFn/verdictCommentFn/reviewCutoffAtValue выставляются В ОДНОМ if(SWARM_AUTO_REVIEW)
|
|
17476
18265
|
// блоке выше, поэтому один и тот же гейт на все три (без отдельных тернарных условий).
|
|
17477
|
-
...spawnReviewerFn ? { spawnReviewer: spawnReviewerFn, verdictComment: verdictCommentFn, reviewCap: SWARM_REVIEW_CAP, reviewCutoffAt: reviewCutoffAtValue } : {}
|
|
18266
|
+
...spawnReviewerFn ? { spawnReviewer: spawnReviewerFn, verdictComment: verdictCommentFn, reviewCap: SWARM_REVIEW_CAP, reviewCutoffAt: reviewCutoffAtValue } : {},
|
|
18267
|
+
// Верификация (TASWM-344) — свой тумблер, независимый от авто-ревью: проверить, правда
|
|
18268
|
+
// ли работает заявленное, осмысленно и там, где ревью диффа выключено.
|
|
18269
|
+
...spawnVerifierFn ? { spawnVerifier: spawnVerifierFn, verifyVerdictComment: verifyVerdictCommentFn } : {}
|
|
17478
18270
|
};
|
|
17479
18271
|
let web;
|
|
17480
18272
|
let droneChannel = null;
|
|
@@ -17705,14 +18497,41 @@ async function runDaemon(opts) {
|
|
|
17705
18497
|
// печатает `swarm status`. Без него человек снова гадал бы, какие репозитории машина
|
|
17706
18498
|
// считает своими и в каком дереве работает при двух копиях одного клона.
|
|
17707
18499
|
repoMap: () => repoMapRows(repoMapState),
|
|
18500
|
+
// TASWM-325: СВЯЗЬ С ПРОДОМ и ёмкость — то, чего `swarm status` не мог узнать ниоткуда.
|
|
18501
|
+
// Собираем из трёх источников, каждый из которых знает свою часть: отказ по токену —
|
|
18502
|
+
// authState (TASWM-324, он ловит 401 на подключении, где его только и видно), живо ли
|
|
18503
|
+
// соединение — транспорт (mcpResilient.link), буфер и момент удачного пульса — клиент.
|
|
18504
|
+
// Мерить связь по возврату прод-вызова НЕЛЬЗЯ: боевой каллер не бросает, а отдаёт
|
|
18505
|
+
// {isError:true}, и такой замер показывал бы «связь есть» на мёртвом проде.
|
|
18506
|
+
// В sqlite-режиме клиента нет вовсе — раздел честно молчит, а не рисует зелёное.
|
|
18507
|
+
...db instanceof SwarmClient ? {
|
|
18508
|
+
link: () => ({
|
|
18509
|
+
...composeLink({
|
|
18510
|
+
api: TASKLESS_API,
|
|
18511
|
+
authDenied: authStateLine(),
|
|
18512
|
+
transport: prodMcp?.link() ?? null,
|
|
18513
|
+
pending: db.pendingCount(),
|
|
18514
|
+
now: Date.now()
|
|
18515
|
+
}),
|
|
18516
|
+
heardAt: db.lastHeardAt()
|
|
18517
|
+
})
|
|
18518
|
+
} : {},
|
|
18519
|
+
// Потолок дронов — УЖЕ посчитанный тиком (lastDroneCap), без похода в прод из ручки
|
|
18520
|
+
// статуса: /status обязан отвечать мгновенно и при лежащем проде.
|
|
18521
|
+
maxDrones: () => lastDroneCap,
|
|
17708
18522
|
// Гейт мержа (TASWM-355): ручка /merge-gate отвечает по ЖИВОЙ настройке autoReview того
|
|
17709
18523
|
// проекта, которому принадлежит PR (ридер выше), а не по глобальному env машины. Именно
|
|
17710
18524
|
// этот ответ и превращается в запрет: PreToolUse-хук на `gh pr merge` спрашивает его
|
|
17711
18525
|
// через `swarm merge-gate` и отказывает в команде, пока вердикта ревьюера нет.
|
|
18526
|
+
// TASWM-396: тот же ридер решает и подъём ревьюера, не только мерж — иначе проектная
|
|
18527
|
+
// настройка «ревью не нужно» ничего не выключала, а машина продолжала платить.
|
|
18528
|
+
...autoReviewProject ? { autoReviewForRepo: autoReviewProject } : {},
|
|
17712
18529
|
mergeGate: makeMergeGateResolver({
|
|
17713
18530
|
listRuns: () => db.listRuns(),
|
|
17714
18531
|
...autoReviewProject ? { autoReview: autoReviewProject } : {},
|
|
17715
|
-
fallbackAutoReview: SWARM_AUTO_REVIEW
|
|
18532
|
+
fallbackAutoReview: SWARM_AUTO_REVIEW,
|
|
18533
|
+
autoVerify: SWARM_AUTO_VERIFY
|
|
18534
|
+
// TASWM-344: вторая ось приёмки — вердикт верификатора
|
|
17716
18535
|
}),
|
|
17717
18536
|
// Оператор-пауза + «Оживить» (TASWM-214 Фаза 2): тот же shared-флаг, что читает тик;
|
|
17718
18537
|
// narrate — операторские действия видны в ленте роя; revive — сброс залипшего шторма.
|
|
@@ -17760,6 +18579,11 @@ async function runDaemon(opts) {
|
|
|
17760
18579
|
console.log(
|
|
17761
18580
|
`overmind \u043F\u043E\u0434\u043D\u044F\u0442. default-repo=${opts.repo || "(\u043D\u0435\u0442)"} backend=${SWARM_BACKEND} taskless=${tl ? "on" : "off (slug-only)"}`
|
|
17762
18581
|
);
|
|
18582
|
+
const denied = authStateLine();
|
|
18583
|
+
if (denied) {
|
|
18584
|
+
console.error(`[overmind] \u26D4 ${denied}`);
|
|
18585
|
+
console.error("[overmind] \u0430\u0432\u0442\u043E\u043D\u043E\u043C\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C \u041D\u0415 \u0432\u044B\u0431\u0440\u0430\u043D \u2014 \u043E\u043D \u043D\u0430\u0432\u044F\u0437\u0430\u043D \u043E\u0442\u043A\u0430\u0437\u043E\u043C: \u0440\u043E\u0439 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0443, \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u043A\u043E\u043F\u044F\u0442\u0441\u044F \u0432 outbox.");
|
|
18586
|
+
}
|
|
17763
18587
|
console.log(`\u0432\u0435\u0431-\u043C\u043E\u0440\u0434\u0430: http://127.0.0.1:${web.port} \u2190 \u043E\u0442\u043A\u0440\u043E\u0439 \u0438 \u0432\u043E\u0439\u0434\u0438 \u0447\u0435\u0440\u0435\u0437 Taskless`);
|
|
17764
18588
|
const interval = opts.intervalMs ?? 4e3;
|
|
17765
18589
|
writeHeartbeat(HEARTBEAT_PATH, Date.now());
|
|
@@ -17847,6 +18671,7 @@ export {
|
|
|
17847
18671
|
STALL_FLOOR_MS,
|
|
17848
18672
|
STALL_MS,
|
|
17849
18673
|
STORM_DEAD_LIMIT,
|
|
18674
|
+
VERIFY_SPAWN_LIMIT,
|
|
17850
18675
|
WATCHDOG_PARK_REASON,
|
|
17851
18676
|
buildChangesWakeNudge,
|
|
17852
18677
|
buildCiReadyNudge,
|
|
@@ -17856,6 +18681,7 @@ export {
|
|
|
17856
18681
|
buildResolverPrompt,
|
|
17857
18682
|
buildReviewChecklist,
|
|
17858
18683
|
buildReviewerPrompt,
|
|
18684
|
+
buildVerifierPrompt,
|
|
17859
18685
|
cancelRun,
|
|
17860
18686
|
hasLiveDescendant,
|
|
17861
18687
|
lastChangesSummary,
|
|
@@ -17870,6 +18696,7 @@ export {
|
|
|
17870
18696
|
reviveStuck,
|
|
17871
18697
|
runDaemon,
|
|
17872
18698
|
spawnReviewer,
|
|
18699
|
+
spawnVerifier,
|
|
17873
18700
|
syncCrashedOnStartup,
|
|
17874
18701
|
tickOnce,
|
|
17875
18702
|
verdictCommentMark,
|