@taskless-app/swarm 0.6.66 → 0.6.68
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 +211 -43
- package/dist/overmind.js +550 -63
- package/package.json +3 -2
package/dist/overmind.js
CHANGED
|
@@ -188,6 +188,9 @@ function reviewPhaseKindOf(label2) {
|
|
|
188
188
|
function computeReviewPhase(input) {
|
|
189
189
|
const { reviewVerdict, ciRed } = input;
|
|
190
190
|
const verifyVerdict = input.verifyVerdict ?? null;
|
|
191
|
+
if (input.cyclesExhausted) {
|
|
192
|
+
return { kind: "cycles_exhausted", label: REVIEW_PHASE_TEXT.cycles_exhausted, ready: true };
|
|
193
|
+
}
|
|
191
194
|
if (!ciRed && isNegativeVerifyVerdict(verifyVerdict)) {
|
|
192
195
|
const kind = verifyVerdict === "found_issues" ? "verify_found" : "verify_not_reproduced";
|
|
193
196
|
return { kind, label: REVIEW_PHASE_TEXT[kind], ready: false };
|
|
@@ -242,7 +245,8 @@ var init_reviewPhase = __esm({
|
|
|
242
245
|
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
243
246
|
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
244
247
|
ready_awaiting_ci: "\u0413\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443 \u2014 \u0436\u0434\u0451\u0442 CI",
|
|
245
|
-
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
248
|
+
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
249
|
+
cycles_exhausted: "\u041B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
246
250
|
};
|
|
247
251
|
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
248
252
|
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
@@ -2020,7 +2024,7 @@ var init_effort = __esm({
|
|
|
2020
2024
|
});
|
|
2021
2025
|
|
|
2022
2026
|
// ../swarm-core/src/queuePlan.ts
|
|
2023
|
-
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS;
|
|
2027
|
+
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS, SERVICE_DONE_KEEP_MS, PR_HEAD_LAG_MS, SERVICE_DONE_MARKER_MS;
|
|
2024
2028
|
var init_queuePlan = __esm({
|
|
2025
2029
|
"../swarm-core/src/queuePlan.ts"() {
|
|
2026
2030
|
"use strict";
|
|
@@ -2033,6 +2037,9 @@ var init_queuePlan = __esm({
|
|
|
2033
2037
|
MIN_MS = 6e4;
|
|
2034
2038
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
2035
2039
|
QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
|
|
2040
|
+
SERVICE_DONE_KEEP_MS = 2 * 60 * MIN_MS;
|
|
2041
|
+
PR_HEAD_LAG_MS = 5 * MIN_MS;
|
|
2042
|
+
SERVICE_DONE_MARKER_MS = 8 * MIN_MS;
|
|
2036
2043
|
}
|
|
2037
2044
|
});
|
|
2038
2045
|
|
|
@@ -4085,7 +4092,7 @@ var init_linkFailure = __esm({
|
|
|
4085
4092
|
"use strict";
|
|
4086
4093
|
init_agentToken();
|
|
4087
4094
|
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";
|
|
4088
|
-
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;
|
|
4095
|
+
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|request timed out|-32001\b/i;
|
|
4089
4096
|
MISSING_TOOL_RE = /tool[^.]{0,40}not found|unknown tool|tool_not_found|method not found|-32601\b/i;
|
|
4090
4097
|
}
|
|
4091
4098
|
});
|
|
@@ -6017,6 +6024,47 @@ var init_oauth = __esm({
|
|
|
6017
6024
|
}
|
|
6018
6025
|
});
|
|
6019
6026
|
|
|
6027
|
+
// src/prodHttp.ts
|
|
6028
|
+
import { Agent, fetch as undiciFetch } from "undici";
|
|
6029
|
+
function prodDispatcher() {
|
|
6030
|
+
agent ??= new Agent({
|
|
6031
|
+
keepAliveTimeout: PROD_KEEPALIVE_MS,
|
|
6032
|
+
keepAliveMaxTimeout: PROD_KEEPALIVE_MS,
|
|
6033
|
+
headersTimeout: PROD_SOCKET_TIMEOUT_MS,
|
|
6034
|
+
bodyTimeout: PROD_SOCKET_TIMEOUT_MS,
|
|
6035
|
+
connect: { timeout: PROD_CONNECT_TIMEOUT_MS }
|
|
6036
|
+
});
|
|
6037
|
+
return agent;
|
|
6038
|
+
}
|
|
6039
|
+
function isProdTimeout(e) {
|
|
6040
|
+
for (let cur = e, depth = 0; cur != null && depth < 4; cur = cur.cause, depth++) {
|
|
6041
|
+
const code = cur.code;
|
|
6042
|
+
const name = cur.name;
|
|
6043
|
+
const msg = cur instanceof Error ? cur.message : String(cur);
|
|
6044
|
+
if (code === -32001 || code === "UND_ERR_HEADERS_TIMEOUT" || code === "UND_ERR_BODY_TIMEOUT") return true;
|
|
6045
|
+
if (name === "TimeoutError" || /request timed out/i.test(msg)) return true;
|
|
6046
|
+
}
|
|
6047
|
+
return false;
|
|
6048
|
+
}
|
|
6049
|
+
var PROD_CALL_TIMEOUT_MS, FETCH_GRACE_MS, PROD_KEEPALIVE_MS, PROD_SOCKET_TIMEOUT_MS, PROD_CONNECT_TIMEOUT_MS, agent, prodFetch;
|
|
6050
|
+
var init_prodHttp = __esm({
|
|
6051
|
+
"src/prodHttp.ts"() {
|
|
6052
|
+
"use strict";
|
|
6053
|
+
PROD_CALL_TIMEOUT_MS = 15e3;
|
|
6054
|
+
FETCH_GRACE_MS = 5e3;
|
|
6055
|
+
PROD_KEEPALIVE_MS = 1e4;
|
|
6056
|
+
PROD_SOCKET_TIMEOUT_MS = 3e4;
|
|
6057
|
+
PROD_CONNECT_TIMEOUT_MS = 1e4;
|
|
6058
|
+
agent = null;
|
|
6059
|
+
prodFetch = (url, init) => {
|
|
6060
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
6061
|
+
const deadline = method === "GET" ? null : AbortSignal.timeout(PROD_CALL_TIMEOUT_MS + FETCH_GRACE_MS);
|
|
6062
|
+
const signal = deadline && init?.signal ? AbortSignal.any([init.signal, deadline]) : deadline ?? init?.signal ?? void 0;
|
|
6063
|
+
return undiciFetch(url, { ...init, ...signal ? { signal } : {}, dispatcher: prodDispatcher() });
|
|
6064
|
+
};
|
|
6065
|
+
}
|
|
6066
|
+
});
|
|
6067
|
+
|
|
6020
6068
|
// src/authState.ts
|
|
6021
6069
|
function noteTokenRejected(nowMs = Date.now()) {
|
|
6022
6070
|
if (!denial) {
|
|
@@ -6505,8 +6553,8 @@ function escapeRegExp(s) {
|
|
|
6505
6553
|
}
|
|
6506
6554
|
async function resolveApiToken(api, explicit) {
|
|
6507
6555
|
if (explicit) return explicit;
|
|
6508
|
-
const
|
|
6509
|
-
if (
|
|
6556
|
+
const agent2 = loadAgentToken();
|
|
6557
|
+
if (agent2?.token) return agent2.token;
|
|
6510
6558
|
const store = loadStore();
|
|
6511
6559
|
if (store) {
|
|
6512
6560
|
const r = await ensureAccess(store, { api });
|
|
@@ -6531,9 +6579,9 @@ function resetLegacyTokenWarning() {
|
|
|
6531
6579
|
async function preflightAuth(opts = {}) {
|
|
6532
6580
|
const api = opts.api ?? TASKLESS_API;
|
|
6533
6581
|
const out = opts.out ?? ((s) => console.error(s));
|
|
6534
|
-
const
|
|
6535
|
-
if (!
|
|
6536
|
-
const probe = await probeAgentToken({ api, token:
|
|
6582
|
+
const agent2 = loadAgentToken();
|
|
6583
|
+
if (!agent2?.token) return true;
|
|
6584
|
+
const probe = await probeAgentToken({ api, token: agent2.token }, opts.fetchFn ?? fetch);
|
|
6537
6585
|
if (probe === "revoked") {
|
|
6538
6586
|
out(noteTokenRejected() ?? revokedTokenComplaint());
|
|
6539
6587
|
return false;
|
|
@@ -6553,7 +6601,8 @@ async function connectTaskless(cfg) {
|
|
|
6553
6601
|
if (!token) return null;
|
|
6554
6602
|
const url = new URL(mcpEndpoint(api));
|
|
6555
6603
|
const transport = new StreamableHTTPClientTransport(url, {
|
|
6556
|
-
requestInit: { headers: { Authorization: `Bearer ${token}` } }
|
|
6604
|
+
requestInit: { headers: { Authorization: `Bearer ${token}` } },
|
|
6605
|
+
fetch: prodFetch
|
|
6557
6606
|
});
|
|
6558
6607
|
const client = new Client({ name: "taskless-swarm", version: "0.1.0" }, { capabilities: {} });
|
|
6559
6608
|
try {
|
|
@@ -6568,7 +6617,14 @@ async function connectTaskless(cfg) {
|
|
|
6568
6617
|
return null;
|
|
6569
6618
|
}
|
|
6570
6619
|
noteTokenAccepted();
|
|
6571
|
-
const call = async (name, args) =>
|
|
6620
|
+
const call = async (name, args) => {
|
|
6621
|
+
try {
|
|
6622
|
+
return await client.callTool({ name, arguments: args }, void 0, { timeout: PROD_CALL_TIMEOUT_MS });
|
|
6623
|
+
} catch (e) {
|
|
6624
|
+
if (isProdTimeout(e)) console.warn(prodTimeoutLine("Taskless", name, PROD_CALL_TIMEOUT_MS));
|
|
6625
|
+
throw e;
|
|
6626
|
+
}
|
|
6627
|
+
};
|
|
6572
6628
|
return makeTasklessClient(
|
|
6573
6629
|
call,
|
|
6574
6630
|
workspace || void 0,
|
|
@@ -6586,7 +6642,9 @@ async function connectSwarmMcp(cfg) {
|
|
|
6586
6642
|
}
|
|
6587
6643
|
const url = new URL(mcpEndpoint(api));
|
|
6588
6644
|
const transport = new StreamableHTTPClientTransport(url, {
|
|
6589
|
-
requestInit: { headers: { Authorization: `Bearer ${token}` } }
|
|
6645
|
+
requestInit: { headers: { Authorization: `Bearer ${token}` } },
|
|
6646
|
+
fetch: prodFetch
|
|
6647
|
+
// TASWM-611
|
|
6590
6648
|
});
|
|
6591
6649
|
const client = new Client({ name: "taskless-swarm-client", version: "0.1.0" }, { capabilities: {} });
|
|
6592
6650
|
try {
|
|
@@ -6605,12 +6663,31 @@ async function connectSwarmMcp(cfg) {
|
|
|
6605
6663
|
}
|
|
6606
6664
|
swarmConnectFailure = null;
|
|
6607
6665
|
noteTokenAccepted();
|
|
6608
|
-
|
|
6666
|
+
const timeout = cfg?.callTimeoutMs ?? PROD_CALL_TIMEOUT_MS;
|
|
6667
|
+
return async (name, args) => {
|
|
6668
|
+
try {
|
|
6669
|
+
return await client.callTool({ name, arguments: swarmToolArgs(name, args, workspace) }, void 0, { timeout });
|
|
6670
|
+
} catch (e) {
|
|
6671
|
+
if (isProdTimeout(e)) {
|
|
6672
|
+
console.warn(`${prodTimeoutLine("Swarm MCP", name, timeout)} \u2014 \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435 \u0441\u0431\u0440\u043E\u0448\u0435\u043D\u043E, \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0432\u044B\u0437\u043E\u0432 \u043F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u0441\u044F`);
|
|
6673
|
+
}
|
|
6674
|
+
void client.close().catch(() => {
|
|
6675
|
+
});
|
|
6676
|
+
throw e;
|
|
6677
|
+
}
|
|
6678
|
+
};
|
|
6679
|
+
}
|
|
6680
|
+
function prodTimeoutLine(scope, tool, timeoutMs) {
|
|
6681
|
+
return `[${scope}] \u23F1 ${tool}: \u043F\u0440\u043E\u0434 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${Math.round(timeoutMs / 100) / 10}\u0441, \u0432\u044B\u0437\u043E\u0432 \u043F\u0440\u0435\u0440\u0432\u0430\u043D`;
|
|
6609
6682
|
}
|
|
6610
6683
|
function lastSwarmConnectFailure() {
|
|
6611
6684
|
return swarmConnectFailure;
|
|
6612
6685
|
}
|
|
6613
6686
|
function swarmToolArgs(name, args, workspace) {
|
|
6687
|
+
if ("workspaceId" in args && args.workspaceId == null) {
|
|
6688
|
+
const { workspaceId: _drop, ...rest } = args;
|
|
6689
|
+
return rest;
|
|
6690
|
+
}
|
|
6614
6691
|
if (!WS_SCOPED_SWARM_TOOLS.has(name) || !workspace) return args;
|
|
6615
6692
|
return { workspaceId: workspace, ...args };
|
|
6616
6693
|
}
|
|
@@ -6625,6 +6702,7 @@ var init_taskless = __esm({
|
|
|
6625
6702
|
init_oauth();
|
|
6626
6703
|
init_agentToken();
|
|
6627
6704
|
init_linkFailure();
|
|
6705
|
+
init_prodHttp();
|
|
6628
6706
|
init_toolFailure();
|
|
6629
6707
|
init_authState();
|
|
6630
6708
|
init_workspace();
|
|
@@ -10333,7 +10411,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
10333
10411
|
// 14.07/18.07, TASWM-201 — общий карантин; здесь — конкретный источник). Резолвим
|
|
10334
10412
|
// слаг→cuid через get_task (кэш — taskIdCache) ПЕРЕД тем как слать create.
|
|
10335
10413
|
taskIdCache = /* @__PURE__ */ new Map();
|
|
10336
|
-
// task_slug → cuid прода
|
|
10414
|
+
// task_slug → cuid прода и воркспейс задачи
|
|
10337
10415
|
// Create для этого runKey резолвился окончательно неудачно (задача не найдена/
|
|
10338
10416
|
// недоступна) — событие/вопросы того же прогона тоже никогда не получат prod-id,
|
|
10339
10417
|
// без карантина они бы бросали «нет prod-id» на каждом флаше и СНОВА вставали
|
|
@@ -10348,12 +10426,20 @@ var SwarmClient = class _SwarmClient {
|
|
|
10348
10426
|
const cacheKey = `${taskRef}\0${repoFullName ?? ""}\0${workspaceId ?? ""}`;
|
|
10349
10427
|
const cached = this.taskIdCache.get(cacheKey);
|
|
10350
10428
|
if (cached) return cached;
|
|
10429
|
+
const first = await this.readTaskRef(taskRef, repoFullName, workspaceId);
|
|
10430
|
+
const res = first === null && repoFullName && !workspaceId ? await this.readTaskRef(taskRef, repoFullName, null) : first;
|
|
10431
|
+
if (res && res !== "transient") this.taskIdCache.set(cacheKey, res);
|
|
10432
|
+
return res;
|
|
10433
|
+
}
|
|
10434
|
+
/** Один get_task: cuid задачи и её воркспейс; null — прод ответил «нет такой». */
|
|
10435
|
+
async readTaskRef(taskRef, repoFullName, workspaceId) {
|
|
10351
10436
|
let res;
|
|
10352
10437
|
try {
|
|
10353
10438
|
res = await this.call("get_task", {
|
|
10354
10439
|
id: taskRef,
|
|
10355
10440
|
...repoFullName ? { repoFullName } : {},
|
|
10356
|
-
|
|
10441
|
+
// null — «без пина демона» (см. swarmToolArgs), undefined — пин по умолчанию.
|
|
10442
|
+
...workspaceId ? { workspaceId } : workspaceId === null ? { workspaceId: null } : {}
|
|
10357
10443
|
});
|
|
10358
10444
|
} catch {
|
|
10359
10445
|
return "transient";
|
|
@@ -10363,10 +10449,9 @@ var SwarmClient = class _SwarmClient {
|
|
|
10363
10449
|
const text = (res.content ?? []).map((c) => c.text ?? "").join(" ");
|
|
10364
10450
|
return /not_found/i.test(text) ? null : "transient";
|
|
10365
10451
|
}
|
|
10366
|
-
const
|
|
10367
|
-
if (!id) return null;
|
|
10368
|
-
|
|
10369
|
-
return id;
|
|
10452
|
+
const task = res.structuredContent?.task;
|
|
10453
|
+
if (!task?.id) return null;
|
|
10454
|
+
return { id: task.id, workspaceId: typeof task.workspaceId === "string" ? task.workspaceId : null };
|
|
10370
10455
|
}
|
|
10371
10456
|
// MCP-31: привести repo прогона к owner/repo-форме для подсказки резолверу
|
|
10372
10457
|
// задачи. Прод сматчит подсказку с ProjectRepo.repoFullName → воркспейс. repo
|
|
@@ -10405,18 +10490,19 @@ var SwarmClient = class _SwarmClient {
|
|
|
10405
10490
|
this.poisonedRunKeys.add(e.runKey);
|
|
10406
10491
|
return "skip";
|
|
10407
10492
|
}
|
|
10408
|
-
const
|
|
10409
|
-
if (
|
|
10493
|
+
const task = await this.resolveTaskCuid(e.taskRef, this.repoFullNameFor(fields.repo), e.workspaceId);
|
|
10494
|
+
if (task === "transient") {
|
|
10410
10495
|
throw new Error(`slug\u2192cuid \u0434\u043B\u044F \xAB${e.taskRef}\xBB \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (\u043F\u0440\u043E\u0434 \u043C\u043E\u0440\u0433\u043D\u0443\u043B) \u2014 retry`);
|
|
10411
10496
|
}
|
|
10412
|
-
if (!
|
|
10497
|
+
if (!task) {
|
|
10413
10498
|
console.error(
|
|
10414
10499
|
`[swarm-client] create-\u0444\u043B\u0430\u0448 \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D: \u043F\u0440\u043E\u0434 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \xAB${e.taskRef}\xBB (runKey=${e.runKey}) \u2014 \u0437\u0430\u043F\u0438\u0441\u044C \u0432\u044B\u0431\u0440\u043E\u0448\u0435\u043D\u0430 \u0438\u0437 outbox`
|
|
10415
10500
|
);
|
|
10416
10501
|
this.poisonedRunKeys.add(e.runKey);
|
|
10417
10502
|
return "skip";
|
|
10418
10503
|
}
|
|
10419
|
-
|
|
10504
|
+
const workspaceId = e.workspaceId || task.workspaceId;
|
|
10505
|
+
return { tool: "swarm_run_upsert", args: { taskId: task.id, ...workspaceId ? { workspaceId } : {}, ...fields } };
|
|
10420
10506
|
}
|
|
10421
10507
|
if (this.poisonedRunKeys.has(e.runKey)) {
|
|
10422
10508
|
console.error(`[swarm-client] ${e.t}-\u0444\u043B\u0430\u0448 \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D: create \u043F\u0440\u043E\u0433\u043E\u043D\u0430 runKey=${e.runKey} \u043D\u0435 \u0440\u0435\u0437\u043E\u043B\u0432\u0438\u043B\u0441\u044F \u0440\u0430\u043D\u0435\u0435 \u2014 \u0437\u0430\u043F\u0438\u0441\u044C \u0432\u044B\u0431\u0440\u043E\u0448\u0435\u043D\u0430 \u0438\u0437 outbox`);
|
|
@@ -10468,6 +10554,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
10468
10554
|
} catch {
|
|
10469
10555
|
break;
|
|
10470
10556
|
}
|
|
10557
|
+
if (res?.isError && res.unavailable) break;
|
|
10471
10558
|
if (res?.isError) {
|
|
10472
10559
|
const reason = (res.content ?? []).map((c) => c.text).filter(Boolean).join(" ").trim() || "\u0431\u0435\u0437 \u043E\u0431\u044A\u044F\u0441\u043D\u0435\u043D\u0438\u044F";
|
|
10473
10560
|
const tries = (this.rejected.get(e.seq) ?? 0) + 1;
|
|
@@ -10590,8 +10677,39 @@ function tickPulse(health, now, intervalMs) {
|
|
|
10590
10677
|
return { seq: health.seq, ageMs: tickAgeMs(health, now), intervalMs };
|
|
10591
10678
|
}
|
|
10592
10679
|
|
|
10680
|
+
// src/tickSteps.ts
|
|
10681
|
+
var SLOW_PASS_MS = 3e4;
|
|
10682
|
+
function makeStepClock(now = Date.now) {
|
|
10683
|
+
const startedAt = now();
|
|
10684
|
+
const spent = /* @__PURE__ */ new Map();
|
|
10685
|
+
let current = "\u043D\u0430\u0447\u0430\u043B\u043E \u043F\u0440\u043E\u0445\u043E\u0434\u0430";
|
|
10686
|
+
let since = startedAt;
|
|
10687
|
+
const close = () => {
|
|
10688
|
+
const t = now();
|
|
10689
|
+
spent.set(current, (spent.get(current) ?? 0) + (t - since));
|
|
10690
|
+
since = t;
|
|
10691
|
+
};
|
|
10692
|
+
return {
|
|
10693
|
+
mark(step) {
|
|
10694
|
+
close();
|
|
10695
|
+
current = step;
|
|
10696
|
+
},
|
|
10697
|
+
finish() {
|
|
10698
|
+
close();
|
|
10699
|
+
const steps = [...spent].map(([step, ms]) => ({ step, ms })).sort((a, b) => b.ms - a.ms);
|
|
10700
|
+
return { totalMs: since - startedAt, steps };
|
|
10701
|
+
}
|
|
10702
|
+
};
|
|
10703
|
+
}
|
|
10704
|
+
function slowPassLine(seq, done, thresholdMs = SLOW_PASS_MS) {
|
|
10705
|
+
if (done.totalMs <= thresholdMs) return null;
|
|
10706
|
+
const sec = (ms) => `${Math.round(ms / 100) / 10}\u0441`;
|
|
10707
|
+
const top = done.steps.slice(0, 3).map((s) => `${s.step} ${sec(s.ms)}`).join(", ");
|
|
10708
|
+
return `[overmind] \u23F1 \u043F\u0440\u043E\u0445\u043E\u0434 \u0442\u0438\u043A\u0430 #${seq} \u0434\u043B\u0438\u043B\u0441\u044F ${sec(done.totalMs)} (\u043F\u043E\u0440\u043E\u0433 ${sec(thresholdMs)}); \u0434\u043E\u043B\u044C\u0448\u0435 \u0432\u0441\u0435\u0433\u043E: ${top}`;
|
|
10709
|
+
}
|
|
10710
|
+
|
|
10593
10711
|
// src/logRotate.ts
|
|
10594
|
-
import { copyFileSync as copyFileSync4, renameSync as renameSync5, statSync as statSync7, fstatSync, truncateSync, unlinkSync as unlinkSync3, existsSync as existsSync9 } from "fs";
|
|
10712
|
+
import { copyFileSync as copyFileSync4, renameSync as renameSync5, statSync as statSync7, fstatSync, truncateSync, unlinkSync as unlinkSync3, existsSync as existsSync9, openSync as openSync2, writeSync, closeSync as closeSync2 } from "fs";
|
|
10595
10713
|
var realIo = {
|
|
10596
10714
|
size: (path) => {
|
|
10597
10715
|
try {
|
|
@@ -10625,6 +10743,7 @@ var realOwnerIo = {
|
|
|
10625
10743
|
}
|
|
10626
10744
|
};
|
|
10627
10745
|
function stdoutGoesTo(path, io = realOwnerIo) {
|
|
10746
|
+
if (teedTo === path) return true;
|
|
10628
10747
|
const out = io.statFd(1);
|
|
10629
10748
|
if (!out || !out.isFile) return false;
|
|
10630
10749
|
const file = io.statPath(path);
|
|
@@ -10649,13 +10768,14 @@ function rotateDaemonLog(path, opts, io = realIo) {
|
|
|
10649
10768
|
io.truncate(path);
|
|
10650
10769
|
return "rotated";
|
|
10651
10770
|
}
|
|
10771
|
+
var teedTo = null;
|
|
10652
10772
|
|
|
10653
10773
|
// src/daemonLaunch.ts
|
|
10654
|
-
import { openSync as
|
|
10774
|
+
import { openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync9 } from "fs";
|
|
10655
10775
|
import { spawn } from "child_process";
|
|
10656
10776
|
var realRespawnIo = {
|
|
10657
10777
|
isTty: () => process.stdout.isTTY === true,
|
|
10658
|
-
open: (path) =>
|
|
10778
|
+
open: (path) => openSync3(path, "a")
|
|
10659
10779
|
};
|
|
10660
10780
|
function respawnStdio(logPath, io = realRespawnIo) {
|
|
10661
10781
|
if (io.isTty()) return "inherit";
|
|
@@ -11457,7 +11577,7 @@ function buildReliefCostCandidates(cost, t, sustainMs, build) {
|
|
|
11457
11577
|
|
|
11458
11578
|
// src/launcher.ts
|
|
11459
11579
|
import { spawn as spawn2 } from "child_process";
|
|
11460
|
-
import { closeSync as
|
|
11580
|
+
import { closeSync as closeSync4, existsSync as existsSync12, mkdirSync as mkdirSync9, openSync as openSync4, readdirSync as readdirSync10 } from "fs";
|
|
11461
11581
|
import { createRequire } from "module";
|
|
11462
11582
|
import { homedir as homedir6 } from "os";
|
|
11463
11583
|
import { dirname as dirname3, join as join18 } from "path";
|
|
@@ -11666,14 +11786,14 @@ function openNextRunLog(runLogsDir, runLogKeys) {
|
|
|
11666
11786
|
const path = nextRunLogPath(runLogsDir, runLogKeys);
|
|
11667
11787
|
let out;
|
|
11668
11788
|
try {
|
|
11669
|
-
out =
|
|
11789
|
+
out = openSync4(path, "wx");
|
|
11670
11790
|
} catch (e) {
|
|
11671
11791
|
if (e.code !== "EEXIST") throw e;
|
|
11672
11792
|
continue;
|
|
11673
11793
|
}
|
|
11674
11794
|
let err = null;
|
|
11675
11795
|
try {
|
|
11676
|
-
err =
|
|
11796
|
+
err = openSync4(`${path.slice(0, -".json".length)}.err.log`, "w");
|
|
11677
11797
|
} catch {
|
|
11678
11798
|
err = null;
|
|
11679
11799
|
}
|
|
@@ -11803,8 +11923,8 @@ function makeLauncher(cfg) {
|
|
|
11803
11923
|
// они про его собственное состояние, и перебивать их ройным нечем.
|
|
11804
11924
|
env: { ...droneEnv(worktree, process.env, home ? { droneHome: home, realHome } : void 0, cfg.droneCap?.() ?? null), ...plan.env }
|
|
11805
11925
|
});
|
|
11806
|
-
if (outFd !== void 0)
|
|
11807
|
-
if (errFd !== void 0)
|
|
11926
|
+
if (outFd !== void 0) closeSync4(outFd);
|
|
11927
|
+
if (errFd !== void 0) closeSync4(errFd);
|
|
11808
11928
|
child.unref();
|
|
11809
11929
|
if (child.pid == null) throw new Error("\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430 (\u043D\u0435\u0442 pid)");
|
|
11810
11930
|
return child.pid;
|
|
@@ -12085,7 +12205,7 @@ import { execa } from "execa";
|
|
|
12085
12205
|
|
|
12086
12206
|
// src/prepareLock.ts
|
|
12087
12207
|
import { createHash } from "crypto";
|
|
12088
|
-
import { closeSync as
|
|
12208
|
+
import { closeSync as closeSync5, openSync as openSync5, rmSync as rmSync2, statSync as statSync12, writeSync as writeSync2 } from "fs";
|
|
12089
12209
|
import { join as join23 } from "path";
|
|
12090
12210
|
import { tmpdir } from "os";
|
|
12091
12211
|
var WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
@@ -12096,11 +12216,11 @@ function machineLockPath(key, dir = tmpdir()) {
|
|
|
12096
12216
|
}
|
|
12097
12217
|
function defaultTryCreate(path, content) {
|
|
12098
12218
|
try {
|
|
12099
|
-
const fd =
|
|
12219
|
+
const fd = openSync5(path, "wx");
|
|
12100
12220
|
try {
|
|
12101
|
-
|
|
12221
|
+
writeSync2(fd, content);
|
|
12102
12222
|
} finally {
|
|
12103
|
-
|
|
12223
|
+
closeSync5(fd);
|
|
12104
12224
|
}
|
|
12105
12225
|
return true;
|
|
12106
12226
|
} catch {
|
|
@@ -12839,6 +12959,18 @@ var FileOverlapError = class extends SpawnDeferredError {
|
|
|
12839
12959
|
}
|
|
12840
12960
|
blockedByRunId;
|
|
12841
12961
|
};
|
|
12962
|
+
var SpawnAbandonedError = class extends Error {
|
|
12963
|
+
constructor(status) {
|
|
12964
|
+
super(`\u043F\u0440\u043E\u0433\u043E\u043D \u0441\u043D\u044F\u0442 (${status}), \u043F\u043E\u043A\u0430 \u0433\u043E\u0442\u043E\u0432\u0438\u043B\u0430\u0441\u044C \u0440\u0430\u0431\u043E\u0447\u0430\u044F \u043A\u043E\u043F\u0438\u044F \u2014 \u0434\u0440\u043E\u043D\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E`);
|
|
12965
|
+
this.status = status;
|
|
12966
|
+
this.name = "SpawnAbandonedError";
|
|
12967
|
+
}
|
|
12968
|
+
status;
|
|
12969
|
+
};
|
|
12970
|
+
function assertStillWanted(db, runId) {
|
|
12971
|
+
const fresh = db.getRun(runId);
|
|
12972
|
+
if (fresh && isTerminalStatus(fresh.status)) throw new SpawnAbandonedError(fresh.status);
|
|
12973
|
+
}
|
|
12842
12974
|
function healTaskSlug(db, run2, task) {
|
|
12843
12975
|
const slug = task?.slug;
|
|
12844
12976
|
if (!slug || slug === run2.task_slug || !isTaskSlug(slug) || isTaskSlug(run2.task_slug)) return run2;
|
|
@@ -12856,6 +12988,7 @@ async function spawnDrone(deps, run2) {
|
|
|
12856
12988
|
if (run2.area && deps.db.runningAreas("").includes(run2.area)) throw new AreaBusyError(run2.area);
|
|
12857
12989
|
deps.db.addEvent(run2.id, "step", `repoless-\u043F\u0440\u043E\u0433\u043E\u043D: \u0441\u043A\u0440\u0430\u0442\u0447-\u043A\u0430\u0442\u0430\u043B\u043E\u0433 ${workdir} (\u0431\u0435\u0437 worktree/\u0432\u0435\u0442\u043A\u0438/PR \u2014 TASWM-595)`);
|
|
12858
12990
|
const built2 = await deps.buildPrompt(run2, workdir);
|
|
12991
|
+
assertStillWanted(deps.db, run2.id);
|
|
12859
12992
|
const pid2 = deps.launch({ worktree: workdir, run: run2, prompt: built2 });
|
|
12860
12993
|
deps.db.addEvent(run2.id, "step", `\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D \u0432 ${workdir} (pid ${pid2})`);
|
|
12861
12994
|
return deps.db.updateRun(run2.id, {
|
|
@@ -12891,6 +13024,7 @@ async function spawnDrone(deps, run2) {
|
|
|
12891
13024
|
const prompt = notice ? `${built}
|
|
12892
13025
|
|
|
12893
13026
|
${notice}` : built;
|
|
13027
|
+
assertStillWanted(deps.db, run2.id);
|
|
12894
13028
|
const pid = deps.launch({ worktree, run: run2, prompt });
|
|
12895
13029
|
deps.db.addEvent(run2.id, "step", `\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D \u0432 ${worktree} (pid ${pid})`);
|
|
12896
13030
|
return deps.db.updateRun(run2.id, {
|
|
@@ -13067,7 +13201,7 @@ function parkReasonUnretriable(reason) {
|
|
|
13067
13201
|
// package.json
|
|
13068
13202
|
var package_default = {
|
|
13069
13203
|
name: "@taskless-app/swarm",
|
|
13070
|
-
version: "0.6.
|
|
13204
|
+
version: "0.6.68",
|
|
13071
13205
|
type: "module",
|
|
13072
13206
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
13073
13207
|
keywords: [
|
|
@@ -13116,9 +13250,10 @@ var package_default = {
|
|
|
13116
13250
|
"@hono/node-server": "^2.0.12",
|
|
13117
13251
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
13118
13252
|
execa: "^10.0.0",
|
|
13253
|
+
undici: "^7.25.0",
|
|
13119
13254
|
zod: "3.25.76"
|
|
13120
13255
|
},
|
|
13121
|
-
gitHead: "
|
|
13256
|
+
gitHead: "4bfd78926aa37ef7de8ea06a6763d7e84a2fded6"
|
|
13122
13257
|
};
|
|
13123
13258
|
|
|
13124
13259
|
// src/version.ts
|
|
@@ -14127,12 +14262,36 @@ init_src();
|
|
|
14127
14262
|
|
|
14128
14263
|
// src/reviewCycle.ts
|
|
14129
14264
|
init_src();
|
|
14265
|
+
var REVIEW_CYCLE_LIMIT_DEFAULT = 3;
|
|
14266
|
+
var REVIEW_CYCLE_LIMIT_CAP = 20;
|
|
14267
|
+
function validReviewCycleLimit(n) {
|
|
14268
|
+
return n != null && Number.isInteger(n) && n >= 1 && n <= REVIEW_CYCLE_LIMIT_CAP;
|
|
14269
|
+
}
|
|
14270
|
+
function parseReviewCycleLimitEnv(raw) {
|
|
14271
|
+
if (raw == null || raw.trim() === "") return REVIEW_CYCLE_LIMIT_DEFAULT;
|
|
14272
|
+
const n = Number(raw);
|
|
14273
|
+
return validReviewCycleLimit(n) ? n : REVIEW_CYCLE_LIMIT_DEFAULT;
|
|
14274
|
+
}
|
|
14275
|
+
var REVIEW_CYCLE_LIMIT = parseReviewCycleLimitEnv(process.env.SWARM_REVIEW_CYCLE_LIMIT);
|
|
14276
|
+
var configuredReviewCycleLimit = null;
|
|
14277
|
+
function configureReviewCycleLimit(n) {
|
|
14278
|
+
configuredReviewCycleLimit = validReviewCycleLimit(n) ? n : null;
|
|
14279
|
+
}
|
|
14280
|
+
function effectiveReviewCycleLimit() {
|
|
14281
|
+
return configuredReviewCycleLimit ?? REVIEW_CYCLE_LIMIT;
|
|
14282
|
+
}
|
|
14283
|
+
function reviewCyclesExhausted(run2) {
|
|
14284
|
+
return (run2.review_cycle ?? 0) >= effectiveReviewCycleLimit();
|
|
14285
|
+
}
|
|
14130
14286
|
function needsFixIteration(run2) {
|
|
14131
14287
|
return run2.review_verdict === "changes" || isNegativeVerifyVerdict(toVerifyVerdict(run2.verify_verdict));
|
|
14132
14288
|
}
|
|
14133
14289
|
function reviewTransitionPatch(run2, prUrl) {
|
|
14134
14290
|
const isFixIteration = needsFixIteration(run2);
|
|
14135
14291
|
if (isFixIteration) {
|
|
14292
|
+
if (reviewCyclesExhausted(run2)) {
|
|
14293
|
+
return { isFixIteration: false, cycle: null, patch: { status: "review", pr_url: prUrl } };
|
|
14294
|
+
}
|
|
14136
14295
|
const cycle = (run2.review_cycle ?? 0) + 1;
|
|
14137
14296
|
return {
|
|
14138
14297
|
isFixIteration: true,
|
|
@@ -15767,11 +15926,17 @@ init_procList();
|
|
|
15767
15926
|
// src/mcpResilient.ts
|
|
15768
15927
|
init_linkFailure();
|
|
15769
15928
|
init_taskless();
|
|
15929
|
+
init_prodHttp();
|
|
15770
15930
|
var DEFAULT_BACKOFF_MS = [5e3, 15e3, 3e4, 6e4];
|
|
15771
15931
|
var DEFAULT_DOWN_AFTER_MS = 5 * 6e4;
|
|
15772
15932
|
var DEFAULT_RETRY_DELAYS_MS = [200, 800];
|
|
15773
|
-
|
|
15774
|
-
|
|
15933
|
+
var PROD_PASS_BUDGET_MS = 2e4;
|
|
15934
|
+
function errorResult(reason, unavailable) {
|
|
15935
|
+
return {
|
|
15936
|
+
isError: true,
|
|
15937
|
+
...unavailable ? { unavailable: true } : {},
|
|
15938
|
+
content: [{ type: "text", text: `Swarm MCP \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${reason}` }]
|
|
15939
|
+
};
|
|
15775
15940
|
}
|
|
15776
15941
|
function resilientSwarmMcp(opts = {}) {
|
|
15777
15942
|
const connect = opts.connect ?? (() => connectSwarmMcp());
|
|
@@ -15779,6 +15944,13 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
15779
15944
|
const downAfterMs = opts.downAfterMs ?? DEFAULT_DOWN_AFTER_MS;
|
|
15780
15945
|
const retryDelays = opts.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
|
|
15781
15946
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15947
|
+
const clock = opts.now ?? Date.now;
|
|
15948
|
+
const passBudgetMs = opts.passBudgetMs ?? PROD_PASS_BUDGET_MS;
|
|
15949
|
+
let passSpentMs = null;
|
|
15950
|
+
let budgetNoted = false;
|
|
15951
|
+
const spend = (since) => {
|
|
15952
|
+
if (passSpentMs !== null) passSpentMs += Math.max(0, clock() - since);
|
|
15953
|
+
};
|
|
15782
15954
|
let caller = null;
|
|
15783
15955
|
let lastTryAt = 0;
|
|
15784
15956
|
let failures = 0;
|
|
@@ -15801,6 +15973,7 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
15801
15973
|
if (!force && lastTryAt !== 0 && now - lastTryAt < wait) return null;
|
|
15802
15974
|
if (!force) lastTryAt = now;
|
|
15803
15975
|
let fresh = null;
|
|
15976
|
+
const connectStart = clock();
|
|
15804
15977
|
try {
|
|
15805
15978
|
fresh = await connect();
|
|
15806
15979
|
if (!fresh) lastConnectReason = lastSwarmConnectFailure() ?? UNKNOWN_LINK_REASON;
|
|
@@ -15817,17 +15990,30 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
15817
15990
|
downReported = false;
|
|
15818
15991
|
return caller;
|
|
15819
15992
|
}
|
|
15993
|
+
spend(connectStart);
|
|
15820
15994
|
if (!force) failures += 1;
|
|
15821
15995
|
if (downSince === null) downSince = now;
|
|
15822
15996
|
return null;
|
|
15823
15997
|
}
|
|
15824
15998
|
const call = async (name, args) => {
|
|
15825
15999
|
for (let attempt = 0; ; attempt++) {
|
|
16000
|
+
if (passSpentMs !== null && passSpentMs >= passBudgetMs) {
|
|
16001
|
+
if (!budgetNoted) {
|
|
16002
|
+
budgetNoted = true;
|
|
16003
|
+
opts.onBudgetSpent?.({ spentMs: passSpentMs, budgetMs: passBudgetMs });
|
|
16004
|
+
}
|
|
16005
|
+
maybeReportDown();
|
|
16006
|
+
return errorResult(
|
|
16007
|
+
`\u043F\u0440\u043E\u0434 \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u0431\u044E\u0434\u0436\u0435\u0442 \u043F\u0440\u043E\u0445\u043E\u0434\u0430 \u043D\u0430 \u0441\u0435\u0442\u044C (${Math.round(passBudgetMs / 1e3)}\u0441) \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0434\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043F\u0440\u043E\u0445\u043E\u0434\u0430 \u0431\u0435\u0437 \u0441\u0435\u0442\u0438${lastCallReason ?? lastConnectReason ? ` (\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u043F\u0440\u0438\u0447\u0438\u043D\u0430: ${lastCallReason ?? lastConnectReason})` : ""}`,
|
|
16008
|
+
true
|
|
16009
|
+
);
|
|
16010
|
+
}
|
|
15826
16011
|
const active = await ensureCaller(attempt > 0);
|
|
15827
16012
|
if (!active) {
|
|
15828
16013
|
maybeReportDown();
|
|
15829
|
-
return errorResult(lastConnectReason ?? UNKNOWN_LINK_REASON);
|
|
16014
|
+
return errorResult(lastConnectReason ?? UNKNOWN_LINK_REASON, true);
|
|
15830
16015
|
}
|
|
16016
|
+
const callStart = clock();
|
|
15831
16017
|
try {
|
|
15832
16018
|
const res = await active(name, args);
|
|
15833
16019
|
lastOkAt = Date.now();
|
|
@@ -15835,13 +16021,14 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
15835
16021
|
return res;
|
|
15836
16022
|
} catch (e) {
|
|
15837
16023
|
caller = null;
|
|
16024
|
+
spend(callStart);
|
|
15838
16025
|
if (downSince === null) downSince = Date.now();
|
|
15839
16026
|
const { kind, detail: reason } = explainLinkFailure(e);
|
|
15840
16027
|
lastCallReason = reason;
|
|
15841
16028
|
const delay = retryDelays[attempt];
|
|
15842
|
-
if (delay === void 0 || kind !== "offline") {
|
|
16029
|
+
if (delay === void 0 || kind !== "offline" || isProdTimeout(e)) {
|
|
15843
16030
|
maybeReportDown();
|
|
15844
|
-
return errorResult(reason);
|
|
16031
|
+
return errorResult(reason, kind === "offline" || kind === "unknown" || kind === "unauthorized" || isProdTimeout(e));
|
|
15845
16032
|
}
|
|
15846
16033
|
opts.onRetry?.({ tool: name, attempt: attempt + 1, reason });
|
|
15847
16034
|
await sleep2(delay);
|
|
@@ -15849,6 +16036,10 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
15849
16036
|
}
|
|
15850
16037
|
};
|
|
15851
16038
|
return Object.assign(call, {
|
|
16039
|
+
beginPass: () => {
|
|
16040
|
+
passSpentMs = 0;
|
|
16041
|
+
budgetNoted = false;
|
|
16042
|
+
},
|
|
15852
16043
|
// Причина — ТА ЖЕ, что уехала в лог: сначала свежий отказ вызова, иначе отказ
|
|
15853
16044
|
// подключения. Своего разбора здесь нет намеренно (см. lastCallReason выше).
|
|
15854
16045
|
link: () => ({ connected: caller !== null, downSince, lastOkAt, lastError: caller !== null ? null : lastCallReason ?? lastConnectReason })
|
|
@@ -19133,14 +19324,86 @@ var BASE_MERGED_MARK = "\u0431\u0430\u0437\u0430 \u0432\u043B\u0438\u0442\u0430
|
|
|
19133
19324
|
var BASE_NOOP_MARK = "\u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E";
|
|
19134
19325
|
var RESOLVER_RESOLVED_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D \u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440\u043E\u043C";
|
|
19135
19326
|
var REVIEW_SPAWN_LIMIT = Number(process.env.SWARM_REVIEW_SPAWN_LIMIT) || 3;
|
|
19136
|
-
|
|
19137
|
-
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
19327
|
+
function reviewCycleHumanBody(cycle, limit) {
|
|
19328
|
+
return `\u041B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D (${cycle} \u0438\u0437 ${limit}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A. \u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0440\u0435\u0432\u044C\u044E, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0438 \u043F\u043E\u0431\u0443\u0434\u043A\u0438 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u044B \u2014 PR \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443.`;
|
|
19329
|
+
}
|
|
19330
|
+
var CYCLE_TASK_COMMENT_SENT = "\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043B\u0438\u043C\u0438\u0442\u0430 \u043A\u0440\u0443\u0433\u043E\u0432 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D";
|
|
19331
|
+
var CYCLE_PR_COMMENT_SENT = "\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043B\u0438\u043C\u0438\u0442\u0430 \u043A\u0440\u0443\u0433\u043E\u0432 \u0432 PR \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D";
|
|
19332
|
+
var cycleCommentInflight = /* @__PURE__ */ new Map();
|
|
19333
|
+
function takeCycleCommentSlot(runId, side) {
|
|
19334
|
+
let slot = cycleCommentInflight.get(runId);
|
|
19335
|
+
if (!slot) {
|
|
19336
|
+
slot = { task: false, pr: false };
|
|
19337
|
+
cycleCommentInflight.set(runId, slot);
|
|
19338
|
+
}
|
|
19339
|
+
if (slot[side]) return false;
|
|
19340
|
+
slot[side] = true;
|
|
19341
|
+
return true;
|
|
19141
19342
|
}
|
|
19142
|
-
function
|
|
19143
|
-
|
|
19343
|
+
function freeCycleCommentSlot(runId, side) {
|
|
19344
|
+
const slot = cycleCommentInflight.get(runId);
|
|
19345
|
+
if (slot) slot[side] = false;
|
|
19346
|
+
}
|
|
19347
|
+
async function noteReviewCyclesExhausted(deps, run2) {
|
|
19348
|
+
const texts = deps.db.events(run2.id).map((e) => e.text ?? "");
|
|
19349
|
+
const marked = texts.some((t) => t.includes(REVIEW_CYCLE_ESCALATION_MARK));
|
|
19350
|
+
const taskSent = texts.some((t) => t.includes(CYCLE_TASK_COMMENT_SENT));
|
|
19351
|
+
const prSent = texts.some((t) => t.includes(CYCLE_PR_COMMENT_SENT));
|
|
19352
|
+
const cycle = run2.review_cycle ?? 0;
|
|
19353
|
+
const body = reviewCycleHumanBody(cycle, effectiveReviewCycleLimit());
|
|
19354
|
+
if (!marked) {
|
|
19355
|
+
deps.db.addEvent(run2.id, "step", `${REVIEW_CYCLE_ESCALATION_MARK}: ${body}`);
|
|
19356
|
+
try {
|
|
19357
|
+
deps.narrate?.(body);
|
|
19358
|
+
} catch {
|
|
19359
|
+
}
|
|
19360
|
+
}
|
|
19361
|
+
if (deps.reportComment && !taskSent && takeCycleCommentSlot(run2.id, "task")) {
|
|
19362
|
+
const ok = await deps.reportComment(
|
|
19363
|
+
run2.task_slug,
|
|
19364
|
+
body,
|
|
19365
|
+
{ swarmKind: "report", swarmDroneName: run2.name || "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)", swarmReviewCycle: cycle },
|
|
19366
|
+
run2.workspace_id
|
|
19367
|
+
).then(() => true).catch((e) => {
|
|
19368
|
+
deps.db.addEvent(run2.id, "step", `\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \xAB\u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A\xBB \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0443\u0448\u0451\u043B: ${e instanceof Error ? e.message : String(e)}`);
|
|
19369
|
+
return false;
|
|
19370
|
+
});
|
|
19371
|
+
if (ok) deps.db.addEvent(run2.id, "step", CYCLE_TASK_COMMENT_SENT);
|
|
19372
|
+
freeCycleCommentSlot(run2.id, "task");
|
|
19373
|
+
}
|
|
19374
|
+
if (run2.pr_url && deps.prComment && !prSent && takeCycleCommentSlot(run2.id, "pr")) {
|
|
19375
|
+
const ok = await deps.prComment(run2.pr_url, body).then(() => true).catch((e) => {
|
|
19376
|
+
deps.db.addEvent(run2.id, "step", `\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \xAB\u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A\xBB \u0432 PR \u043D\u0435 \u0443\u0448\u0451\u043B: ${e instanceof Error ? e.message : String(e)}`);
|
|
19377
|
+
return false;
|
|
19378
|
+
});
|
|
19379
|
+
if (ok) deps.db.addEvent(run2.id, "step", CYCLE_PR_COMMENT_SENT);
|
|
19380
|
+
freeCycleCommentSlot(run2.id, "pr");
|
|
19381
|
+
}
|
|
19382
|
+
}
|
|
19383
|
+
function cycleHolder(db, run2) {
|
|
19384
|
+
if (run2.kind === "hero" || run2.resolves_run_id != null) return null;
|
|
19385
|
+
const parentId = run2.reviews_run_id ?? run2.verifies_run_id;
|
|
19386
|
+
if (parentId != null) return db.getRun(parentId) ?? null;
|
|
19387
|
+
return run2;
|
|
19388
|
+
}
|
|
19389
|
+
function cycleCapHolder(db, run2) {
|
|
19390
|
+
const holder = cycleHolder(db, run2);
|
|
19391
|
+
return holder && reviewCyclesExhausted(holder) ? holder : null;
|
|
19392
|
+
}
|
|
19393
|
+
var CYCLE_CAP_STOP_REASON = "\u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A";
|
|
19394
|
+
async function handOffAtCycleCap(deps, run2, holder, where) {
|
|
19395
|
+
await noteReviewCyclesExhausted(deps, holder);
|
|
19396
|
+
const text = `${CYCLE_CAP_STOP_REASON} (${holder.review_cycle ?? 0} \u0438\u0437 ${effectiveReviewCycleLimit()}): ${where} \u2014 \u0441\u0435\u0441\u0441\u0438\u044E \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E`;
|
|
19397
|
+
if (holder.id !== run2.id) {
|
|
19398
|
+
if (run2.worktree_path) await deps.removeWorktree(run2.repo, run2.worktree_path).catch(() => {
|
|
19399
|
+
});
|
|
19400
|
+
deps.db.updateRun(run2.id, { status: "stopped", stop_reason: CYCLE_CAP_STOP_REASON });
|
|
19401
|
+
deps.db.addEvent(run2.id, "stopped", text);
|
|
19402
|
+
return;
|
|
19403
|
+
}
|
|
19404
|
+
if (run2.pr_url) deps.db.updateRun(run2.id, { status: "review", stop_reason: null });
|
|
19405
|
+
else deps.db.updateRun(run2.id, { status: "paused", stop_reason: CYCLE_CAP_STOP_REASON });
|
|
19406
|
+
deps.db.addEvent(run2.id, "step", text);
|
|
19144
19407
|
}
|
|
19145
19408
|
var CI_WAKE_LIMIT = Number(process.env.SWARM_CI_WAKE_LIMIT) || 3;
|
|
19146
19409
|
var CI_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CI_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
@@ -19168,7 +19431,7 @@ var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430
|
|
|
19168
19431
|
var CHANGES_FIX_COST_MARK = "\u0442\u0440\u0430\u0442\u044B \u0434\u043E \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438";
|
|
19169
19432
|
var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
19170
19433
|
var BUDGET_STOP_REASON = "\u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442: \u0441\u0435\u0441\u0441\u0438\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B \u0431\u044E\u0434\u0436\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430";
|
|
19171
|
-
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON, ABORTED_STOP_REASON, AWAITING_BUDGET_REASON];
|
|
19434
|
+
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON, ABORTED_STOP_REASON, AWAITING_BUDGET_REASON, CYCLE_CAP_STOP_REASON];
|
|
19172
19435
|
var REVIEW_NO_VERDICT_REASON = "\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u043E\u0441\u044C \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430: \u043D\u0438 approve, \u043D\u0438 changes \u2014 \u0437\u0430\u0441\u0447\u0438\u0442\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E, \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 \u0437\u0430\u0445\u043E\u0434 (TASWM-375)";
|
|
19173
19436
|
function verdictCommentMark(createdAt) {
|
|
19174
19437
|
return `\u043A\u043E\u043C\u043C\u0435\u043D\u0442-\u0432\u0435\u0440\u0434\u0438\u043A\u0442@${createdAt}`;
|
|
@@ -19331,7 +19594,9 @@ function reviewPhaseInputFor(deps, run2, projectWantsVerify = true) {
|
|
|
19331
19594
|
reviewerDead,
|
|
19332
19595
|
verifyVerdict: run2.verify_verdict,
|
|
19333
19596
|
verifyExpected,
|
|
19334
|
-
verifierDead
|
|
19597
|
+
verifierDead,
|
|
19598
|
+
// TASWM-606: потолок кругов важнее «чинит CI» и «ждёт ревьюера» — человека уже позвали.
|
|
19599
|
+
cyclesExhausted: reviewCyclesExhausted(run2)
|
|
19335
19600
|
};
|
|
19336
19601
|
}
|
|
19337
19602
|
async function refreshReviewPhases(deps) {
|
|
@@ -19365,8 +19630,19 @@ function countInFlight(db) {
|
|
|
19365
19630
|
return db.listRuns().filter((r) => r.kind !== "hero" && IN_FLIGHT_STATUSES.has(r.status)).length;
|
|
19366
19631
|
}
|
|
19367
19632
|
function countActiveDrones(db) {
|
|
19368
|
-
|
|
19633
|
+
const preparing = preparingRuns(db);
|
|
19634
|
+
return db.listRuns().filter((r) => r.kind !== "hero" && (ACTIVE_DRONE_STATUSES.has(r.status) || preparing.has(r.id))).length;
|
|
19635
|
+
}
|
|
19636
|
+
var preparingByStore = /* @__PURE__ */ new WeakMap();
|
|
19637
|
+
function preparingRuns(db) {
|
|
19638
|
+
let map = preparingByStore.get(db);
|
|
19639
|
+
if (!map) preparingByStore.set(db, map = /* @__PURE__ */ new Map());
|
|
19640
|
+
return map;
|
|
19369
19641
|
}
|
|
19642
|
+
function hasPreparingRuns(db) {
|
|
19643
|
+
return preparingRuns(db).size > 0;
|
|
19644
|
+
}
|
|
19645
|
+
var SPAWN_INLINE_WAIT_MS = 2e3;
|
|
19370
19646
|
function countActiveReviewers(db) {
|
|
19371
19647
|
return db.listRuns().filter((r) => r.kind === "reviewer" && ACTIVE_DRONE_STATUSES.has(r.status)).length;
|
|
19372
19648
|
}
|
|
@@ -19715,6 +19991,10 @@ async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
|
19715
19991
|
if (run2.kind !== "drone") continue;
|
|
19716
19992
|
if (run2.reviews_run_id != null || run2.resolves_run_id != null || run2.verifies_run_id != null) continue;
|
|
19717
19993
|
if (!run2.pr_url || !isGithubPrUrl(run2.pr_url)) continue;
|
|
19994
|
+
if (reviewCyclesExhausted(run2)) {
|
|
19995
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
19996
|
+
continue;
|
|
19997
|
+
}
|
|
19718
19998
|
if (run2.verify_verdict != null) continue;
|
|
19719
19999
|
if (deps.db.verifierCountFor(run2.id) > 0) continue;
|
|
19720
20000
|
const attempts = deps.db.verifierAttemptCountFor(run2.id);
|
|
@@ -19740,6 +20020,18 @@ async function spawnVerifiersForOpenPrs(deps, stormActive) {
|
|
|
19740
20020
|
}
|
|
19741
20021
|
}
|
|
19742
20022
|
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.";
|
|
20023
|
+
function buildSubmittedContinueNudge(opts) {
|
|
20024
|
+
const verify = opts.verifySummary ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E:
|
|
20025
|
+
${opts.verifySummary}` : "\u0412\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E \u0432 \u043B\u0435\u043D\u0442\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u2014 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0435\u0433\u043E \u0441\u0430\u043C: `gh pr view --comments`.";
|
|
20026
|
+
const review = opts.reviewSummary ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430:
|
|
20027
|
+
${opts.reviewSummary}` : "";
|
|
20028
|
+
return [
|
|
20029
|
+
`\u0422\u044B \u0443\u0436\u0435 \u0441\u0434\u0430\u043B \u0440\u0430\u0431\u043E\u0442\u0443: PR ${opts.prUrl} \u043E\u0442\u043A\u0440\u044B\u0442. \u041D\u0415 \u0432\u044B\u0437\u044B\u0432\u0430\u0439 done() \u043D\u0430 \u0442\u043E\u043C \u0436\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u2014 \u044D\u0442\u043E \u0441\u043E\u0436\u0436\u0451\u0442 \u0435\u0449\u0451 \u043E\u0434\u0438\u043D \u043A\u0440\u0443\u0433 \u043F\u0440\u0430\u0432\u043E\u043A \u0432\u043F\u0443\u0441\u0442\u0443\u044E.`,
|
|
20030
|
+
verify,
|
|
20031
|
+
review,
|
|
20032
|
+
"\u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442, \u043F\u043E\u0447\u0438\u043D\u0438 \u043F\u0440\u0438\u0447\u0438\u043D\u0443, \u0437\u0430\u043A\u043E\u043C\u043C\u0438\u0442\u044C \u041D\u041E\u0412\u0423\u042E \u0440\u0430\u0431\u043E\u0442\u0443 \u0438 \u0437\u0430\u043F\u0443\u0448\u044C \u0432 \u0442\u0443 \u0436\u0435 \u0432\u0435\u0442\u043A\u0443. \u0422\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u043D\u043E\u0432\u043E\u0433\u043E \u043A\u043E\u043C\u043C\u0438\u0442\u0430 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url) \u0441 \u0442\u043E\u0439 \u0436\u0435 \u0441\u0441\u044B\u043B\u043A\u043E\u0439. \u0412\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0432 stdout \u043D\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0439 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0435\u0440\u0435\u0437 MCP-\u0442\u0443\u043B ask."
|
|
20033
|
+
].filter((part) => part.length > 0).join("\n\n");
|
|
20034
|
+
}
|
|
19743
20035
|
function buildCiWakeNudge(summary) {
|
|
19744
20036
|
return [
|
|
19745
20037
|
"\u{1F534} CI \u0443\u043F\u0430\u043B \u043D\u0430 \u0442\u0432\u043E\u0451\u043C PR \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043B `gh pr checks` \u0438 \u043F\u0440\u0438\u0441\u043B\u0430\u043B \u0442\u0435\u0431\u0435 \u0441\u0436\u0430\u0442\u044B\u0439 \u0440\u0430\u0437\u0431\u043E\u0440 \u0444\u0435\u0439\u043B\u0430 (\u043D\u0435 \u0432\u0435\u0441\u044C \u043B\u043E\u0433, \u0442\u043E\u043A\u0435\u043D-\u0434\u0438\u0441\u0446\u0438\u043F\u043B\u0438\u043D\u0430):",
|
|
@@ -20061,6 +20353,15 @@ function lastVerdictEvent(db, reviewRunId) {
|
|
|
20061
20353
|
function lastChangesSummary(db, reviewRunId) {
|
|
20062
20354
|
return lastVerdictEvent(db, reviewRunId)?.text ?? null;
|
|
20063
20355
|
}
|
|
20356
|
+
var VERIFY_EVENT_PREFIXES = ["\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043E\u0442", "\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D \u0438\u0437 PR-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430"];
|
|
20357
|
+
function lastVerifySummary(db, runId) {
|
|
20358
|
+
const events = db.events(runId);
|
|
20359
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
20360
|
+
const text = events[i].text ?? "";
|
|
20361
|
+
if (VERIFY_EVENT_PREFIXES.some((p) => text.startsWith(p))) return text;
|
|
20362
|
+
}
|
|
20363
|
+
return null;
|
|
20364
|
+
}
|
|
20064
20365
|
function changesVerdictAt(db, reviewRunId) {
|
|
20065
20366
|
return lastVerdictEvent(db, reviewRunId)?.at ?? null;
|
|
20066
20367
|
}
|
|
@@ -20240,6 +20541,11 @@ async function resurrectStormDead(deps, worktreeExists, hasTranscript, opts) {
|
|
|
20240
20541
|
let resurrected = 0;
|
|
20241
20542
|
for (const run2 of deps.db.listRuns("storm_dead")) {
|
|
20242
20543
|
if (countActiveDrones(deps.db) >= cap) break;
|
|
20544
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
20545
|
+
if (capHolder) {
|
|
20546
|
+
await handOffAtCycleCap(deps, run2, capHolder, "\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u0441\u043B\u0435 \u0448\u0442\u043E\u0440\u043C\u0430");
|
|
20547
|
+
continue;
|
|
20548
|
+
}
|
|
20243
20549
|
if (!await projectQuotaGateAllows(deps, run2)) continue;
|
|
20244
20550
|
const wt = run2.worktree_path;
|
|
20245
20551
|
const mode = !!wt && worktreeExists(wt) && hasTranscript(run2) ? "continue" : "fresh";
|
|
@@ -20263,6 +20569,16 @@ async function wakeRun(deps, runId, opts = {}) {
|
|
|
20263
20569
|
}
|
|
20264
20570
|
if (run2.pid != null && deps.isAlive(run2.pid)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 (pid ${run2.pid}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
20265
20571
|
if (isTerminalStatus(run2.status)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435\u043D (${run2.status}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
20572
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
20573
|
+
if (capHolder) {
|
|
20574
|
+
await noteReviewCyclesExhausted(deps, capHolder);
|
|
20575
|
+
const limit = effectiveReviewCycleLimit();
|
|
20576
|
+
const whose = capHolder.id === run2.id ? `\u043F\u0440\u043E\u0433\u043E\u043D #${runId}` : `\u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D #${capHolder.id} (\u0434\u043B\u044F #${runId})`;
|
|
20577
|
+
return {
|
|
20578
|
+
ok: false,
|
|
20579
|
+
why: `${whose} \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043B \u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A (${capHolder.review_cycle ?? 0} \u0438\u0437 ${limit}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u043F\u043E\u0431\u0443\u0434\u043A\u0438 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u044B`
|
|
20580
|
+
};
|
|
20581
|
+
}
|
|
20266
20582
|
const stoppedForHuman = deps.db.events(run2.id).some((e) => isEscalationText(e.text ?? "", run2, "author"));
|
|
20267
20583
|
if (run2.status === "review" && run2.pr_url && !needsFixIteration(run2) && !stoppedForHuman) {
|
|
20268
20584
|
return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0441\u0434\u0430\u043D \u043D\u0430 \u0440\u0435\u0432\u044C\u044E (${run2.pr_url}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E; \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u043E\u0439 \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443` };
|
|
@@ -20276,8 +20592,11 @@ async function wakeRun(deps, runId, opts = {}) {
|
|
|
20276
20592
|
return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u2014 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 (\u0440\u0435\u0432\u044C\u044E\u0435\u0440/\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440), \u0430 \u0441\u0435\u0441\u0441\u0438\u044E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C: fresh \u043F\u043E\u0442\u0435\u0440\u044F\u043B \u0431\u044B \u0435\u0433\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442` };
|
|
20277
20593
|
}
|
|
20278
20594
|
const mode = canContinue ? "continue" : "fresh";
|
|
20595
|
+
const verifySummary = lastVerifySummary(deps.db, run2.id);
|
|
20596
|
+
const reviewSummary = lastChangesSummary(deps.db, run2.id);
|
|
20597
|
+
const nudge = mode === "continue" && !isSubrun(run2) && run2.kind !== "hero" && run2.pr_url && (needsFixIteration(run2) || verifySummary || reviewSummary) ? buildSubmittedContinueNudge({ prUrl: run2.pr_url, verifySummary, reviewSummary }) : void 0;
|
|
20279
20598
|
try {
|
|
20280
|
-
await deps.respawn(run2, mode);
|
|
20599
|
+
await deps.respawn(run2, mode, nudge);
|
|
20281
20600
|
} catch (e) {
|
|
20282
20601
|
return { ok: false, why: `\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${runId} \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C: ${String(e)}` };
|
|
20283
20602
|
}
|
|
@@ -20469,6 +20788,10 @@ function closeCiWatch(deps, run2, mark, why, narration) {
|
|
|
20469
20788
|
}
|
|
20470
20789
|
async function wakeAuthorOnRedCi(deps, run2, now, ci, worktreeExists, hasTranscript, droneCap) {
|
|
20471
20790
|
if (run2.pid != null && deps.isAlive(run2.pid)) return false;
|
|
20791
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20792
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20793
|
+
return false;
|
|
20794
|
+
}
|
|
20472
20795
|
if (deps.resourcesOk && !deps.resourcesOk()) return false;
|
|
20473
20796
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
20474
20797
|
if (!await projectQuotaGateAllows(deps, run2)) return false;
|
|
@@ -20538,6 +20861,15 @@ async function wakeReviewerOnCiReady(deps, run2, over) {
|
|
|
20538
20861
|
const reviewer = awaitingCiReviewer(deps.db, run2);
|
|
20539
20862
|
if (!reviewer) return false;
|
|
20540
20863
|
if (reviewer.pid != null && deps.isAlive(reviewer.pid)) return false;
|
|
20864
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20865
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20866
|
+
deps.db.addEvent(
|
|
20867
|
+
run2.id,
|
|
20868
|
+
"ci_checked",
|
|
20869
|
+
`${ciReadyLabel(over)} \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0431\u0443\u0436\u0443: \u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
|
|
20870
|
+
);
|
|
20871
|
+
return true;
|
|
20872
|
+
}
|
|
20541
20873
|
if (deps.resourcesOk && !deps.resourcesOk()) {
|
|
20542
20874
|
deps.db.addEvent(run2.id, "ci_checked", `${ciReadyLabel(over)} \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0436\u0434\u0451\u0442, \u043C\u0430\u0448\u0438\u043D\u0430 \u043F\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0435\u043D\u0430`);
|
|
20543
20875
|
return true;
|
|
@@ -20612,7 +20944,8 @@ async function spawnFreshReviewerAfterStall(deps, run2, why) {
|
|
|
20612
20944
|
deps.db.addEvent(run2.id, "step", `\u0441\u0432\u0435\u0436\u0438\u0439 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043D\u0435 \u043D\u0443\u0436\u0435\u043D: ${live2[0].name} \u0435\u0449\u0451 \u0436\u0438\u0432 (${live2[0].stop_reason ?? live2[0].status})`);
|
|
20613
20945
|
return;
|
|
20614
20946
|
}
|
|
20615
|
-
if (run2
|
|
20947
|
+
if (reviewCyclesExhausted(run2) || deps.db.reviewerAttemptCountFor(run2.id) >= REVIEW_SPAWN_LIMIT) {
|
|
20948
|
+
if (reviewCyclesExhausted(run2)) await noteReviewCyclesExhausted(deps, run2);
|
|
20616
20949
|
return;
|
|
20617
20950
|
}
|
|
20618
20951
|
const gate = await prGateForReviewer(deps, run2, run2.pr_url, "\u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B CI");
|
|
@@ -20636,7 +20969,10 @@ async function wakeAuthorOnChanges(deps, snapshot, now, droneCap) {
|
|
|
20636
20969
|
if (deps.resourcesOk && !deps.resourcesOk()) return false;
|
|
20637
20970
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
20638
20971
|
if (!await projectQuotaGateAllows(deps, run2)) return false;
|
|
20639
|
-
if (run2
|
|
20972
|
+
if (reviewCyclesExhausted(run2)) {
|
|
20973
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
20974
|
+
return false;
|
|
20975
|
+
}
|
|
20640
20976
|
const prUrl = run2.pr_url;
|
|
20641
20977
|
const events = deps.db.events(run2.id);
|
|
20642
20978
|
const mark = `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run2.review_cycle ?? 0})`;
|
|
@@ -20927,6 +21263,7 @@ function noteStormStall(deps) {
|
|
|
20927
21263
|
for (const run2 of waiting) deferSpawn(deps, run2, reason, { quiet: true });
|
|
20928
21264
|
}
|
|
20929
21265
|
async function trySpawnQueuedRun(deps, run2) {
|
|
21266
|
+
if (preparingRuns(deps.db).has(run2.id)) return "skipped";
|
|
20930
21267
|
if (deps.control?.paused) {
|
|
20931
21268
|
const why = deps.control.state?.reason;
|
|
20932
21269
|
return deferSpawn(deps, run2, `\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)`, {
|
|
@@ -21021,12 +21358,55 @@ async function trySpawnQueuedRun(deps, run2) {
|
|
|
21021
21358
|
await fillSuggestedArea(deps, run2);
|
|
21022
21359
|
const repoGap = unresolvedRepoReason(run2.repo, deps.reposServed ? deps.reposServed() : []);
|
|
21023
21360
|
if (repoGap) return await failBeforeStart(deps, run2, repoGap);
|
|
21361
|
+
return await spawnOrPrepareInBackground(deps, run2);
|
|
21362
|
+
}
|
|
21363
|
+
async function spawnOrPrepareInBackground(deps, run2) {
|
|
21364
|
+
const preparing = preparingRuns(deps.db);
|
|
21365
|
+
const repoKey = normalizeRepo(run2.repo || deps.repoFallback || "");
|
|
21366
|
+
for (const [otherId, otherRepo] of preparing) {
|
|
21367
|
+
if (otherId !== run2.id && otherRepo === repoKey) {
|
|
21368
|
+
return deferSpawn(deps, run2, `\u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0449\u0451 \u0433\u043E\u0442\u043E\u0432\u0438\u0442\u0441\u044F \u0440\u0430\u0431\u043E\u0447\u0430\u044F \u043A\u043E\u043F\u0438\u044F \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${otherId} \u2014 \u0436\u0434\u0443 \u0435\u0451, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0434\u0432\u0435 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0438 \u0440\u0430\u0437\u043E\u043C (TASWM-611)`);
|
|
21369
|
+
}
|
|
21370
|
+
}
|
|
21371
|
+
preparing.set(run2.id, repoKey);
|
|
21372
|
+
const attempt = spawnQueuedRun(deps, run2);
|
|
21373
|
+
const release = () => {
|
|
21374
|
+
preparing.delete(run2.id);
|
|
21375
|
+
};
|
|
21376
|
+
void attempt.then(release, release);
|
|
21377
|
+
let timer;
|
|
21378
|
+
const background = /* @__PURE__ */ Symbol("background");
|
|
21379
|
+
const waited = new Promise((resolve5) => {
|
|
21380
|
+
timer = setTimeout(() => resolve5(background), SPAWN_INLINE_WAIT_MS);
|
|
21381
|
+
});
|
|
21382
|
+
try {
|
|
21383
|
+
const first = await Promise.race([attempt, waited]);
|
|
21384
|
+
if (first !== background) return first;
|
|
21385
|
+
} finally {
|
|
21386
|
+
clearTimeout(timer);
|
|
21387
|
+
}
|
|
21388
|
+
deps.db.addEvent(run2.id, "step", "\u0433\u043E\u0442\u043E\u0432\u0438\u0442\u0441\u044F: \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 \u0440\u0430\u0431\u043E\u0447\u0435\u0439 \u043A\u043E\u043F\u0438\u0438 \u0438\u0434\u0451\u0442 \u0444\u043E\u043D\u043E\u043C, \u0442\u0438\u043A \u0435\u0451 \u043D\u0435 \u0436\u0434\u0451\u0442 (TASWM-611)");
|
|
21389
|
+
console.log(`[overmind] ${droneLabel(run2)} (${run2.task_slug}) \u0433\u043E\u0442\u043E\u0432\u0438\u0442\u0441\u044F \u0444\u043E\u043D\u043E\u043C \u2014 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 \u0434\u043E\u043B\u044C\u0448\u0435 ${SPAWN_INLINE_WAIT_MS / 1e3}\u0441, \u0442\u0438\u043A \u0438\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435`);
|
|
21390
|
+
void attempt.then(
|
|
21391
|
+
(outcome) => {
|
|
21392
|
+
if (outcome !== "spawned") console.warn(`[overmind] \u0444\u043E\u043D\u043E\u0432\u0430\u044F \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 ${run2.task_slug} \u043A\u043E\u043D\u0447\u0438\u043B\u0430\u0441\u044C \u0431\u0435\u0437 \u0434\u0440\u043E\u043D\u0430: ${outcome}`);
|
|
21393
|
+
},
|
|
21394
|
+
(e) => console.error(`[overmind] \u0444\u043E\u043D\u043E\u0432\u0430\u044F \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 ${run2.task_slug} \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C: ${describeTickError(e)}`)
|
|
21395
|
+
);
|
|
21396
|
+
return "spawned";
|
|
21397
|
+
}
|
|
21398
|
+
async function spawnQueuedRun(deps, run2) {
|
|
21024
21399
|
try {
|
|
21025
21400
|
await deps.spawn(run2);
|
|
21026
21401
|
if (deps.spawnDefer) clearDefer(deps.spawnDefer, `run:${run2.id}`);
|
|
21027
21402
|
narrate(deps, `\u0411\u0435\u0440\u0443 ${run2.task_slug} \u0438\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u2014 \u0441\u043F\u0430\u0432\u043D\u044E ${droneLabel(run2)}.`);
|
|
21028
21403
|
return "spawned";
|
|
21029
21404
|
} catch (e) {
|
|
21405
|
+
if (e instanceof SpawnAbandonedError) {
|
|
21406
|
+
deps.db.addEvent(run2.id, "step", e.message);
|
|
21407
|
+
console.log(`[overmind] ${run2.task_slug}: ${e.message}`);
|
|
21408
|
+
return "closed";
|
|
21409
|
+
}
|
|
21030
21410
|
if (e instanceof FileOverlapError) {
|
|
21031
21411
|
const logged = deps.overlapLogged ??= /* @__PURE__ */ new Map();
|
|
21032
21412
|
if (logged.get(run2.id) !== e.message) {
|
|
@@ -21189,6 +21569,11 @@ async function resumeWatchdogParked(deps, droneCap) {
|
|
|
21189
21569
|
}
|
|
21190
21570
|
if (!candidate) return;
|
|
21191
21571
|
if (candidate.pid != null && deps.isAlive(candidate.pid)) return;
|
|
21572
|
+
const capHolder = cycleCapHolder(deps.db, candidate);
|
|
21573
|
+
if (capHolder) {
|
|
21574
|
+
await handOffAtCycleCap(deps, candidate, capHolder, "\u0432\u043E\u0437\u0432\u0440\u0430\u0442 \u0438\u0437 \u043F\u0430\u0440\u043A\u043E\u0432\u043A\u0438 \u0441\u0442\u043E\u0440\u043E\u0436\u0430");
|
|
21575
|
+
return;
|
|
21576
|
+
}
|
|
21192
21577
|
const worktreeExists = deps.worktreeExists ?? existsSync22;
|
|
21193
21578
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
21194
21579
|
const wt = candidate.worktree_path;
|
|
@@ -21248,6 +21633,11 @@ async function applyWatchdogAction(deps, action) {
|
|
|
21248
21633
|
}
|
|
21249
21634
|
case "fresh": {
|
|
21250
21635
|
if (!deps.respawn) return;
|
|
21636
|
+
const capHolder = cycleCapHolder(deps.db, action.run);
|
|
21637
|
+
if (capHolder) {
|
|
21638
|
+
await handOffAtCycleCap(deps, action.run, capHolder, "\u0441\u0432\u0435\u0436\u0438\u0439 \u043F\u043E\u0434\u044A\u0451\u043C \u0441\u0442\u043E\u0440\u043E\u0436\u0435\u043C \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0430");
|
|
21639
|
+
return;
|
|
21640
|
+
}
|
|
21251
21641
|
try {
|
|
21252
21642
|
await deps.respawn(action.run, "fresh", buildFreshRestartNudge(action.run));
|
|
21253
21643
|
deps.db.addEvent(action.run.id, "resumed", `\u0441\u0442\u043E\u0440\u043E\u0436 \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0430: ${action.reason}`);
|
|
@@ -21491,6 +21881,11 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
21491
21881
|
}
|
|
21492
21882
|
if (now - loopEntry.lastPauseAt < reliefBackoffMs(loopEntry.pauses, cfg.rampWindowMs, loopCfg)) return;
|
|
21493
21883
|
}
|
|
21884
|
+
const capHolder = cycleCapHolder(deps.db, resumeCandidate);
|
|
21885
|
+
if (capHolder) {
|
|
21886
|
+
await handOffAtCycleCap(deps, resumeCandidate, capHolder, "\u0432\u043E\u0437\u0432\u0440\u0430\u0442 \u0438\u0437 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043A\u0438");
|
|
21887
|
+
return;
|
|
21888
|
+
}
|
|
21494
21889
|
const wt = resumeCandidate.worktree_path;
|
|
21495
21890
|
const canContinue = !!wt && worktreeAndTranscriptAlive(deps, resumeCandidate);
|
|
21496
21891
|
const mode = canContinue ? "continue" : "fresh";
|
|
@@ -21911,6 +22306,8 @@ async function answerBudgetQuestions(deps, ctx) {
|
|
|
21911
22306
|
respawn: deps.respawn,
|
|
21912
22307
|
...deps.worktreeExists ? { worktreeExists: deps.worktreeExists } : {},
|
|
21913
22308
|
...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
|
|
22309
|
+
...deps.prComment ? { prComment: deps.prComment } : {},
|
|
22310
|
+
...deps.reportComment ? { reportComment: deps.reportComment } : {},
|
|
21914
22311
|
narrate: (m) => narrate(deps, m)
|
|
21915
22312
|
},
|
|
21916
22313
|
run2.id,
|
|
@@ -21920,6 +22317,11 @@ async function answerBudgetQuestions(deps, ctx) {
|
|
|
21920
22317
|
budgetSyncAt.delete(run2.id);
|
|
21921
22318
|
continue;
|
|
21922
22319
|
}
|
|
22320
|
+
if (reviewCyclesExhausted(run2) && run2.pr_url) {
|
|
22321
|
+
deps.db.updateRun(run2.id, { status: "review", stop_reason: null });
|
|
22322
|
+
budgetSyncAt.delete(run2.id);
|
|
22323
|
+
continue;
|
|
22324
|
+
}
|
|
21923
22325
|
const note = `\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u043F\u043E \u043E\u0442\u0432\u0435\u0442\u0443 \xAB\u0434\u0430\xBB \u043D\u0435 \u0432\u044B\u0448\u043B\u0430: ${woken.why} \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u044E`;
|
|
21924
22326
|
if (deps.db.events(run2.id).at(-1)?.text !== note) deps.db.addEvent(run2.id, "step", note);
|
|
21925
22327
|
}
|
|
@@ -21978,7 +22380,9 @@ async function tickOnce(deps) {
|
|
|
21978
22380
|
const now = deps.now ? deps.now() : Date.now();
|
|
21979
22381
|
const worktreeExists = deps.worktreeExists ?? existsSync22;
|
|
21980
22382
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
22383
|
+
deps.stepClock?.mark("0 \u0437\u0430\u043C\u0435\u0440 \u0445\u043E\u0441\u0442\u0430");
|
|
21981
22384
|
deps.sampleHost?.();
|
|
22385
|
+
deps.stepClock?.mark("0a re-hydrate \u0437\u0435\u0440\u043A\u0430\u043B\u0430");
|
|
21982
22386
|
if (deps.rehydrate) {
|
|
21983
22387
|
const rh = deps.rehydrate;
|
|
21984
22388
|
const sinceLast = rh.lastAt == null ? Infinity : now - rh.lastAt;
|
|
@@ -21991,6 +22395,7 @@ async function tickOnce(deps) {
|
|
|
21991
22395
|
}
|
|
21992
22396
|
}
|
|
21993
22397
|
}
|
|
22398
|
+
deps.stepClock?.mark("0a1 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043E\u0447\u0435\u0440\u0435\u0434\u0438");
|
|
21994
22399
|
if (deps.db.refreshDeps) {
|
|
21995
22400
|
const waiting = deps.db.listRuns("queued").flatMap((r) => r.depends_on ?? []);
|
|
21996
22401
|
if (waiting.length) {
|
|
@@ -22001,6 +22406,7 @@ async function tickOnce(deps) {
|
|
|
22001
22406
|
}
|
|
22002
22407
|
}
|
|
22003
22408
|
}
|
|
22409
|
+
deps.stepClock?.mark("0a2b \u043C\u043E\u0441\u0442 \xABsqlite \u0434\u0440\u043E\u043D\u0430 \u2192 \u0437\u0435\u0440\u043A\u0430\u043B\u043E \u2192 \u043F\u0440\u043E\u0434\xBB");
|
|
22004
22410
|
if (deps.droneBridge) {
|
|
22005
22411
|
const br = deps.droneBridge;
|
|
22006
22412
|
const sinceLast = br.lastAt == null ? Infinity : now - br.lastAt;
|
|
@@ -22013,6 +22419,7 @@ async function tickOnce(deps) {
|
|
|
22013
22419
|
}
|
|
22014
22420
|
}
|
|
22015
22421
|
}
|
|
22422
|
+
deps.stepClock?.mark("0a3 \u0441\u0432\u0435\u0440\u043A\u0430 \u043A\u043E\u0434\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u0441 main");
|
|
22016
22423
|
if (deps.daemonVersion) {
|
|
22017
22424
|
const dv = deps.daemonVersion;
|
|
22018
22425
|
const sinceLast = dv.lastAt == null ? Infinity : now - dv.lastAt;
|
|
@@ -22021,6 +22428,7 @@ async function tickOnce(deps) {
|
|
|
22021
22428
|
await dv.run();
|
|
22022
22429
|
}
|
|
22023
22430
|
}
|
|
22431
|
+
deps.stepClock?.mark("0a4 \u043A\u0430\u0440\u0442\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u043C\u0430\u0448\u0438\u043D\u044B");
|
|
22024
22432
|
if (deps.repoMap) {
|
|
22025
22433
|
const rm4 = deps.repoMap;
|
|
22026
22434
|
const sinceLast = rm4.lastAt == null ? Infinity : now - rm4.lastAt;
|
|
@@ -22033,6 +22441,7 @@ async function tickOnce(deps) {
|
|
|
22033
22441
|
}
|
|
22034
22442
|
}
|
|
22035
22443
|
}
|
|
22444
|
+
deps.stepClock?.mark("0b reap \u0432\u043D\u0435\u0448\u043D\u0435-\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0445");
|
|
22036
22445
|
if (deps.rehydrate && deps.kill) {
|
|
22037
22446
|
const kill = deps.kill;
|
|
22038
22447
|
for (const run2 of deps.db.listRuns()) {
|
|
@@ -22046,6 +22455,7 @@ async function tickOnce(deps) {
|
|
|
22046
22455
|
});
|
|
22047
22456
|
}
|
|
22048
22457
|
}
|
|
22458
|
+
deps.stepClock?.mark("0c \u0437\u0430\u0449\u0438\u0442\u043D\u044B\u0439 sweep \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0438\u0445 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u043E\u0432/doc");
|
|
22049
22459
|
if (deps.sweep) {
|
|
22050
22460
|
const sw = deps.sweep;
|
|
22051
22461
|
const sinceLast = sw.lastAt == null ? Infinity : now - sw.lastAt;
|
|
@@ -22058,6 +22468,7 @@ async function tickOnce(deps) {
|
|
|
22058
22468
|
}
|
|
22059
22469
|
}
|
|
22060
22470
|
}
|
|
22471
|
+
deps.stepClock?.mark("0c2 \u0436\u043D\u0435\u0446 worktree");
|
|
22061
22472
|
if (deps.reapWorktrees) {
|
|
22062
22473
|
const rw = deps.reapWorktrees;
|
|
22063
22474
|
const sinceLast = rw.lastAt == null ? Infinity : now - rw.lastAt;
|
|
@@ -22070,6 +22481,7 @@ async function tickOnce(deps) {
|
|
|
22070
22481
|
}
|
|
22071
22482
|
}
|
|
22072
22483
|
}
|
|
22484
|
+
deps.stepClock?.mark("0d \u0445\u043E\u0437\u044F\u0439\u0441\u0442\u0432\u043E \u0434\u043E\u043B\u0433\u043E\u0436\u0438\u0432\u0443\u0449\u0435\u0433\u043E \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430");
|
|
22073
22485
|
if (deps.logRotate) {
|
|
22074
22486
|
const lr = deps.logRotate;
|
|
22075
22487
|
const sinceLast = lr.lastAt == null ? Infinity : now - lr.lastAt;
|
|
@@ -22083,6 +22495,7 @@ async function tickOnce(deps) {
|
|
|
22083
22495
|
}
|
|
22084
22496
|
}
|
|
22085
22497
|
pruneGateLogs(deps);
|
|
22498
|
+
deps.stepClock?.mark("0 \u0448\u0442\u043E\u0440\u043C");
|
|
22086
22499
|
let stormActive = false;
|
|
22087
22500
|
if (deps.storm) {
|
|
22088
22501
|
for (const run2 of deps.db.listRuns("running")) {
|
|
@@ -22100,7 +22513,9 @@ async function tickOnce(deps) {
|
|
|
22100
22513
|
stormActive = await manageStorm(deps, now, worktreeExists, hasTranscript);
|
|
22101
22514
|
}
|
|
22102
22515
|
const droneCap = deps.maxDrones ? await deps.maxDrones() : Infinity;
|
|
22516
|
+
deps.stepClock?.mark("1 \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u0447\u0435\u0440\u0435\u0434\u044C");
|
|
22103
22517
|
if (stormActive) noteStormStall(deps);
|
|
22518
|
+
deps.stepClock?.mark("0b \u0432\u0435\u0441 \u0437\u0430\u0434\u0430\u0447\u0438");
|
|
22104
22519
|
try {
|
|
22105
22520
|
await manageWeight(deps);
|
|
22106
22521
|
} catch (e) {
|
|
@@ -22111,14 +22526,17 @@ async function tickOnce(deps) {
|
|
|
22111
22526
|
} else if (!stormActive) {
|
|
22112
22527
|
await batchSpawnQueue(deps, droneCap, now);
|
|
22113
22528
|
}
|
|
22529
|
+
deps.stepClock?.mark("1b \u0432\u043E\u0437\u0432\u0440\u0430\u0442 \u0437\u0430\u043F\u0430\u0440\u043A\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0441\u0442\u043E\u0440\u043E\u0436\u0435\u043C \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0430");
|
|
22114
22530
|
if (!stormActive) {
|
|
22115
22531
|
await resumeWatchdogParked(deps, droneCap);
|
|
22116
22532
|
}
|
|
22533
|
+
deps.stepClock?.mark("1c \u043F\u043E\u0432\u0442\u043E\u0440 \u043E\u0442\u043B\u043E\u0436\u0435\u043D\u043D\u043E\u0433\u043E \u0441\u043F\u0430\u0432\u043D\u0430");
|
|
22117
22534
|
try {
|
|
22118
22535
|
await retryDeferredSpawns(deps, now, droneCap, stormActive);
|
|
22119
22536
|
} catch (e) {
|
|
22120
22537
|
console.error("[overmind] \u043F\u043E\u0432\u0442\u043E\u0440 \u043E\u0442\u043B\u043E\u0436\u0435\u043D\u043D\u043E\u0433\u043E \u0441\u043F\u0430\u0432\u043D\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:", e);
|
|
22121
22538
|
}
|
|
22539
|
+
deps.stepClock?.mark("2 review \u2192 \u0441\u043C\u0435\u0440\u0436\u0435\u043D? \u0437\u0430\u043A\u0440\u044B\u0442\u044C");
|
|
22122
22540
|
for (let run2 of deps.db.listRuns("review")) {
|
|
22123
22541
|
try {
|
|
22124
22542
|
if (run2.resolves_run_id != null) {
|
|
@@ -22305,11 +22723,8 @@ async function tickOnce(deps) {
|
|
|
22305
22723
|
// рано, спавнить второго нельзя.
|
|
22306
22724
|
liveOrWaitingReviewers(deps.db, run2.id).length === 0;
|
|
22307
22725
|
if (reviewerGatesOk && deps.spawnReviewer && await projectWantsChecks(deps, run2)) {
|
|
22308
|
-
if (run2
|
|
22309
|
-
|
|
22310
|
-
deps.db.addEvent(run2.id, "step", `${REVIEW_CYCLE_ESCALATION_MARK}: ${run2.review_cycle} \u0446\u0438\u043A\u043B(\u043E\u0432) \u0440\u0435\u0432\u044C\u044E-\u0444\u0438\u043A\u0441\u043E\u0432 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`);
|
|
22311
|
-
narrate(deps, `\u0420\u0435-\u0440\u0435\u0432\u044C\u044E ${droneLabel(run2)} \u0437\u0430\u0446\u0438\u043A\u043B\u0438\u043B\u043E\u0441\u044C (${run2.review_cycle} \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A.`);
|
|
22312
|
-
}
|
|
22726
|
+
if (reviewCyclesExhausted(run2)) {
|
|
22727
|
+
await noteReviewCyclesExhausted(deps, run2);
|
|
22313
22728
|
continue;
|
|
22314
22729
|
}
|
|
22315
22730
|
const attempts = deps.db.reviewerAttemptCountFor(run2.id);
|
|
@@ -22371,12 +22786,19 @@ async function tickOnce(deps) {
|
|
|
22371
22786
|
console.error(`[overmind] \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B review-\u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run2.id} (${run2.task_slug}) \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:`, e);
|
|
22372
22787
|
}
|
|
22373
22788
|
}
|
|
22789
|
+
deps.stepClock?.mark("2b \u0434\u0443\u0431\u043B\u0438-\u0440\u0430\u043D\u044B \u0437\u0430\u0434\u0430\u0447\u0438");
|
|
22374
22790
|
await closeMergedTaskRuns(deps);
|
|
22791
|
+
deps.stepClock?.mark("2b1 \u0430\u0432\u0442\u043E\u043F\u043E\u0434\u044A\u0451\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E");
|
|
22375
22792
|
await rescueReviewlessPrs(deps, stormActive, now);
|
|
22793
|
+
deps.stepClock?.mark("2b1a \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0431\u0435\u0437 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438");
|
|
22376
22794
|
relinkUnboundReviewers(deps);
|
|
22795
|
+
deps.stepClock?.mark("2b2 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u044B-\u0441\u0438\u0440\u043E\u0442\u044B");
|
|
22377
22796
|
await closeOrphanedReviewers(deps);
|
|
22797
|
+
deps.stepClock?.mark("2b3 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F");
|
|
22378
22798
|
await spawnVerifiersForOpenPrs(deps, stormActive);
|
|
22799
|
+
deps.stepClock?.mark("2c \u0432\u044B\u0447\u0438\u0441\u043B\u044F\u0435\u043C\u0430\u044F \u0444\u0430\u0437\u0430 \u0440\u0435\u0432\u044C\u044E");
|
|
22379
22800
|
await refreshReviewPhases(deps);
|
|
22801
|
+
deps.stepClock?.mark("3 liveness/stall/\u0430\u0432\u0442\u043E-\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435");
|
|
22380
22802
|
let liveChildPpids;
|
|
22381
22803
|
try {
|
|
22382
22804
|
liveChildPpids = deps.livePpids?.() ?? null;
|
|
@@ -22465,6 +22887,14 @@ async function tickOnce(deps) {
|
|
|
22465
22887
|
continue;
|
|
22466
22888
|
}
|
|
22467
22889
|
}
|
|
22890
|
+
if (!alive && run2.status === "running" && !isSubrun(run2) && run2.kind !== "hero") {
|
|
22891
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
22892
|
+
if (capHolder) {
|
|
22893
|
+
await handOffAtCycleCap(deps, run2, capHolder, `\u0441\u0435\u0441\u0441\u0438\u044F \u0430\u0432\u0442\u043E\u0440\u0430 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0430\u0441\u044C (${outcome})`);
|
|
22894
|
+
narrate(deps, `${droneLabel(run2)} \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u043B\u0438\u043C\u0438\u0442 \u043A\u0440\u0443\u0433\u043E\u0432 \u043F\u0440\u0430\u0432\u043E\u043A \u2014 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E, PR \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443.`);
|
|
22895
|
+
continue;
|
|
22896
|
+
}
|
|
22897
|
+
}
|
|
22468
22898
|
if (!alive && (outcome === "graceful" || outcome === "aborted") && run2.status === "running" && !isSubrun(run2) && // суб-дроны (резолвер/ревьюер/верификатор) — своими ветками ниже, TASWM-528
|
|
22469
22899
|
run2.kind !== "hero") {
|
|
22470
22900
|
if (!wasAborted(outcome) && run2.pr_url) {
|
|
@@ -22570,6 +23000,11 @@ async function tickOnce(deps) {
|
|
|
22570
23000
|
}
|
|
22571
23001
|
}
|
|
22572
23002
|
if (!alive && outcome === "graceful" && run2.status === "running" && isSubrun(run2)) {
|
|
23003
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
23004
|
+
if (capHolder) {
|
|
23005
|
+
await handOffAtCycleCap(deps, run2, capHolder, "\u0441\u0443\u0431-\u0434\u0440\u043E\u043D \u0432\u044B\u0448\u0435\u043B \u0441\u0430\u043C, \u043D\u0435 \u0432\u044B\u043D\u0435\u0441\u044F \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430");
|
|
23006
|
+
continue;
|
|
23007
|
+
}
|
|
22573
23008
|
const wt = run2.worktree_path;
|
|
22574
23009
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run2);
|
|
22575
23010
|
if (deps.respawn && canContinue && run2.respawns < RESPAWN_LIMIT) {
|
|
@@ -22623,6 +23058,7 @@ async function tickOnce(deps) {
|
|
|
22623
23058
|
});
|
|
22624
23059
|
}
|
|
22625
23060
|
}
|
|
23061
|
+
deps.stepClock?.mark("3b \u043F\u043E\u043B \u0441\u043D\u0438\u0437\u0443");
|
|
22626
23062
|
for (const run2 of deps.db.listRuns("paused")) {
|
|
22627
23063
|
if (run2.kind === "hero") continue;
|
|
22628
23064
|
if (run2.stop_reason === AWAITING_CI_REASON || isReliefPause(run2.stop_reason)) continue;
|
|
@@ -22637,7 +23073,9 @@ async function tickOnce(deps) {
|
|
|
22637
23073
|
deps.db.addEvent(run2.id, "stalled", reason);
|
|
22638
23074
|
narrate(deps, `${droneLabel(run2)} \u0432\u0438\u0441\u0435\u043B paused \u0431\u0435\u0437 \u0434\u0432\u0438\u0436\u0435\u043D\u0438\u044F ${Math.round(STALL_FLOOR_MS / 6e4)} \u043C\u0438\u043D \u2014 \u043F\u043E\u043C\u0435\u0447\u0430\u044E \u0437\u0430\u0432\u0438\u0441\u0448\u0438\u043C (TASWM-275).`);
|
|
22639
23075
|
}
|
|
23076
|
+
deps.stepClock?.mark("3c \u043F\u0440\u0435\u0434\u0435\u043B \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F ci");
|
|
22640
23077
|
await escalateStaleCiWaits(deps, now);
|
|
23078
|
+
deps.stepClock?.mark("4 \u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0435\u043D\u0438\u0435 \u0437\u0430\u043C\u0435\u0440\u0448\u0438\u0445");
|
|
22641
23079
|
if (deps.respawn && !stormActive) {
|
|
22642
23080
|
for (const run2 of deps.db.listRuns("paused")) {
|
|
22643
23081
|
if (run2.kind === "hero") continue;
|
|
@@ -22645,6 +23083,11 @@ async function tickOnce(deps) {
|
|
|
22645
23083
|
if (isReliefPause(run2.stop_reason)) continue;
|
|
22646
23084
|
if (NO_AUTO_WAKE_REASONS.some((r) => run2.stop_reason?.startsWith(r))) continue;
|
|
22647
23085
|
if (run2.pid != null && deps.isAlive(run2.pid)) continue;
|
|
23086
|
+
const capHolder = cycleCapHolder(deps.db, run2);
|
|
23087
|
+
if (capHolder) {
|
|
23088
|
+
await handOffAtCycleCap(deps, run2, capHolder, "\u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0435\u043D\u0438\u0435 \u0437\u0430\u043C\u0435\u0440\u0448\u0435\u0433\u043E");
|
|
23089
|
+
continue;
|
|
23090
|
+
}
|
|
22648
23091
|
if (!await projectQuotaGateAllows(deps, run2)) continue;
|
|
22649
23092
|
const wt = run2.worktree_path;
|
|
22650
23093
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run2);
|
|
@@ -22662,9 +23105,11 @@ async function tickOnce(deps) {
|
|
|
22662
23105
|
}
|
|
22663
23106
|
}
|
|
22664
23107
|
}
|
|
23108
|
+
deps.stepClock?.mark("4c \u0433\u0435\u0439\u0442-\u0443\u0432\u0430\u0436\u0430\u044E\u0449\u0438\u0439 \u043F\u043E\u0434\u044A\u0451\u043C \u043E\u0441\u0442\u0430\u0432\u0448\u0438\u0445\u0441\u044F storm_d");
|
|
22665
23109
|
if (deps.respawn && deps.resourcesOk && !stormActive) {
|
|
22666
23110
|
await resurrectStormDead(deps, worktreeExists, hasTranscript, { gateOpen: deps.resourcesOk(), cap: droneCap });
|
|
22667
23111
|
}
|
|
23112
|
+
deps.stepClock?.mark("4d \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0438\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B");
|
|
22668
23113
|
try {
|
|
22669
23114
|
const reaped = reapOrphanedQuestions(deps.db, now);
|
|
22670
23115
|
if (reaped.withdrawn > 0) {
|
|
@@ -22675,11 +23120,13 @@ async function tickOnce(deps) {
|
|
|
22675
23120
|
} catch (e) {
|
|
22676
23121
|
console.error("[overmind] \u0443\u0431\u043E\u0440\u043A\u0430 \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0438\u0445 \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C:", e);
|
|
22677
23122
|
}
|
|
23123
|
+
deps.stepClock?.mark("4e \u043E\u0442\u0432\u0435\u0442 \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 \u043E \u0431\u044E\u0434\u0436\u0435\u0442\u0435");
|
|
22678
23124
|
try {
|
|
22679
23125
|
await answerBudgetQuestions(deps, { stormActive, droneCap });
|
|
22680
23126
|
} catch (e) {
|
|
22681
23127
|
console.error("[overmind] \u0440\u0430\u0437\u0431\u043E\u0440 \u043E\u0442\u0432\u0435\u0442\u043E\u0432 \u043E \u0431\u044E\u0434\u0436\u0435\u0442\u0435 \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:", e);
|
|
22682
23128
|
}
|
|
23129
|
+
deps.stepClock?.mark("4b \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0439 \u043D\u0430\u0434\u0437\u043E\u0440");
|
|
22683
23130
|
if (deps.anomalies && !deps.control?.paused) {
|
|
22684
23131
|
const az = deps.anomalies;
|
|
22685
23132
|
let ready = [];
|
|
@@ -22756,6 +23203,7 @@ async function tickOnce(deps) {
|
|
|
22756
23203
|
}
|
|
22757
23204
|
}
|
|
22758
23205
|
}
|
|
23206
|
+
deps.stepClock?.mark("4c \u0441\u0442\u043E\u0440\u043E\u0436 \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0430");
|
|
22759
23207
|
if (deps.watchdog) {
|
|
22760
23208
|
try {
|
|
22761
23209
|
const action = decideWatchdogAction(deps.db, now, deps.watchdog.state, deps.watchdog.thresholds, droneCap, { exists: worktreeExists, hasTranscript }, await projectQuotaBlockedRunIds(deps));
|
|
@@ -22764,6 +23212,7 @@ async function tickOnce(deps) {
|
|
|
22764
23212
|
console.error("[overmind] \u0441\u0442\u043E\u0440\u043E\u0436 \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0430: \u0442\u0438\u043A \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:", e);
|
|
22765
23213
|
}
|
|
22766
23214
|
}
|
|
23215
|
+
deps.stepClock?.mark("5 \u0430\u0432\u0442\u043E-\u0438\u043D\u0442\u0435\u0439\u043A");
|
|
22767
23216
|
const heavyInFlight = heavyRunsInFlight(deps);
|
|
22768
23217
|
if (heavyInFlight.length && deps.intake) {
|
|
22769
23218
|
const d = noteDefer(deps.spawnDefer ??= /* @__PURE__ */ new Map(), "weight-intake-hold", intakeHoldReason(heavyInFlight.map(droneLabel).join(", ")), GATE_LOG_HEARTBEAT_TICKS);
|
|
@@ -23202,7 +23651,11 @@ async function makeStore(repo, worktreesDir, repoFlag) {
|
|
|
23202
23651
|
// Ретрай не молчит: обрыв, переигранный в том же вызове, всё равно виден.
|
|
23203
23652
|
// Без этой строки самолечение связи — единственный путь, о котором в логе
|
|
23204
23653
|
// ни слова, и разбор «почему тик подтормозил» упирается в тишину.
|
|
23205
|
-
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}.`)
|
|
23654
|
+
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}.`),
|
|
23655
|
+
// TASWM-611: молчащий прод больше не держит проход минутами — остаток идёт без сети.
|
|
23656
|
+
onBudgetSpent: ({ spentMs, budgetMs }) => console.warn(
|
|
23657
|
+
`[overmind] \u043F\u0440\u043E\u0434-MCP \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442: \u043F\u0440\u043E\u0445\u043E\u0434 \u0443\u0436\u0435 \u043F\u043E\u0442\u0440\u0430\u0442\u0438\u043B \u043D\u0430 \u043D\u0435\u0433\u043E ${Math.round(spentMs / 1e3)}\u0441 (\u0431\u044E\u0434\u0436\u0435\u0442 ${Math.round(budgetMs / 1e3)}\u0441) \u2014 \u0434\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043F\u0440\u043E\u0445\u043E\u0434\u0430 \u0440\u0430\u0431\u043E\u0442\u0430\u044E \u0431\u0435\u0437 \u0441\u0435\u0442\u0438, \u0437\u0430\u043F\u0438\u0441\u0438 \u0436\u0434\u0443\u0442 \u0432 outbox.`
|
|
23658
|
+
)
|
|
23206
23659
|
});
|
|
23207
23660
|
const explicitRegistry = await buildRepoRegistry(repoPaths);
|
|
23208
23661
|
if (repoPaths.length > 0 && explicitRegistry.length < repoPaths.length) {
|
|
@@ -24328,7 +24781,18 @@ async function runDaemon(opts) {
|
|
|
24328
24781
|
wakeRun: async (slug) => {
|
|
24329
24782
|
const run2 = db.activeRunForSlug(slug);
|
|
24330
24783
|
if (!run2) return null;
|
|
24331
|
-
return wakeRun(
|
|
24784
|
+
return wakeRun(
|
|
24785
|
+
{
|
|
24786
|
+
db,
|
|
24787
|
+
isAlive: pidAlive,
|
|
24788
|
+
respawn: deps.respawn,
|
|
24789
|
+
narrate: narrator.narrate,
|
|
24790
|
+
...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
|
|
24791
|
+
...deps.prComment ? { prComment: deps.prComment } : {},
|
|
24792
|
+
...deps.reportComment ? { reportComment: deps.reportComment } : {}
|
|
24793
|
+
},
|
|
24794
|
+
run2.id
|
|
24795
|
+
);
|
|
24332
24796
|
},
|
|
24333
24797
|
update: async (onStep) => {
|
|
24334
24798
|
const pm = detectGlobalPm(process.argv[1]);
|
|
@@ -24478,7 +24942,18 @@ async function runDaemon(opts) {
|
|
|
24478
24942
|
// Побудка прогона по id (TASWM-240): вход для `swarm wake <id>` — тот же respawn с
|
|
24479
24943
|
// полными deps демона (getTask/verifyMode/реестр репо), что и авто-оживление, и по
|
|
24480
24944
|
// зеркалу демона, а не по короткоживущему стору CLI.
|
|
24481
|
-
wake: (runId) => wakeRun(
|
|
24945
|
+
wake: (runId) => wakeRun(
|
|
24946
|
+
{
|
|
24947
|
+
db,
|
|
24948
|
+
isAlive: pidAlive,
|
|
24949
|
+
respawn: deps.respawn,
|
|
24950
|
+
narrate: narrator.narrate,
|
|
24951
|
+
...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
|
|
24952
|
+
...deps.prComment ? { prComment: deps.prComment } : {},
|
|
24953
|
+
...deps.reportComment ? { reportComment: deps.reportComment } : {}
|
|
24954
|
+
},
|
|
24955
|
+
runId
|
|
24956
|
+
),
|
|
24482
24957
|
replay: (qid, chosen) => replayDecision(
|
|
24483
24958
|
{
|
|
24484
24959
|
db,
|
|
@@ -24550,12 +25025,14 @@ async function runDaemon(opts) {
|
|
|
24550
25025
|
tickHealth.startedAt = Date.now();
|
|
24551
25026
|
for (; ; ) {
|
|
24552
25027
|
let tickError;
|
|
25028
|
+
prodMcp?.beginPass?.();
|
|
25029
|
+
deps.stepClock = makeStepClock();
|
|
24553
25030
|
try {
|
|
24554
25031
|
writeHeartbeat(HEARTBEAT_PATH, Date.now());
|
|
24555
25032
|
await tickOnce(deps);
|
|
24556
25033
|
if (restart.phase === "waiting") {
|
|
24557
25034
|
const live2 = liveSessionIds(db, pidAlive);
|
|
24558
|
-
if (live2.length === 0) performRestart([], restart.intent);
|
|
25035
|
+
if (live2.length === 0 && !hasPreparingRuns(db)) performRestart([], restart.intent);
|
|
24559
25036
|
else onRestartStillWaiting?.(live2.length);
|
|
24560
25037
|
}
|
|
24561
25038
|
} catch (e) {
|
|
@@ -24563,6 +25040,8 @@ async function runDaemon(opts) {
|
|
|
24563
25040
|
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);
|
|
24564
25041
|
}
|
|
24565
25042
|
markTickDone(tickHealth, Date.now(), tickError);
|
|
25043
|
+
const slow = slowPassLine(tickHealth.seq, deps.stepClock.finish());
|
|
25044
|
+
if (slow) console.warn(slow);
|
|
24566
25045
|
await new Promise((r) => setTimeout(r, interval));
|
|
24567
25046
|
}
|
|
24568
25047
|
}
|
|
@@ -24613,6 +25092,7 @@ export {
|
|
|
24613
25092
|
CI_WAKE_LIMIT,
|
|
24614
25093
|
CONFLICT_RESOLVE_COOLDOWN_MS,
|
|
24615
25094
|
CONTINUE_NUDGE,
|
|
25095
|
+
CYCLE_CAP_STOP_REASON,
|
|
24616
25096
|
DEAD_HOLDER_SILENCE_MS,
|
|
24617
25097
|
EXITED_CONTINUE_NUDGE,
|
|
24618
25098
|
EXITED_NO_DONE_REASON,
|
|
@@ -24639,6 +25119,7 @@ export {
|
|
|
24639
25119
|
REVIEW_SPAWN_LIMIT,
|
|
24640
25120
|
REVIEW_UNRECOVERABLE_MARK,
|
|
24641
25121
|
SELF_REFERENCE_MARK,
|
|
25122
|
+
SPAWN_INLINE_WAIT_MS,
|
|
24642
25123
|
STALL_FLOOR_MS,
|
|
24643
25124
|
STALL_MS,
|
|
24644
25125
|
STORM_DEAD_LIMIT,
|
|
@@ -24658,14 +25139,18 @@ export {
|
|
|
24658
25139
|
buildResolverPrompt,
|
|
24659
25140
|
buildReviewChecklist,
|
|
24660
25141
|
buildReviewerPrompt,
|
|
25142
|
+
buildSubmittedContinueNudge,
|
|
24661
25143
|
buildVerifierPrompt,
|
|
24662
25144
|
cancelRun,
|
|
24663
25145
|
capacityProvenance,
|
|
24664
25146
|
configureReviewCycleLimit,
|
|
25147
|
+
cycleCapHolder,
|
|
24665
25148
|
effectiveReviewCycleLimit,
|
|
24666
25149
|
hasLiveDescendant,
|
|
25150
|
+
hasPreparingRuns,
|
|
24667
25151
|
isSubrun,
|
|
24668
25152
|
lastChangesSummary,
|
|
25153
|
+
lastVerifySummary,
|
|
24669
25154
|
listDroneProcesses,
|
|
24670
25155
|
livePpids,
|
|
24671
25156
|
makeCheckerAttachmentsReader,
|
|
@@ -24674,6 +25159,8 @@ export {
|
|
|
24674
25159
|
resolveConflict,
|
|
24675
25160
|
respawnRun,
|
|
24676
25161
|
reviewBudgetMark,
|
|
25162
|
+
reviewCycleHumanBody,
|
|
25163
|
+
reviewCyclesExhausted,
|
|
24677
25164
|
reviewDefectClasses,
|
|
24678
25165
|
reviewUnrecoverableMark,
|
|
24679
25166
|
reviveStuck,
|