@taskless-app/swarm 0.6.15 → 0.6.16
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 +558 -176
- package/dist/overmind.js +311 -58
- package/package.json +2 -2
package/dist/overmind.js
CHANGED
|
@@ -58,6 +58,13 @@ var init_types = __esm({
|
|
|
58
58
|
// вопрос дрона оператору (прод: SwarmEvent kind=question, TASWM-74)
|
|
59
59
|
"answer",
|
|
60
60
|
// ответ оператора на вопрос дрона (прод: SwarmEvent kind=answer)
|
|
61
|
+
// Черновик ответа, собранный машиной (TASWM-430): контекст задачи/репозитория + один
|
|
62
|
+
// вызов модели, реплика в ленте прогона рядом с вопросом. НЕ "answer" с флагом — и это
|
|
63
|
+
// главное в этом kind: вопрос закрывают (и дрона будят) все, кто сравнивает kind со
|
|
64
|
+
// строкой "answer"/"answered" — прод (state/taskRuns/timeline/fleetMetrics), веб-чат и
|
|
65
|
+
// синхронизация демона (droneBridge тянет вниз только ответ). Отдельный kind не знает
|
|
66
|
+
// никто из них, поэтому черновик физически не может разбудить дрона: он ждёт человека.
|
|
67
|
+
"answer_draft",
|
|
61
68
|
"ci_wake",
|
|
62
69
|
// review-PR покраснел (gh pr checks) — рой разбудил дрона со сжатым summary фейла (TASWM-102)
|
|
63
70
|
"ci_checked",
|
|
@@ -914,9 +921,17 @@ var init_baseBranch = __esm({
|
|
|
914
921
|
});
|
|
915
922
|
|
|
916
923
|
// ../swarm-core/src/hosts.ts
|
|
924
|
+
function hostTickStallMs(intervalMs) {
|
|
925
|
+
const iv = intervalMs != null && Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : 0;
|
|
926
|
+
return Math.max(iv * HOST_TICK_STALL_TICKS, HOST_TICK_STALL_FLOOR_MS);
|
|
927
|
+
}
|
|
928
|
+
var HOST_TICK_STALL_TICKS, HOST_TICK_STALL_FLOOR_MS, HOST_TICK_STALLED_ACTION;
|
|
917
929
|
var init_hosts = __esm({
|
|
918
930
|
"../swarm-core/src/hosts.ts"() {
|
|
919
931
|
"use strict";
|
|
932
|
+
HOST_TICK_STALL_TICKS = 10;
|
|
933
|
+
HOST_TICK_STALL_FLOOR_MS = 12e4;
|
|
934
|
+
HOST_TICK_STALLED_ACTION = '\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438 \u0434\u0435\u043C\u043E\u043D \u043C\u0430\u0448\u0438\u043D\u044B \u2014 swarm_host_command{kind:"restart"}';
|
|
920
935
|
}
|
|
921
936
|
});
|
|
922
937
|
|
|
@@ -947,10 +962,27 @@ var init_hostResources = __esm({
|
|
|
947
962
|
});
|
|
948
963
|
|
|
949
964
|
// ../swarm-core/src/hostPool.ts
|
|
965
|
+
var HOST_REJECTION_TEXT, HOST_REPO_BLOCK_TEXT;
|
|
950
966
|
var init_hostPool = __esm({
|
|
951
967
|
"../swarm-core/src/hostPool.ts"() {
|
|
952
968
|
"use strict";
|
|
953
969
|
init_hosts();
|
|
970
|
+
HOST_REJECTION_TEXT = {
|
|
971
|
+
offline: "\u043D\u0435 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438",
|
|
972
|
+
tick_stalled: `\u0446\u0438\u043A\u043B \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B\u0430 \u0432\u0441\u0442\u0430\u043B \u2014 \u043F\u0443\u043B\u044C\u0441 \u0438\u0434\u0451\u0442, \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442; ${HOST_TICK_STALLED_ACTION}`,
|
|
973
|
+
not_signed_in: "\u043D\u0435 \u0432\u043E\u0448\u043B\u0430 \u0432 Taskless \u2014 \u043D\u0430 \u0435\u0451 \u0432\u0435\u0431-\u043C\u043E\u0440\u0434\u0435 \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D \u0432\u0445\u043E\u0434",
|
|
974
|
+
paused: "\u043D\u0430 \u043F\u0430\u0443\u0437\u0435",
|
|
975
|
+
draining: "\u0432\u044B\u0432\u043E\u0434\u0438\u0442\u0441\u044F \u043D\u0430 \u043E\u0431\u0441\u043B\u0443\u0436\u0438\u0432\u0430\u043D\u0438\u0435",
|
|
976
|
+
repos_off: "\u0432\u0441\u0435 \u0435\u0451 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u044B",
|
|
977
|
+
full: "\u0437\u0430\u043D\u044F\u0442\u0430 \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E"
|
|
978
|
+
};
|
|
979
|
+
HOST_REPO_BLOCK_TEXT = {
|
|
980
|
+
offline: HOST_REJECTION_TEXT.offline,
|
|
981
|
+
paused: HOST_REJECTION_TEXT.paused,
|
|
982
|
+
draining: HOST_REJECTION_TEXT.draining,
|
|
983
|
+
no_clone: "\u043A\u043B\u043E\u043D\u0430 \u043D\u0435\u0442",
|
|
984
|
+
repo_off: "\u0440\u0430\u0431\u043E\u0442\u0430 \u043F\u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u0430"
|
|
985
|
+
};
|
|
954
986
|
}
|
|
955
987
|
});
|
|
956
988
|
|
|
@@ -4552,6 +4584,28 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4552
4584
|
const text = res.content?.find((c) => c.type === "text")?.text;
|
|
4553
4585
|
return typeof text === "string" && text.trim() ? text : null;
|
|
4554
4586
|
}
|
|
4587
|
+
async function proposeRepoRules(input) {
|
|
4588
|
+
const res = await safeCall("swarm_repo_rule_propose", {
|
|
4589
|
+
repoFullName: input.repoFullName,
|
|
4590
|
+
rules: input.rules,
|
|
4591
|
+
...input.by ? { by: input.by } : {},
|
|
4592
|
+
...input.taskSlug ? { taskSlug: input.taskSlug } : {}
|
|
4593
|
+
});
|
|
4594
|
+
if (!res || res.isError) return null;
|
|
4595
|
+
const text = res.content?.find((c) => c.type === "text")?.text;
|
|
4596
|
+
return typeof text === "string" && text.trim() ? text : null;
|
|
4597
|
+
}
|
|
4598
|
+
async function decideRepoRules(input) {
|
|
4599
|
+
const res = await safeCall("swarm_repo_rule_decide", {
|
|
4600
|
+
repoFullName: input.repoFullName,
|
|
4601
|
+
decisions: input.decisions,
|
|
4602
|
+
...input.by ? { by: input.by } : {},
|
|
4603
|
+
...input.taskSlug ? { taskSlug: input.taskSlug } : {}
|
|
4604
|
+
});
|
|
4605
|
+
if (!res || res.isError) return null;
|
|
4606
|
+
const text = res.content?.find((c) => c.type === "text")?.text;
|
|
4607
|
+
return typeof text === "string" && text.trim() ? text : null;
|
|
4608
|
+
}
|
|
4555
4609
|
async function resolveId(idOrSlug) {
|
|
4556
4610
|
const cached = idCache.get(idOrSlug);
|
|
4557
4611
|
if (cached) return cached;
|
|
@@ -4632,6 +4686,8 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4632
4686
|
getDocument,
|
|
4633
4687
|
getTaskContext,
|
|
4634
4688
|
reportAcceptanceCheck,
|
|
4689
|
+
proposeRepoRules,
|
|
4690
|
+
decideRepoRules,
|
|
4635
4691
|
createTask: async (input) => {
|
|
4636
4692
|
if (!input.projectId) {
|
|
4637
4693
|
throw new Error("createTask: projectId \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D \u2014 \u0430\u0432\u0442\u043E-\u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0440\u043E\u0435\u043C \u0431\u0435\u0437 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u043E (TASWM-204)");
|
|
@@ -6004,6 +6060,54 @@ import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync5, rea
|
|
|
6004
6060
|
import { dirname } from "path";
|
|
6005
6061
|
import { randomUUID } from "crypto";
|
|
6006
6062
|
|
|
6063
|
+
// src/repoRules.ts
|
|
6064
|
+
var RULE_FIELDS = ["brief", "zones", "env", "verify", "conventions", "spine"];
|
|
6065
|
+
var DRONE_RULES_PER_RUN_MAX = 2;
|
|
6066
|
+
function parsePendingRepoRules(raw) {
|
|
6067
|
+
if (!Array.isArray(raw)) return [];
|
|
6068
|
+
const out = [];
|
|
6069
|
+
for (const item of raw) {
|
|
6070
|
+
if (!item || typeof item !== "object") continue;
|
|
6071
|
+
const r = item;
|
|
6072
|
+
const field = RULE_FIELDS.find((f) => f === r.field);
|
|
6073
|
+
if (!field) continue;
|
|
6074
|
+
out.push({
|
|
6075
|
+
field,
|
|
6076
|
+
value: r.value,
|
|
6077
|
+
by: typeof r.by === "string" ? r.by : null,
|
|
6078
|
+
task: typeof r.task === "string" ? r.task : null,
|
|
6079
|
+
why: typeof r.why === "string" ? r.why : null,
|
|
6080
|
+
cheap: r.cheap === true,
|
|
6081
|
+
overwritesHuman: r.overwritesHuman === true
|
|
6082
|
+
});
|
|
6083
|
+
}
|
|
6084
|
+
return out;
|
|
6085
|
+
}
|
|
6086
|
+
function ruleValueLine(value) {
|
|
6087
|
+
const json = JSON.stringify(value ?? null);
|
|
6088
|
+
return json.length > 400 ? `${json.slice(0, 400)}\u2026 (\u043E\u0431\u0440\u0435\u0437\u0430\u043D\u043E)` : json;
|
|
6089
|
+
}
|
|
6090
|
+
function renderPendingRulesBlock(rules) {
|
|
6091
|
+
if (rules.length === 0) return "";
|
|
6092
|
+
const lines = rules.map((r) => {
|
|
6093
|
+
const who = r.by ? `\u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438\u043B ${r.by}` : "\u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438\u043B \u0434\u0440\u043E\u043D";
|
|
6094
|
+
const on = r.task ? ` \u043D\u0430 ${r.task}` : "";
|
|
6095
|
+
const fate = r.cheap && !r.overwritesHuman ? "\u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u0441\u044F \u0432 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0443 \u0441\u0440\u0430\u0437\u0443" : r.overwritesHuman ? "\u043F\u0435\u0440\u0435\u043F\u0438\u0441\u0430\u043B\u043E \u0431\u044B \u043F\u0440\u0430\u0432\u043A\u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u2014 \u0443\u0439\u0434\u0451\u0442 \u0435\u043C\u0443 \u043D\u0430 \u043F\u0440\u0438\u0451\u043C\u043A\u0443, \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u043F\u0440\u0438\u043C\u0435\u0448\u044C" : "\u0443\u0439\u0434\u0451\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u043D\u0430 \u043F\u0440\u0438\u0451\u043C\u043A\u0443, \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u043F\u0440\u0438\u043C\u0435\u0448\u044C (env/zones/spine \u0440\u043E\u0439 \u0441\u0430\u043C \u043D\u0435 \u043F\u0438\u0448\u0435\u0442)";
|
|
6096
|
+
return [
|
|
6097
|
+
`\u2022 ${r.field} \u2014 ${who}${on}. ${fate}.`,
|
|
6098
|
+
` \u043F\u043E\u0447\u0435\u043C\u0443: ${r.why?.trim() || "(\u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u043D\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0430 \u2014 \u044D\u0442\u043E \u0441\u0430\u043C\u043E \u043F\u043E \u0441\u0435\u0431\u0435 \u043F\u043E\u0432\u043E\u0434 \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442\u044C)"}`,
|
|
6099
|
+
` \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: ${ruleValueLine(r.value)}`
|
|
6100
|
+
].join("\n");
|
|
6101
|
+
});
|
|
6102
|
+
return [
|
|
6103
|
+
"\u{1F4CC} \u041F\u0420\u0410\u0412\u0418\u041B\u0410 \u041E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u0418, \u041F\u0420\u0418\u041D\u0415\u0421\u0401\u041D\u041D\u042B\u0415 \u0414\u0420\u041E\u041D\u041E\u041C (TASWM-431) \u2014 \u0440\u0435\u0448\u0430\u0435\u0448\u044C \u0442\u044B, \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u043C.",
|
|
6104
|
+
"\u0414\u0440\u043E\u043D \u043F\u0440\u0435\u0434\u043B\u0430\u0433\u0430\u0435\u0442 \u0442\u043E, \u043E\u0431\u043E \u0447\u0442\u043E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u0438 \u0447\u0442\u043E \u0432 \u043A\u043E\u0434\u0435 \u043D\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u043E (\u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u0442\u0430\u043A\u043E\u0435 \u043D\u0435 \u043D\u0430\u0445\u043E\u0434\u0438\u0442). \u0422\u044B \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043B \u0434\u0438\u0444\u0444 \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430, \u0432\u0438\u0434\u0438\u0448\u044C \u0447\u0438\u0441\u043B\u043E \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u0438 \u0447\u0442\u043E \u0434\u0440\u043E\u043D \u0447\u0438\u043D\u0438\u043B \u2014 \u0442\u043E \u0435\u0441\u0442\u044C \u0435\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0439, \u043A\u0442\u043E \u043E\u0442\u043B\u0438\u0447\u0438\u0442 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043E\u0442 \u043E\u0442\u043F\u0438\u0441\u043A\u0438.",
|
|
6105
|
+
...lines,
|
|
6106
|
+
'\u0420\u0435\u0448\u0438 \u043F\u043E \u041A\u0410\u0416\u0414\u041E\u041C\u0423 \u0438 \u043F\u0435\u0440\u0435\u0434\u0430\u0439 \u0440\u0435\u0448\u0435\u043D\u0438\u044F \u0432 done: `rule_verdicts: [{ field: "verify", apply: true }, \u2026]`. apply=true \u2014 \xAB\u043F\u0440\u0430\u0432\u0434\u0430, \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0434\u0440\u043E\u043D \u043E\u0431\u044F\u0437\u0430\u043D \u044D\u0442\u043E \u0437\u043D\u0430\u0442\u044C\xBB; apply=false \u2014 \u043E\u0442\u043F\u0438\u0441\u043A\u0430/\u043E\u0431\u0449\u0435\u0435 \u043C\u0435\u0441\u0442\u043E (\xAB\u0442\u0435\u0441\u0442\u044B \u0433\u043E\u043D\u044F\u044E\u0442\u0441\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 \u0438\u0437 README\xBB), \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u0441\u043D\u0438\u043C\u0435\u0442\u0441\u044F. \u041D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D \u2014 apply=false: \u043F\u0440\u0430\u0432\u0438\u043B\u043E, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043D\u0435\u0442, \u0441\u0442\u043E\u0438\u0442 \u0434\u0435\u0448\u0435\u0432\u043B\u0435 \u043D\u0435\u0432\u0435\u0440\u043D\u043E\u0433\u043E.',
|
|
6107
|
+
"\u041C\u043E\u043B\u0447\u0430\u043D\u0438\u0435 \u2014 \u0442\u043E\u0436\u0435 \u0438\u0441\u0445\u043E\u0434: \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u043B \u0440\u0435\u0448\u0435\u043D\u0438\u0435, \u0438 \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043E\u0441\u0442\u0430\u043D\u0435\u0442\u0441\u044F \u0432\u0438\u0441\u0435\u0442\u044C \u043D\u0430 \u043F\u0440\u0438\u0451\u043C\u043A\u0435 \u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430. \u042D\u0442\u043E \u043D\u0435 \u043E\u0448\u0438\u0431\u043A\u0430, \u043D\u043E \u0438 \u043D\u0435 \u0440\u0430\u0431\u043E\u0442\u0430."
|
|
6108
|
+
].join("\n");
|
|
6109
|
+
}
|
|
6110
|
+
|
|
6007
6111
|
// src/logTail.ts
|
|
6008
6112
|
import { closeSync, existsSync as existsSync5, openSync, readSync, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
6009
6113
|
import { join as join7 } from "path";
|
|
@@ -7518,6 +7622,25 @@ var SwarmClient = class _SwarmClient {
|
|
|
7518
7622
|
const sc = res.structuredContent ?? {};
|
|
7519
7623
|
return normalizeBaseBranch(sc.config?.baseBranch) ?? null;
|
|
7520
7624
|
}
|
|
7625
|
+
/**
|
|
7626
|
+
* ПРАВИЛА ОТ ДРОНОВ, ждущие вердикта ревьюера (TASWM-431): их демон кладёт в промпт
|
|
7627
|
+
* ревьюера — тот решает по ним в своём же вердикте. `taskSlug` — правила ЭТОГО
|
|
7628
|
+
* прогона: ревьюер отвечает за находки своего дрона, а не за чужие.
|
|
7629
|
+
*
|
|
7630
|
+
* Прод недоступен / старый прод (тула нет) / ответ не той формы → пустой список:
|
|
7631
|
+
* ревью обязано подниматься и без этого блока — правила тут не главное.
|
|
7632
|
+
*/
|
|
7633
|
+
async repoPendingRules(repoFullName, taskSlug) {
|
|
7634
|
+
let res;
|
|
7635
|
+
try {
|
|
7636
|
+
res = await this.call("swarm_repo_rule_pending", { repoFullName, ...taskSlug ? { taskSlug } : {} });
|
|
7637
|
+
} catch {
|
|
7638
|
+
return [];
|
|
7639
|
+
}
|
|
7640
|
+
if (res?.isError) return [];
|
|
7641
|
+
const sc = res.structuredContent ?? {};
|
|
7642
|
+
return parsePendingRepoRules(sc.rules);
|
|
7643
|
+
}
|
|
7521
7644
|
/**
|
|
7522
7645
|
* Live КОМАНДЫ ПОДГОТОВКИ worktree из Taskless (TASWM-312): тот же swarm_repo_config по
|
|
7523
7646
|
* repoFullName — Project.swarmConfig.prepare. Настройка сильнее детекта демона (deps.ts):
|
|
@@ -7561,29 +7684,11 @@ var SwarmClient = class _SwarmClient {
|
|
|
7561
7684
|
const p = sc.profile;
|
|
7562
7685
|
return p && typeof p === "object" && !Array.isArray(p) ? p : null;
|
|
7563
7686
|
}
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
*
|
|
7570
|
-
* Мимо outbox (в отличие от мутаций прогонов): предложение имеет смысл только вместе
|
|
7571
|
-
* со свежим состоянием репозитория, и доехавшее через сутки после разведки — хуже,
|
|
7572
|
-
* чем не доехавшее вовсе. Прод недоступен — человек перезапустит разведку.
|
|
7573
|
-
*/
|
|
7574
|
-
async reportRepoScout(repoFullName, report, by) {
|
|
7575
|
-
let res;
|
|
7576
|
-
try {
|
|
7577
|
-
res = await this.call("swarm_repo_scout_report", { repoFullName, report, ...by ? { by } : {} });
|
|
7578
|
-
} catch (e) {
|
|
7579
|
-
return { ok: false, error: `\u043F\u0440\u043E\u0434 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${e.message}` };
|
|
7580
|
-
}
|
|
7581
|
-
const text = res?.content?.map((c) => "text" in c ? c.text : "").join(" ") ?? "";
|
|
7582
|
-
if (res?.isError) return { ok: false, error: text || "\u043F\u0440\u043E\u0434 \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u043B \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0438" };
|
|
7583
|
-
const sc = res.structuredContent ?? {};
|
|
7584
|
-
const diff = Array.isArray(sc.diff) ? sc.diff : [];
|
|
7585
|
-
return { ok: true, diff, display: text };
|
|
7586
|
-
}
|
|
7687
|
+
// TASWM-441: отправка предложения разведки в прод (`swarm_repo_scout_report`) убрана —
|
|
7688
|
+
// разведчик больше не предлагает разметку в базу, а ведёт файл В САМОМ репозитории
|
|
7689
|
+
// (docs/AGENT_BRIEF.md, см. repoScout.ts). Прод-сторона тула и экран приёмки уезжают
|
|
7690
|
+
// отдельной задачей TASWM-442; `repoProfile` выше — её же остаток, читателей у него
|
|
7691
|
+
// в демоне не осталось.
|
|
7587
7692
|
/**
|
|
7588
7693
|
* Live ТРЕБОВАНИЯ К ОКРУЖЕНИЮ машины из Taskless (TASWM-313): тот же swarm_repo_config по
|
|
7589
7694
|
* repoFullName — Project.swarmConfig.requires. Демон проверяет их ПЕРЕД спавном дрона и не
|
|
@@ -7799,7 +7904,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7799
7904
|
*
|
|
7800
7905
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
7801
7906
|
*/
|
|
7802
|
-
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn) {
|
|
7907
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick) {
|
|
7803
7908
|
try {
|
|
7804
7909
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
7805
7910
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -7826,6 +7931,9 @@ var SwarmClient = class _SwarmClient {
|
|
|
7826
7931
|
args.hostId = this.host.hostId;
|
|
7827
7932
|
args.hostName = this.host.hostName;
|
|
7828
7933
|
}
|
|
7934
|
+
if (tick && Number.isFinite(tick.seq) && Number.isFinite(tick.ageMs) && tick.intervalMs > 0) {
|
|
7935
|
+
args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
|
|
7936
|
+
}
|
|
7829
7937
|
const res = await this.call("swarm_heartbeat", args);
|
|
7830
7938
|
if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
|
|
7831
7939
|
this.heartbeatOkAt = this.now();
|
|
@@ -8423,6 +8531,34 @@ function parseDecision(v) {
|
|
|
8423
8531
|
init_config();
|
|
8424
8532
|
init_heartbeat();
|
|
8425
8533
|
|
|
8534
|
+
// src/tickHealth.ts
|
|
8535
|
+
init_src();
|
|
8536
|
+
function initialTickHealth(now) {
|
|
8537
|
+
return { seq: 0, lastAt: null, startedAt: now, lastError: null };
|
|
8538
|
+
}
|
|
8539
|
+
function markTickDone(health, now, error) {
|
|
8540
|
+
health.seq += 1;
|
|
8541
|
+
health.lastAt = now;
|
|
8542
|
+
health.lastError = error === void 0 ? null : describeTickError(error);
|
|
8543
|
+
}
|
|
8544
|
+
function describeTickError(error) {
|
|
8545
|
+
if (error instanceof Error) return error.message || error.name;
|
|
8546
|
+
return String(error);
|
|
8547
|
+
}
|
|
8548
|
+
function tickAgeMs(health, now) {
|
|
8549
|
+
return Math.max(0, now - (health.lastAt ?? health.startedAt));
|
|
8550
|
+
}
|
|
8551
|
+
var TICK_SELF_HEAL_FLOOR_MS = 6e5;
|
|
8552
|
+
function tickSelfHealMs(intervalMs) {
|
|
8553
|
+
return Math.max(hostTickStallMs(intervalMs) * 2, TICK_SELF_HEAL_FLOOR_MS);
|
|
8554
|
+
}
|
|
8555
|
+
function tickSelfHealDue(health, now, intervalMs) {
|
|
8556
|
+
return tickAgeMs(health, now) > tickSelfHealMs(intervalMs);
|
|
8557
|
+
}
|
|
8558
|
+
function tickPulse(health, now, intervalMs) {
|
|
8559
|
+
return { seq: health.seq, ageMs: tickAgeMs(health, now), intervalMs };
|
|
8560
|
+
}
|
|
8561
|
+
|
|
8426
8562
|
// src/logRotate.ts
|
|
8427
8563
|
import { copyFileSync, renameSync as renameSync5, statSync as statSync5, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
|
|
8428
8564
|
var realIo = {
|
|
@@ -10488,6 +10624,7 @@ init_attachments();
|
|
|
10488
10624
|
init_shotUpload();
|
|
10489
10625
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10490
10626
|
import { z } from "zod";
|
|
10627
|
+
init_git();
|
|
10491
10628
|
init_src();
|
|
10492
10629
|
|
|
10493
10630
|
// src/reviewCycle.ts
|
|
@@ -10864,10 +11001,57 @@ ${notFoundText}` : notFoundText }],
|
|
|
10864
11001
|
),
|
|
10865
11002
|
screenshots: z.array(z.string()).optional().describe(
|
|
10866
11003
|
`\u041F\u0443\u0442\u0438 \u043A \u0441\u043D\u044F\u0442\u044B\u043C \u0441\u043A\u0440\u0438\u043D\u0430\u043C (png/jpg/webp/gif) \u2014 \u0438\u0445 \u043F\u0440\u0438\u043B\u043E\u0436\u0430\u0442 \u043A \u0442\u0432\u043E\u0435\u043C\u0443 \u043E\u0442\u0447\u0451\u0442\u0443 \u0412 \u0417\u0410\u0414\u0410\u0427\u0415, \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0441\u043C\u043E\u0442\u0440\u0438\u0442 \u0442\u0440\u0435\u043A\u0435\u0440, \u0430 \u043D\u0435 \u0433\u0438\u0442\u0445\u0430\u0431. \u041F\u0443\u0442\u044C \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 (\u043E\u0442 worktree) \u0438\u043B\u0438 \u0430\u0431\u0441\u043E\u043B\u044E\u0442\u043D\u044B\u0439, \u0434\u043E ${SHOT_MAX_FILES} \u0444\u0430\u0439\u043B\u043E\u0432. \u041C\u0435\u043D\u044F\u043B \u0432\u0438\u0434\u0438\u043C\u044B\u0439 UI \u2014 \u043F\u0440\u0438\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u0439 \u043E\u0431\u0430 \u0432\u044C\u044E\u043F\u043E\u0440\u0442\u0430 (desktop+mobile), \u044D\u0442\u043E \u0438 \u0435\u0441\u0442\u044C \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u0440\u0430\u0431\u043E\u0442\u044B.`
|
|
11004
|
+
),
|
|
11005
|
+
// TASWM-431: правило о репозитории — то, обо что дрон СПОТКНУЛСЯ. Форму значения
|
|
11006
|
+
// проверяет прод (там живёт схема разметки), здесь она намеренно свободная: гейт
|
|
11007
|
+
// на форму в двух местах разъехался бы, а отказ прода дрон и так увидит в ответе.
|
|
11008
|
+
rules: z.array(
|
|
11009
|
+
z.object({
|
|
11010
|
+
field: z.enum([...RULE_FIELDS]).describe("\u043A\u0430\u043A\u043E\u0435 \u043F\u043E\u043B\u0435 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0438 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u044D\u0442\u043E \u043F\u0440\u0430\u0432\u0438\u043B\u043E: verify | conventions | brief | env | zones | spine"),
|
|
11011
|
+
value: z.unknown().describe("\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044F \u2014 \u0442\u043E\u0439 \u0436\u0435 \u0444\u043E\u0440\u043C\u044B, \u0447\u0442\u043E \u0432 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0435 (verify: {commands,green}, env: {database,migrations,vars}, conventions: {commitLanguage,slugInCommit,baseBranch}, brief: \u0441\u0442\u0440\u043E\u043A\u0430, zones: \u043C\u0430\u0441\u0441\u0438\u0432 \u0437\u043E\u043D, spine: \u043C\u0430\u0441\u0441\u0438\u0432 \u043F\u0443\u0442\u0435\u0439)"),
|
|
11012
|
+
why: z.string().describe("\u043E\u0431\u043E \u0447\u0442\u043E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u0438 \u0447\u0435\u0433\u043E \u044D\u0442\u043E \u0441\u0442\u043E\u0438\u043B\u043E \u2014 \u043F\u043E \u044D\u0442\u043E\u043C\u0443 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0438 \u043E\u0442\u043B\u0438\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043E\u0442 \u043E\u0442\u043F\u0438\u0441\u043A\u0438")
|
|
11013
|
+
})
|
|
11014
|
+
).optional().describe(
|
|
11015
|
+
`\u041F\u0420\u0410\u0412\u0418\u041B\u041E \u041E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u0418, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u0432 \u043D\u0451\u043C \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E (TASWM-431) \u2014 \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${DRONE_RULES_PER_RUN_MAX} \u0437\u0430 \u043F\u0440\u043E\u0433\u043E\u043D \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u043E, \u043E\u0431\u043E \u0447\u0442\u043E \u0442\u044B \u0420\u0415\u0410\u041B\u042C\u041D\u041E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u0438 \u043F\u043E\u0442\u0440\u0430\u0442\u0438\u043B \u0432\u0440\u0435\u043C\u044F (\xAB\u0438\u043D\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043E\u043D\u043D\u044B\u0435 \u0442\u0435\u0441\u0442\u044B \u0436\u0434\u0443\u0442 \u043F\u043E\u0441\u0442\u0433\u0440\u0435\u0441 \u043D\u0430 5433\xBB). \u041E\u0431\u0449\u0435\u0435 \u043C\u0435\u0441\u0442\u043E (\xAB\u0442\u0435\u0441\u0442\u044B \u0433\u043E\u043D\u044F\u044E\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 pnpm test\xBB) \u043F\u0440\u0430\u0432\u0438\u043B\u043E\u043C \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F. \u0423\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u0435 \u043E\u0442\u0441\u0435\u0435\u0442\u0441\u044F \u0441\u0430\u043C\u043E. \u0420\u0435\u0448\u0430\u0435\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0432 \u0441\u0432\u043E\u0451\u043C \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435.`
|
|
11016
|
+
),
|
|
11017
|
+
rule_verdicts: z.array(z.object({ field: z.enum([...RULE_FIELDS]), apply: z.boolean() })).optional().describe(
|
|
11018
|
+
"\u0422\u041E\u041B\u042C\u041A\u041E \u0414\u041B\u042F \u0420\u0415\u0412\u042C\u042E\u0415\u0420\u0410 (TASWM-431): \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u043E \u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043F\u0440\u0438\u043D\u0451\u0441 \u0434\u0440\u043E\u043D (\u043E\u043D\u0438 \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u044B \u0432 \u0442\u0432\u043E\u0451\u043C \u043F\u0440\u043E\u043C\u043F\u0442\u0435). apply=true \u2014 \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u0435, apply=false \u2014 \u043E\u0442\u043F\u0438\u0441\u043A\u0430, \u0441\u043D\u044F\u0442\u044C. verify/conventions/brief \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0442\u0441\u044F \u0432 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0443 \u0441\u0440\u0430\u0437\u0443, env/zones/spine \u0443\u0439\u0434\u0443\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0432 \u043B\u044E\u0431\u043E\u043C \u0441\u043B\u0443\u0447\u0430\u0435."
|
|
10867
11019
|
)
|
|
10868
11020
|
},
|
|
10869
|
-
guard(async ({ pr_url, verdict, summary, screenshots }) => {
|
|
11021
|
+
guard(async ({ pr_url, verdict, summary, screenshots, rules, rule_verdicts }) => {
|
|
10870
11022
|
const run = await store.myRun();
|
|
11023
|
+
let repoFullNameCache = null;
|
|
11024
|
+
const repoFullNameOnce = () => repoFullNameCache ??= remoteRepoFullName(opts.worktree ?? process.cwd()).catch(() => null);
|
|
11025
|
+
const offerRules = async (taskSlug) => {
|
|
11026
|
+
if (!rules?.length) return "";
|
|
11027
|
+
if (!opts.taskless?.proposeRepoRules) return "\n\n\u26A0\uFE0F \u041F\u0440\u0430\u0432\u0438\u043B\u043E \u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E: Taskless \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D.";
|
|
11028
|
+
const repoFullName = await repoFullNameOnce();
|
|
11029
|
+
if (!repoFullName) {
|
|
11030
|
+
await store.addEvent(null, "step", "\u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C owner/repo \u043F\u043E origin");
|
|
11031
|
+
return "\n\n\u26A0\uFE0F \u041F\u0440\u0430\u0432\u0438\u043B\u043E \u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C owner/repo \u2014 \u0441\u043A\u0430\u0436\u0438 \u044D\u0442\u043E \u0432 PR.";
|
|
11032
|
+
}
|
|
11033
|
+
const text = await opts.taskless.proposeRepoRules({ repoFullName, rules, ...run?.name ? { by: run.name } : {}, ...taskSlug ? { taskSlug } : {} }).catch((e) => `\u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E: ${e instanceof Error ? e.message : String(e)}`);
|
|
11034
|
+
const line = text ?? "\u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E (\u043F\u0440\u043E\u0434 \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u0438\u043B\u0438 \u0442\u0443\u043B \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D)";
|
|
11035
|
+
await store.addEvent(null, "step", `\u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043E\u0442 \u0434\u0440\u043E\u043D\u0430: ${line}`);
|
|
11036
|
+
return `
|
|
11037
|
+
|
|
11038
|
+
\u{1F4CC} ${line}`;
|
|
11039
|
+
};
|
|
11040
|
+
const decideRules = async (taskSlug) => {
|
|
11041
|
+
if (!rule_verdicts?.length) return "";
|
|
11042
|
+
if (!opts.taskless?.decideRepoRules) return "";
|
|
11043
|
+
const repoFullName = await repoFullNameOnce();
|
|
11044
|
+
if (!repoFullName) {
|
|
11045
|
+
await store.addEvent(null, "step", "\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C owner/repo \u043F\u043E origin");
|
|
11046
|
+
return "\n\n\u26A0\uFE0F \u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u041D\u0415 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C owner/repo.";
|
|
11047
|
+
}
|
|
11048
|
+
const text = await opts.taskless.decideRepoRules({ repoFullName, decisions: rule_verdicts, ...run?.name ? { by: run.name } : {}, ...taskSlug ? { taskSlug } : {} }).catch((e) => `\u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${e instanceof Error ? e.message : String(e)}`);
|
|
11049
|
+
const line = text ?? "\u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D (\u043F\u0440\u043E\u0434 \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u0438\u043B\u0438 \u0442\u0443\u043B \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D)";
|
|
11050
|
+
await store.addEvent(null, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F: ${line}`);
|
|
11051
|
+
return `
|
|
11052
|
+
|
|
11053
|
+
\u{1F4CC} ${line}`;
|
|
11054
|
+
};
|
|
10871
11055
|
const reportSlug = (slug) => opts.expectedTaskSlug ?? slug;
|
|
10872
11056
|
const uploadShots = async () => {
|
|
10873
11057
|
if (!screenshots?.length) return { attachments: [], warning: "" };
|
|
@@ -10953,13 +11137,14 @@ ${notFoundText}` : notFoundText }],
|
|
|
10953
11137
|
commentWarning = opts.taskless ? "\u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0435\u0432\u044C\u044E \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430" : "\u043A\u043B\u0438\u0435\u043D\u0442 Taskless \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D";
|
|
10954
11138
|
await store.addEvent(run.reviews_run_id, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${commentWarning}`);
|
|
10955
11139
|
}
|
|
11140
|
+
const rulesNote = await decideRules(reviewedSlug);
|
|
10956
11141
|
const trustedPrUrl = reviewed?.pr_url ?? pr_url;
|
|
10957
11142
|
await store.updateRun(null, { status: "review", pr_url: trustedPrUrl });
|
|
10958
11143
|
await store.addEvent(null, "pr_opened", trustedPrUrl);
|
|
10959
11144
|
const commentNote = commentWarning ? `
|
|
10960
11145
|
|
|
10961
11146
|
\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"\`.` : "";
|
|
10962
|
-
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}` }] };
|
|
11147
|
+
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}${rulesNote}` }] };
|
|
10963
11148
|
}
|
|
10964
11149
|
if (iAmVerifier(run) && run?.verifies_run_id == null) {
|
|
10965
11150
|
return {
|
|
@@ -11066,22 +11251,24 @@ ${report}${shots.warning}`,
|
|
|
11066
11251
|
await store.updateRun(null, { ...t.patch, ...branchFix?.patch ?? {} });
|
|
11067
11252
|
await store.addEvent(null, "pr_opened", pr_url);
|
|
11068
11253
|
await postReport(reportSlug(run.task_slug), t.cycle ?? run.review_cycle ?? 0);
|
|
11254
|
+
const rulesNote = await offerRules(reportSlug(run.task_slug));
|
|
11069
11255
|
if (t.isFixIteration) {
|
|
11070
11256
|
await store.addEvent(
|
|
11071
11257
|
null,
|
|
11072
11258
|
"step",
|
|
11073
11259
|
`\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F: \u043D\u043E\u0432\u044B\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u043F\u043E\u0441\u043B\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB \u2014 \u043E\u0442\u043A\u0440\u044B\u0442 \u0446\u0438\u043A\u043B \u0440\u0435-\u0440\u0435\u0432\u044C\u044E #${t.cycle}`
|
|
11074
11260
|
);
|
|
11075
|
-
return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F)${shotsNote(shots)}` }] };
|
|
11261
|
+
return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F)${shotsNote(shots)}${rulesNote}` }] };
|
|
11076
11262
|
}
|
|
11077
|
-
return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}` }] };
|
|
11263
|
+
return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}${rulesNote}` }] };
|
|
11078
11264
|
}
|
|
11079
11265
|
await store.updateRun(null, { status: "review", pr_url, ...branchFix?.patch ?? {} });
|
|
11080
11266
|
await store.addEvent(null, "pr_opened", pr_url);
|
|
11081
11267
|
const fallbackSlug = reportSlug(void 0);
|
|
11082
11268
|
if (fallbackSlug) await postReport(fallbackSlug, void 0);
|
|
11083
11269
|
else await store.addEvent(null, "step", `\u043E\u0442\u0447\u0451\u0442 \u0434\u0440\u043E\u043D\u0430 (\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430 \u2014 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 \u0432 \u0441\u0442\u043E\u0440\u0435): ${report}`);
|
|
11084
|
-
|
|
11270
|
+
const fallbackRulesNote = await offerRules(fallbackSlug);
|
|
11271
|
+
return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}${fallbackRulesNote}` }] };
|
|
11085
11272
|
}
|
|
11086
11273
|
await store.updateRun(null, { status: "done" });
|
|
11087
11274
|
await store.addEvent(null, "done", pr_url ? `\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E \u0431\u0435\u0437 GitHub-PR (${pr_url})` : "\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E \u0431\u0435\u0437 GitHub-PR");
|
|
@@ -12112,13 +12299,35 @@ function makeExclusiveReader(opts) {
|
|
|
12112
12299
|
});
|
|
12113
12300
|
}
|
|
12114
12301
|
function makeAutoReviewReader(opts) {
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
12302
|
+
const ttl = opts.ttlMs ?? 6e4;
|
|
12303
|
+
const now = opts.now ?? Date.now;
|
|
12304
|
+
const log = opts.log ?? ((msg) => console.warn(msg));
|
|
12305
|
+
const cache = /* @__PURE__ */ new Map();
|
|
12306
|
+
const lastSource = /* @__PURE__ */ new Map();
|
|
12307
|
+
const say = (repo, source, msg) => {
|
|
12308
|
+
if (lastSource.get(repo) === source) return;
|
|
12309
|
+
lastSource.set(repo, source);
|
|
12310
|
+
log(msg);
|
|
12311
|
+
};
|
|
12312
|
+
return async (repo) => {
|
|
12313
|
+
const t = now();
|
|
12314
|
+
const hit = cache.get(repo);
|
|
12315
|
+
if (hit && t - hit.at < ttl) return hit.value;
|
|
12316
|
+
const live = await opts.fetch(repo).catch(() => null);
|
|
12317
|
+
if (typeof live === "boolean") {
|
|
12318
|
+
const changed = hit?.value !== live;
|
|
12319
|
+
cache.set(repo, { value: live, at: t });
|
|
12320
|
+
if (changed) lastSource.delete(repo);
|
|
12321
|
+
say(repo, "live", `[autoReview] ${repo}: \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u0430 \u2014 autoReview=${live}`);
|
|
12322
|
+
return live;
|
|
12323
|
+
}
|
|
12324
|
+
if (hit) {
|
|
12325
|
+
say(repo, "stale", `[autoReview] ${repo}: \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C \u043D\u0435 \u0432\u044B\u0448\u043B\u043E \u2014 \u0434\u0435\u0440\u0436\u0443 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u043E\u0435 autoReview=${hit.value}`);
|
|
12326
|
+
return hit.value;
|
|
12327
|
+
}
|
|
12328
|
+
say(repo, "env", `[autoReview] ${repo}: \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u0430 \u043D\u0438 \u0440\u0430\u0437\u0443 \u2014 \u0431\u0435\u0440\u0443 env SWARM_AUTO_REVIEW=${opts.fallback}`);
|
|
12329
|
+
return opts.fallback;
|
|
12330
|
+
};
|
|
12122
12331
|
}
|
|
12123
12332
|
function makeDroneCapReader(opts) {
|
|
12124
12333
|
const read = makeRepoValueReader({
|
|
@@ -12619,7 +12828,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
12619
12828
|
// package.json
|
|
12620
12829
|
var package_default = {
|
|
12621
12830
|
name: "@taskless-app/swarm",
|
|
12622
|
-
version: "0.6.
|
|
12831
|
+
version: "0.6.16",
|
|
12623
12832
|
type: "module",
|
|
12624
12833
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
12625
12834
|
keywords: [
|
|
@@ -12669,7 +12878,7 @@ var package_default = {
|
|
|
12669
12878
|
execa: "^10.0.0",
|
|
12670
12879
|
zod: "3.25.76"
|
|
12671
12880
|
},
|
|
12672
|
-
gitHead: "
|
|
12881
|
+
gitHead: "785b1478f2b81406c9b6524913939707b6c096fb"
|
|
12673
12882
|
};
|
|
12674
12883
|
|
|
12675
12884
|
// src/version.ts
|
|
@@ -15553,6 +15762,7 @@ async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
|
15553
15762
|
}
|
|
15554
15763
|
if (deps.resourcesOk && !deps.resourcesOk()) continue;
|
|
15555
15764
|
if (deps.reviewCap !== void 0 && countActiveVerifiers(deps.db) >= deps.reviewCap) continue;
|
|
15765
|
+
if (!await projectWantsChecks(deps, run)) continue;
|
|
15556
15766
|
const st = await deps.prStatus(run.pr_url).catch(() => null);
|
|
15557
15767
|
if (prLiveness(st) !== "open") continue;
|
|
15558
15768
|
if (st?.isDraft || st?.conflicting) continue;
|
|
@@ -15670,6 +15880,12 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
15670
15880
|
"4b. \u0421\u041D\u0418\u041C\u0410\u0419 \u041F\u0420\u041E\u0414\u0423\u041A\u0422, \u0410 \u041D\u0415 \u041F\u0420\u0415\u0412\u042C\u042E-\u0421\u0422\u0420\u0410\u041D\u0418\u0426\u0423 (TASWM-404). \u0414\u0435\u043C\u043E-\u0440\u043E\u0443\u0442 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 (`/dev/*`, \u043F\u0435\u0441\u043E\u0447\u043D\u0438\u0446\u0430, \u0441\u0442\u043E\u0440\u0438 \u0432 \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u043C \u0444\u0440\u0435\u0439\u043C\u0435) \u2014 \u041D\u0415 \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u0439 \u0432\u0451\u0440\u0441\u0442\u043A\u0438: \u0443 \u0442\u0430\u043A\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0441\u0432\u043E\u044F \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0430, \u0435\u0451 \u043C\u0435\u043D\u044E \u043E\u0431\u044B\u0447\u043D\u043E \u043D\u0435 \u0441\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u043D\u0430 \u0443\u0437\u043A\u043E\u043C \u044D\u043A\u0440\u0430\u043D\u0435, \u0438 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u043C\u0443 \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u043F\u043E\u043B\u043E\u0441\u0430 \u0432 \u043F\u0430\u0440\u0443 \u0441\u043E\u0442\u0435\u043D \u043F\u0438\u043A\u0441\u0435\u043B\u0435\u0439 \u0432\u043C\u0435\u0441\u0442\u043E \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u0439 \u0448\u0438\u0440\u0438\u043D\u044B. \u041F\u043E \u0442\u0430\u043A\u043E\u043C\u0443 \u0441\u043D\u0438\u043C\u043A\u0443 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u043D\u0438 \u0448\u0438\u0440\u0438\u043D\u044B \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432, \u043D\u0438 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u044F \u0448\u0430\u043F\u043A\u0438, \u043D\u0438 \u043D\u0430\u043F\u043E\u043B\u0437\u0430\u043D\u0438\u044F \u043B\u0438\u043F\u043A\u0438\u0445 \u043F\u043E\u0434\u043F\u0438\u0441\u0435\u0439 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442 \u0412\u0421\u0415\u0413\u0414\u0410 \u0438 \u043D\u0435 \u043B\u043E\u0432\u0438\u0442 \u043D\u0438\u0447\u0435\u0433\u043E (\u0442\u0430\u043A \u0441\u0434\u0430\u043B\u0438 \u0441\u043B\u043E\u043C\u0430\u043D\u043D\u0443\u044E \u043C\u043E\u0431\u0438\u043B\u0443 \u0432 TASWM-403, \u0441\u043A\u0440\u0438\u043D \u043F\u0440\u0438 \u044D\u0442\u043E\u043C \u0431\u044B\u043B \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D). \u0421\u043D\u0438\u043C\u0430\u0439 \u0420\u0415\u0410\u041B\u042C\u041D\u042B\u0419 \u0440\u043E\u0443\u0442 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043F\u043E\u0434 \u0436\u0438\u0432\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0435\u0439; \u043A\u0430\u043A \u044D\u0442\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 \u0432 \u044D\u0442\u043E\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u0435 \u2014 \u0432 \u0435\u0433\u043E \u0441\u043A\u0438\u043B\u043B\u0430\u0445. \u041F\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043A\u043E\u043D\u043D\u043E \u0440\u043E\u0432\u043D\u043E \u0442\u043E\u0433\u0434\u0430, \u043A\u043E\u0433\u0434\u0430 \u043F\u0440\u0430\u0432\u0438\u0448\u044C \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442 \u0432\u043D\u0435 \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0438 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F.",
|
|
15671
15881
|
"\u0427\u0438\u0441\u0442\u043E \u0431\u044D\u043A\u0435\u043D\u0434/\u043B\u043E\u0433\u0438\u043A\u0443/API/\u0434\u0435\u043C\u043E\u043D/CI/\u0442\u0438\u043F\u044B \u2014 \u0448\u0430\u0433\u0438 3\u20134 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438 (\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u041D\u0415 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0439 \u0438 \u0441\u043A\u0440\u0438\u043D\u044B \u043D\u0435 \u0441\u043D\u0438\u043C\u0430\u0439): \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0435 \u0442\u0435\u0441\u0442\u044B (\u0448\u0430\u0433 1) + e2e \u0432 CI \u044D\u0442\u043E \u0443\u0436\u0435 \u043F\u043E\u043A\u0440\u044B\u0432\u0430\u044E\u0442; \u044D\u043A\u043E\u043D\u043E\u043C\u0438\u043C \u0432\u0440\u0435\u043C\u044F \u0438 \u0442\u043E\u043A\u0435\u043D\u044B.",
|
|
15672
15882
|
"\u0422\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u044D\u0442\u043E\u0433\u043E \u0437\u043E\u0432\u0438 done(pr_url), \u0438 \u0432 \u0441\u0434\u0430\u0447\u0435 \u043F\u043E\u043A\u0430\u0436\u0438 \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E (\u0437\u0435\u043B\u0451\u043D\u044B\u0439 \u0432\u044B\u0432\u043E\u0434/\u043D\u0430\u0431\u043B\u044E\u0434\u0430\u0435\u043C\u043E\u0435 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435), \u0430 \u043D\u0435 \xAB\u0434\u043E\u043B\u0436\u043D\u043E \u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C\xBB.",
|
|
15883
|
+
// TASWM-431: половина правил репозитория в коде не написана, и выясняет их ДРОН —
|
|
15884
|
+
// а закрытая задача уносила это знание с собой. Планка намеренно высокая: «не
|
|
15885
|
+
// больше двух» и «обо что споткнулся» — единственное, что отделяет правило от
|
|
15886
|
+
// пересказа README, а лишнее правило дороже отсутствующего (его читает каждый
|
|
15887
|
+
// следующий дрон).
|
|
15888
|
+
`\u{1F4CC} \u0421\u041F\u041E\u0422\u041A\u041D\u0423\u041B\u0421\u042F \u041E \u041D\u0415\u0417\u0410\u041F\u0418\u0421\u0410\u041D\u041D\u041E\u0415 \u2014 \u041F\u0420\u0415\u0414\u041B\u041E\u0416\u0418 \u041F\u0420\u0410\u0412\u0418\u041B\u041E. \u041F\u043E\u0442\u0440\u0430\u0442\u0438\u043B \u0432\u0440\u0435\u043C\u044F \u043D\u0430 \u0442\u043E, \u0447\u0435\u0433\u043E \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0438\u0433\u0434\u0435 \u043D\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u043E (\u0438\u043D\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043E\u043D\u043D\u044B\u0435 \u0442\u0435\u0441\u0442\u044B \u0436\u0434\u0443\u0442 \u043F\u043E\u0441\u0442\u0433\u0440\u0435\u0441 \u043D\u0430 5433; \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043D\u0430\u043A\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u0442\u0435\u043C, \u0447\u0435\u043C \u043A\u0430\u0436\u0435\u0442\u0441\u044F; \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0437\u0435\u043B\u0451\u043D\u0430\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u0441 \u0442\u0430\u043A\u0438\u043C \u0444\u043B\u0430\u0433\u043E\u043C) \u2014 \u043F\u0435\u0440\u0435\u0434\u0430\u0439 \u044D\u0442\u043E \u0432 done: \`rules: [{ field: "verify"|"conventions"|"brief"|"env"|"zones"|"spine", value: <\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0438>, why: "\u043E\u0431\u043E \u0447\u0442\u043E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u0438 \u0447\u0435\u0433\u043E \u044D\u0442\u043E \u0441\u0442\u043E\u0438\u043B\u043E" }]\`. \u041D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${DRONE_RULES_PER_RUN_MAX} \u0437\u0430 \u043F\u0440\u043E\u0433\u043E\u043D \u0438 \u0422\u041E\u041B\u042C\u041A\u041E \u0442\u043E, \u043E\u0431\u043E \u0447\u0442\u043E \u0440\u0435\u0430\u043B\u044C\u043D\u043E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F: \xAB\u0442\u0435\u0441\u0442\u044B \u0433\u043E\u043D\u044F\u044E\u0442\u0441\u044F \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439, \u043E\u043D\u0430 \u0432 README\xBB \u2014 \u043D\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u043E, \u0430 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437 README. \u0423\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u0435 \u043E\u0442\u0441\u0435\u0435\u0442\u0441\u044F \u0441\u0430\u043C\u043E; \u0440\u0435\u0448\u0438\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u043C. \u041D\u0438\u0447\u0435\u0433\u043E \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435 \u0431\u044B\u043B\u043E \u2014 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439, \u043F\u0443\u0441\u0442\u043E\u0439 \u0441\u043F\u0438\u0441\u043E\u043A \u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434 \u043F\u0440\u043E\u0433\u043E\u043D\u0430.`,
|
|
15673
15889
|
"\u0421\u043A\u0438\u043B\u043B\u044B \u043F\u0440\u0438\u0445\u043E\u0434\u044F\u0442 \u043A \u0442\u0435\u0431\u0435 \u0414\u0412\u0423\u041C\u042F \u043D\u0430\u0431\u043E\u0440\u0430\u043C\u0438, \u0438 \u043E\u0431\u0430 \u0443\u0436\u0435 \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D\u044B (\u0441\u043F\u0430\u0432\u043D \u0438\u0437\u043E\u043B\u0438\u0440\u043E\u0432\u0430\u043D \u043E\u0442 \u044E\u0437\u0435\u0440\u0441\u043A\u043E\u0433\u043E ~/.claude, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u041B\u0418\u0427\u041D\u042B\u0415 \u0441\u043A\u0438\u043B\u043B\u044B \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0442\u0435\u0431\u0435 \u043D\u0435 \u0432\u0438\u0434\u043D\u044B). \u2460 \u0420\u043E\u0439\u043D\u044B\u0435 `swarm-drone:*` \u0435\u0434\u0443\u0442 \u0441 \u0420\u041E\u0415\u041C \u0432 \u043B\u044E\u0431\u043E\u0439 \u043F\u0440\u043E\u0435\u043A\u0442 \u2014 \u0434\u0438\u0441\u0446\u0438\u043F\u043B\u0438\u043D\u0430, \u0432\u0435\u0440\u043D\u0430\u044F \u0432\u0435\u0437\u0434\u0435: swarm-save-often (\u043D\u0435\u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u043E\u0435 \u0443\u043C\u0438\u0440\u0430\u0435\u0442 \u0441 \u0434\u0440\u043E\u043D\u043E\u043C), swarm-merge-main (\u0431\u0430\u0437\u043E\u0432\u0443\u044E \u0432\u0435\u0442\u043A\u0443 \u0432\u043B\u0438\u0432\u0430\u0442\u044C, \u0430 \u043D\u0435 \u0440\u0435\u0431\u0435\u0439\u0437\u0438\u0442\u044C \u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u0443\u044E), swarm-await-process (\u0436\u0434\u0430\u0442\u044C \u0443\u0441\u043B\u043E\u0432\u0438\u0435, \u043D\u0435 sleep), swarm-verify-green (\u0447\u0442\u043E \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C), systematic-debugging (\u043F\u0435\u0440\u0432\u043E\u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u0434\u043E \u0444\u0438\u043A\u0441\u0430). \u2461 \u0421\u043A\u0438\u043B\u043B\u044B \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u042F (.claude/skills, \u0441\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043E\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430) \u2014 \u0437\u043D\u0430\u043D\u0438\u044F \u043E\u0431 \u044D\u0442\u043E\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u0435: \u043A\u0430\u043A \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0441\u0442\u0435\u043A, \u043A\u0430\u043A \u0433\u043E\u043D\u044F\u0442\u044C \u0442\u0435\u0441\u0442\u044B \u0438 \u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0437\u0435\u043B\u0451\u043D\u043E\u0435, UI-\u043F\u0440\u0430\u0432\u0438\u043B\u0430. \u041F\u043E\u043B\u044C\u0437\u0443\u0439\u0441\u044F \u043E\u0431\u043E\u0438\u043C\u0438, \u043D\u0435 \u043F\u0435\u0440\u0435\u0438\u0437\u043E\u0431\u0440\u0435\u0442\u0430\u0439; \u043D\u0435\u0442 \u043D\u0443\u0436\u043D\u043E\u0433\u043E \u0437\u043D\u0430\u043D\u0438\u044F \u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0435 \u2014 \u044D\u0442\u043E \u043F\u0440\u043E\u0431\u0435\u043B \u0435\u0433\u043E \u0441\u043A\u0438\u043B\u043B\u043E\u0432, \u0441\u043A\u0430\u0436\u0438 note(), \u0430 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u044B."
|
|
15674
15890
|
];
|
|
15675
15891
|
if (run.decision) {
|
|
@@ -15820,8 +16036,9 @@ var REVIEW_MISSION_BLOCK = [
|
|
|
15820
16036
|
"\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.",
|
|
15821
16037
|
"\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."
|
|
15822
16038
|
].join("\n");
|
|
15823
|
-
function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachmentsBlock, verifyMode = SWARM_VERIFY) {
|
|
16039
|
+
function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachmentsBlock, verifyMode = SWARM_VERIFY, pendingRules = []) {
|
|
15824
16040
|
const name = reviewer.name || `#${reviewer.id}`;
|
|
16041
|
+
const rulesBlock = renderPendingRulesBlock(pendingRules);
|
|
15825
16042
|
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)";
|
|
15826
16043
|
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), ";
|
|
15827
16044
|
return [
|
|
@@ -15863,6 +16080,7 @@ function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments
|
|
|
15863
16080
|
`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.`
|
|
15864
16081
|
],
|
|
15865
16082
|
`3. \u0412\u044B\u0437\u043E\u0432\u0438 done(${prUrl}, verdict: "approve"|"changes", summary: "\u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u0447\u0442\u043E \u0440\u0435\u0448\u0438\u043B") \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0440\u0430\u0437\u043D\u0435\u0441\u0451\u0442 summary \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 \u0437\u0430\u0434\u0430\u0447\u0443-\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A, \u0430 verdict \u0437\u0430\u043F\u0438\u0448\u0435\u0442 \u043D\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D (\u0431\u0435\u0439\u0434\u0436 \u0432 \u0432\u0435\u0431\u0435). \u0422\u044B \u0441\u0430\u043C \u0440\u0435\u0432\u044C\u044E \u041D\u0415 \u043F\u043E\u0434\u043B\u0435\u0436\u0438\u0448\u044C \u2014 \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0438 \u043D\u0435\u0442.`,
|
|
16083
|
+
...rulesBlock ? ["", rulesBlock] : [],
|
|
15866
16084
|
'\u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D: \u0442\u0440\u0435\u0442\u044C\u0435\u0433\u043E \u0438\u0441\u0445\u043E\u0434\u0430 \u043D\u0435\u0442, \u0438 \u0443\u0441\u043F\u0435\u0445\u043E\u043C \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442\u0441\u044F (TASWM-375) \u2014 done() \u0431\u0435\u0437 verdict \u043E\u0442\u043A\u0430\u0436\u0435\u0442, \u0430 \u043F\u0440\u043E\u0433\u043E\u043D, \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0432\u0448\u0438\u0439\u0441\u044F \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430, \u0440\u043E\u0439 \u043F\u043E\u043C\u0435\u0442\u0438\u0442 "\u0443\u043F\u0430\u043B" \u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043D\u043E\u0432\u043E. \u041E\u0442\u043A\u0430\u0437\u0430\u043B done() \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0441\u0451 \u0440\u0430\u0432\u043D\u043E \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 PR (\u0448\u0430\u0433 2): \u0434\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u0445\u0432\u0430\u0442\u0438\u0442 \u0435\u0433\u043E \u043E\u0442\u0442\u0443\u0434\u0430.'
|
|
15867
16085
|
].join("\n");
|
|
15868
16086
|
}
|
|
@@ -16177,7 +16395,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
16177
16395
|
if (stats.count >= CI_WAKE_LIMIT || !canContinue) {
|
|
16178
16396
|
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`;
|
|
16179
16397
|
deps.db.updateRun(reviewer.id, { status: "stalled" });
|
|
16180
|
-
const freshWanted = await
|
|
16398
|
+
const freshWanted = await projectWantsChecks(deps, run);
|
|
16181
16399
|
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";
|
|
16182
16400
|
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}`);
|
|
16183
16401
|
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}.`);
|
|
@@ -16221,9 +16439,12 @@ function worktreeAndTranscriptAlive(deps, worktree) {
|
|
|
16221
16439
|
const transcript = deps.hasTranscript ?? executorHasSession;
|
|
16222
16440
|
return exists(worktree) && transcript(worktree);
|
|
16223
16441
|
}
|
|
16224
|
-
async function
|
|
16442
|
+
async function projectWantsChecks(deps, run) {
|
|
16225
16443
|
if (!deps.autoReviewForRepo) return true;
|
|
16226
|
-
return await deps.autoReviewForRepo(effectiveRepo(deps, run)).catch(() =>
|
|
16444
|
+
return await deps.autoReviewForRepo(effectiveRepo(deps, run)).catch((e) => {
|
|
16445
|
+
console.warn(`[overmind] autoReview \u043F\u0440\u043E\u0435\u043A\u0442\u0430 (${effectiveRepo(deps, run)}) \u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u043A\u0430\u043A \u0440\u0430\u043D\u044C\u0448\u0435:`, e);
|
|
16446
|
+
return true;
|
|
16447
|
+
});
|
|
16227
16448
|
}
|
|
16228
16449
|
async function spawnFreshReviewerAfterStall(deps, run, why) {
|
|
16229
16450
|
if (!deps.spawnReviewer) return;
|
|
@@ -17081,14 +17302,6 @@ async function tickOnce(deps) {
|
|
|
17081
17302
|
}
|
|
17082
17303
|
}
|
|
17083
17304
|
}
|
|
17084
|
-
if (deps.heartbeat) {
|
|
17085
|
-
const hb = deps.heartbeat;
|
|
17086
|
-
const sinceLast = hb.lastAt == null ? Infinity : now - hb.lastAt;
|
|
17087
|
-
if (sinceLast >= hb.intervalMs) {
|
|
17088
|
-
hb.lastAt = now;
|
|
17089
|
-
await hb.push();
|
|
17090
|
-
}
|
|
17091
|
-
}
|
|
17092
17305
|
if (deps.droneBridge) {
|
|
17093
17306
|
const br = deps.droneBridge;
|
|
17094
17307
|
const sinceLast = br.lastAt == null ? Infinity : now - br.lastAt;
|
|
@@ -17309,7 +17522,7 @@ async function tickOnce(deps) {
|
|
|
17309
17522
|
// не порядком веток в 900-строчной функции: увидели живого — значит закрывать его
|
|
17310
17523
|
// рано, спавнить второго нельзя.
|
|
17311
17524
|
liveOrWaitingReviewers(deps.db, run.id).length === 0;
|
|
17312
|
-
if (reviewerGatesOk && deps.spawnReviewer && await
|
|
17525
|
+
if (reviewerGatesOk && deps.spawnReviewer && await projectWantsChecks(deps, run)) {
|
|
17313
17526
|
if (run.review_cycle >= REVIEW_CYCLE_LIMIT) {
|
|
17314
17527
|
if (!deps.db.events(run.id).some((e) => e.kind === "step" && (e.text ?? "").includes(REVIEW_CYCLE_ESCALATION_MARK))) {
|
|
17315
17528
|
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`);
|
|
@@ -17637,7 +17850,7 @@ async function tickOnce(deps) {
|
|
|
17637
17850
|
if (deps.autoReviewForRepo && az.autoReviewEnabled) {
|
|
17638
17851
|
for (const r of deps.db.listRuns("review")) {
|
|
17639
17852
|
if (!r.pr_url || autoReviewSnapshot.has(r.repo)) continue;
|
|
17640
|
-
autoReviewSnapshot.set(r.repo, await
|
|
17853
|
+
autoReviewSnapshot.set(r.repo, await projectWantsChecks(deps, r));
|
|
17641
17854
|
}
|
|
17642
17855
|
}
|
|
17643
17856
|
const candidates = [
|
|
@@ -17842,7 +18055,8 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
17842
18055
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
17843
18056
|
await deps.git.setWorktreeIdentity(wt, reviewer.name);
|
|
17844
18057
|
const attachments = await deps.taskAttachments?.(reviewRun.task_slug, wt, reviewRun.workspace_id);
|
|
17845
|
-
const
|
|
18058
|
+
const pendingRules = await deps.pendingRepoRules?.(repo, reviewRun.task_slug).catch(() => []) ?? [];
|
|
18059
|
+
const prompt = buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments, verifyMode, pendingRules);
|
|
17846
18060
|
const pid = deps.launch({ worktree: wt, run: reviewer, prompt });
|
|
17847
18061
|
deps.db.updateRun(reviewer.id, { status: "running", worktree_path: wt, branch, pid, pr_url: reviewRun.pr_url });
|
|
17848
18062
|
deps.db.addEvent(reviewRun.id, "step", `\u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E: \u043F\u043E\u0434\u043D\u044F\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 ${reviewer.name} (#${reviewer.id})`);
|
|
@@ -18283,6 +18497,8 @@ async function runDaemon(opts) {
|
|
|
18283
18497
|
}
|
|
18284
18498
|
let onHostCommands = () => {
|
|
18285
18499
|
};
|
|
18500
|
+
const tickIntervalMs = opts.intervalMs ?? 4e3;
|
|
18501
|
+
const tickHealth = initialTickHealth(Date.now());
|
|
18286
18502
|
let heartbeat;
|
|
18287
18503
|
if (SWARM_BACKEND === "client" && db instanceof SwarmClient && SWARM_PROD_HEARTBEAT_MS > 0) {
|
|
18288
18504
|
const client = db;
|
|
@@ -18306,7 +18522,12 @@ async function runDaemon(opts) {
|
|
|
18306
18522
|
// отвергают», а 12.08 входа не делали вовсе, и по отказу машина числилась бы
|
|
18307
18523
|
// вошедшей. Адрес — WEB_PORT (он же SWARM_WEB_PORT из окружения, на нём и
|
|
18308
18524
|
// поднимается морда): зашитый 7420 отправил бы человека туда, где ничего нет.
|
|
18309
|
-
{ signedIn: signedInToTaskless(), webUrl: `http://127.0.0.1:${WEB_PORT}` }
|
|
18525
|
+
{ signedIn: signedInToTaskless(), webUrl: `http://127.0.0.1:${WEB_PORT}` },
|
|
18526
|
+
// TASWM-452: и ПРИЗНАК ЖИВОГО ТИКА — номер последнего завершённого прохода
|
|
18527
|
+
// реконсайла и его возраст. Возраст, а не момент: часы машины и прода
|
|
18528
|
+
// расходятся (ноутбук после сна — на минуты), а сверяет признак прод со своим
|
|
18529
|
+
// настенным временем. Не «демон запущен», а «цикл прокрутился».
|
|
18530
|
+
tickPulse(tickHealth, Date.now(), tickIntervalMs)
|
|
18310
18531
|
);
|
|
18311
18532
|
if (reply.maxDrones !== void 0) operatorMaxDrones = reply.maxDrones;
|
|
18312
18533
|
if (reply.commands.length) onHostCommands(reply.commands);
|
|
@@ -18431,8 +18652,13 @@ async function runDaemon(opts) {
|
|
|
18431
18652
|
let exclusiveProject;
|
|
18432
18653
|
let projectDroneCap;
|
|
18433
18654
|
let autoReviewProject;
|
|
18655
|
+
let pendingRepoRules;
|
|
18434
18656
|
if (SWARM_BACKEND === "client" && db instanceof SwarmClient) {
|
|
18435
18657
|
const client = db;
|
|
18658
|
+
pendingRepoRules = async (repo, taskSlug) => {
|
|
18659
|
+
const repoFullName = await remoteRepoFullName(repo).catch(() => null);
|
|
18660
|
+
return repoFullName ? client.repoPendingRules(repoFullName, taskSlug) : [];
|
|
18661
|
+
};
|
|
18436
18662
|
verifyModeReader = makeVerifyModeReader({
|
|
18437
18663
|
fetch: async (repo) => {
|
|
18438
18664
|
const repoFullName = await remoteRepoFullName(repo).catch(() => null);
|
|
@@ -18504,6 +18730,9 @@ async function runDaemon(opts) {
|
|
|
18504
18730
|
// иначе ревьюер судит проект с verifyMode=local по правилам ci и блокирует PR
|
|
18505
18731
|
// за отсутствие CI, которого у проекта нет и не предполагается.
|
|
18506
18732
|
...verifyModeReader ? { verifyMode: verifyModeReader } : {},
|
|
18733
|
+
// TASWM-431: правила о репозитории, принесённые дроном этого прогона, — ревьюер
|
|
18734
|
+
// решает их тем же вердиктом (см. buildReviewerPrompt).
|
|
18735
|
+
...pendingRepoRules ? { pendingRepoRules } : {},
|
|
18507
18736
|
// TASWM-283: те же вложения задачи, что у исполнителя — ревьюер без макета
|
|
18508
18737
|
// не может проверить «сделано ли как просили».
|
|
18509
18738
|
...tl?.fetchFile ? {
|
|
@@ -18860,8 +19089,6 @@ async function runDaemon(opts) {
|
|
|
18860
19089
|
// Периодический re-hydrate зеркала (client-режим): ключ есть только когда сконфигурен.
|
|
18861
19090
|
...rehydrate ? { rehydrate } : {},
|
|
18862
19091
|
...droneBridge ? { droneBridge } : {},
|
|
18863
|
-
// Пульс overmind в прод (client-режим, TASWM-129): ключ есть только когда сконфигурен.
|
|
18864
|
-
...heartbeat ? { heartbeat } : {},
|
|
18865
19092
|
// Сверка кода демона с main (TASWM-251): ключ есть только когда entryFile в git-репо.
|
|
18866
19093
|
...daemonVersion ? { daemonVersion } : {},
|
|
18867
19094
|
// Карта репозиториев машины (TASWM-336): ключ есть только в client-режиме с непустым TTL.
|
|
@@ -19201,8 +19428,30 @@ async function runDaemon(opts) {
|
|
|
19201
19428
|
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.");
|
|
19202
19429
|
}
|
|
19203
19430
|
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`);
|
|
19204
|
-
const interval =
|
|
19431
|
+
const interval = tickIntervalMs;
|
|
19205
19432
|
writeHeartbeat(HEARTBEAT_PATH, Date.now());
|
|
19433
|
+
if (heartbeat) {
|
|
19434
|
+
const hb = heartbeat;
|
|
19435
|
+
let inFlight = false;
|
|
19436
|
+
setInterval(() => {
|
|
19437
|
+
if (inFlight) return;
|
|
19438
|
+
inFlight = true;
|
|
19439
|
+
void hb.push().catch((e) => console.error("[overmind] \u043F\u0443\u043B\u044C\u0441 \u0432 \u043F\u0440\u043E\u0434 \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:", e)).finally(() => {
|
|
19440
|
+
inFlight = false;
|
|
19441
|
+
});
|
|
19442
|
+
}, hb.intervalMs);
|
|
19443
|
+
}
|
|
19444
|
+
setInterval(() => {
|
|
19445
|
+
if (restart.phase === "restarting") return;
|
|
19446
|
+
const now = Date.now();
|
|
19447
|
+
if (!tickSelfHealDue(tickHealth, now, interval)) return;
|
|
19448
|
+
const age = Math.round(tickAgeMs(tickHealth, now) / 1e3);
|
|
19449
|
+
console.error(
|
|
19450
|
+
`[overmind] \u0442\u0438\u043A \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B\u0430 \u0432\u0441\u0442\u0430\u043B: \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0439 \u043F\u0440\u043E\u0445\u043E\u0434 #${tickHealth.seq} \u0431\u044B\u043B ${age}\u0441 \u043D\u0430\u0437\u0430\u0434${tickHealth.lastError ? ` (\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u043E\u0448\u0438\u0431\u043A\u0430: ${tickHealth.lastError})` : ""} \u2014 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430\u044E \u0441\u0435\u0431\u044F`
|
|
19451
|
+
);
|
|
19452
|
+
narrator.narrate(`\u{1FA7A} \u0426\u0438\u043A\u043B \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B\u0430 \u0432\u0441\u0442\u0430\u043B (\u043F\u0440\u043E\u0445\u043E\u0434 #${tickHealth.seq}, ${age}\u0441 \u0431\u0435\u0437 \u0434\u0432\u0438\u0436\u0435\u043D\u0438\u044F) \u2014 \u0434\u0435\u043C\u043E\u043D \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442 \u0441\u0435\u0431\u044F \u0441\u0430\u043C.`);
|
|
19453
|
+
performRestart(liveSessionIds(db, pidAlive));
|
|
19454
|
+
}, Math.max(1e3, interval));
|
|
19206
19455
|
if (deps.sweep) {
|
|
19207
19456
|
deps.sweep.lastAt = Date.now();
|
|
19208
19457
|
await deps.sweep.run().catch((e) => console.error("[overmind] \u0441\u0442\u0430\u0440\u0442\u043E\u0432\u044B\u0439 sweep \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:", e));
|
|
@@ -19211,7 +19460,9 @@ async function runDaemon(opts) {
|
|
|
19211
19460
|
deps.daemonVersion.lastAt = Date.now();
|
|
19212
19461
|
await deps.daemonVersion.run().catch((e) => console.error("[overmind] \u0441\u0442\u0430\u0440\u0442\u043E\u0432\u0430\u044F \u0441\u0432\u0435\u0440\u043A\u0430 \u0432\u0435\u0440\u0441\u0438\u0438 \u0434\u0435\u043C\u043E\u043D\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C:", e));
|
|
19213
19462
|
}
|
|
19463
|
+
tickHealth.startedAt = Date.now();
|
|
19214
19464
|
for (; ; ) {
|
|
19465
|
+
let tickError;
|
|
19215
19466
|
try {
|
|
19216
19467
|
writeHeartbeat(HEARTBEAT_PATH, Date.now());
|
|
19217
19468
|
await tickOnce(deps);
|
|
@@ -19221,8 +19472,10 @@ async function runDaemon(opts) {
|
|
|
19221
19472
|
else onRestartStillWaiting?.(live.length);
|
|
19222
19473
|
}
|
|
19223
19474
|
} catch (e) {
|
|
19224
|
-
|
|
19475
|
+
tickError = e;
|
|
19476
|
+
console.error(`[overmind] \u0442\u0438\u043A \u0443\u043F\u0430\u043B: ${describeTickError(e)} \u2014 \u0446\u0438\u043A\u043B \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0435\u0442 \u0440\u0430\u0431\u043E\u0442\u0443`, e);
|
|
19225
19477
|
}
|
|
19478
|
+
markTickDone(tickHealth, Date.now(), tickError);
|
|
19226
19479
|
await new Promise((r) => setTimeout(r, interval));
|
|
19227
19480
|
}
|
|
19228
19481
|
}
|